diff options
| author | Tres Seaver <tseaver@palladion.com> | 2012-04-06 04:14:32 +0000 |
|---|---|---|
| committer | Tres Seaver <tseaver@palladion.com> | 2012-04-06 04:14:32 +0000 |
| commit | cb999924ae583a863ae79b6b5329ba4eeeffda4b (patch) | |
| tree | d8e2d737fd66251ef987622e9461d5afa6266ef6 | |
| parent | ec600824cdad96913bd8e0fa20c778c6c7465565 (diff) | |
| download | zope-interface-cb999924ae583a863ae79b6b5329ba4eeeffda4b.tar.gz | |
Close Py3k coverage gap to absolute minimum.
We *can't* test the classAdvice stuff under Py3k. Need to deprecate it
before the 4.0 release.
| -rw-r--r-- | src/zope/interface/_compat.py | 21 | ||||
| -rw-r--r-- | src/zope/interface/advice.py | 6 | ||||
| -rw-r--r-- | src/zope/interface/declarations.py | 37 | ||||
| -rw-r--r-- | src/zope/interface/registry.py | 8 | ||||
| -rw-r--r-- | src/zope/interface/tests/test_advice.py | 145 | ||||
| -rw-r--r-- | src/zope/interface/tests/test_declarations.py | 25 |
6 files changed, 206 insertions, 36 deletions
diff --git a/src/zope/interface/_compat.py b/src/zope/interface/_compat.py index d5a02e7..e5733a9 100644 --- a/src/zope/interface/_compat.py +++ b/src/zope/interface/_compat.py @@ -26,8 +26,8 @@ if sys.version_info[0] < 3: #pragma NO COVER return unicode(name) raise TypeError("name must be a regular or unicode string") - class_types = (type, types.ClassType) - string_types = (basestring,) + CLASS_TYPES = (type, types.ClassType) + STRING_TYPES = (basestring,) _FUNC_DEFAULTS = 'func_defaults' _FUNC_CODE = 'func_code' @@ -35,6 +35,9 @@ if sys.version_info[0] < 3: #pragma NO COVER _IM_FUNC = 'im_func' _BUILTINS = '__builtin__' + PYTHON3 = False + PYTHON2 = True + else: #pragma NO COVER def _u(s): @@ -47,8 +50,8 @@ else: #pragma NO COVER return name raise TypeError("name must be a string or ASCII-only bytes") - class_types = type - string_types = (str,) + CLASS_TYPES = (type,) + STRING_TYPES = (str,) _FUNC_DEFAULTS = '__defaults__' _FUNC_CODE = '__code__' @@ -56,9 +59,19 @@ else: #pragma NO COVER _IM_FUNC = '__func__' _BUILTINS = 'builtins' + PYTHON3 = True + PYTHON2 = False + def _skip_under_py3k(test_method): #pragma NO COVER if sys.version_info[0] < 3: return test_method def _dummy(*args): pass return _dummy + +def _skip_under_py2(test_method): #pragma NO COVER + if sys.version_info[0] > 2: + return test_method + def _dummy(*args): + pass + return _dummy diff --git a/src/zope/interface/advice.py b/src/zope/interface/advice.py index 7e0257c..788663f 100644 --- a/src/zope/interface/advice.py +++ b/src/zope/interface/advice.py @@ -94,6 +94,10 @@ def addClassAdvisor(callback, depth=2): callbacks *after* the last '__metaclass__' assignment in the containing class will be executed. Be sure that classes using "advising" functions declare any '__metaclass__' *first*, to ensure all callbacks are run.""" + # This entire approach is invalid under Py3K. Don't even try to fix + # the coverage for this block there. :( + if __python3: #pragma NO COVER + raise TypeError('Class advice impossible in Python3') frame = sys._getframe(depth) kind, module, caller_locals, caller_globals = getFrameInfo(frame) @@ -184,7 +188,7 @@ def determineMetaclass(bases, explicit_mc=None): def minimalBases(classes): """Reduce a list of base classes to its ordered minimum equivalent""" - if not __python3: + if not __python3: #pragma NO COVER classes = [c for c in classes if c is not ClassType] candidates = [] diff --git a/src/zope/interface/declarations.py b/src/zope/interface/declarations.py index f0b90f7..8e7a56c 100644 --- a/src/zope/interface/declarations.py +++ b/src/zope/interface/declarations.py @@ -36,6 +36,7 @@ from zope.interface.advice import addClassAdvisor from zope.interface.interface import InterfaceClass from zope.interface.interface import SpecificationBase from zope.interface.interface import Specification +from zope.interface._compat import CLASS_TYPES as DescriptorAwareMetaClasses # Registry of class-implementation specifications BuiltinImplementationSpecifications = {} @@ -249,7 +250,7 @@ def classImplements(cls, *interfaces): b = implementedBy(c) if b not in seen: seen[b] = 1 - bases.append(b) + bases.append(b) spec.__bases__ = tuple(bases) @@ -295,8 +296,8 @@ class implementer: def __call__(self, ob): if isinstance(ob, DescriptorAwareMetaClasses): classImplements(ob, *self.interfaces) - return ob - + return ob + spec = Implements(*self.interfaces) try: ob.__implemented__ = spec @@ -341,9 +342,11 @@ class implementer_only: else: # Assume it's a class: classImplementsOnly(ob, *self.interfaces) - return ob - + return ob + def _implements(name, interfaces, classImplements): + # This entire approach is invalid under Py3K. Don't even try to fix + # the coverage for this block there. :( if sys.version_info[0] >= 3: #pragma NO COVER raise TypeError('Class advice impossible in Python3') frame = sys._getframe(2) @@ -386,7 +389,9 @@ def implements(*interfaces): classImplements(C, I1) after the class has been created. - """ + """ + # This entire approach is invalid under Py3K. Don't even try to fix + # the coverage for this block there. :( _implements("implements", interfaces, classImplements) def implementsOnly(*interfaces): @@ -410,7 +415,9 @@ def implementsOnly(*interfaces): classImplementsOnly(I1) after the class has been created. - """ + """ + # This entire approach is invalid under Py3K. Don't even try to fix + # the coverage for this block there. :( _implements("implementsOnly", interfaces, classImplementsOnly) ############################################################################## @@ -465,12 +472,7 @@ def Provides(*interfaces): Provides.__safe_for_unpickling__ = True -try: - from types import ClassType - DescriptorAwareMetaClasses = ClassType, type -except ImportError: #pragma NO COVERAGE (Python 3) - DescriptorAwareMetaClasses = (type,) - + def directlyProvides(object, *interfaces): """Declare interfaces declared directly for an object @@ -485,7 +487,8 @@ def directlyProvides(object, *interfaces): # It's a meta class (well, at least it it could be an extension class) # Note that we can't get here from Py3k tests: there is no normal # class which isn't descriptor aware. - if not isinstance(object, DescriptorAwareMetaClasses): + if not isinstance(object, + DescriptorAwareMetaClasses): #pragma NO COVER Py3k raise TypeError("Attempt to make an interface declaration on a " "non-descriptor-aware class") @@ -616,6 +619,8 @@ def classProvides(*interfaces): after the class has been created. """ + # This entire approach is invalid under Py3K. Don't even try to fix + # the coverage for this block there. :( if sys.version_info[0] >= 3: #pragma NO COVER raise TypeError('Class advice impossible in Python3') @@ -636,6 +641,8 @@ def classProvides(*interfaces): addClassAdvisor(_classProvides_advice, depth=2) def _classProvides_advice(cls): + # This entire approach is invalid under Py3K. Don't even try to fix + # the coverage for this block there. :( interfaces = cls.__dict__['__provides__'] del cls.__provides__ directlyProvides(cls, *interfaces) @@ -649,7 +656,7 @@ class provider: def __call__(self, ob): directlyProvides(ob, *self.interfaces) - return ob + return ob def moduleProvides(*interfaces): """Declare interfaces provided by a module diff --git a/src/zope/interface/registry.py b/src/zope/interface/registry.py index 0a8ec83..1882e0d 100644 --- a/src/zope/interface/registry.py +++ b/src/zope/interface/registry.py @@ -35,15 +35,15 @@ from zope.interface.declarations import implementer_only from zope.interface.declarations import providedBy from zope.interface.adapter import AdapterRegistry from zope.interface._compat import _u -from zope.interface._compat import class_types -from zope.interface._compat import string_types +from zope.interface._compat import CLASS_TYPES +from zope.interface._compat import STRING_TYPES @implementer(IComponents) class Components(object): def __init__(self, name='', bases=()): - assert isinstance(name, string_types) + assert isinstance(name, STRING_TYPES) self.__name__ = name self._init_registries() self._init_registrations() @@ -415,7 +415,7 @@ def _getAdapterRequired(factory, required): if r is None: r = Interface elif not ISpecification.providedBy(r): - if isinstance(r, class_types): + if isinstance(r, CLASS_TYPES): r = implementedBy(r) else: raise TypeError("Required specification must be a " diff --git a/src/zope/interface/tests/test_advice.py b/src/zope/interface/tests/test_advice.py index 04c403b..195b935 100644 --- a/src/zope/interface/tests/test_advice.py +++ b/src/zope/interface/tests/test_advice.py @@ -28,6 +28,7 @@ Visit the PEAK home page at http://peak.telecommunity.com for more information. import unittest import sys +from zope.interface._compat import _skip_under_py2 from zope.interface._compat import _skip_under_py3k @@ -185,17 +186,139 @@ class AdviceTests(unittest.TestCase): self.assertEqual(type(klass), ClassType) - @_skip_under_py3k - def test_meta_of_class(self): +class Test_isClassAdvisor(unittest.TestCase): + + def _callFUT(self, *args, **kw): + from zope.interface.advice import isClassAdvisor + return isClassAdvisor(*args, **kw) + + def test_w_non_function(self): + self.assertEqual(self._callFUT(self), False) + + def test_w_normal_function(self): + def foo(): + pass + self.assertEqual(self._callFUT(foo), False) + + def test_w_advisor_function(self): + def bar(): + pass + bar.previousMetaclass = object() + self.assertEqual(self._callFUT(bar), True) + + +class Test_determineMetaclass(unittest.TestCase): + + def _callFUT(self, *args, **kw): from zope.interface.advice import determineMetaclass + return determineMetaclass(*args, **kw) + + @_skip_under_py3k + def test_empty(self): + from types import ClassType + self.assertEquals(self._callFUT(()), ClassType) + + def test_empty_w_explicit_metatype(self): + class Meta(type): + pass + self.assertEquals(self._callFUT((), Meta), Meta) + + def test_single(self): + class Meta(type): + pass + self.assertEquals(self._callFUT((Meta,)), type) + @_skip_under_py3k + def test_meta_of_class(self): class Metameta(type): pass class Meta(type): __metaclass__ = Metameta - self.assertEquals(determineMetaclass((Meta, type)), Metameta) + self.assertEquals(self._callFUT((Meta, type)), Metameta) + + @_skip_under_py2 + def test_meta_of_class_py3k(self): + # Work around SyntaxError under Python2. + EXEC = '\n'.join([ + 'class Metameta(type):', + ' pass', + 'class Meta(type, metaclass=Metameta):', + ' pass', + ]) + globs = {} + exec(EXEC, globs) + Meta = globs['Meta'] + Metameta = globs['Metameta'] + + self.assertEquals(self._callFUT((Meta, type)), Metameta) + + @_skip_under_py3k + def test_multiple_in_hierarchy(self): + class Meta_A(type): + pass + class Meta_B(Meta_A): + pass + class A(type): + __metaclass__ = Meta_A + class B(type): + __metaclass__ = Meta_B + self.assertEquals(self._callFUT((A, B,)), Meta_B) + + @_skip_under_py2 + def test_multiple_in_hierarchy_py3k(self): + # Work around SyntaxError under Python2. + EXEC = '\n'.join([ + 'class Meta_A(type):', + ' pass', + 'class Meta_B(Meta_A):', + ' pass', + 'class A(type, metaclass=Meta_A):', + ' pass', + 'class B(type, metaclass=Meta_B):', + ' pass', + ]) + globs = {} + exec(EXEC, globs) + Meta_A = globs['Meta_A'] + Meta_B = globs['Meta_B'] + A = globs['A'] + B = globs['B'] + self.assertEquals(self._callFUT((A, B)), Meta_B) + + @_skip_under_py3k + def test_multiple_not_in_hierarchy(self): + class Meta_A(type): + pass + class Meta_B(type): + pass + class A(type): + __metaclass__ = Meta_A + class B(type): + __metaclass__ = Meta_B + self.assertRaises(TypeError, self._callFUT, (A, B,)) + + @_skip_under_py2 + def test_multiple_not_in_hierarchy_py3k(self): + # Work around SyntaxError under Python2. + EXEC = '\n'.join([ + 'class Meta_A(type):', + ' pass', + 'class Meta_B(type):', + ' pass', + 'class A(type, metaclass=Meta_A):', + ' pass', + 'class B(type, metaclass=Meta_B):', + ' pass', + ]) + globs = {} + exec(EXEC, globs) + Meta_A = globs['Meta_A'] + Meta_B = globs['Meta_B'] + A = globs['A'] + B = globs['B'] + self.assertRaises(TypeError, self._callFUT, (A, B)) class Test_minimalBases(unittest.TestCase): @@ -252,12 +375,10 @@ class Test_minimalBases(unittest.TestCase): def test_suite(): - if sys.version[0] == '2': - return unittest.TestSuite(( - unittest.makeSuite(FrameInfoTest), - unittest.makeSuite(AdviceTests), - unittest.makeSuite(Test_minimalBases), - )) - else: - # Advise metaclasses doesn't work in Python 3 - return unittest.TestSuite([]) + return unittest.TestSuite(( + unittest.makeSuite(FrameInfoTest), + unittest.makeSuite(AdviceTests), + unittest.makeSuite(Test_isClassAdvisor), + unittest.makeSuite(Test_determineMetaclass), + unittest.makeSuite(Test_minimalBases), + )) diff --git a/src/zope/interface/tests/test_declarations.py b/src/zope/interface/tests/test_declarations.py index edc1fea..1976be3 100644 --- a/src/zope/interface/tests/test_declarations.py +++ b/src/zope/interface/tests/test_declarations.py @@ -641,6 +641,31 @@ class Test_implementsOnly(_SilencePy3Deprecations, _Py3ClassAdvice): from zope.interface.declarations import implementsOnly return implementsOnly + def test_simple(self): + from zope.interface.declarations import implementsOnly + from zope.interface._compat import PYTHON3 + from zope.interface.interface import InterfaceClass + IFoo = InterfaceClass("IFoo") + globs = {'implementsOnly': implementsOnly, + 'IFoo': IFoo, + } + locs = {} + CODE = "\n".join([ + 'class Foo(object):' + ' implementsOnly(IFoo)', + ]) + try: + exec(CODE, globs, locs) + except TypeError: + if not PYTHON3: + raise + else: + if PYTHON3: + self.fail("Didn't raise TypeError") + Foo = locs['Foo'] + spec = Foo.__implemented__ + self.assertEqual(list(spec), [IFoo]) + def test_called_once_from_class_w_bases(self): from zope.interface.declarations import implements from zope.interface.declarations import implementsOnly |
