| import sys, os, py |
| import subprocess |
| import cffi |
| from testing.udir import udir |
| from shutil import rmtree |
| from tempfile import mkdtemp |
| |
| |
| 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.executable = os.path.abspath(sys.executable) |
| self.rootdir = os.path.abspath(os.path.dirname(os.path.dirname( |
| cffi.__file__))) |
| 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 run(self, args, cwd=None): |
| env = os.environ.copy() |
| # a horrible hack to prevent distutils from finding ~/.pydistutils.cfg |
| # (there is the --no-user-cfg option, but not in Python 2.6...) |
| # NOTE: pointing $HOME to a nonexistent directory can break certain things |
| # that look there for configuration (like ccache). |
| tmp_home = mkdtemp() |
| assert tmp_home != None, "cannot create temporary homedir" |
| env['HOME'] = tmp_home |
| if cwd is None: |
| newpath = self.rootdir |
| if 'PYTHONPATH' in env: |
| newpath += os.pathsep + env['PYTHONPATH'] |
| env['PYTHONPATH'] = newpath |
| try: |
| subprocess.check_call([self.executable] + args, cwd=cwd, env=env) |
| finally: |
| rmtree(tmp_home) |
| |
| def _prepare_setuptools(self): |
| if hasattr(TestDist, '_setuptools_ready'): |
| return |
| try: |
| import setuptools |
| except ImportError: |
| py.test.skip("setuptools not found") |
| if os.path.exists(os.path.join(self.rootdir, 'setup.py')): |
| self.run(['setup.py', 'egg_info'], cwd=self.rootdir) |
| TestDist._setuptools_ready = True |
| |
| 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') or name.endswith('.dll')): |
| found_so = os.path.join(curdir, name) |
| # foo.so => foo |
| parts = name.split('.') |
| del parts[-1] |
| if len(parts) > 1 and parts[-1] != 'bar': |
| # foo.cpython-34m.so => foo, but foo.bar.so => foo.bar |
| del parts[-1] |
| name = '.'.join(parts) |
| # foo_d => foo (Python 2 debug builds) |
| if name.endswith('_d') and hasattr(sys, 'gettotalrefcount'): |
| name = name[:-2] |
| name += '.SO' |
| if name.startswith('pycparser') and name.endswith('.egg'): |
| continue # no clue why this shows up sometimes and not others |
| if name == '.eggs': |
| continue # seems new in 3.5, ignore it |
| 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) |
| if value == '?': |
| continue |
| 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() |
| if sys.platform != 'win32': |
| 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) |
| else: |
| self.check_produced_files({ |
| 'mod_name_in_package': {'mymod.SO': None, |
| 'mymod.c': None}, |
| 'Release': '?'}) |
| |
| @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') |
| if sys.platform != 'win32': |
| 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) |
| else: |
| self.check_produced_files({ |
| 'output': {'mod_name_in_package': {'mymod.SO': None, |
| 'mymod.c': None}, |
| 'Release': '?'}}) |
| |
| @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'))) |
| if sys.platform != 'win32': |
| 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) |
| else: |
| self.check_produced_files({ |
| 'foo': {'mod_name_in_package': {'mymod.SO': None, |
| 'mymod.c': None}, |
| 'Release': '?'}}) |
| |
| @chdir_to_tmp |
| def test_api_compile_explicit_target_1(self): |
| ffi = cffi.FFI() |
| ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") |
| x = ffi.compile(target="foo.bar.*") |
| if sys.platform != 'win32': |
| sofile = self.check_produced_files({ |
| 'mod_name_in_package': {'foo.bar.SO': None, |
| 'mymod.c': None, |
| 'mymod.o': None}}) |
| assert os.path.isabs(x) and os.path.samefile(x, sofile) |
| else: |
| self.check_produced_files({ |
| 'mod_name_in_package': {'foo.bar.SO': None, |
| 'mymod.c': None}, |
| 'Release': '?'}) |
| |
| @chdir_to_tmp |
| def test_api_compile_explicit_target_3(self): |
| ffi = cffi.FFI() |
| ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") |
| x = ffi.compile(target="foo.bar.baz") |
| if sys.platform != 'win32': |
| self.check_produced_files({ |
| 'mod_name_in_package': {'foo.bar.baz': None, |
| 'mymod.c': None, |
| 'mymod.o': None}}) |
| sofile = os.path.join(str(self.udir), |
| 'mod_name_in_package', 'foo.bar.baz') |
| assert os.path.isabs(x) and os.path.samefile(x, sofile) |
| else: |
| self.check_produced_files({ |
| 'mod_name_in_package': {'foo.bar.baz': None, |
| 'mymod.c': None}, |
| 'Release': '?'}) |
| |
| @chdir_to_tmp |
| def test_api_distutils_extension_1(self): |
| ffi = cffi.FFI() |
| ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") |
| ext = ffi.distutils_extension() |
| self.check_produced_files({'build': { |
| 'mod_name_in_package': {'mymod.c': None}}}) |
| if hasattr(os.path, 'samefile'): |
| assert os.path.samefile(ext.sources[0], |
| 'build/mod_name_in_package/mymod.c') |
| |
| @from_outside |
| def test_api_distutils_extension_2(self): |
| ffi = cffi.FFI() |
| ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") |
| ext = ffi.distutils_extension(str(self.udir.join('foo'))) |
| self.check_produced_files({'foo': { |
| 'mod_name_in_package': {'mymod.c': None}}}) |
| if hasattr(os.path, 'samefile'): |
| assert os.path.samefile(ext.sources[0], |
| str(self.udir.join('foo/mod_name_in_package/mymod.c'))) |
| |
| |
| def _make_distutils_api(self): |
| os.mkdir("src") |
| os.mkdir(os.path.join("src", "pack1")) |
| with open(os.path.join("src", "pack1", "__init__.py"), "w") as f: |
| pass |
| with open("setup.py", "w") as f: |
| f.write("""if 1: |
| # https://bugs.python.org/issue23246 |
| import sys |
| if sys.platform == 'win32': |
| try: |
| import setuptools |
| except ImportError: |
| pass |
| |
| import cffi |
| ffi = cffi.FFI() |
| ffi.set_source("pack1.mymod", "/*code would be here*/") |
| |
| from distutils.core import setup |
| setup(name='example1', |
| version='0.1', |
| packages=['pack1'], |
| package_dir={'': 'src'}, |
| ext_modules=[ffi.distutils_extension()]) |
| """) |
| |
| @chdir_to_tmp |
| def test_distutils_api_1(self): |
| self._make_distutils_api() |
| self.run(["setup.py", "build"]) |
| self.check_produced_files({'setup.py': None, |
| 'build': '?', |
| 'src': {'pack1': {'__init__.py': None}}}) |
| |
| @chdir_to_tmp |
| def test_distutils_api_2(self): |
| self._make_distutils_api() |
| self.run(["setup.py", "build_ext", "-i"]) |
| self.check_produced_files({'setup.py': None, |
| 'build': '?', |
| 'src': {'pack1': {'__init__.py': None, |
| 'mymod.SO': None}}}) |
| |
| def _make_setuptools_abi(self): |
| self._prepare_setuptools() |
| os.mkdir("src0") |
| os.mkdir(os.path.join("src0", "pack2")) |
| with open(os.path.join("src0", "pack2", "__init__.py"), "w") as f: |
| pass |
| with open(os.path.join("src0", "pack2", "_build.py"), "w") as f: |
| f.write("""if 1: |
| import cffi |
| ffi = cffi.FFI() |
| ffi.set_source("pack2.mymod", None) |
| """) |
| with open("setup.py", "w") as f: |
| f.write("""if 1: |
| from setuptools import setup |
| setup(name='example1', |
| version='0.1', |
| packages=['pack2'], |
| package_dir={'': 'src0'}, |
| cffi_modules=["src0/pack2/_build.py:ffi"]) |
| """) |
| |
| @chdir_to_tmp |
| def test_setuptools_abi_1(self): |
| self._make_setuptools_abi() |
| self.run(["setup.py", "build"]) |
| self.check_produced_files({'setup.py': None, |
| 'build': '?', |
| 'src0': {'pack2': {'__init__.py': None, |
| '_build.py': None}}}) |
| |
| @chdir_to_tmp |
| def test_setuptools_abi_2(self): |
| self._make_setuptools_abi() |
| self.run(["setup.py", "build_ext", "-i"]) |
| self.check_produced_files({'setup.py': None, |
| 'src0': {'pack2': {'__init__.py': None, |
| '_build.py': None, |
| 'mymod.py': None}}}) |
| |
| def _make_setuptools_api(self): |
| self._prepare_setuptools() |
| os.mkdir("src1") |
| os.mkdir(os.path.join("src1", "pack3")) |
| with open(os.path.join("src1", "pack3", "__init__.py"), "w") as f: |
| pass |
| with open(os.path.join("src1", "pack3", "_build.py"), "w") as f: |
| f.write("""if 1: |
| import cffi |
| ffi = cffi.FFI() |
| ffi.set_source("pack3.mymod", "/*code would be here*/") |
| ffi._hi_there = 42 |
| """) |
| with open("setup.py", "w") as f: |
| f.write("from __future__ import print_function\n" |
| """if 1: |
| from setuptools import setup |
| from distutils.command.build_ext import build_ext |
| import os |
| |
| class TestBuildExt(build_ext): |
| def pre_run(self, ext, ffi): |
| print('_make_setuptools_api: in pre_run:', end=" ") |
| assert ffi._hi_there == 42 |
| assert ext.name == "pack3.mymod" |
| fn = os.path.join(os.path.dirname(self.build_lib), |
| '..', 'see_me') |
| print('creating %r' % (fn,)) |
| open(fn, 'w').close() |
| |
| setup(name='example1', |
| version='0.1', |
| packages=['pack3'], |
| package_dir={'': 'src1'}, |
| cffi_modules=["src1/pack3/_build.py:ffi"], |
| cmdclass={'build_ext': TestBuildExt}, |
| ) |
| """) |
| |
| @chdir_to_tmp |
| def test_setuptools_api_1(self): |
| self._make_setuptools_api() |
| self.run(["setup.py", "build"]) |
| self.check_produced_files({'setup.py': None, |
| 'build': '?', |
| 'see_me': None, |
| 'src1': {'pack3': {'__init__.py': None, |
| '_build.py': None}}}) |
| |
| @chdir_to_tmp |
| def test_setuptools_api_2(self): |
| self._make_setuptools_api() |
| self.run(["setup.py", "build_ext", "-i"]) |
| self.check_produced_files({'setup.py': None, |
| 'build': '?', |
| 'see_me': None, |
| 'src1': {'pack3': {'__init__.py': None, |
| '_build.py': None, |
| 'mymod.SO': None}}}) |