def setup_class(cls): cls.module = None from extra_tests.cffi_tests.udir import udir udir.join('testownlib.c').write(SOURCE) if sys.platform == 'win32': # did we already build it? if cls.Backend is CTypesBackend: dll_path = str( udir ) + '\\testownlib1.dll' # only ascii for the ctypes backend else: dll_path = str(udir) + '\\' + (u + 'testownlib\u03be.dll' ) # non-ascii char if os.path.exists(dll_path): cls.module = dll_path return # try (not too hard) to find the version used to compile this python # no mingw from distutils.msvc9compiler import get_build_version version = get_build_version() toolskey = "VS%0.f0COMNTOOLS" % version toolsdir = os.environ.get(toolskey, None) if toolsdir is None: return productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") productdir = os.path.abspath(productdir) vcvarsall = os.path.join(productdir, "vcvarsall.bat") # 64? arch = 'x86' if sys.maxsize > 2**32: arch = 'amd64' if os.path.isfile(vcvarsall): cmd = '"%s" %s' % (vcvarsall, arch) + ' & cl.exe testownlib.c ' \ ' /LD /Fetestownlib.dll' subprocess.check_call(cmd, cwd=str(udir), shell=True) os.rename(str(udir) + '\\testownlib.dll', dll_path) cls.module = dll_path else: encoded = None if cls.Backend is not CTypesBackend: try: unicode_name = u + 'testownlibcaf\xe9' encoded = unicode_name.encode(sys.getfilesystemencoding()) if sys.version_info >= (3, ): encoded = str(unicode_name) except UnicodeEncodeError: pass if encoded is None: unicode_name = u + 'testownlib' encoded = str(unicode_name) subprocess.check_call("cc testownlib.c -shared -fPIC -o '%s.so'" % (encoded, ), cwd=str(udir), shell=True) cls.module = os.path.join(str(udir), unicode_name + (u + '.so')) print(repr(cls.module))
def test_verifier_args(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!4*/#include "test_verifier_args.h"\n' % self udir.join('test_verifier_args.h').write('#include <math.h>\n') v = Verifier(ffi, csrc, include_dirs=[str(udir)], force_generic_engine=self.generic, libraries=[self.lib_m]) library = v.load_library() assert library.sin(12.3) == math.sin(12.3)
def really_run_setup_and_program(dirname, venv_dir_and_paths, python_snippet): venv_dir, paths = venv_dir_and_paths def remove(dir): dir = str(SNIPPET_DIR.join(dirname, dir)) shutil.rmtree(dir, ignore_errors=True) remove('build') remove('__pycache__') for basedir in os.listdir(str(SNIPPET_DIR.join(dirname))): remove(os.path.join(basedir, '__pycache__')) olddir = os.getcwd() python_f = udir.join('x.py') python_f.write(py.code.Source(python_snippet)) try: os.chdir(str(SNIPPET_DIR.join(dirname))) if os.name == 'nt': bindir = 'Scripts' else: bindir = 'bin' vp = str(venv_dir.join(bindir).join('python')) env = os.environ.copy() env['PYTHONPATH'] = paths subprocess.check_call((vp, 'setup.py', 'clean'), env=env) # there's a setuptools/easy_install bug that causes this to fail when the build/install occur together and # we're in the same directory with the build (it tries to look up dependencies for itself on PyPI); # subsequent runs will succeed because this test doesn't properly clean up the build- use pip for now. subprocess.check_call((vp, '-m', 'pip', 'install', '.'), env=env) subprocess.check_call((vp, str(python_f)), env=env) finally: os.chdir(olddir)
def really_run_setup_and_program(dirname, venv_dir_and_paths, python_snippet): venv_dir, paths = venv_dir_and_paths def remove(dir): dir = str(SNIPPET_DIR.join(dirname, dir)) shutil.rmtree(dir, ignore_errors=True) remove('build') remove('__pycache__') for basedir in os.listdir(str(SNIPPET_DIR.join(dirname))): remove(os.path.join(basedir, '__pycache__')) olddir = os.getcwd() python_f = udir.join('x.py') python_f.write(py.code.Source(python_snippet)) try: os.chdir(str(SNIPPET_DIR.join(dirname))) if os.name == 'nt': bindir = 'Scripts' else: bindir = 'bin' vp = str(venv_dir.join(bindir).join('python')) env = os.environ.copy() env['PYTHONPATH'] = paths subprocess.check_call((vp, 'setup.py', 'clean'), env=env) subprocess.check_call((vp, 'setup.py', 'install'), env=env) subprocess.check_call((vp, str(python_f)), env=env) finally: os.chdir(olddir)
def test_invalid_dotdotdot_in_macro(): ffi = FFI() ffi.cdef("#define FOO ...") target = udir.join('test_invalid_dotdotdot_in_macro.py') e = py.test.raises(VerificationError, make_py_source, ffi, 'test_invalid_dotdotdot_in_macro', str(target)) assert str(e.value) == ("macro FOO: cannot use the syntax '...' in " "'#define FOO ...' when using the ABI mode")
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 test_no_cross_include(): baseffi = FFI() baseffi.set_source('test_no_cross_include_base', "..source..") # ffi = FFI() ffi.include(baseffi) target = udir.join('test_no_cross_include.py') py.test.raises(VerificationError, make_py_source, ffi, 'test_no_cross_include', str(target))
def test_struct_included(): baseffi = FFI() baseffi.cdef("struct foo_s { int x; };") baseffi.set_source('test_struct_included_base', None) # ffi = FFI() ffi.include(baseffi) target = udir.join('test_struct_included.py') make_py_source(ffi, 'test_struct_included', str(target)) assert target.read() == r"""# auto-generated file
def test_include(): ffi = FFI() ffi.cdef("#define ABC 123") ffi.set_source('test_include', None) target = udir.join('test_include.py') make_py_source(ffi, 'test_include', str(target)) assert target.read() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_include', _version = 0x2601, _types = b'', _globals = (b'\xFF\xFF\xFF\x1FABC',123,), ) """ # ffi2 = FFI() ffi2.include(ffi) target2 = udir.join('test2_include.py') make_py_source(ffi2, 'test2_include', str(target2)) assert target2.read() == r"""# auto-generated file
def test_write_source_explicit_filename(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!*/\n#include <math.h>\n' % self v = Verifier(ffi, csrc, force_generic_engine=self.generic, libraries=[self.lib_m]) v.sourcefilename = filename = str(udir.join('write_source.c')) v.write_source() assert filename == v.sourcefilename with open(filename, 'r') as f: data = f.read() assert csrc in data
def test_install_and_reload_module(self, targetpackage='', ext_package=''): KEY = repr(self) 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, %s, %s */" % (KEY, targetpackage, ext_package)) 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=ext_package, **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: if targetpackage == ext_package: 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_compile_module_explicit_filename(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!2*/\n#include <math.h>\n' % self v = Verifier(ffi, csrc, force_generic_engine=self.generic, libraries=[self.lib_m]) basename = self.__class__.__name__ + 'test_compile_module' v.modulefilename = filename = str(udir.join(basename + '.so')) v.compile_module() assert filename == v.modulefilename assert v.get_module_name() == basename if v.generates_python_module(): mod = imp.load_dynamic(v.get_module_name(), v.modulefilename) assert hasattr(mod, '_cffi_setup')
def test_fputs_custom_FILE(self): if self.Backend is CTypesBackend: py.test.skip("FILE not supported with the ctypes backend") filename = str(udir.join('fputs_custom_FILE')) ffi = FFI(backend=self.Backend()) ffi.cdef("int fputs(const char *, FILE *);") needs_dlopen_none() C = ffi.dlopen(None) with open(filename, 'wb') as f: f.write(b'[') C.fputs(b"hello from custom file", f) f.write(b'][') C.fputs(b"some more output", f) f.write(b']') with open(filename, 'rb') as f: res = f.read() assert res == b'[hello from custom file][some more output]'
def create_venv(name): tmpdir = udir.join(name) try: subprocess.check_call([ 'virtualenv', #'--never-download', <= could be added, but causes failures # in random cases on random machines '-p', os.path.abspath(sys.executable), str(tmpdir) ]) except OSError as e: py.test.skip("Cannot execute virtualenv: %s" % (e, )) site_packages = None for dirpath, dirnames, filenames in os.walk(str(tmpdir)): if os.path.basename(dirpath) == 'site-packages': site_packages = dirpath break paths = "" if site_packages: try: from cffi import _pycparser modules = ('cffi', '_cffi_backend') except ImportError: modules = ('cffi', '_cffi_backend', 'pycparser') try: import ply except ImportError: pass else: modules += ('ply', ) # needed for older versions of pycparser paths = [] for module in modules: target = __import__(module, None, None, []) if not hasattr(target, '__file__'): # for _cffi_backend on pypy continue src = os.path.abspath(target.__file__) for end in ['__init__.pyc', '__init__.pyo', '__init__.py']: if src.lower().endswith(end): src = src[:-len(end) - 1] break paths.append(os.path.dirname(src)) paths = os.pathsep.join(paths) return tmpdir, paths
def test_extension_object_extra_sources(self): ffi = FFI() ffi.cdef("double test1eoes(double x);") extra_source = str(udir.join('extension_extra_sources.c')) with open(extra_source, 'w') as f: f.write('double test1eoes(double x) { return x * 6.0; }\n') csrc = '/*9%s*/' % self + ''' double test1eoes(double x); /* or #include "extra_sources.h" */ ''' lib = ffi.verify(csrc, sources=[extra_source], force_generic_engine=self.generic) assert lib.test1eoes(7.0) == 42.0 v = ffi.verifier ext = v.get_extension() assert 'distutils.extension.Extension' in str(ext.__class__) or \ 'setuptools.extension.Extension' in str(ext.__class__) assert ext.sources == [ maybe_relative_path(v.sourcefilename), extra_source ] assert ext.name == v.get_module_name()
def setup_method(self, meth): check_lib_python_found(str(udir.ensure('embedding', dir=1))) self._path = udir.join('embedding', meth.__name__) if sys.platform == "win32" or sys.platform == "darwin": self._compiled_modules.clear() # workaround
def test_array(): ffi = FFI() ffi.cdef("typedef int32_t my_array_t[42];") target = udir.join('test_array.py') make_py_source(ffi, 'test_array', str(target)) assert target.read() == r"""# auto-generated file
def test_array_overflow(): ffi = FFI() ffi.cdef("typedef int32_t my_array_t[3000000000];") target = udir.join('test_array_overflow.py') py.test.raises(OverflowError, make_py_source, ffi, 'test_array_overflow', str(target))
def test_global_var(): ffi = FFI() ffi.cdef("int myglob;") target = udir.join('test_global_var.py') make_py_source(ffi, 'test_global_var', str(target)) assert target.read() == r"""# auto-generated file
def test_struct(): ffi = FFI() ffi.cdef("struct foo_s { int a; signed char b[]; }; struct bar_s;") target = udir.join('test_struct.py') make_py_source(ffi, 'test_struct', str(target)) assert target.read() == r"""# auto-generated file
def test_simple(): ffi = FFI() ffi.cdef("int close(int); static const int BB = 42; int somevar;") target = udir.join('test_simple.py') make_py_source(ffi, 'test_simple', str(target)) assert target.read() == r"""# auto-generated file
def test_negative_constant(): ffi = FFI() ffi.cdef("static const int BB = -42;") target = udir.join('test_negative_constant.py') make_py_source(ffi, 'test_negative_constant', str(target)) assert target.read() == r"""# auto-generated file
def test_global_constant(): ffi = FFI() ffi.cdef("static const long BB; static const float BF = 12;") target = udir.join('test_valid_global_constant.py') make_py_source(ffi, 'test_valid_global_constant', str(target)) assert target.read() == r"""# auto-generated file
def test_bitfield(): ffi = FFI() ffi.cdef("struct foo_s { int y:10; short x:5; };") target = udir.join('test_bitfield.py') make_py_source(ffi, 'test_bitfield', str(target)) assert target.read() == r"""# auto-generated file
def setup_module(mod): SRC = """ #include <string.h> #define FOOBAR (-42) static const int FOOBAZ = -43; #define BIGPOS 420000000000L #define BIGNEG -420000000000L int add42(int x) { return x + 42; } int add43(int x, ...) { return x; } int globalvar42 = 1234; const int globalconst42 = 4321; const char *const globalconsthello = "hello"; struct foo_s; typedef struct bar_s { int x; signed char a[]; } bar_t; enum foo_e { AA, BB, CC }; void init_test_re_python(void) { } /* windows hack */ void PyInit__test_re_python(void) { } /* windows hack */ """ tmpdir = udir.join('test_re_python') tmpdir.ensure(dir=1) c_file = tmpdir.join('_test_re_python.c') c_file.write(SRC) ext = ffiplatform.get_extension(str(c_file), '_test_re_python', export_symbols=[ 'add42', 'add43', 'globalvar42', 'globalconst42', 'globalconsthello' ]) outputfilename = ffiplatform.compile(str(tmpdir), ext) # test with a non-ascii char ofn, oext = os.path.splitext(outputfilename) if sys.platform == "win32": unicode_name = ofn + (u + '\u03be') + oext else: unicode_name = ofn + (u + '\xe9') + oext try: unicode_name.encode(sys.getfilesystemencoding()) except UnicodeEncodeError: unicode_name = None if unicode_name is not None: print(repr(outputfilename) + ' ==> ' + repr(unicode_name)) os.rename(outputfilename, unicode_name) outputfilename = unicode_name mod.extmod = outputfilename mod.tmpdir = tmpdir # ffi = FFI() ffi.cdef(""" #define FOOBAR -42 static const int FOOBAZ = -43; #define BIGPOS 420000000000L #define BIGNEG -420000000000L int add42(int); int add43(int, ...); extern int globalvar42; const int globalconst42; const char *const globalconsthello; int no_such_function(int); extern int no_such_globalvar; struct foo_s; typedef struct bar_s { int x; signed char a[]; } bar_t; enum foo_e { AA, BB, CC }; int strlen(const char *); struct with_union { union { int a; char b; }; }; union with_struct { struct { int a; char b; }; }; struct with_struct_with_union { struct { union { int x; }; } cp; }; struct NVGcolor { union { float rgba[4]; struct { float r,g,b,a; }; }; }; typedef struct selfref { struct selfref *next; } *selfref_ptr_t; """) ffi.set_source('re_python_pysrc', None) ffi.emit_python_code(str(tmpdir.join('re_python_pysrc.py'))) mod.original_ffi = ffi # sys.path.insert(0, str(tmpdir))
def test_typename(): ffi = FFI() ffi.cdef("typedef int foobar_t;") target = udir.join('test_typename.py') make_py_source(ffi, 'test_typename', str(target)) assert target.read() == r"""# auto-generated file
def test_types(tp_args, tp_result): global TEST_RUN_COUNTER print(tp_args, tp_result) cdefs = [] structs = {} def build_type(tp): if type(tp) is list: field_types = [build_type(tp1) for tp1 in tp] fields = ['%s f%d;' % (ftp, j) for (j, ftp) in enumerate(field_types)] fields = '\n '.join(fields) name = 's%d' % len(cdefs) cdefs.append("typedef struct {\n %s\n} %s;" % (fields, name)) structs[name] = field_types return name else: return tp args = [build_type(tp) for tp in tp_args] result = build_type(tp_result) TEST_RUN_COUNTER += 1 signature = "%s testfargs(%s)" % (result, ', '.join(['%s a%d' % (arg, i) for (i, arg) in enumerate(args)]) or 'void') source = list(cdefs) cdefs.append("%s;" % signature) cdefs.append("extern %s testfargs_result;" % result) for i, arg in enumerate(args): cdefs.append("extern %s testfargs_arg%d;" % (arg, i)) source.append("%s testfargs_result;" % result) for i, arg in enumerate(args): source.append("%s testfargs_arg%d;" % (arg, i)) source.append(signature) source.append("{") for i, arg in enumerate(args): source.append(" testfargs_arg%d = a%d;" % (i, i)) source.append(" return testfargs_result;") source.append("}") typedef_line = "typedef %s;" % (signature.replace('testfargs', '(*mycallback_t)'),) assert signature.endswith(')') sig_callback = "%s testfcallback(mycallback_t callback)" % result cdefs.append(typedef_line) cdefs.append("%s;" % sig_callback) source.append(typedef_line) source.append(sig_callback) source.append("{") source.append(" return callback(%s);" % ', '.join(["testfargs_arg%d" % i for i in range(len(args))])) source.append("}") ffi = FFI() ffi.cdef("\n".join(cdefs)) lib = verify(ffi, 'test_function_args_%d' % TEST_RUN_COUNTER, "\n".join(source), no_cpp=True) # when getting segfaults, enable this: if False: from extra_tests.cffi_tests.udir import udir import subprocess f = open(str(udir.join('run1.py')), 'w') f.write('import sys; sys.path = %r\n' % (sys.path,)) f.write('from _CFFI_test_function_args_%d import ffi, lib\n' % TEST_RUN_COUNTER) for i in range(len(args)): f.write('a%d = ffi.new("%s *")\n' % (i, args[i])) aliststr = ', '.join(['a%d[0]' % i for i in range(len(args))]) f.write('lib.testfargs(%s)\n' % aliststr) f.write('ffi.addressof(lib, "testfargs")(%s)\n' % aliststr) f.close() print("checking for segfault for direct call...") rc = subprocess.call([sys.executable, 'run1.py'], cwd=str(udir)) assert rc == 0, rc def make_arg(tp): if tp in structs: return [make_arg(tp1) for tp1 in structs[tp]] else: return draw_primitive(ffi, tp) passed_args = [make_arg(arg) for arg in args] returned_value = make_arg(result) def write(p, v): if type(v) is list: for i, v1 in enumerate(v): write(ffi.addressof(p, 'f%d' % i), v1) else: p[0] = v write(ffi.addressof(lib, 'testfargs_result'), returned_value) ## CALL forcing libffi print("CALL forcing libffi") received_return = ffi.addressof(lib, 'testfargs')(*passed_args) ## _tp_long_double = ffi.typeof("long double") def check(p, v): if type(v) is list: for i, v1 in enumerate(v): check(ffi.addressof(p, 'f%d' % i), v1) else: if ffi.typeof(p).item is _tp_long_double: assert ffi.cast("double", p[0]) == v else: assert p[0] == v for i, arg in enumerate(passed_args): check(ffi.addressof(lib, 'testfargs_arg%d' % i), arg) ret = ffi.new(result + "*", received_return) check(ret, returned_value) ## CALLBACK def expand(value): if isinstance(value, ffi.CData): t = ffi.typeof(value) if t is _tp_long_double: return float(ffi.cast("double", value)) return [expand(getattr(value, 'f%d' % i)) for i in range(len(t.fields))] else: return value # when getting segfaults, enable this: if False: from extra_tests.cffi_tests.udir import udir import subprocess f = open(str(udir.join('run1.py')), 'w') f.write('import sys; sys.path = %r\n' % (sys.path,)) f.write('from _CFFI_test_function_args_%d import ffi, lib\n' % TEST_RUN_COUNTER) f.write('def callback(*args): return ffi.new("%s *")[0]\n' % result) f.write('fptr = ffi.callback("%s(%s)", callback)\n' % (result, ','.join(args))) f.write('print(lib.testfcallback(fptr))\n') f.close() print("checking for segfault for callback...") rc = subprocess.call([sys.executable, 'run1.py'], cwd=str(udir)) assert rc == 0, rc seen_args = [] def callback(*args): seen_args.append([expand(arg) for arg in args]) return returned_value fptr = ffi.callback("%s(%s)" % (result, ','.join(args)), callback) print("CALL with callback") received_return = lib.testfcallback(fptr) assert len(seen_args) == 1 assert passed_args == seen_args[0] ret = ffi.new(result + "*", received_return) check(ret, returned_value)
def test_enum(): ffi = FFI() ffi.cdef("enum myenum_e { AA, BB, CC=-42 };") target = udir.join('test_enum.py') make_py_source(ffi, 'test_enum', str(target)) assert target.read() == r"""# auto-generated file