summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2020-01-22 09:36:38 -0600
committerGitHub <noreply@github.com>2020-01-22 09:36:38 -0600
commit540401cee8cbc7a7258d18e1bad40e396db60024 (patch)
treed89929c120d7f7febdbd312ec286982723e4baf8
parente8e5c1257b079a2f96a2e408d8d501992248f37c (diff)
parentb928f8a53cb299f72ff60fc8e622b8e41cabad12 (diff)
downloadzope-interface-540401cee8cbc7a7258d18e1bad40e396db60024.tar.gz
Merge pull request #151 from zopefoundation/no-pure-python-build
Remove checking of PURE_PYTHON at build time
-rw-r--r--.travis.yml10
-rw-r--r--CHANGES.rst117
-rw-r--r--README.rst5
-rw-r--r--appveyor.yml2
-rw-r--r--setup.py61
-rw-r--r--src/zope/interface/_compat.py110
-rw-r--r--src/zope/interface/_zope_interface_coptimizations.c41
-rw-r--r--src/zope/interface/adapter.py22
-rw-r--r--src/zope/interface/declarations.py71
-rw-r--r--src/zope/interface/interface.py45
-rw-r--r--src/zope/interface/tests/__init__.py34
-rw-r--r--src/zope/interface/tests/ifoo.py26
-rw-r--r--src/zope/interface/tests/ifoo_other.py26
-rw-r--r--src/zope/interface/tests/m2.py15
-rw-r--r--src/zope/interface/tests/test_adapter.py38
-rw-r--r--src/zope/interface/tests/test_declarations.py112
-rw-r--r--src/zope/interface/tests/test_interface.py138
-rw-r--r--tox.ini39
18 files changed, 477 insertions, 435 deletions
diff --git a/.travis.yml b/.travis.yml
index 7c596f1..dd4c3e0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -27,6 +27,14 @@ jobs:
- sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest
after_success:
+ - name: PyPy C Extensions
+ env: PURE_PYTHON=0
+ python: pypy
+
+ - name: CPython No C Extension
+ env: PURE_PYTHON=1
+ python: 3.8
+
# manylinux wheel builds
- name: 64-bit manylinux wheels (all Pythons)
services: docker
@@ -80,7 +88,7 @@ install:
script:
- python --version
- - coverage run setup.py -q test
+ - coverage run -m unittest discover -s src
- python setup.py -q bdist_wheel
after_success:
diff --git a/CHANGES.rst b/CHANGES.rst
index f4b5620..0039191 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,21 +1,26 @@
-Changes
-=======
+=========
+ Changes
+=========
-4.7.2 (unreleased)
-------------------
+4.8.0 (unreleased)
+==================
-- Nothing changed yet.
+- Support the ``PURE_PYTHON`` environment variable at runtime instead
+ of just at wheel build time. A value of 0 forces the C extensions to
+ be used (even on PyPy) failing if they aren't present. Any other
+ value forces the Python implementation to be used, ignoring the C
+ extensions.
4.7.1 (2019-11-11)
-------------------
+==================
- Use Python 3 syntax in the documentation. See `issue 119
<https://github.com/zopefoundation/zope.interface/issues/119>`_.
4.7.0 (2019-11-11)
-------------------
+==================
- Drop support for Python 3.4.
@@ -27,7 +32,7 @@ Changes
4.6.0 (2018-10-23)
-------------------
+==================
- Add support for Python 3.7
@@ -37,7 +42,7 @@ Changes
4.5.0 (2018-04-19)
-------------------
+==================
- Drop support for 3.3, avoid accidental dependence breakage via setup.py.
See `PR 110 <https://github.com/zopefoundation/zope.interface/pull/110>`_.
@@ -49,7 +54,7 @@ Changes
4.4.3 (2017-09-22)
-------------------
+==================
- Avoid exceptions when the ``__annotations__`` attribute is added to
interface definitions with Python 3.x type hints. See `issue 98
@@ -60,7 +65,7 @@ Changes
4.4.2 (2017-06-14)
-------------------
+==================
- Fix a regression storing
``zope.component.persistentregistry.PersistentRegistry`` instances.
@@ -71,7 +76,7 @@ Changes
<https://github.com/zopefoundation/zope.interface/issues/93>`_.
4.4.1 (2017-05-13)
-------------------
+==================
- Simplify the caching of utility-registration data. In addition to
simplification, avoids spurious test failures when checking for
@@ -82,7 +87,7 @@ Changes
methods: prevents corruption of lookup caches.
4.4.0 (2017-04-21)
-------------------
+==================
- Avoid a warning from the C compiler.
(https://github.com/zopefoundation/zope.interface/issues/71)
@@ -90,7 +95,7 @@ Changes
- Add support for Python 3.6.
4.3.3 (2016-12-13)
-------------------
+==================
- Correct typos and ReST formatting errors in documentation.
@@ -104,21 +109,21 @@ Changes
4.3.2 (2016-09-05)
-------------------
+==================
- Fix equality testing of ``implementedBy`` objects and proxies.
(https://github.com/zopefoundation/zope.interface/issues/55)
4.3.1 (2016-08-31)
-------------------
+==================
- Support Components subclasses that are not hashable.
(https://github.com/zopefoundation/zope.interface/issues/53)
4.3.0 (2016-08-31)
-------------------
+==================
- Add the ability to sort the objects returned by ``implementedBy``.
This is compatible with the way interface classes sort so they can
@@ -136,7 +141,7 @@ Changes
4.2.0 (2016-06-10)
-------------------
+==================
- Add support for Python 3.5
@@ -144,14 +149,14 @@ Changes
4.1.3 (2015-10-05)
-------------------
+==================
- Fix installation without a C compiler on Python 3.5
(https://github.com/zopefoundation/zope.interface/issues/24).
4.1.2 (2014-12-27)
-------------------
+==================
- Add support for PyPy3.
@@ -162,13 +167,13 @@ Changes
4.1.1 (2014-03-19)
-------------------
+==================
- Add support for Python 3.4.
4.1.0 (2014-02-05)
-------------------
+==================
- Update ``boostrap.py`` to version 2.2.
@@ -177,25 +182,25 @@ Changes
4.0.5 (2013-02-28)
-------------------
+==================
- Fix a bug where a decorated method caused false positive failures on
``verifyClass()``.
4.0.4 (2013-02-21)
-------------------
+==================
- Fix a bug that was revealed by porting zope.traversing. During a loop, the
loop body modified a weakref dict causing a ``RuntimeError`` error.
4.0.3 (2012-12-31)
-------------------
+==================
- Fleshed out PyPI Trove classifiers.
4.0.2 (2012-11-21)
-------------------
+==================
- Add support for Python 3.3.
@@ -205,7 +210,7 @@ Changes
in Python 3.3.
4.0.1 (2012-05-22)
-------------------
+==================
- Drop explicit ``DeprecationWarnings`` for "class advice" APIS (these
APIs are still deprecated under Python 2.x, and still raise an exception
@@ -213,7 +218,7 @@ Changes
Python 2.x).
4.0.0 (2012-05-16)
-------------------
+==================
- Automated build of Sphinx HTML docs and running doctest snippets via tox.
@@ -252,7 +257,7 @@ Changes
Pitrou for the patch.
3.8.0 (2011-09-22)
-------------------
+==================
- New module ``zope.interface.registry``. This is code moved from
``zope.component.registry`` which implements a basic nonperistent component
@@ -281,18 +286,18 @@ Changes
- No longer Python 2.4 compatible (tested under 2.5, 2.6, 2.7, and 3.2).
3.7.0 (2011-08-13)
-------------------
+==================
- Move changes from 3.6.2 - 3.6.5 to a new 3.7.x release line.
3.6.7 (2011-08-20)
-------------------
+==================
- Fix sporadic failures on x86-64 platforms in tests of rich comparisons
of interfaces.
3.6.6 (2011-08-13)
-------------------
+==================
- LP #570942: Now correctly compare interfaces from different modules but
with the same names.
@@ -304,7 +309,7 @@ Changes
- Revert to software as released with 3.6.1 for "stable" 3.6 release branch.
3.6.5 (2011-08-11)
-------------------
+==================
- LP #811792: work around buggy behavior in some subclasses of
``zope.interface.interface.InterfaceClass``, which invoke ``__hash__``
@@ -319,18 +324,18 @@ Changes
- Fix testing deprecation warnings issued when tested under Py3K.
3.6.4 (2011-07-04)
-------------------
+==================
- LP 804951: InterfaceClass instances were unhashable under Python 3.x.
3.6.3 (2011-05-26)
-------------------
+==================
- LP #570942: Now correctly compare interfaces from different modules but
with the same names.
3.6.2 (2011-05-17)
-------------------
+==================
- Moved detailed documentation out-of-line from PyPI page, linking instead to
http://docs.zope.org/zope.interface .
@@ -346,7 +351,7 @@ Changes
running under Python 3.
3.6.1 (2010-05-03)
-------------------
+==================
- A non-ASCII character in the changelog made 3.6.0 uninstallable on
Python 3 systems with another default encoding than UTF-8.
@@ -354,7 +359,7 @@ Changes
- Fix compiler warnings under GCC 4.3.3.
3.6.0 (2010-04-29)
-------------------
+==================
- LP #185974: Clear the cache used by ``Specificaton.get`` inside
``Specification.changed``. Thanks to Jacob Holm for the patch.
@@ -388,21 +393,21 @@ Changes
3.5.4 (2009-12-23)
-------------------
+==================
- Use the standard Python doctest module instead of zope.testing.doctest, which
has been deprecated.
3.5.3 (2009-12-08)
-------------------
+==================
- Fix an edge case: make providedBy() work when a class has '__provides__' in
its __slots__ (see http://thread.gmane.org/gmane.comp.web.zope.devel/22490)
3.5.2 (2009-07-01)
-------------------
+==================
- BaseAdapterRegistry.unregister, unsubscribe: Remove empty portions of
the data structures when something is removed. This avoids leaving
@@ -411,7 +416,7 @@ Changes
3.5.1 (2009-03-18)
-------------------
+==================
- verifyObject: use getattr instead of hasattr to test for object attributes
in order to let exceptions other than AttributeError raised by properties
@@ -427,7 +432,7 @@ Changes
3.5.0 (2008-10-26)
-------------------
+==================
- Fix declaration of _zope_interface_coptimizations, it's not a top level
package.
@@ -447,20 +452,20 @@ Changes
3.4.1 (2007-10-02)
-------------------
+==================
- Fix a setup bug that prevented installation from source on systems
without setuptools.
3.4.0 (2007-07-19)
-------------------
+==================
- Final release for 3.4.0.
3.4.0b3 (2007-05-22)
---------------------
+====================
- When checking whether an object is already registered, use identity
@@ -468,17 +473,17 @@ Changes
3.3.0.1 (2007-01-03)
---------------------
+====================
- Made a reference to OverflowWarning, which disappeared in Python
2.5, conditional.
3.3.0 (2007/01/03)
-------------------
+==================
New Features
-++++++++++++
+------------
- Refactor the adapter-lookup algorithim to make it much simpler and faster.
@@ -497,33 +502,33 @@ New Features
zope.interface.taggedValue).
Bug Fixes
-+++++++++
+---------
- A bug in multi-adapter lookup sometimes caused incorrect adapters to
be returned.
3.2.0.2 (2006-04-15)
---------------------
+====================
- Fix packaging bug: 'package_dir' must be a *relative* path.
3.2.0.1 (2006-04-14)
---------------------
+====================
- Packaging change: suppress inclusion of 'setup.cfg' in 'sdist' builds.
3.2.0 (2006-01-05)
-------------------
+==================
- Corresponds to the verison of the zope.interface package shipped as part of
the Zope 3.2.0 release.
3.1.0 (2005-10-03)
-------------------
+==================
- Corresponds to the verison of the zope.interface package shipped as part of
the Zope 3.1.0 release.
@@ -536,7 +541,7 @@ Bug Fixes
3.0.1 (2005-07-27)
-------------------
+==================
- Corresponds to the verison of the zope.interface package shipped as part of
the Zope X3.0.1 release.
@@ -546,7 +551,7 @@ Bug Fixes
3.0.0 (2004-11-07)
-------------------
+==================
- Corresponds to the verison of the zope.interface package shipped as part of
the Zope X3.0.0 release.
diff --git a/README.rst b/README.rst
index 377c594..966350b 100644
--- a/README.rst
+++ b/README.rst
@@ -1,5 +1,6 @@
-``zope.interface``
-==================
+====================
+ ``zope.interface``
+====================
.. image:: https://img.shields.io/pypi/v/zope.interface.svg
:target: https://pypi.python.org/pypi/zope.interface/
diff --git a/appveyor.yml b/appveyor.yml
index 078991d..25ab921 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -33,7 +33,7 @@ build_script:
- python -W ignore setup.py -q bdist_wheel
test_script:
- - python setup.py -q test
+ - python -m unittest discover -s src
artifacts:
- path: 'dist\*.whl'
diff --git a/setup.py b/setup.py
index ce66157..d6d0e34 100644
--- a/setup.py
+++ b/setup.py
@@ -20,17 +20,16 @@
"""
import os
-import platform
import sys
-from setuptools import setup, Extension
-from setuptools.command.build_ext import build_ext
-from setuptools import find_packages
-
from distutils.errors import CCompilerError
from distutils.errors import DistutilsExecError
from distutils.errors import DistutilsPlatformError
+from setuptools import setup, Extension
+from setuptools.command.build_ext import build_ext
+from setuptools import find_packages
+
class optional_build_ext(build_ext):
"""This class subclasses build_ext and allows
@@ -68,20 +67,20 @@ codeoptimization = [
[os.path.normcase(codeoptimization_c)]
),
]
-py_impl = getattr(platform, 'python_implementation', lambda: None)
-is_pypy = py_impl() == 'PyPy'
+
is_jython = 'java' in sys.platform
-is_pure = 'PURE_PYTHON' in os.environ
-# Jython cannot build the C optimizations, while on PyPy they are
-# anti-optimizations (the C extension compatibility layer is known-slow,
-# and defeats JIT opportunities).
-if is_pypy or is_jython or is_pure:
+# Jython cannot build the C optimizations. Everywhere else,
+# including PyPy, defer the decision to runtime.
+if is_jython:
ext_modules = []
else:
ext_modules = codeoptimization
-tests_require = ['zope.event']
-testing_extras = tests_require + ['nose', 'coverage']
+tests_require = [
+ 'coverage >= 5.0.3',
+ 'zope.event',
+]
+testing_extras = tests_require
def read(*rnames):
@@ -96,7 +95,7 @@ long_description = (
)
setup(name='zope.interface',
- version='4.7.2.dev0',
+ version='4.8.0.dev0',
url='https://github.com/zopefoundation/zope.interface',
license='ZPL 2.1',
description='Interfaces for Python',
@@ -104,22 +103,22 @@ setup(name='zope.interface',
author_email='zope-dev@zope.org',
long_description=long_description,
classifiers=[
- "Development Status :: 5 - Production/Stable",
- "Intended Audience :: Developers",
- "License :: OSI Approved :: Zope Public License",
- "Operating System :: OS Independent",
- "Programming Language :: Python",
- "Programming Language :: Python :: 2",
- "Programming Language :: Python :: 2.7",
- "Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.5",
- "Programming Language :: Python :: 3.6",
- "Programming Language :: Python :: 3.7",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: Implementation :: CPython",
- "Programming Language :: Python :: Implementation :: PyPy",
- "Framework :: Zope :: 3",
- "Topic :: Software Development :: Libraries :: Python Modules",
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: Zope Public License",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 2",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: Implementation :: CPython",
+ "Programming Language :: Python :: Implementation :: PyPy",
+ "Framework :: Zope :: 3",
+ "Topic :: Software Development :: Libraries :: Python Modules",
],
packages=find_packages('src'),
package_dir={'': 'src'},
diff --git a/src/zope/interface/_compat.py b/src/zope/interface/_compat.py
index fb61e13..f8b7887 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,105 @@ 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: # pragma: no cover (only Jython doesn't build extensions)
+ 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: # pragma: no cover (only Jython doesn't build extensions)
+ 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/ifoo.py b/src/zope/interface/tests/ifoo.py
deleted file mode 100644
index 29a7877..0000000
--- a/src/zope/interface/tests/ifoo.py
+++ /dev/null
@@ -1,26 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""IFoo test module
-"""
-from zope.interface import Interface
-
-class IFoo(Interface):
- """
- Dummy interface for unit tests.
- """
-
- def bar(baz):
- """
- Just a note.
- """
diff --git a/src/zope/interface/tests/ifoo_other.py b/src/zope/interface/tests/ifoo_other.py
deleted file mode 100644
index 29a7877..0000000
--- a/src/zope/interface/tests/ifoo_other.py
+++ /dev/null
@@ -1,26 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""IFoo test module
-"""
-from zope.interface import Interface
-
-class IFoo(Interface):
- """
- Dummy interface for unit tests.
- """
-
- def bar(baz):
- """
- Just a note.
- """
diff --git a/src/zope/interface/tests/m2.py b/src/zope/interface/tests/m2.py
deleted file mode 100644
index 511cd9c..0000000
--- a/src/zope/interface/tests/m2.py
+++ /dev/null
@@ -1,15 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Test module that doesn't declare an interface
-"""
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):
diff --git a/tox.ini b/tox.ini
index 941e14c..2c60a50 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,27 +1,33 @@
[tox]
envlist =
- py27,py27-pure,py35,py35-pure,py36,py37,py38,pypy,pypy3,coverage,docs
+ py27,py27-pure,py35,py35-pure,py36,py37,py38,py38-cext,pypy,pypy3,coverage,docs
# NB: if you add new environments, please also add them to depends in
# testenv:coverage so that parallel runs (tox -p auto) will work correctly
[testenv]
+# ``usedevelop`` is required otherwise unittest complains that it
+# discovers a file in src/... but imports it from .tox/.../
+# ``skip_install`` also basically works, but that causes the ``extras``
+# not to be installed (though ``deps`` still are), and doesn't
+# rebuild C extensions.
+usedevelop = true
commands =
- coverage run setup.py -q test -q {posargs}
-deps =
- .[test]
- coverage
-setenv =
- COVERAGE_FILE=.coverage.{envname}
+ coverage run -p -m unittest discover -s src
+extras = test
+
[testenv:py27-pure]
setenv =
PURE_PYTHON=1
- PIP_CACHE_DIR = {envdir}/.cache
[testenv:py35-pure]
setenv =
PURE_PYTHON=1
- PIP_CACHE_DIR = {envdir}/.cache
+
+[testenv:py38-cext]
+# Require the C extension
+setenv =
+ PURE_PYTHON=0
[testenv:py]
commands =
@@ -29,16 +35,15 @@ commands =
{[testenv]commands}
[testenv:coverage]
-setenv =
- COVERAGE_FILE=.coverage
-skip_install = true
+# The -i/--ignore arg is necessary; we get
+# No source for code: '//.tox/pypy/site-packages/zope/interface/__init__.py'
+# without it.
commands =
- coverage erase
coverage combine
- coverage report
- coverage html
- coverage xml
-depends = py27,py27-pure,py35,py35-pure,py36,py37,py38,pypy,pypy3
+ coverage report -i
+ coverage html -i
+ coverage xml -i
+depends = py27,py27-pure,py35,py35-pure,py36,py37,py38,py38-cext,pypy,pypy3
parallel_show_output = true
[testenv:docs]