summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTres Seaver <tseaver@palladion.com>2012-04-06 04:14:32 +0000
committerTres Seaver <tseaver@palladion.com>2012-04-06 04:14:32 +0000
commitcb999924ae583a863ae79b6b5329ba4eeeffda4b (patch)
treed8e2d737fd66251ef987622e9461d5afa6266ef6
parentec600824cdad96913bd8e0fa20c778c6c7465565 (diff)
downloadzope-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.py21
-rw-r--r--src/zope/interface/advice.py6
-rw-r--r--src/zope/interface/declarations.py37
-rw-r--r--src/zope/interface/registry.py8
-rw-r--r--src/zope/interface/tests/test_advice.py145
-rw-r--r--src/zope/interface/tests/test_declarations.py25
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