diff options
| author | Jason Madden <jamadden@gmail.com> | 2020-01-21 07:59:31 -0600 |
|---|---|---|
| committer | Jason Madden <jamadden@gmail.com> | 2020-01-21 09:38:45 -0600 |
| commit | e76d03f51909c2b3f8cdad38eb91fa5f523b5e2e (patch) | |
| tree | ba6babf07a9512520d0c5fa885b983dc3188a4ef /src/zope/interface | |
| parent | efc83ea76655733d56e8b5b4a6ad9971776a8611 (diff) | |
| download | zope-interface-e76d03f51909c2b3f8cdad38eb91fa5f523b5e2e.tar.gz | |
Test PURE_PYTHON at runtime.
Turns out that the C extensions build and work fine with PyPy, but don't use them by default. Let them
be forced, though.
Tests needed some refactoring to account for the various permutations.
Diffstat (limited to 'src/zope/interface')
| -rw-r--r-- | src/zope/interface/_compat.py | 109 | ||||
| -rw-r--r-- | src/zope/interface/_zope_interface_coptimizations.c | 41 | ||||
| -rw-r--r-- | src/zope/interface/adapter.py | 22 | ||||
| -rw-r--r-- | src/zope/interface/declarations.py | 71 | ||||
| -rw-r--r-- | src/zope/interface/interface.py | 45 | ||||
| -rw-r--r-- | src/zope/interface/tests/__init__.py | 34 | ||||
| -rw-r--r-- | src/zope/interface/tests/test_adapter.py | 38 | ||||
| -rw-r--r-- | src/zope/interface/tests/test_declarations.py | 112 | ||||
| -rw-r--r-- | src/zope/interface/tests/test_interface.py | 138 |
9 files changed, 350 insertions, 260 deletions
diff --git a/src/zope/interface/_compat.py b/src/zope/interface/_compat.py index fb61e13..1a834d5 100644 --- a/src/zope/interface/_compat.py +++ b/src/zope/interface/_compat.py @@ -11,8 +11,14 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -"""Basic components support """ +Support functions for dealing with differences in platforms, including Python +versions and implementations. + +This file should have no imports from the rest of zope.interface because it is +used during early bootstrapping. +""" +import os import sys import types @@ -56,3 +62,104 @@ def _skip_under_py3k(test_method): def _skip_under_py2(test_method): import unittest return unittest.skipIf(sys.version_info[0] < 3, "Only on Python 3")(test_method) + + +def _c_optimizations_required(): + """ + Return a true value if the C optimizations are required. + + This uses the ``PURE_PYTHON`` variable as documented in `_use_c_impl`. + """ + pure_env = os.environ.get('PURE_PYTHON') + require_c = pure_env == "0" + return require_c + + +def _c_optimizations_available(): + """ + Return the C optimization module, if available, otherwise + a false value. + + If the optimizations are required but not available, this + raises the ImportError. + + This does not say whether they should be used or not. + """ + catch = () if _c_optimizations_required() else (ImportError,) + try: + from zope.interface import _zope_interface_coptimizations as c_opt + return c_opt + except catch: + return False + + +def _c_optimizations_ignored(): + """ + The opposite of `_c_optimizations_required`. + """ + pure_env = os.environ.get('PURE_PYTHON') + return pure_env is not None and pure_env != "0" + + +def _should_attempt_c_optimizations(): + """ + Return a true value if we should attempt to use the C optimizations. + + This takes into account whether we're on PyPy and the value of the + ``PURE_PYTHON`` environment variable, as defined in `_use_c_impl`. + """ + is_pypy = hasattr(sys, 'pypy_version_info') + + if _c_optimizations_required(): + return True + if is_pypy: + return False + return not _c_optimizations_ignored() + +def _use_c_impl(py_impl, name=None, globs=None): + """ + Decorator. Given an object implemented in Python, with a name like + ``Foo``, import the corresponding C implementation from + ``zope.interface._zope_interface_coptimizations`` with the name + ``Foo`` and use it instead. + + If the ``PURE_PYTHON`` environment variable is set to any value + other than ``"0"``, or we're on PyPy, ignore the C implementation + and return the Python version. If the C implementation cannot be + imported, return the Python version. If ``PURE_PYTHON`` is set to + 0, *require* the C implementation (let the ImportError propagate); + note that PyPy can import the C implementation in this case (and all + tests pass). + + In all cases, the Python version is kept available. in the module + globals with the name ``FooPy`` and the name ``FooFallback`` (both + conventions have been used; the C implementation of some functions + looks for the ``Fallback`` version, as do some of the Sphinx + documents). + + Example:: + + @_use_c_impl + class Foo(object): + ... + """ + name = name or py_impl.__name__ + globs = globs or sys._getframe(1).f_globals + + def find_impl(): + if not _should_attempt_c_optimizations(): + return py_impl + + c_opt = _c_optimizations_available() + if not c_opt: + return py_impl + + __traceback_info__ = c_opt + return getattr(c_opt, name) + + c_impl = find_impl() + # Always make available by the FooPy name and FooFallback + # name (for testing and documentation) + globs[name + 'Py'] = py_impl + globs[name + 'Fallback'] = py_impl + return c_impl diff --git a/src/zope/interface/_zope_interface_coptimizations.c b/src/zope/interface/_zope_interface_coptimizations.c index b1e955e..d025a09 100644 --- a/src/zope/interface/_zope_interface_coptimizations.c +++ b/src/zope/interface/_zope_interface_coptimizations.c @@ -264,6 +264,7 @@ providedBy(PyObject *ignored, PyObject *ob) matter. */ +#ifndef PYPY_VERSION static PyObject * inst_attr(PyObject *self, PyObject *name) { @@ -275,6 +276,32 @@ inst_attr(PyObject *self, PyObject *name) PyErr_SetObject(PyExc_AttributeError, name); return NULL; } +#else +/* + PyPy. + _PyObject_GetDictPtr is a private CPython API and + using it on PyPy doesn't work as expected. We must use the documented + APIs. This has some subtle differences, notably it would use descriptors. + But the tests pass. +*/ +static PyObject* +inst_attr(PyObject* self, PyObject* name) +{ + PyObject* result; + result = PyObject_GetAttr(self, name); + if (result != NULL) { + /* + The CPython version returns a borrowed reference. + We don't have that ability with the standard API, + so we decref here to mimic it. That should be fine if the + attribute was really in the dictionary, but it could be an + issue if it was a new object from a descriptor. + */ + Py_DECREF(result); + } + return result; +} +#endif static PyObject * @@ -286,17 +313,9 @@ Spec_extends(PyObject *self, PyObject *other) if (implied == NULL) return NULL; -#ifdef Py_True if (PyDict_GetItem(implied, other) != NULL) - { - Py_INCREF(Py_True); - return Py_True; - } - Py_INCREF(Py_False); - return Py_False; -#else - return PyInt_FromLong(PyDict_GetItem(implied, other) != NULL); -#endif + Py_RETURN_TRUE; + Py_RETURN_FALSE; } static char Spec_extends__doc__[] = @@ -1336,7 +1355,7 @@ verifying_clear(verify *self) static void verifying_dealloc(verify *self) { - PyObject_GC_UnTrack((PyObject *)self); + PyObject_GC_UnTrack((PyObject *)self); verifying_clear(self); Py_TYPE(self)->tp_free((PyObject*)self); } diff --git a/src/zope/interface/adapter.py b/src/zope/interface/adapter.py index aae3155..9587f9c 100644 --- a/src/zope/interface/adapter.py +++ b/src/zope/interface/adapter.py @@ -23,6 +23,7 @@ from zope.interface.interfaces import IAdapterRegistry from zope.interface._compat import _normalize_name from zope.interface._compat import STRING_TYPES +from zope.interface._compat import _use_c_impl _BLANK = u'' @@ -298,7 +299,9 @@ class BaseAdapterRegistry(object): _not_in_mapping = object() -class LookupBaseFallback(object): + +@_use_c_impl +class LookupBase(object): def __init__(self): self._cache = {} @@ -406,15 +409,9 @@ class LookupBaseFallback(object): return result -LookupBasePy = LookupBaseFallback # BBB - -try: - from zope.interface._zope_interface_coptimizations import LookupBase -except ImportError: - LookupBase = LookupBaseFallback - -class VerifyingBaseFallback(LookupBaseFallback): +@_use_c_impl +class VerifyingBase(LookupBaseFallback): # Mixin for lookups against registries which "chain" upwards, and # whose lookups invalidate their own caches whenever a parent registry # bumps its own '_generation' counter. E.g., used by @@ -442,13 +439,6 @@ class VerifyingBaseFallback(LookupBaseFallback): self._verify() return LookupBaseFallback.subscriptions(self, required, provided) -VerifyingBasePy = VerifyingBaseFallback #BBB - -try: - from zope.interface._zope_interface_coptimizations import VerifyingBase -except ImportError: - VerifyingBase = VerifyingBaseFallback - class AdapterLookupBase(object): diff --git a/src/zope/interface/declarations.py b/src/zope/interface/declarations.py index b80245f..100417f 100644 --- a/src/zope/interface/declarations.py +++ b/src/zope/interface/declarations.py @@ -38,6 +38,7 @@ from zope.interface.interface import SpecificationBase from zope.interface.interface import Specification from zope.interface._compat import CLASS_TYPES as DescriptorAwareMetaClasses from zope.interface._compat import PYTHON3 +from zope.interface._compat import _use_c_impl # Registry of class-implementation specifications BuiltinImplementationSpecifications = {} @@ -57,6 +58,7 @@ class named(object): ob.__component_name__ = self.name return ob + class Declaration(Specification): """Interface declarations""" @@ -91,10 +93,10 @@ class Declaration(Specification): """ return Declaration( *[i for i in self.interfaces() - if not [j for j in other.interfaces() - if i.extends(j, 0)] - ] - ) + if not [j for j in other.interfaces() + if i.extends(j, 0)] + ] + ) def __add__(self, other): """Add two specifications or a specification and an interface @@ -168,7 +170,7 @@ class Implements(Declaration): return -1 n1 = (self.__name__, self.__module__) - n2 = (getattr(other, '__name__', ''), getattr(other, '__module__', '')) + n2 = (getattr(other, '__name__', ''), getattr(other, '__module__', '')) # This spelling works under Python3, which doesn't have cmp(). return (n1 > n2) - (n1 < n2) @@ -211,7 +213,9 @@ def _implements_name(ob): return (getattr(ob, '__module__', '?') or '?') + \ '.' + (getattr(ob, '__name__', '?') or '?') -def implementedByFallback(cls): + +@_use_c_impl +def implementedBy(cls): """Return the interfaces implemented for a class' instances The value returned is an `~zope.interface.interfaces.IDeclaration`. @@ -296,7 +300,6 @@ def implementedByFallback(cls): return spec -implementedBy = implementedByFallback def classImplementsOnly(cls, *interfaces): """Declare the only interfaces implemented by instances of a class @@ -312,6 +315,7 @@ def classImplementsOnly(cls, *interfaces): spec.inherit = None classImplements(cls, *interfaces) + def classImplements(cls, *interfaces): """Declare additional interfaces implemented for instances of a class @@ -574,7 +578,7 @@ def directlyProvides(object, *interfaces): replace interfaces previously declared for the object. """ cls = getattr(object, '__class__', None) - if cls is not None and getattr(cls, '__class__', None) is cls: + if cls is not None and getattr(cls, '__class__', None) is cls: # 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. @@ -611,6 +615,7 @@ def alsoProvides(object, *interfaces): """ directlyProvides(object, directlyProvidedBy(object), *interfaces) + def noLongerProvides(object, interface): """ Removes a directly provided interface from an object. """ @@ -618,7 +623,9 @@ def noLongerProvides(object, interface): if interface.providedBy(object): raise ValueError("Can only remove directly provided interfaces.") -class ClassProvidesBaseFallback(object): + +@_use_c_impl +class ClassProvidesBase(object): def __get__(self, inst, cls): if cls is self._cls: @@ -633,17 +640,6 @@ class ClassProvidesBaseFallback(object): raise AttributeError('__provides__') -ClassProvidesBasePy = ClassProvidesBaseFallback # BBB -ClassProvidesBase = ClassProvidesBaseFallback - -# Try to get C base: -try: - import zope.interface._zope_interface_coptimizations -except ImportError: - pass -else: - from zope.interface._zope_interface_coptimizations import ClassProvidesBase - class ClassProvides(Declaration, ClassProvidesBase): """Special descriptor for class ``__provides__`` @@ -664,6 +660,7 @@ class ClassProvides(Declaration, ClassProvidesBase): # Copy base-class method for speed __get__ = ClassProvidesBase.__get__ + def directlyProvidedBy(object): """Return the interfaces directly provided by the given object @@ -676,12 +673,13 @@ def directlyProvidedBy(object): # optimization. If so, it's like having only one base, that we # lop off to exclude class-supplied declarations: isinstance(provides, Implements) - ): + ): return _empty # Strip off the class part of the spec: return Declaration(provides.__bases__[:-1]) + def classProvides(*interfaces): """Declare interfaces provided directly by a class @@ -740,6 +738,7 @@ def _classProvides_advice(cls): directlyProvides(cls, *interfaces) return cls + class provider(object): """Class decorator version of classProvides""" @@ -750,6 +749,7 @@ class provider(object): directlyProvides(ob, *self.interfaces) return ob + def moduleProvides(*interfaces): """Declare interfaces provided by a module @@ -788,6 +788,7 @@ def moduleProvides(*interfaces): locals["__provides__"] = Provides(ModuleType, *_normalizeargs(interfaces)) + ############################################################################## # # Declaration querying support @@ -801,7 +802,8 @@ def ObjectSpecification(direct, cls): """ return Provides(cls, direct) # pragma: no cover fossil -def getObjectSpecificationFallback(ob): +@_use_c_impl +def getObjectSpecification(ob): provides = getattr(ob, '__provides__', None) if provides is not None: @@ -816,9 +818,9 @@ def getObjectSpecificationFallback(ob): return implementedBy(cls) -getObjectSpecification = getObjectSpecificationFallback -def providedByFallback(ob): +@_use_c_impl +def providedBy(ob): # Here we have either a special object, an old-style declaration # or a descriptor @@ -836,7 +838,6 @@ def providedByFallback(ob): # descriptors. We'll make sure we got one by trying to get # the only attribute, which all specs have. r.extends - except AttributeError: # The object's class doesn't understand descriptors. @@ -867,9 +868,10 @@ def providedByFallback(ob): return implementedBy(ob.__class__) return r -providedBy = providedByFallback -class ObjectSpecificationDescriptorFallback(object): + +@_use_c_impl +class ObjectSpecificationDescriptor(object): """Implement the `__providedBy__` attribute The `__providedBy__` attribute computes the interfaces peovided by @@ -888,11 +890,10 @@ class ObjectSpecificationDescriptorFallback(object): return implementedBy(cls) -ObjectSpecificationDescriptor = ObjectSpecificationDescriptorFallback ############################################################################## -def _normalizeargs(sequence, output = None): +def _normalizeargs(sequence, output=None): """Normalize declaration arguments Normalization arguments might contain Declarions, tuples, or single @@ -914,16 +915,4 @@ def _normalizeargs(sequence, output = None): _empty = Declaration() -try: - import zope.interface._zope_interface_coptimizations -except ImportError: - pass -else: - from zope.interface._zope_interface_coptimizations import implementedBy - from zope.interface._zope_interface_coptimizations import providedBy - from zope.interface._zope_interface_coptimizations import ( - getObjectSpecification) - from zope.interface._zope_interface_coptimizations import ( - ObjectSpecificationDescriptor) - objectSpecificationDescriptor = ObjectSpecificationDescriptor() diff --git a/src/zope/interface/interface.py b/src/zope/interface/interface.py index 9ecdca6..05f5594 100644 --- a/src/zope/interface/interface.py +++ b/src/zope/interface/interface.py @@ -13,7 +13,6 @@ ############################################################################## """Interface object implementation """ -from __future__ import generators import sys from types import MethodType @@ -21,6 +20,7 @@ from types import FunctionType import warnings import weakref +from zope.interface._compat import _use_c_impl from zope.interface.exceptions import Invalid from zope.interface.ro import ro @@ -30,6 +30,9 @@ CO_VARKEYWORDS = 8 TAGGED_DATA = '__interface_tagged_values__' _decorator_non_return = object() +_marker = object() + + def invariant(call): f_locals = sys._getframe(1).f_locals @@ -62,8 +65,8 @@ class Element(object): __doc__ = __name__ __name__ = None - self.__name__=__name__ - self.__doc__=__doc__ + self.__name__ = __name__ + self.__doc__ = __doc__ self.__tagged_values = {} def getName(self): @@ -90,7 +93,9 @@ class Element(object): """ Associates 'value' with 'key'. """ self.__tagged_values[tag] = value -class SpecificationBasePy(object): + +@_use_c_impl +class SpecificationBase(object): def providedBy(self, ob): """Is the interface implemented by an object @@ -113,14 +118,9 @@ class SpecificationBasePy(object): __call__ = isOrExtends -SpecificationBase = SpecificationBasePy -try: - from zope.interface._zope_interface_coptimizations import SpecificationBase -except ImportError: - pass -_marker = object() -class InterfaceBasePy(object): +@_use_c_impl +class InterfaceBase(object): """Base class that wants to be replaced with a C base :) """ @@ -154,18 +154,7 @@ class InterfaceBasePy(object): return adapter -InterfaceBase = InterfaceBasePy -try: - from zope.interface._zope_interface_coptimizations import InterfaceBase -except ImportError: - pass - - -adapter_hooks = [] -try: - from zope.interface._zope_interface_coptimizations import adapter_hooks -except ImportError: - pass +adapter_hooks = _use_c_impl([], 'adapter_hooks') class Specification(SpecificationBase): @@ -538,8 +527,8 @@ class InterfaceClass(Element, InterfaceBase, Specification): if other is None: return -1 - n1 = (getattr(self, '__name__', ''), getattr(self, '__module__', '')) - n2 = (getattr(other, '__name__', ''), getattr(other, '__module__', '')) + n1 = (getattr(self, '__name__', ''), getattr(self, '__module__', '')) + n2 = (getattr(other, '__name__', ''), getattr(other, '__module__', '')) # This spelling works under Python3, which doesn't have cmp(). return (n1 > n2) - (n1 < n2) @@ -576,7 +565,8 @@ class InterfaceClass(Element, InterfaceBase, Specification): return c >= 0 -Interface = InterfaceClass("Interface", __module__ = 'zope.interface') +Interface = InterfaceClass("Interface", __module__='zope.interface') + class Attribute(Element): """Attribute descriptions @@ -638,6 +628,7 @@ class Method(Attribute): return "(%s)" % ", ".join(sig) + def fromFunction(func, interface=None, imlevel=0, name=None): name = name or func.__name__ method = Method(name, func.__doc__) @@ -650,7 +641,7 @@ def fromFunction(func, interface=None, imlevel=0, name=None): # Number of required arguments nr = na-len(defaults) if nr < 0: - defaults=defaults[-nr:] + defaults = defaults[-nr:] nr = 0 # Determine the optional arguments. diff --git a/src/zope/interface/tests/__init__.py b/src/zope/interface/tests/__init__.py index 15259c1..c37dffc 100644 --- a/src/zope/interface/tests/__init__.py +++ b/src/zope/interface/tests/__init__.py @@ -1 +1,33 @@ -# Make this directory a package. +from zope.interface._compat import _should_attempt_c_optimizations + + +class OptimizationTestMixin(object): + """ + Helper for testing that C optimizations are used + when appropriate. + """ + + def _getTargetClass(self): + """ + Define this to return the implementation in use, + without the 'Py' or 'Fallback' suffix. + """ + raise NotImplementedError + + def _getFallbackClass(self): + """ + Define this to return the fallback Python implementation. + """ + # Is there an algorithmic way to do this? The C + # objects all come from the same module so I don't see how we can + # get the Python object from that. + raise NotImplementedError + + def test_optimizations(self): + used = self._getTargetClass() + fallback = self._getFallbackClass() + + if _should_attempt_c_optimizations(): + self.assertIsNot(used, fallback) + else: + self.assertIs(used, fallback) diff --git a/src/zope/interface/tests/test_adapter.py b/src/zope/interface/tests/test_adapter.py index 41c618c..25cb4bb 100644 --- a/src/zope/interface/tests/test_adapter.py +++ b/src/zope/interface/tests/test_adapter.py @@ -15,6 +15,8 @@ """ import unittest +from zope.interface.tests import OptimizationTestMixin + def _makeInterfaces(): from zope.interface import Interface @@ -234,10 +236,10 @@ class BaseAdapterRegistryTests(unittest.TestCase): registry.subscribe([IB1], None, orig) registry.unsubscribe([IB1], None, nomatch) #doesn't raise self.assertEqual(len(registry._subscribers), 2) - + def _instance_method_notify_target(self): self.fail("Example method, not intended to be called.") - + def test_unsubscribe_instance_method(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() registry = self._makeOne() @@ -249,10 +251,12 @@ class BaseAdapterRegistryTests(unittest.TestCase): class LookupBaseFallbackTests(unittest.TestCase): - def _getTargetClass(self): + def _getFallbackClass(self): from zope.interface.adapter import LookupBaseFallback return LookupBaseFallback + _getTargetClass = _getFallbackClass + def _makeOne(self, uc_lookup=None, uc_lookupAll=None, uc_subscriptions=None): if uc_lookup is None: @@ -559,28 +563,22 @@ class LookupBaseFallbackTests(unittest.TestCase): self.assertEqual(_called_with, [(('A',), 'B')]) -class LookupBaseTests(LookupBaseFallbackTests): +class LookupBaseTests(LookupBaseFallbackTests, + OptimizationTestMixin): def _getTargetClass(self): from zope.interface.adapter import LookupBase return LookupBase - def test_optimizations(self): - from zope.interface.adapter import LookupBaseFallback - try: - import zope.interface._zope_interface_coptimizations - except ImportError: - self.assertIs(self._getTargetClass(), LookupBaseFallback) - else: - self.assertIsNot(self._getTargetClass(), LookupBaseFallback) - class VerifyingBaseFallbackTests(unittest.TestCase): - def _getTargetClass(self): + def _getFallbackClass(self): from zope.interface.adapter import VerifyingBaseFallback return VerifyingBaseFallback + _getTargetClass = _getFallbackClass + def _makeOne(self, registry, uc_lookup=None, uc_lookupAll=None, uc_subscriptions=None): if uc_lookup is None: @@ -730,21 +728,13 @@ class VerifyingBaseFallbackTests(unittest.TestCase): self.assertEqual(found, tuple(_results_2)) -class VerifyingBaseTests(VerifyingBaseFallbackTests): +class VerifyingBaseTests(VerifyingBaseFallbackTests, + OptimizationTestMixin): def _getTargetClass(self): from zope.interface.adapter import VerifyingBase return VerifyingBase - def test_optimizations(self): - from zope.interface.adapter import VerifyingBaseFallback - try: - import zope.interface._zope_interface_coptimizations - except ImportError: - self.assertIs(self._getTargetClass(), VerifyingBaseFallback) - else: - self.assertIsNot(self._getTargetClass(), VerifyingBaseFallback) - class AdapterLookupBaseTests(unittest.TestCase): diff --git a/src/zope/interface/tests/test_declarations.py b/src/zope/interface/tests/test_declarations.py index 43f95c8..a1ebef8 100644 --- a/src/zope/interface/tests/test_declarations.py +++ b/src/zope/interface/tests/test_declarations.py @@ -16,6 +16,7 @@ import unittest from zope.interface._compat import _skip_under_py3k +from zope.interface.tests import OptimizationTestMixin class _Py3ClassAdvice(object): @@ -326,9 +327,14 @@ class TestImplements(unittest.TestCase): class Test_implementedByFallback(unittest.TestCase): - def _callFUT(self, *args, **kw): + def _getTargetClass(self): from zope.interface.declarations import implementedByFallback - return implementedByFallback(*args, **kw) + return implementedByFallback + + _getFallbackClass = _getTargetClass + + def _callFUT(self, *args, **kw): + return self._getTargetClass()(*args, **kw) def test_dictless_wo_existing_Implements_wo_registrations(self): class Foo(object): @@ -471,22 +477,13 @@ class Test_implementedByFallback(unittest.TestCase): self.assertTrue(self._callFUT(Foo) is impl) -class Test_implementedBy(Test_implementedByFallback): +class Test_implementedBy(Test_implementedByFallback, + OptimizationTestMixin): # Repeat tests for C optimizations - def _callFUT(self, *args, **kw): - from zope.interface.declarations import implementedBy - return implementedBy(*args, **kw) - - def test_optimizations(self): - from zope.interface.declarations import implementedByFallback + def _getTargetClass(self): from zope.interface.declarations import implementedBy - try: - import zope.interface._zope_interface_coptimizations - except ImportError: - self.assertIs(implementedBy, implementedByFallback) - else: - self.assertIsNot(implementedBy, implementedByFallback) + return implementedBy class Test_classImplementsOnly(unittest.TestCase): @@ -1124,21 +1121,17 @@ class ClassProvidesBaseFallbackTests(unittest.TestCase): self.assertRaises(AttributeError, getattr, bar, '__provides__') -class ClassProvidesBaseTests(ClassProvidesBaseFallbackTests): +class ClassProvidesBaseTests(OptimizationTestMixin, + ClassProvidesBaseFallbackTests): # Repeat tests for C optimizations def _getTargetClass(self): from zope.interface.declarations import ClassProvidesBase return ClassProvidesBase - def test_optimizations(self): + def _getFallbackClass(self): from zope.interface.declarations import ClassProvidesBaseFallback - try: - import zope.interface._zope_interface_coptimizations - except ImportError: - self.assertIs(self._getTargetClass(), ClassProvidesBaseFallback) - else: - self.assertIsNot(self._getTargetClass(), ClassProvidesBaseFallback) + return ClassProvidesBaseFallback class ClassProvidesTests(unittest.TestCase): @@ -1366,9 +1359,14 @@ class Test_moduleProvides(unittest.TestCase): class Test_getObjectSpecificationFallback(unittest.TestCase): - def _callFUT(self, *args, **kw): + def _getFallbackClass(self): from zope.interface.declarations import getObjectSpecificationFallback - return getObjectSpecificationFallback(*args, **kw) + return getObjectSpecificationFallback + + _getTargetClass = _getFallbackClass + + def _callFUT(self, *args, **kw): + return self._getTargetClass()(*args, **kw) def test_wo_existing_provides_classless(self): the_dict = {} @@ -1434,31 +1432,25 @@ class Test_getObjectSpecificationFallback(unittest.TestCase): self.assertEqual(list(spec), []) -class Test_getObjectSpecification(Test_getObjectSpecificationFallback): +class Test_getObjectSpecification(Test_getObjectSpecificationFallback, + OptimizationTestMixin): # Repeat tests for C optimizations - def _callFUT(self, *args, **kw): - from zope.interface.declarations import getObjectSpecification - return getObjectSpecification(*args, **kw) - - def test_optimizations(self): - from zope.interface.declarations import getObjectSpecificationFallback + def _getTargetClass(self): from zope.interface.declarations import getObjectSpecification - try: - import zope.interface._zope_interface_coptimizations - except ImportError: - self.assertIs(getObjectSpecification, - getObjectSpecificationFallback) - else: - self.assertIsNot(getObjectSpecification, - getObjectSpecificationFallback) + return getObjectSpecification class Test_providedByFallback(unittest.TestCase): - def _callFUT(self, *args, **kw): + def _getFallbackClass(self): from zope.interface.declarations import providedByFallback - return providedByFallback(*args, **kw) + return providedByFallback + + _getTargetClass = _getFallbackClass + + def _callFUT(self, *args, **kw): + return self._getTargetClass()(*args, **kw) def test_wo_providedBy_on_class_wo_implements(self): class Foo(object): @@ -1531,31 +1523,24 @@ class Test_providedByFallback(unittest.TestCase): self.assertEqual(list(spec), [IFoo]) -class Test_providedBy(Test_providedByFallback): +class Test_providedBy(Test_providedByFallback, + OptimizationTestMixin): # Repeat tests for C optimizations - def _callFUT(self, *args, **kw): - from zope.interface.declarations import providedBy - return providedBy(*args, **kw) - - def test_optimizations(self): - from zope.interface.declarations import providedByFallback + def _getTargetClass(self): from zope.interface.declarations import providedBy - try: - import zope.interface._zope_interface_coptimizations - except ImportError: - self.assertIs(providedBy, providedByFallback) - else: - self.assertIsNot(providedBy, providedByFallback) + return providedBy class ObjectSpecificationDescriptorFallbackTests(unittest.TestCase): - def _getTargetClass(self): + def _getFallbackClass(self): from zope.interface.declarations \ import ObjectSpecificationDescriptorFallback return ObjectSpecificationDescriptorFallback + _getTargetClass = _getFallbackClass + def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) @@ -1602,25 +1587,14 @@ class ObjectSpecificationDescriptorFallbackTests(unittest.TestCase): class ObjectSpecificationDescriptorTests( - ObjectSpecificationDescriptorFallbackTests): + ObjectSpecificationDescriptorFallbackTests, + OptimizationTestMixin): # Repeat tests for C optimizations def _getTargetClass(self): from zope.interface.declarations import ObjectSpecificationDescriptor return ObjectSpecificationDescriptor - def test_optimizations(self): - from zope.interface.declarations import ( - ObjectSpecificationDescriptorFallback) - try: - import zope.interface._zope_interface_coptimizations - except ImportError: - self.assertIs(self._getTargetClass(), - ObjectSpecificationDescriptorFallback) - else: - self.assertIsNot(self._getTargetClass(), - ObjectSpecificationDescriptorFallback) - # Test _normalizeargs through its callers. diff --git a/src/zope/interface/tests/test_interface.py b/src/zope/interface/tests/test_interface.py index 387897b..69b1d47 100644 --- a/src/zope/interface/tests/test_interface.py +++ b/src/zope/interface/tests/test_interface.py @@ -17,6 +17,7 @@ import unittest from zope.interface._compat import _skip_under_py3k +from zope.interface.tests import OptimizationTestMixin _marker = object() @@ -136,12 +137,14 @@ class ElementTests(unittest.TestCase): self.assertEqual(element.queryTaggedValue('foo'), 'bar') -class SpecificationBasePyTests(unittest.TestCase): - - def _getTargetClass(self): +class GenericSpecificationBaseTests(unittest.TestCase): + # Tests that work with both implementations + def _getFallbackClass(self): from zope.interface.interface import SpecificationBasePy return SpecificationBasePy + _getTargetClass = _getFallbackClass + def _makeOne(self): return self._getTargetClass()() @@ -154,16 +157,6 @@ class SpecificationBasePyTests(unittest.TestCase): with _Monkey(interface, providedBy=_providedBy): self.assertFalse(sb.providedBy(object())) - def test_providedBy_hit(self): - from zope.interface import interface - sb = self._makeOne() - class _Decl(object): - _implied = {sb: {},} - def _providedBy(obj): - return _Decl() - with _Monkey(interface, providedBy=_providedBy): - self.assertTrue(sb.providedBy(object())) - def test_implementedBy_miss(self): from zope.interface import interface from zope.interface.declarations import _empty @@ -173,61 +166,70 @@ class SpecificationBasePyTests(unittest.TestCase): with _Monkey(interface, implementedBy=_implementedBy): self.assertFalse(sb.implementedBy(object())) - def test_implementedBy_hit(self): - from zope.interface import interface - sb = self._makeOne() - class _Decl(object): - _implied = {sb: {},} - def _implementedBy(obj): - return _Decl() - with _Monkey(interface, implementedBy=_implementedBy): - self.assertTrue(sb.implementedBy(object())) - def test_isOrExtends_miss(self): +class SpecificationBaseTests(GenericSpecificationBaseTests, + OptimizationTestMixin): + # Tests that use the C implementation + + def _getTargetClass(self): + from zope.interface.interface import SpecificationBase + return SpecificationBase + +class SpecificationBasePyTests(GenericSpecificationBaseTests): + # Tests that only work with the Python implementation + + def test___call___miss(self): sb = self._makeOne() sb._implied = {} # not defined by SpecificationBasePy self.assertFalse(sb.isOrExtends(object())) - def test_isOrExtends_hit(self): + def test___call___hit(self): sb = self._makeOne() testing = object() sb._implied = {testing: {}} # not defined by SpecificationBasePy self.assertTrue(sb(testing)) - def test___call___miss(self): + def test_isOrExtends_miss(self): sb = self._makeOne() sb._implied = {} # not defined by SpecificationBasePy self.assertFalse(sb.isOrExtends(object())) - def test___call___hit(self): + def test_isOrExtends_hit(self): sb = self._makeOne() testing = object() sb._implied = {testing: {}} # not defined by SpecificationBasePy self.assertTrue(sb(testing)) + def test_implementedBy_hit(self): + from zope.interface import interface + sb = self._makeOne() + class _Decl(object): + _implied = {sb: {},} + def _implementedBy(obj): + return _Decl() + with _Monkey(interface, implementedBy=_implementedBy): + self.assertTrue(sb.implementedBy(object())) -class SpecificationBaseTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.interface import SpecificationBase - return SpecificationBase - - def test_optimizations(self): - from zope.interface.interface import SpecificationBasePy - try: - import zope.interface._zope_interface_coptimizations - except ImportError: - self.assertIs(self._getTargetClass(), SpecificationBasePy) - else: - self.assertIsNot(self._getTargetClass(), SpecificationBasePy) + def test_providedBy_hit(self): + from zope.interface import interface + sb = self._makeOne() + class _Decl(object): + _implied = {sb: {},} + def _providedBy(obj): + return _Decl() + with _Monkey(interface, providedBy=_providedBy): + self.assertTrue(sb.providedBy(object())) -class InterfaceBasePyTests(unittest.TestCase): +class GenericInterfaceBaseTests(unittest.TestCase): + # Tests for both C and Python implementation - def _getTargetClass(self): + def _getFallbackClass(self): from zope.interface.interface import InterfaceBasePy return InterfaceBasePy + _getTargetClass = _getFallbackClass + def _makeOne(self, object_should_provide): class IB(self._getTargetClass()): def _call_conform(self, conform): @@ -244,29 +246,42 @@ class InterfaceBasePyTests(unittest.TestCase): return conformed self.assertTrue(ib(_Adapted()) is conformed) - def test___call___w___conform___miss_ob_provides(self): - ib = self._makeOne(True) - class _Adapted(object): - def __conform__(self, iface): - return None - adapted = _Adapted() - self.assertTrue(ib(adapted) is adapted) - def test___call___wo___conform___ob_no_provides_w_alternate(self): ib = self._makeOne(False) adapted = object() alternate = object() - self.assertTrue(ib(adapted, alternate) is alternate) + self.assertIs(ib(adapted, alternate), alternate) def test___call___w___conform___ob_no_provides_wo_alternate(self): ib = self._makeOne(False) adapted = object() self.assertRaises(TypeError, ib, adapted) + + +class InterfaceBaseTests(GenericInterfaceBaseTests, + OptimizationTestMixin): + # Tests that work with the C implementation + def _getTargetClass(self): + from zope.interface.interface import InterfaceBase + return InterfaceBase + + +class InterfaceBasePyTests(GenericInterfaceBaseTests): + # Tests that only work with the Python implementation + + def test___call___w___conform___miss_ob_provides(self): + ib = self._makeOne(True) + class _Adapted(object): + def __conform__(self, iface): + return None + adapted = _Adapted() + self.assertIs(ib(adapted), adapted) + def test___adapt___ob_provides(self): ib = self._makeOne(True) adapted = object() - self.assertTrue(ib.__adapt__(adapted) is adapted) + self.assertIs(ib.__adapt__(adapted), adapted) def test___adapt___ob_no_provides_uses_hooks(self): from zope.interface import interface @@ -279,26 +294,9 @@ class InterfaceBasePyTests(unittest.TestCase): def _hook_hit(iface, obj): return obj with _Monkey(interface, adapter_hooks=[_hook_miss, _hook_hit]): - self.assertTrue(ib.__adapt__(adapted) is adapted) + self.assertIs(ib.__adapt__(adapted), adapted) self.assertEqual(_missed, [(ib, adapted)]) - -class InterfaceBaseTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.interface import InterfaceBase - return InterfaceBase - - def test_optimizations(self): - from zope.interface.interface import InterfaceBasePy - try: - import zope.interface._zope_interface_coptimizations - except ImportError: - self.assertIs(self._getTargetClass(), InterfaceBasePy) - else: - self.assertIsNot(self._getTargetClass(), InterfaceBasePy) - - class SpecificationTests(unittest.TestCase): def _getTargetClass(self): |
