[pypy-commit] cffi default: Progress.
arigo
noreply at buildbot.pypy.org
Sun Jul 15 13:34:13 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r646:5a1dd2f000dc
Date: 2012-07-15 13:33 +0200
http://bitbucket.org/cffi/cffi/changeset/5a1dd2f000dc/
Log: Progress.
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -50,6 +50,7 @@
self._parsed_types = new.module('parsed_types').__dict__
self._new_types = new.module('new_types').__dict__
self._function_caches = []
+ self._cdefsources = []
if hasattr(backend, 'set_ffi'):
backend.set_ffi(self)
#
@@ -65,6 +66,7 @@
equiv = 'signed %s'
lines.append('typedef %s %s;' % (equiv % by_size[size], name))
self.cdef('\n'.join(lines))
+ del self._cdefsources[:]
#
self.NULL = self.cast("void *", 0)
@@ -75,6 +77,7 @@
The types can be used in 'ffi.new()' and other functions.
"""
self._parser.parse(csource, override=override)
+ self._cdefsources.append(csource)
if override:
for cache in self._function_caches:
cache.clear()
@@ -226,7 +229,8 @@
which requires binary compatibility in the signatures.
"""
from .verifier import Verifier
- return Verifier(self, source, **kwargs).verify()
+ self.verifier = Verifier(self, source, **kwargs)
+ return self.verifier.load_library()
def _get_errno(self):
return self._backend.get_errno()
diff --git a/cffi/ffiplatform.py b/cffi/ffiplatform.py
--- a/cffi/ffiplatform.py
+++ b/cffi/ffiplatform.py
@@ -10,15 +10,8 @@
cdef, but no verification has been done
"""
-_file_counter = 0
_tmpdir = None
-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 _tmpdir
@@ -31,14 +24,14 @@
return _tmpdir
-def compile(tmpdir, modname, **kwds):
+def compile(tmpdir, srcfilename, modname, **kwds):
"""Compile a C extension module using distutils."""
saved_environ = os.environ.copy()
saved_path = os.getcwd()
try:
os.chdir(tmpdir)
- outputfilename = _build(modname, kwds)
+ outputfilename = _build(srcfilename, modname, kwds)
outputfilename = os.path.abspath(outputfilename)
finally:
os.chdir(saved_path)
@@ -49,12 +42,12 @@
os.environ[key] = value
return outputfilename
-def _build(modname, kwds):
+def _build(srcfilename, modname, kwds):
# XXX compact but horrible :-(
from distutils.core import Distribution, Extension
import distutils.errors
#
- ext = Extension(name=modname, sources=[modname + '.c'], **kwds)
+ ext = Extension(name=modname, sources=[srcfilename], **kwds)
dist = Distribution({'ext_modules': [ext]})
options = dist.get_option_dict('build_ext')
options['force'] = ('ffiplatform', True)
diff --git a/cffi/verifier.py b/cffi/verifier.py
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -1,15 +1,86 @@
-import os
+import sys, os, md5, imp, shutil
from . import model, ffiplatform
+from . import __version__
class Verifier(object):
+ _status = '?'
def __init__(self, ffi, preamble, **kwds):
+ import _cffi_backend
+ if ffi._backend is not _cffi_backend:
+ raise NotImplementedError(
+ "verify() is only available for the _cffi_backend")
+ #
self.ffi = ffi
self.preamble = preamble
self.kwds = kwds
self._typesdict = {}
self._need_size = set()
self._need_size_order = []
+ #
+ m = md5.md5('\x00'.join([sys.version[:3], __version__, preamble] +
+ ffi._cdefsources))
+ modulename = '_cffi_%s' % m.hexdigest()
+ suffix = self._get_so_suffix()
+ self.modulefilename = os.path.join('__pycache__', modulename + suffix)
+ self.sourcefilename = os.path.join('__pycache__', m.hexdigest() + '.c')
+ self._status = 'init'
+
+ def write_source(self, file=None):
+ """Write the C source code. It is produced in 'self.sourcefilename',
+ which can be tweaked beforehand."""
+ if self._status == 'init':
+ self._write_source(file)
+ else:
+ raise ffiplatform.VerificationError("source code already written")
+
+ def compile_module(self):
+ """Write the C source code (if not done already) and compile it.
+ This produces a dynamic link library in 'self.modulefilename'."""
+ if self._status == 'init':
+ self._write_source()
+ if self._status == 'source':
+ self._compile_module()
+ else:
+ raise ffiplatform.VerificationError("module already compiled")
+
+ def load_library(self):
+ """Get a C module from this Verifier instance.
+ Returns an instance of a FFILibrary class that behaves like the
+ objects returned by ffi.dlopen(), but that delegates all
+ operations to the C module. If necessary, the C code is written
+ and compiled first.
+ """
+ if self._status == 'init': # source code not written yet
+ self._locate_module()
+ if self._status == 'init':
+ self._write_source()
+ if self._status == 'source':
+ self._compile_module()
+ assert self._status == 'module'
+ return self._load_library()
+
+ def getmodulename(self):
+ return os.path.splitext(os.path.basename(self.modulefilename))[0]
+
+ # ----------
+
+ @staticmethod
+ def _get_so_suffix():
+ for suffix, mode, type in imp.get_suffixes():
+ if type == imp.C_EXTENSION:
+ return suffix
+ raise ffiplatform.VerificationError("no C_EXTENSION available")
+
+ def _locate_module(self):
+ try:
+ f, filename, descr = imp.find_module(self.getmodulename())
+ except ImportError:
+ return
+ if f is not None:
+ f.close()
+ self.modulefilename = filename
+ self._status = 'module'
def _prnt(self, what=''):
print >> self._f, what
@@ -25,21 +96,20 @@
self._typesdict[type] = num
return num
- def verify(self):
- """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.
- """
- import _cffi_backend
- if self.ffi._backend is not _cffi_backend:
- raise NotImplementedError(
- "verify() is only available for the _cffi_backend")
+ def _write_source(self, file=None):
+ must_close = (file is None)
+ if must_close:
+ file = open(self.sourcefilename, 'w')
+ self._f = file
+ try:
+ self._write_source_to_f()
+ finally:
+ del self._f
+ if must_close:
+ file.close()
+ self._status = 'source'
- modname = ffiplatform.undercffi_module_name()
- tmpdir = ffiplatform.tmpdir()
- filebase = os.path.join(tmpdir, modname)
-
+ def _write_source_to_f(self):
# 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
@@ -48,55 +118,66 @@
# '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'
+ #
+ prnt = self._prnt
+ # first paste some standard set of lines that are mostly '#define'
+ prnt(cffimod_header)
+ prnt()
+ # then paste the C source given by the user, verbatim.
+ prnt(self.preamble)
+ prnt()
+ #
+ # call generate_cpy_xxx_decl(), for every xxx found from
+ # ffi._parser._declarations. This generates all the functions.
+ self._generate("decl")
+ #
+ # implement the function _cffi_setup_custom() as calling the
+ # head of the chained list.
+ self._generate_setup_custom()
+ prnt()
+ #
+ # produce the method table, including the entries for the
+ # generated Python->C function wrappers, which are done
+ # by generate_cpy_function_method().
+ prnt('static PyMethodDef _cffi_methods[] = {')
+ self._generate("method")
+ prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS},')
+ prnt(' {NULL, NULL} /* Sentinel */')
+ prnt('};')
+ prnt()
+ #
+ # standard init.
+ modname = self.getmodulename()
+ prnt('PyMODINIT_FUNC')
+ prnt('init%s(void)' % modname)
+ prnt('{')
+ prnt(' Py_InitModule("%s", _cffi_methods);' % modname)
+ prnt(' _cffi_init();')
+ prnt('}')
- with open(filebase + '.c', 'w') as f:
- self._f = f
- prnt = self._prnt
- # first paste some standard set of lines that are mostly '#define'
- prnt(cffimod_header)
- prnt()
- # then paste the C source given by the user, verbatim.
- prnt(self.preamble)
- prnt()
- #
- # call generate_cpy_xxx_decl(), for every xxx found from
- # ffi._parser._declarations. This generates all the functions.
- self._generate("decl")
- #
- # implement the function _cffi_setup_custom() as calling the
- # head of the chained list.
- self._generate_setup_custom()
- prnt()
- #
- # produce the method table, including the entries for the
- # generated Python->C function wrappers, which are done
- # by generate_cpy_function_method().
- prnt('static PyMethodDef _cffi_methods[] = {')
- self._generate("method")
- prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS},')
- prnt(' {NULL, NULL} /* Sentinel */')
- prnt('};')
- prnt()
- #
- # standard init.
- prnt('PyMODINIT_FUNC')
- prnt('init%s(void)' % modname)
- prnt('{')
- prnt(' Py_InitModule("%s", _cffi_methods);' % modname)
- prnt(' _cffi_init();')
- prnt('}')
- #
- del self._f
+ def _compile_module(self):
+ # compile this C source
+ tmpdir = os.path.dirname(self.sourcefilename)
+ sourcename = os.path.basename(self.sourcefilename)
+ modname = self.getmodulename()
+ outputfilename = ffiplatform.compile(tmpdir, sourcename,
+ modname, **self.kwds)
+ try:
+ same = os.path.samefile(outputfilename, self.modulefilename)
+ except OSError:
+ same = False
+ if not same:
+ shutil.move(outputfilename, self.modulefilename)
+ self._status = 'module'
- # compile this C source
- outputfilename = ffiplatform.compile(tmpdir, modname, **self.kwds)
- #
+ def _load_library(self):
+ # XXX review all usages of 'self' here!
# import it as a new extension module
- import imp
try:
- module = imp.load_dynamic(modname, outputfilename)
+ module = imp.load_dynamic(self.getmodulename(), self.modulefilename)
except ImportError, e:
- raise ffiplatform.VerificationError(str(e))
+ error = "importing %r: %s" % (self.modulefilename, e)
+ raise ffiplatform.VerificationError(error)
#
# call loading_cpy_struct() to get the struct layout inferred by
# the C compiler
diff --git a/testing/test_zdistutils.py b/testing/test_zdistutils.py
--- a/testing/test_zdistutils.py
+++ b/testing/test_zdistutils.py
@@ -42,8 +42,8 @@
csrc = '/*hi there!*/\n#include <math.h>\n'
v = Verifier(ffi, csrc)
v.compile_module()
- assert v.modulename.startswith('_cffi_')
- mod = imp.load_dynamic(v.modulename, v.modulefilename)
+ assert v.getmodulename().startswith('_cffi_')
+ mod = imp.load_dynamic(v.getmodulename(), v.modulefilename)
assert hasattr(mod, '_cffi_setup')
def test_compile_module_explicit_filename():
@@ -51,11 +51,11 @@
ffi.cdef("double sin(double x);")
csrc = '/*hi there!2*/\n#include <math.h>\n'
v = Verifier(ffi, csrc)
- v.modulefilename = filename = str(udir.join('compile_module.so'))
+ v.modulefilename = filename = str(udir.join('test_compile_module.so'))
v.compile_module()
assert filename == v.modulefilename
- assert v.modulename.startswith('_cffi_')
- mod = imp.load_dynamic(v.modulename, v.modulefilename)
+ assert v.getmodulename() == 'test_compile_module'
+ mod = imp.load_dynamic(v.getmodulename(), v.modulefilename)
assert hasattr(mod, '_cffi_setup')
def test_name_from_md5_of_cdef():
@@ -64,7 +64,7 @@
ffi = FFI()
ffi.cdef("%s sin(double x);" % csrc)
v = Verifier(ffi, "#include <math.h>")
- names.append(v.modulename)
+ names.append(v.getmodulename())
assert names[0] == names[1] != names[2]
def test_name_from_md5_of_csrc():
@@ -73,7 +73,7 @@
ffi = FFI()
ffi.cdef("double sin(double x);")
v = Verifier(ffi, csrc)
- names.append(v.modulename)
+ names.append(v.getmodulename())
assert names[0] == names[1] != names[2]
def test_load_library():
@@ -119,5 +119,5 @@
ext = v.get_extension()
assert str(ext.__class__) == 'distutils.extension.Extension'
assert ext.sources == [v.sourcefilename]
- assert ext.name == v.modulename
+ assert ext.name == v.getmodulename()
assert ext.define_macros == [('TEST_EXTENSION_OBJECT', '1')]
More information about the pypy-commit
mailing list