Commits

Armin Rigo  committed c2d0ff4

Issue #52: Add ffi.verify(..modulename='foo'..) with test and documentation.

  • Participants
  • Parent commits fc21aeb

Comments (0)

Files changed (3)

File cffi/verifier.py

 
 class Verifier(object):
 
-    def __init__(self, ffi, preamble, tmpdir=None, ext_package=None,
-                 tag='', force_generic_engine=False, **kwds):
+    def __init__(self, ffi, preamble, tmpdir=None, modulename=None,
+                 ext_package=None, tag='', force_generic_engine=False, **kwds):
         self.ffi = ffi
         self.preamble = preamble
-        flattened_kwds = ffiplatform.flatten(kwds)
+        if not modulename:
+            flattened_kwds = ffiplatform.flatten(kwds)
         vengine_class = _locate_engine_class(ffi, force_generic_engine)
         self._vengine = vengine_class(self)
         self._vengine.patch_extension_kwds(kwds)
         self.kwds = kwds
         #
-        key = '\x00'.join([sys.version[:3], __version__, preamble,
-                           flattened_kwds] +
-                          ffi._cdefsources)
-        if sys.version_info >= (3,):
-            key = key.encode('utf-8')
-        k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff)
-        k1 = k1.lstrip('0x').rstrip('L')
-        k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff)
-        k2 = k2.lstrip('0').rstrip('L')
-        modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key,
-                                          k1, k2)
+        if modulename:
+            if tag:
+                raise TypeError("can't specify both 'modulename' and 'tag'")
+        else:
+            key = '\x00'.join([sys.version[:3], __version__, preamble,
+                               flattened_kwds] +
+                              ffi._cdefsources)
+            if sys.version_info >= (3,):
+                key = key.encode('utf-8')
+            k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff)
+            k1 = k1.lstrip('0x').rstrip('L')
+            k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff)
+            k2 = k2.lstrip('0').rstrip('L')
+            modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key,
+                                              k1, k2)
         suffix = _get_so_suffix()
         self.tmpdir = tmpdir or _caller_dir_pycache()
         self.sourcefilename = os.path.join(self.tmpdir, modulename + '.c')

File doc/source/index.rst

 The verification step
 ---------------------
 
-``ffi.verify(source, tmpdir=.., ext_package=.., tag='', **kwargs)``:
+``ffi.verify(source, tmpdir=.., ext_package=.., modulename=.., **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
    middle of the extension module's name: ``_cffi_<tag>_<hash>``.
    Useful to give a bit more context, e.g. when debugging.
 
+.. _`warning about modulename`:
+
+.. versionadded:: 0.5
+   The ``modulename`` argument can be used to force a specific module
+   name, overriding the name ``_cffi_<tag>_<hash>``.  Use with care,
+   e.g. if you are passing variable information to ``verify()`` but
+   still want the module name to be always the same (e.g. absolute
+   paths to local files).  In this case, no hash is computed and if
+   the module name already exists it will be reused without further
+   check.  Be sure to have other means of clearing the ``tmpdir``
+   whenever you change your sources.
+
 
 Working with pointers, structures and arrays
 --------------------------------------------
 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=.., ext_package='', tag='', **kwds)``:
+- ``Verifier(ffi, preamble, tmpdir=.., ext_package='', modulename=None,
+  tag='', **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 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 module name is
-  ``_cffi_<tag>_<hash>``.
-  The keyword arguments are passed directly
+  ``_cffi_<tag>_<hash>``, unless overridden with ``modulename``
+  (see the `warning about modulename`_ above).
+  The other keyword arguments are passed directly
   to `distutils when building the Extension object.`__
 
 .. __: http://docs.python.org/distutils/setupscript.html#describing-extension-module

File testing/test_zdistutils.py

 import sys, os, imp, math, shutil
 import py
 from cffi import FFI, FFIError
-from cffi.verifier import Verifier, _locate_engine_class
+from cffi.verifier import Verifier, _locate_engine_class, _get_so_suffix
 from cffi.ffiplatform import maybe_relative_path
 from testing.udir import udir
 
         assert lib.test1tag(143) == 101.0
         assert '_cffi_xxtest_tagxx_' in ffi.verifier.modulefilename
 
+    def test_modulename(self):
+        ffi = FFI()
+        ffi.cdef("/* %s test_modulename */ double test1foo(double x);" % self)
+        csrc = "double test1foo(double x) { return x - 63.0; }"
+        modname = 'xxtest_modulenamexx%d' % (self.generic,)
+        lib = ffi.verify(csrc, force_generic_engine=self.generic,
+                         modulename=modname)
+        assert lib.test1foo(143) == 80.0
+        suffix = _get_so_suffix()
+        fn1 = os.path.join(ffi.verifier.tmpdir, modname + '.c')
+        fn2 = os.path.join(ffi.verifier.tmpdir, modname + suffix)
+        assert ffi.verifier.sourcefilename == fn1
+        assert ffi.verifier.modulefilename == fn2
+
 
 class TestDistUtilsCPython(DistUtilsTest):
     generic = False