[pypy-commit] cffi default: Simplify the module setup logic; add high-level comments.
arigo
noreply at buildbot.pypy.org
Fri Jun 15 20:17:45 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r372:f8adb916ed0c
Date: 2012-06-15 20:17 +0200
http://bitbucket.org/cffi/cffi/changeset/f8adb916ed0c/
Log: Simplify the module setup logic; add high-level comments.
diff --git a/cffi/verifier.py b/cffi/verifier.py
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -19,34 +19,47 @@
return num
def verify(self, preamble, **kwds):
+ """Produce an extension module, compile it and import it.
+ Then make a fresh FFILibrary class, of which we will return
+ an instance. Finally, we copy all the API elements from
+ the module to the class or the instance as needed.
+ """
modname = ffiplatform.undercffi_module_name()
tmpdir = ffiplatform.tmpdir()
filebase = os.path.join(tmpdir, modname)
- self.chained_list_constants = None
-
+
+ # The new module will have a _cffi_setup() function that receives
+ # objects from the ffi world, and that calls some setup code in
+ # the module. This setup code is split in several independent
+ # functions, e.g. one per constant. The functions are "chained"
+ # by ending in a tail call to each other. The following
+ # 'chained_list_constants' attribute contains the head of this
+ # chained list, as a string that gives the call to do, if any.
+ self.chained_list_constants = '0'
+
with open(filebase + '.c', 'w') as f:
self.f = f
+ # first paste some standard set of lines that are mostly '#define'
self.prnt(cffimod_header)
self.prnt()
+ # then paste the C source given by the user, verbatim.
self.prnt(preamble)
self.prnt()
#
+ # call generate_cpy_xxx_decl(), for every xxx found from
+ # ffi._parser._declarations. This generates all the functions.
self.generate("decl")
#
- self.prnt('static PyObject *_cffi_setup_custom(void)')
+ # implement this function as calling the head of the chained list.
+ self.prnt('static int _cffi_setup_custom(PyObject *lib)')
self.prnt('{')
- self.prnt(' PyObject *dct = PyDict_New();')
- if self.chained_list_constants is not None:
- self.prnt(' if (dct == NULL)')
- self.prnt(' return NULL;')
- self.prnt(' if (%s(dct) < 0) {' % self.chained_list_constants)
- self.prnt(' Py_DECREF(dct);')
- self.prnt(' return NULL;')
- self.prnt(' }')
- self.prnt(' return dct;')
+ self.prnt(' return %s;' % self.chained_list_constants)
self.prnt('}')
self.prnt()
#
+ # produce the method table, including the entries for the
+ # generated Python->C function wrappers, which are done
+ # by generate_cpy_function_method().
self.prnt('static PyMethodDef _cffi_methods[] = {')
self.generate("method")
self.prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS},')
@@ -54,6 +67,7 @@
self.prnt('};')
self.prnt()
#
+ # standard init.
self.prnt('PyMODINIT_FUNC')
self.prnt('init%s(void)' % modname)
self.prnt('{')
@@ -63,26 +77,41 @@
#
del self.f
+ # compile this C source
outputfilename = ffiplatform.compile(tmpdir, modname, **kwds)
#
+ # import it as a new extension module
import imp
try:
module = imp.load_dynamic(modname, outputfilename)
except ImportError, e:
raise ffiplatform.VerificationError(str(e))
#
+ # call loading_cpy_struct() to get the struct layout inferred by
+ # the C compiler
self.load(module, 'loading')
#
+ # the C code will need the <ctype> objects. Collect them in
+ # order in a list.
revmapping = dict([(value, key)
for (key, value) in self.typesdict.items()])
lst = [revmapping[i] for i in range(len(revmapping))]
lst = map(self.ffi._get_cached_btype, lst)
- dct = module._cffi_setup(lst, ffiplatform.VerificationError)
#
+ # build the FFILibrary class and instance and call _cffi_setup().
+ # this will set up some fields like '_cffi_types', and only then
+ # it will invoke the chained list of functions that will really
+ # build (notably) the constant objects, as <cdata> if they are
+ # pointers, and store them as attributes on the 'library' object.
class FFILibrary(object):
pass
library = FFILibrary()
- library.__dict__.update(dct)
+ module._cffi_setup(lst, ffiplatform.VerificationError, library)
+ #
+ # finally, call the loaded_cpy_xxx() functions. This will perform
+ # the final adjustments, like copying the Python->C wrapper
+ # functions from the module to the 'library' object, and setting
+ # up the FFILibrary class with properties for the global C variables.
self.load(module, 'loaded', library=library)
return library
@@ -322,28 +351,18 @@
# ----------
# constants, likely declared with '#define'
- def _generate_chain_header(self, funcname, *vardecls):
+ def _generate_cpy_const(self, is_int, name, tp=None, category='const'):
prnt = self.prnt
- prnt('static int %s(PyObject *dct)' % funcname)
+ funcname = '_cffi_%s_%s' % (category, name)
+ prnt('static int %s(PyObject *lib)' % funcname)
prnt('{')
- for decl in vardecls:
- prnt(' ' + decl)
- if self.chained_list_constants is not None:
- prnt(' if (%s(dct) < 0)' % self.chained_list_constants)
- prnt(' return -1;')
- self.chained_list_constants = funcname
-
- def _generate_cpy_const(self, is_int, name, tp=None, category='const'):
- vardecls = ['PyObject *o;',
- 'int res;']
+ prnt(' PyObject *o;')
+ prnt(' int res;')
if not is_int:
- vardecls.append('%s;' % tp.get_c_name(' i'))
+ prnt(' %s;' % tp.get_c_name(' i'))
else:
assert category == 'const'
- self._generate_chain_header('_cffi_%s_%s' % (category, name),
- *vardecls)
#
- prnt = self.prnt
if not is_int:
if category == 'var':
realexpr = '&' + name
@@ -361,9 +380,12 @@
'(unsigned long long)(%s));' % (name,))
prnt(' if (o == NULL)')
prnt(' return -1;')
- prnt(' res = PyDict_SetItemString(dct, "%s", o);' % name)
+ prnt(' res = PyObject_SetAttrString(lib, "%s", o);' % name)
prnt(' Py_DECREF(o);')
- prnt(' return res;')
+ prnt(' if (res < 0)')
+ prnt(' return -1;')
+ prnt(' return %s;' % self.chained_list_constants)
+ self.chained_list_constants = funcname + '(lib)'
prnt('}')
prnt()
@@ -384,8 +406,10 @@
self._generate_cpy_const(True, enumerator)
return
#
- self._generate_chain_header('_cffi_enum_%s' % name)
+ funcname = '_cffi_enum_%s' % name
prnt = self.prnt
+ prnt('static int %s(PyObject *lib)' % funcname)
+ prnt('{')
for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
prnt(' if (%s != %d) {' % (enumerator, enumvalue))
prnt(' PyErr_Format(_cffi_VerificationError,')
@@ -395,7 +419,8 @@
name, enumerator, enumerator, enumvalue))
prnt(' return -1;')
prnt(' }')
- prnt(' return 0;')
+ prnt(' return %s;' % self.chained_list_constants)
+ self.chained_list_constants = funcname + '(lib)'
prnt('}')
prnt()
@@ -529,16 +554,22 @@
static void *_cffi_exports[_CFFI_NUM_EXPORTS];
static PyObject *_cffi_types, *_cffi_VerificationError;
-static PyObject *_cffi_setup_custom(void); /* forward */
+static int _cffi_setup_custom(PyObject *lib); /* forward */
static PyObject *_cffi_setup(PyObject *self, PyObject *args)
{
- if (!PyArg_ParseTuple(args, "OO", &_cffi_types, &_cffi_VerificationError))
+ PyObject *library;
+ if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError,
+ &library))
+ return NULL;
+
+ if (_cffi_setup_custom(library) < 0)
return NULL;
Py_INCREF(_cffi_types);
Py_INCREF(_cffi_VerificationError);
- return _cffi_setup_custom();
+ Py_INCREF(Py_None);
+ return Py_None;
}
static void _cffi_init(void)
More information about the pypy-commit
mailing list