[pypy-commit] cffi default: Allow the _cffi_crcchecksum module to be installed in a subpackage,

arigo noreply at buildbot.pypy.org
Fri Sep 7 09:03:13 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r918:0b2474de8de5
Date: 2012-09-07 09:02 +0200
http://bitbucket.org/cffi/cffi/changeset/0b2474de8de5/

Log:	Allow the _cffi_crcchecksum module to be installed in a subpackage,
	with the 'ext_package' argument to setup(). Because it's not
	specified on the Extension instance but as a separate argument (for
	no reason I know), it requires a twist. Document it in detail.

diff --git a/cffi/verifier.py b/cffi/verifier.py
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -5,7 +5,7 @@
 
 class Verifier(object):
 
-    def __init__(self, ffi, preamble, tmpdir=None,
+    def __init__(self, ffi, preamble, tmpdir=None, ext_package=None,
                  force_generic_engine=False, **kwds):
         self.ffi = ffi
         self.preamble = preamble
@@ -27,6 +27,7 @@
         self.tmpdir = tmpdir or _caller_dir_pycache()
         self.sourcefilename = os.path.join(self.tmpdir, modulename + '.c')
         self.modulefilename = os.path.join(self.tmpdir, modulename + suffix)
+        self.ext_package = ext_package
         self._has_source = False
         self._has_module = False
 
@@ -79,8 +80,14 @@
 
     def _locate_module(self):
         if not os.path.isfile(self.modulefilename):
+            if self.ext_package:
+                pkg = __import__(self.ext_package, None, None, ['__doc__'])
+                path = pkg.__path__
+            else:
+                path = None
             try:
-                f, filename, descr = imp.find_module(self.get_module_name())
+                f, filename, descr = imp.find_module(self.get_module_name(),
+                                                     path)
             except ImportError:
                 return
             if f is not None:
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -308,6 +308,18 @@
   setup(...
         ext_modules=[yourmodule.ffi.verifier.get_extension()])
 
+If your ``setup.py`` installs a whole package, you can put the extension
+in it too::
+
+  setup(...
+        ext_package='yourpackage',     # but see below!
+        ext_modules=[yourmodule.ffi.verifier.get_extension()])
+
+However in this case you must also give the same ``ext_package``
+argument to the original call to ``ffi.verify()``::
+
+  ffi.verify("...", ext_package='yourpackage')
+
 Usually that's all you need, but see the `Reference: verifier`_ section
 for more details about the ``verifier`` object.
 
@@ -405,7 +417,7 @@
 The verification step
 ---------------------
 
-``ffi.verify(source, tmpdir=.., **kwargs)``:
+``ffi.verify(source, tmpdir=.., ext_package=.., **kwargs)``:
 verifies that the current ffi signatures
 compile on this machine, and return a dynamic library object.  The
 dynamic library can be used to call functions and access global
@@ -517,7 +529,7 @@
    Unions used to crash ``verify()``.  Fixed.
 
 .. versionadded:: 0.4
-   The ``tmpdir`` argument to ``verify()``: it controls where the C
+   The ``tmpdir`` argument to ``verify()`` controls where the C
    files are created and compiled.  By default it is
    ``directory_containing_the_py_file/__pycache__``, using the
    directory name of the .py file that contains the actual call to
@@ -525,6 +537,10 @@
    consistent with the location of the .pyc files for your library.
    The name ``__pycache__`` itself comes from Python 3.)
 
+   The ``ext_package`` argument controls in which package the
+   compiled extension module should be looked from.  This is
+   only useful after `distributing modules using CFFI`_.
+
 
 Working with pointers, structures and arrays
 --------------------------------------------
@@ -1124,9 +1140,14 @@
 can be instantiated directly.  It is normally instantiated for you by
 ``ffi.verify()``, and the instance is attached as ``ffi.verifier``.
 
-- ``Verifier(ffi, preamble, tmpdir=.., **kwds)``: instantiate the class with an
+- ``Verifier(ffi, preamble, tmpdir=.., ext_package='', **kwds)``:
+  instantiate the class with an
   FFI object and a preamble, which is C text that will be pasted into
-  the generated C source.  The keyword arguments are passed directly
+  the generated C source.  The value of ``tmpdir`` defaults to the
+  directory ``directory_of_the_caller/__pycache__``.  The value of
+  ``ext_package`` is used when looking up an already-compiled, already-
+  installed version of the extension module.
+  The keyword arguments are passed directly
   to `distutils when building the Extension object.`__
 
 .. __: http://docs.python.org/distutils/setupscript.html#describing-extension-module
@@ -1134,13 +1155,13 @@
 ``Verifier`` objects have the following public attributes and methods:
 
 - ``sourcefilename``: name of a C file.  Defaults to
-  ``__pycache__/_cffi_CRCHASH.c``, with the ``CRCHASH`` part computed
+  ``tmpdir/_cffi_CRCHASH.c``, with the ``CRCHASH`` part computed
   from the strings you passed to cdef() and verify() as well as the
   version numbers of Python and CFFI.  Can be changed before calling
   ``write_source()`` if you want to write the source somewhere else.
 
 - ``modulefilename``: name of the ``.so`` file (or ``.pyd`` on Windows).
-  Defaults to ``__pycache__/_cffi_CRCHASH.so``.  Can be changed before
+  Defaults to ``tmpdir/_cffi_CRCHASH.so``.  Can be changed before
   calling ``compile_module()``.
 
 - ``get_module_name()``: extract the module name from ``modulefilename``.
@@ -1154,7 +1175,10 @@
   given by ``modulefilename``.
 
 - ``load_library()``: loads the C module (if necessary, making it
-  first).  Returns an instance of a FFILibrary class that behaves like
+  first; it looks for the existing module based on the checksum of the
+  strings passed to ``ffi.cdef()`` and ``preamble``, either in the
+  directory ``tmpdir`` or in the directory of the package ``ext_package``).
+  Returns an instance of a FFILibrary class that behaves like
   the objects returned by ffi.dlopen(), but that delegates all
   operations to the C module.  This is what is returned by
   ``ffi.verify()``.
diff --git a/testing/test_zdistutils.py b/testing/test_zdistutils.py
--- a/testing/test_zdistutils.py
+++ b/testing/test_zdistutils.py
@@ -1,4 +1,4 @@
-import sys, os, imp, math, random
+import sys, os, imp, math, random, shutil
 import py
 from cffi import FFI, FFIError
 from cffi.verifier import Verifier, _locate_engine_class
@@ -175,6 +175,55 @@
         assert ext.sources == [v.sourcefilename, extra_source]
         assert ext.name == v.get_module_name()
 
+    def test_install_and_reload_module(self, targetpackage=''):
+        if not hasattr(os, 'fork'):
+            py.test.skip("test requires os.fork()")
+
+        if targetpackage:
+            udir.ensure(targetpackage, dir=1).ensure('__init__.py')
+        sys.path.insert(0, str(udir))
+
+        def make_ffi(**verifier_args):
+            ffi = FFI()
+            ffi.cdef("/* %s */" % targetpackage)
+            ffi.cdef("double test1iarm(double x);")
+            csrc = "double test1iarm(double x) { return x * 42.0; }"
+            lib = ffi.verify(csrc, force_generic_engine=self.generic,
+                             ext_package=targetpackage,
+                             **verifier_args)
+            return ffi, lib
+
+        childpid = os.fork()
+        if childpid == 0:
+            # in the child
+            ffi, lib = make_ffi()
+            assert lib.test1iarm(1.5) == 63.0
+            # "install" the module by moving it into udir (/targetpackage)
+            if targetpackage:
+                target = udir.join(targetpackage)
+            else:
+                target = udir
+            shutil.move(ffi.verifier.modulefilename, str(target))
+            os._exit(0)
+        # in the parent
+        _, status = os.waitpid(childpid, 0)
+        if not (os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0):
+            raise AssertionError   # see error above in subprocess
+
+        from cffi import ffiplatform
+        prev_compile = ffiplatform.compile
+        try:
+            ffiplatform.compile = lambda *args: dont_call_me_any_more
+            # won't find it in tmpdir, but should find it correctly
+            # installed in udir
+            ffi, lib = make_ffi()
+            assert lib.test1iarm(0.5) == 21.0
+        finally:
+            ffiplatform.compile = prev_compile
+
+    def test_install_and_reload_module_package(self):
+        self.test_install_and_reload_module(targetpackage='foo_iarmp')
+
 
 class TestDistUtilsCPython(DistUtilsTest):
     generic = False


More information about the pypy-commit mailing list