Author: Armin Rigo <[email protected]>
Branch: cpy-extension
Changeset: r285:b33ba96da706
Date: 2012-06-12 14:42 +0200
http://bitbucket.org/cffi/cffi/changeset/b33ba96da706/
Log: Fixed the docstring of ffi.verify() to document the new idea.
Starting on generating the CPython C extension.
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -22,7 +22,12 @@
ffi.cdef("""
int printf(const char *, ...);
""")
- ffi.C.printf("hello, %s!\n", ffi.new("char[]", "world"))
+
+ C = ffi.rawload(name=None) # standard library
+ -or-
+ C = ffi.verify() # use a C compiler: verify the decl above is right
+
+ C.printf("hello, %s!\n", ffi.new("char[]", "world"))
'''
def __init__(self, backend=None):
@@ -46,7 +51,6 @@
self._new_types = new.module('new_types').__dict__
if hasattr(backend, 'set_ffi'):
backend.set_ffi(self)
- self.C = _make_ffi_library(self, None)
#
lines = []
by_size = {}
@@ -64,19 +68,19 @@
def cdef(self, csource):
"""Parse the given C source. This registers all declared functions,
types, and global variables. The functions and global variables can
- then be accessed via 'ffi.C' or 'ffi.load()'. The types can be used
+ then be accessed via 'ffi.rawload()'. The types can be used
in 'ffi.new()' and other functions.
"""
self._parser.parse(csource)
- def load(self, name):
+ def rawload(self, name):
"""Load and return a dynamic library identified by 'name'.
- The standard C library is preloaded into 'ffi.C'.
+ The standard C library can be loaded by passing None.
Note that functions and types declared by 'ffi.cdef()' are not
linked to a particular library, just like C headers; in the
library we only look for the actual (untyped) symbols.
"""
- assert isinstance(name, str)
+ assert isinstance(name, str) or name is None
return _make_ffi_library(self, name)
def typeof(self, cdecl):
@@ -181,7 +185,13 @@
return BType
def verify(self, preamble='', **kwargs):
- """ Verify that the current ffi signatures compile on this machine
+ """Verify 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
+ variables declared in this 'ffi'. The library is compiled
+ by the C compiler: it gives you C-level API compatibility
+ (including calling macros). This is unlike 'ffi.rawload()',
+ which requires binary compatibility in the signatures.
"""
from .verifier import Verifier
return Verifier(self).verify(preamble, **kwargs)
diff --git a/cffi/ffiplatform.py b/cffi/ffiplatform.py
--- a/cffi/ffiplatform.py
+++ b/cffi/ffiplatform.py
@@ -10,15 +10,22 @@
cdef, but no verification has been done
"""
-test_file_counter = 0
+_file_counter = 0
+_tmpdir = None
-def _get_test_file_base():
+def undercffi_module_name():
+ global _file_counter
+ modname = '_cffi_%d' % _file_counter
+ _file_counter += 1
+ return modname
+
+def tmpdir():
# for now, living in the __pycache__ subdirectory
- global test_file_counter
- try:
- os.mkdir('__pycache__')
- except OSError:
- pass
- tst_file_base = '__pycache__/test%d' % test_file_counter
- test_file_counter += 1
- return tst_file_base
+ global _tmpdir
+ if _tmpdir is None:
+ try:
+ os.mkdir('__pycache__')
+ except OSError:
+ pass
+ _tmpdir = os.path.abspath('__pycache__')
+ return _tmpdir
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -26,9 +26,8 @@
except KeyError:
return self.new_backend_type(ffi, *args)
- def verifier_declare_typedef(self, verifier, name):
- verifier.write('__sametype__(%s, %s)' % (
- self.get_c_name('** result'), name))
+ #def generate_cpy_typedef(self, verifier, name, step):
+ # XXX
class VoidType(BaseType):
@@ -79,9 +78,58 @@
def new_backend_type(self, ffi, result, *args):
return ffi._backend.new_function_type(args, result, self.ellipsis)
- def verifier_declare_function(self, verifier, name):
- verifier.write('{ %s = %s; }' % (
- self.get_c_name('result'), name))
+ def generate_cpy_function_decl(self, verifier, name):
+ prnt = verifier.prnt
+ numargs = len(self.args)
+ if numargs == 0:
+ argname = 'no_arg'
+ elif numargs == 1:
+ argname = 'arg0'
+ else:
+ argname = 'args'
+ prnt('static PyObject *_cffi_f_%s(PyObject *self, PyObject *%s)' %
+ (name, argname))
+ prnt('{')
+ assert not self.ellipsis # XXX later
+ #
+ for i in range(len(self.args)):
+ prnt(' double x%d;' % i)
+ prnt(' double result;')
+ #
+ if len(self.args) > 1:
+ rng = range(len(self.args))
+ for i in rng:
+ prnt(' PyObject *arg%d;' % i)
+ prnt()
+ prnt(' if (!PyArg_ParseTuple("%s:%s", %s)) {' % (
+ 'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng])))
+ prnt(' return NULL;')
+ prnt()
+ #
+ for i in range(len(self.args)):
+ prnt(' x%d = PyFloat_AsDouble(arg%d);' % (i, i))
+ prnt(' if (x%d == -1.0 && PyErr_Occurred())' % i)
+ prnt(' return NULL;')
+ prnt()
+ #
+ prnt(' { result = %s(%s); }' % (
+ name, ', '.join(['x%d' % i for i in range(len(self.args))])))
+ prnt()
+ #
+ prnt(' return PyFloat_FromDouble(result);')
+ prnt('}')
+ prnt()
+
+ def generate_cpy_function_method(self, verifier, name):
+ numargs = len(self.args)
+ if numargs == 0:
+ meth = 'METH_NOARGS'
+ elif numargs == 1:
+ meth = 'METH_O'
+ else:
+ meth = 'METH_VARARGS'
+ verifier.prnt(' {"%s", _cffi_f_%s, %s},' % (name, name, meth))
+
class PointerType(BaseType):
diff --git a/cffi/verifier.py b/cffi/verifier.py
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -6,47 +6,62 @@
def __init__(self, ffi):
self.ffi = ffi
- def write(self, what):
- print >> self.f, ' ' + what
+ def prnt(self, what=''):
+ print >> self.f, what
- def write_printf(self, what, *args):
- self.has_printf = True
- if not args:
- print >> self.f, ' printf("%s\\n");' % (what,)
- else:
- print >> self.f, ' printf("%s\\n", %s);' % (
- what, ', '.join(args))
+## def write_printf(self, what, *args):
+## if not args:
+## print >> self.f, ' printf("%s\\n");' % (what,)
+## else:
+## print >> self.f, ' printf("%s\\n", %s);' % (
+## what, ', '.join(args))
+
+ def generate(self, step_name):
+ for name, tp in self.ffi._parser._declarations.iteritems():
+ kind, realname = name.split(' ', 1)
+ method = getattr(tp, 'generate_cpy_%s_%s' % (kind, step_name), 0)
+ if method:
+ method(self, realname)
def verify(self, preamble, **kwargs):
- tst_file_base = ffiplatform._get_test_file_base()
- self.has_printf = False
- with open(tst_file_base + '.c', 'w') as f:
- f.write("""\
-#include <stdio.h>
-#include <stdint.h>
-#include <stddef.h>
-#include <unistd.h>
+ modname = ffiplatform.undercffi_module_name()
+ filebase = os.path.join(ffiplatform.tmpdir(), modname)
+
+ with open(filebase + 'module.c', 'w') as f:
+ self.f = f
+ self.prnt("#include <Python.h>")
+ self.prnt()
+ self.prnt(preamble)
+ self.prnt()
+ #
+ self.generate("decl")
+ #
+ self.prnt('static PyMethodDef _cffi_methods[] = {')
+ self.generate("method")
+ self.prnt(' {NULL, NULL} /* Sentinel */')
+ self.prnt('};')
+ self.prnt()
+ #
+ self.prnt('void init%s()' % modname)
+ self.prnt('{')
+ self.prnt(' Py_InitModule("%s", _cffi_methods);' % modname)
+ self.prnt(' if (PyErr_Occurred()) return;')
+ self.generate("init")
+ self.prnt('}')
+ #
+ del self.f
-#define __sameconstant__(x, y) \\
- { int result[1-2*((x)-(y))*((x)-(y))]; }
-
-#define __sametype__(ppresult, typename) \\
- { ppresult = (typename**)0; }
-
-""")
- f.write(preamble + "\n\n")
- f.write('int main() {\n')
- self.f = f
- for name, tp in self.ffi._parser._declarations.iteritems():
- kind, realname = name.split(' ', 1)
- method = getattr(tp, 'verifier_declare_' + kind)
- method(self, realname)
- del self.f
- f.write(' return 0;\n')
- f.write('}\n')
- err = os.system('gcc -Werror -S %s.c -o %s.s' %
- (tst_file_base, tst_file_base))
+ # XXX use more distutils?
+ import distutils.sysconfig
+ python_h = distutils.sysconfig.get_python_inc()
+ err = os.system("gcc -I'%s' -O2 -shared %smodule.c -o %s.so" %
+ (python_h, filebase, filebase))
if err:
raise ffiplatform.VerificationError(
- '%s.c: see compilation warnings and errors above' %
- (tst_file_base,))
+ '%smodule.c: see compilation errors above' % (filebase,))
+ #
+ import imp
+ try:
+ return imp.load_dynamic(modname, '%s.so' % filebase)
+ except ImportError, e:
+ raise ffiplatform.VerificationError(str(e))
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -1,27 +1,46 @@
import py
+import math
from cffi import FFI, VerificationError, VerificationMissing
-def test_simple_verify():
+def test_missing_function():
ffi = FFI()
ffi.cdef("void some_completely_unknown_function();")
py.test.raises(VerificationError, ffi.verify)
+
+def test_simple_case():
ffi = FFI()
ffi.cdef("double sin(double x);")
- # omission of math.h
- py.test.raises(VerificationError, ffi.verify)
- assert ffi.verify('#include <math.h>') is None
- #
+ lib = ffi.verify('#include <math.h>')
+ assert lib.sin(1.23) == math.sin(1.23)
+
+def test_rounding_1():
ffi = FFI()
ffi.cdef("float sin(double x);")
- py.test.raises(VerificationError, ffi.verify, '#include <math.h>')
+ lib = ffi.verify('#include <math.h>')
+ res = lib.sin(1.23)
+ assert res != math.sin(1.23) # not exact, because of double->float
+ assert abs(res - math.sin(1.23)) < 1E-5
+
+def test_rounding_2():
ffi = FFI()
ffi.cdef("double sin(float x);")
- py.test.raises(VerificationError, ffi.verify, '#include <math.h>')
- #
+ lib = ffi.verify('#include <math.h>')
+ res = lib.sin(1.23)
+ assert res != math.sin(1.23) # not exact, because of double->float
+ assert abs(res - math.sin(1.23)) < 1E-5
+
+def test_strlen_exact():
ffi = FFI()
ffi.cdef("size_t strlen(const char *s);")
- ffi.verify("#include <string.h>")
+ lib = ffi.verify("#include <string.h>")
+ assert lib.strlen("hi there!") == 9
+
+def test_strlen_approximate():
+ ffi = FFI()
+ ffi.cdef("int strlen(char *s);")
+ lib = ffi.verify("#include <string.h>")
+ assert lib.strlen("hi there!") == 9
def test_verify_typedefs():
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit