summaryrefslogtreecommitdiff
path: root/src/zope/interface/_compat.py
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2020-01-21 07:59:31 -0600
committerJason Madden <jamadden@gmail.com>2020-01-21 09:38:45 -0600
commite76d03f51909c2b3f8cdad38eb91fa5f523b5e2e (patch)
treeba6babf07a9512520d0c5fa885b983dc3188a4ef /src/zope/interface/_compat.py
parentefc83ea76655733d56e8b5b4a6ad9971776a8611 (diff)
downloadzope-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/_compat.py')
-rw-r--r--src/zope/interface/_compat.py109
1 files changed, 108 insertions, 1 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