[pypy-commit] cffi default: Test and various fixes for the location of the produced files, notably

arigo noreply at buildbot.pypy.org
Wed May 27 11:36:31 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r2109:01e7a78ca0ff
Date: 2015-05-27 11:37 +0200
http://bitbucket.org/cffi/cffi/changeset/01e7a78ca0ff/

Log:	Test and various fixes for the location of the produced files,
	notably during the calls to ffi.compile()

diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -1159,9 +1159,14 @@
 def make_py_source(ffi, module_name, target_py_file):
     return _make_c_or_py_source(ffi, module_name, None, target_py_file)
 
-def _get_extension(module_name, c_file, kwds):
-    source_name = ffiplatform.maybe_relative_path(c_file)
-    return ffiplatform.get_extension(source_name, module_name, **kwds)
+def _modname_to_file(outputdir, modname, extension):
+    parts = modname.split('.')
+    try:
+        os.makedirs(os.path.join(outputdir, *parts[:-1]))
+    except OSError:
+        pass
+    parts[-1] += extension
+    return os.path.join(outputdir, *parts), parts
 
 def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
               c_file=None, source_extension='.c', **kwds):
@@ -1171,17 +1176,26 @@
         ffi._apply_windows_unicode(kwds)
     if preamble is not None:
         if c_file is None:
-            c_file = os.path.join(tmpdir, module_name + source_extension)
-        ext = _get_extension(module_name, c_file, kwds)
+            c_file, parts = _modname_to_file(tmpdir, module_name,
+                                             source_extension)
+            ext_c_file = os.path.join(*parts)
+        else:
+            ext_c_file = c_file
+        ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds)
         updated = make_c_source(ffi, module_name, preamble, c_file)
         if call_c_compiler:
-            outputfilename = ffiplatform.compile(tmpdir, ext)
+            cwd = os.getcwd()
+            try:
+                os.chdir(tmpdir)
+                outputfilename = ffiplatform.compile('.', ext)
+            finally:
+                os.chdir(cwd)
             return outputfilename
         else:
             return ext, updated
     else:
         if c_file is None:
-            c_file = os.path.join(tmpdir, module_name + '.py')
+            c_file, _ = _modname_to_file(tmpdir, module_name, '.py')
         updated = make_py_source(ffi, module_name, c_file)
         if call_c_compiler:
             return c_file
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
--- a/doc/source/cdef.rst
+++ b/doc/source/cdef.rst
@@ -442,7 +442,9 @@
 directory given by ``tmpdir``.  In the examples given here, we use
 ``if __name__ == "__main__": ffi.compile()`` in the build scripts---if
 they are directly executed, this makes them rebuild the .py/.c file in
-the current directory.
+the current directory.  (Note: if a package is specified in the call
+to ``set_source()``, then a corresponding subdirectory of the ``tmpdir``
+is used.)
 
 **ffi.emit_python_code(filename):** generate the given .py file (same
 as ``ffi.compile()`` for ABI mode, with an explicitly-named file to
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -6,6 +6,10 @@
 1.0.4
 =====
 
+* Issue #196: ``ffi.set_source("package._ffi", None)`` would
+  incorrectly generate the Python source to ``package._ffi.py``
+  instead of ``package/_ffi.py``.
+
 * ffi.addressof(lib, "func_name") now returns a regular cdata object
   of type "pointer to function".  You can use it on any function from a
   library in API mode (in ABI mode, all functions are already regular
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -478,8 +478,11 @@
     old_sys_path = sys.path[:]
     try:
         package_dir = udir.join('test_module_name_in_package')
+        for name in os.listdir(str(udir)):
+            assert not name.startswith('test_module_name_in_package.')
         assert os.path.isdir(str(package_dir))
         assert len(os.listdir(str(package_dir))) > 0
+        assert os.path.exists(str(package_dir.join('mymod.c')))
         package_dir.join('__init__.py').write('')
         #
         sys.path.insert(0, str(udir))
diff --git a/testing/cffi1/test_zdist.py b/testing/cffi1/test_zdist.py
new file mode 100644
--- /dev/null
+++ b/testing/cffi1/test_zdist.py
@@ -0,0 +1,153 @@
+import os, py
+import cffi
+from testing.udir import udir
+
+
+def chdir_to_tmp(f):
+    f.chdir_to_tmp = True
+    return f
+
+def from_outside(f):
+    f.chdir_to_tmp = False
+    return f
+
+
+class TestDist(object):
+
+    def setup_method(self, meth):
+        self.udir = udir.join(meth.__name__)
+        os.mkdir(str(self.udir))
+        if meth.chdir_to_tmp:
+            self.saved_cwd = os.getcwd()
+            os.chdir(str(self.udir))
+
+    def teardown_method(self, meth):
+        if hasattr(self, 'saved_cwd'):
+            os.chdir(self.saved_cwd)
+
+    def check_produced_files(self, content, curdir=None):
+        if curdir is None:
+            curdir = str(self.udir)
+        found_so = None
+        for name in os.listdir(curdir):
+            if (name.endswith('.so') or name.endswith('.pyd') or
+                name.endswith('.dylib')):
+                found_so = os.path.join(curdir, name)
+                name = os.path.splitext(name)[0] + '.SO'
+            assert name in content, "found unexpected file %r" % (
+                os.path.join(curdir, name),)
+            value = content.pop(name)
+            if value is None:
+                assert name.endswith('.SO') or (
+                    os.path.isfile(os.path.join(curdir, name)))
+            else:
+                subdir = os.path.join(curdir, name)
+                assert os.path.isdir(subdir)
+                found_so = self.check_produced_files(value, subdir) or found_so
+        assert content == {}, "files or dirs not produced in %r: %r" % (
+            curdir, content.keys())
+        return found_so
+
+    @chdir_to_tmp
+    def test_empty(self):
+        self.check_produced_files({})
+
+    @chdir_to_tmp
+    def test_abi_emit_python_code_1(self):
+        ffi = cffi.FFI()
+        ffi.set_source("package_name_1.mymod", None)
+        ffi.emit_python_code('xyz.py')
+        self.check_produced_files({'xyz.py': None})
+
+    @chdir_to_tmp
+    def test_abi_emit_python_code_2(self):
+        ffi = cffi.FFI()
+        ffi.set_source("package_name_1.mymod", None)
+        py.test.raises(IOError, ffi.emit_python_code, 'unexisting/xyz.py')
+
+    @from_outside
+    def test_abi_emit_python_code_3(self):
+        ffi = cffi.FFI()
+        ffi.set_source("package_name_1.mymod", None)
+        ffi.emit_python_code(str(self.udir.join('xyt.py')))
+        self.check_produced_files({'xyt.py': None})
+
+    @chdir_to_tmp
+    def test_abi_compile_1(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", None)
+        x = ffi.compile()
+        self.check_produced_files({'mod_name_in_package': {'mymod.py': None}})
+        assert x == os.path.join('.', 'mod_name_in_package', 'mymod.py')
+
+    @chdir_to_tmp
+    def test_abi_compile_2(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", None)
+        x = ffi.compile('build2')
+        self.check_produced_files({'build2': {
+            'mod_name_in_package': {'mymod.py': None}}})
+        assert x == os.path.join('build2', 'mod_name_in_package', 'mymod.py')
+
+    @from_outside
+    def test_abi_compile_3(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", None)
+        tmpdir = str(self.udir.join('build3'))
+        x = ffi.compile(tmpdir)
+        self.check_produced_files({'build3': {
+            'mod_name_in_package': {'mymod.py': None}}})
+        assert x == os.path.join(tmpdir, 'mod_name_in_package', 'mymod.py')
+
+    @chdir_to_tmp
+    def test_api_emit_c_code_1(self):
+        ffi = cffi.FFI()
+        ffi.set_source("package_name_1.mymod", "/*code would be here*/")
+        ffi.emit_c_code('xyz.c')
+        self.check_produced_files({'xyz.c': None})
+
+    @chdir_to_tmp
+    def test_api_emit_c_code_2(self):
+        ffi = cffi.FFI()
+        ffi.set_source("package_name_1.mymod", "/*code would be here*/")
+        py.test.raises(IOError, ffi.emit_c_code, 'unexisting/xyz.c')
+
+    @from_outside
+    def test_api_emit_c_code_3(self):
+        ffi = cffi.FFI()
+        ffi.set_source("package_name_1.mymod", "/*code would be here*/")
+        ffi.emit_c_code(str(self.udir.join('xyu.c')))
+        self.check_produced_files({'xyu.c': None})
+
+    @chdir_to_tmp
+    def test_api_compile_1(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
+        x = ffi.compile()
+        sofile = self.check_produced_files({
+            'mod_name_in_package': {'mymod.SO': None,
+                                    'mymod.c': None,
+                                    'mymod.o': None}})
+        assert os.path.isabs(x) and os.path.samefile(x, sofile)
+
+    @chdir_to_tmp
+    def test_api_compile_2(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
+        x = ffi.compile('output')
+        sofile = self.check_produced_files({
+            'output': {'mod_name_in_package': {'mymod.SO': None,
+                                               'mymod.c': None,
+                                               'mymod.o': None}}})
+        assert os.path.isabs(x) and os.path.samefile(x, sofile)
+
+    @from_outside
+    def test_api_compile_3(self):
+        ffi = cffi.FFI()
+        ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/")
+        x = ffi.compile(str(self.udir.join('foo')))
+        sofile = self.check_produced_files({
+            'foo': {'mod_name_in_package': {'mymod.SO': None,
+                                            'mymod.c': None,
+                                            'mymod.o': None}}})
+        assert os.path.isabs(x) and os.path.samefile(x, sofile)


More information about the pypy-commit mailing list