[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