Author: Armin Rigo <ar...@tunes.org> Branch: release-1.0 Changeset: r2074:b29ad54a7e0e Date: 2015-05-21 11:27 +0200 http://bitbucket.org/cffi/cffi/changeset/b29ad54a7e0e/
Log: hg merge default diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -6050,7 +6050,7 @@ if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0) INITERROR; - v = PyText_FromString("1.0.0"); + v = PyText_FromString("1.0.1"); if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0) INITERROR; diff --git a/c/misc_win32.h b/c/misc_win32.h --- a/c/misc_win32.h +++ b/c/misc_win32.h @@ -218,8 +218,7 @@ static int dlclose(void *handle) { - FreeLibrary((HMODULE)handle); - return 0; + return !FreeLibrary((HMODULE)handle); } static const char *dlerror(void) diff --git a/c/test_c.py b/c/test_c.py --- a/c/test_c.py +++ b/c/test_c.py @@ -3346,4 +3346,4 @@ def test_version(): # this test is here mostly for PyPy - assert __version__ == "1.0.0" + assert __version__ == "1.0.1" diff --git a/cffi/__init__.py b/cffi/__init__.py --- a/cffi/__init__.py +++ b/cffi/__init__.py @@ -4,8 +4,8 @@ from .api import FFI, CDefError, FFIError from .ffiplatform import VerificationError, VerificationMissing -__version__ = "1.0.0" -__version_info__ = (1, 0, 0) +__version__ = "1.0.1" +__version_info__ = (1, 0, 1) # The verifier module file names are based on the CRC32 of a string that # contains the following version number. It may be older than __version__ diff --git a/cffi/api.py b/cffi/api.py --- a/cffi/api.py +++ b/cffi/api.py @@ -109,6 +109,11 @@ if override: for cache in self._function_caches: cache.clear() + finishlist = self._parser._recomplete + if finishlist: + self._parser._recomplete = [] + for tp in finishlist: + tp.finish_backend_type(self, finishlist) def dlopen(self, name, flags=0): """Load and return a dynamic library identified by 'name'. diff --git a/cffi/cparser.py b/cffi/cparser.py --- a/cffi/cparser.py +++ b/cffi/cparser.py @@ -101,6 +101,7 @@ self._override = False self._packed = False self._int_constants = {} + self._recomplete = [] def _parse(self, csource): csource, macros = _preprocess(csource) @@ -555,6 +556,9 @@ raise NotImplementedError("%s: using both bitfields and '...;'" % (tp,)) tp.packed = self._packed + if tp.completed: # must be re-completed: it is not opaque any more + tp.completed = 0 + self._recomplete.append(tp) return tp def _make_partial(self, tp, nested): @@ -604,19 +608,21 @@ def _build_enum_type(self, explicit_name, decls): if decls is not None: - enumerators1 = [enum.name for enum in decls.enumerators] - enumerators = [s for s in enumerators1 - if not _r_enum_dotdotdot.match(s)] - partial = len(enumerators) < len(enumerators1) - enumerators = tuple(enumerators) + partial = False + enumerators = [] enumvalues = [] nextenumvalue = 0 - for enum in decls.enumerators[:len(enumerators)]: + for enum in decls.enumerators: + if _r_enum_dotdotdot.match(enum.name): + partial = True + continue if enum.value is not None: nextenumvalue = self._parse_constant(enum.value) + enumerators.append(enum.name) enumvalues.append(nextenumvalue) self._add_constants(enum.name, nextenumvalue) nextenumvalue += 1 + enumerators = tuple(enumerators) enumvalues = tuple(enumvalues) tp = model.EnumType(explicit_name, enumerators, enumvalues) tp.partial = partial diff --git a/cffi/model.py b/cffi/model.py --- a/cffi/model.py +++ b/cffi/model.py @@ -293,7 +293,7 @@ class StructOrUnion(StructOrUnionOrEnum): fixedlayout = None - completed = False + completed = 0 partial = False packed = False @@ -351,12 +351,13 @@ "for '%s'" % (self.name,)) return BType = ffi._cached_btypes[self] - if self.fldtypes is None: - return # not completing it: it's an opaque struct # self.completed = 1 # - if self.fixedlayout is None: + if self.fldtypes is None: + pass # not completing it: it's an opaque struct + # + elif self.fixedlayout is None: fldtypes = [tp.get_cached_btype(ffi, finishlist) for tp in self.fldtypes] lst = list(zip(self.fldnames, fldtypes, self.fldbitsize)) diff --git a/cffi/setuptools_ext.py b/cffi/setuptools_ext.py --- a/cffi/setuptools_ext.py +++ b/cffi/setuptools_ext.py @@ -76,7 +76,7 @@ from cffi import recompiler allsources = ['$PLACEHOLDER'] - allsources.extend(kwds.get('sources', [])) + allsources.extend(kwds.pop('sources', [])) ext = Extension(name=module_name, sources=allsources, **kwds) def make_mod(tmpdir): diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst --- a/doc/source/cdef.rst +++ b/doc/source/cdef.rst @@ -5,7 +5,9 @@ There are three or four different ways to use CFFI in a project. In order of complexity: -* The **"in-line", "ABI mode"**:: +* The **"in-line", "ABI mode"**: + + .. code-block:: python import cffi @@ -18,7 +20,9 @@ .. _out-of-line-abi: * The **"out-of-line",** but still **"ABI mode",** useful to organize - the code and reduce the import time:: + the code and reduce the import time: + + .. code-block:: python # in a separate file "package/foo_build.py" import cffi @@ -31,7 +35,9 @@ ffi.compile() Running ``python foo_build.py`` produces a file ``_foo.py``, which - can then be imported in the main program:: + can then be imported in the main program: + + .. code-block:: python from package._foo import ffi lib = ffi.dlopen("libpath") @@ -42,7 +48,9 @@ * The **"out-of-line", "API mode"** gives you the most flexibility to access a C library at the level of C, instead of at the binary - level:: + level: + + .. code-block:: python # in a separate file "package/foo_build.py" import cffi @@ -57,7 +65,9 @@ Running ``python foo_build.py`` produces a file ``_foo.c`` and invokes the C compiler to turn it into a file ``_foo.so`` (or ``_foo.pyd`` or ``_foo.dylib``). It is a C extension module which - can be imported in the main program:: + can be imported in the main program: + + .. code-block:: python from package._foo import ffi, lib # no ffi.dlopen() @@ -68,7 +78,9 @@ * Finally, you can (but don't have to) use CFFI's **Distutils** or **Setuptools integration** when writing a ``setup.py``. For - Distutils (only in out-of-line API mode):: + Distutils (only in out-of-line API mode): + + .. code-block:: python # setup.py (requires CFFI to be installed first) from distutils.core import setup @@ -81,7 +93,9 @@ ) For Setuptools (out-of-line, but works in ABI or API mode; - recommended):: + recommended): + + .. code-block:: python # setup.py (with automatic dependency tracking) from setuptools import setup @@ -95,8 +109,8 @@ Note that CFFI actually contains two different ``FFI`` classes. The page `Using the ffi/lib objects`_ describes the common functionality. -This minimum is what you get in the ``from package._foo import ffi`` -lines above. The extended ``FFI`` class is the one you get from +It is what you get in the ``from package._foo import ffi`` lines above. +On the other hand, the extended ``FFI`` class is the one you get from ``import cffi; ffi = cffi.FFI()``. It has the same functionality (for in-line use), but also the extra methods described below (to prepare the FFI). @@ -111,6 +125,15 @@ split into a different PyPI package that only installs ``_cffi_backend``.) +Note that a few small differences do exist: notably, ``from _foo import +ffi`` returns an object of a type written in C, which does not let you +add random attributes to it (nor does it have all the +underscore-prefixed internal attributes of the Python version). +Similarly, the ``lib`` objects returned by the C version are read-only, +apart from writes to global variables. Also, ``lib.__dict__`` no +longer works (it now tries to look up a hypothetical symbol +``__dict__`` from the C library); use instead ``dir(lib)``. + ffi.cdef(): declaring types and functions ----------------------------------------- @@ -277,6 +300,7 @@ like definitions for custom "wrapper" C functions. The goal is that the .c file can be generated like this:: + // C file "module_name.c" #include <Python.h> ...c_header_source... @@ -297,7 +321,7 @@ least ``libraries=['foo']`` in order to link with ``libfoo.so`` or ``libfoo.so.X.Y``, or ``foo.dll`` on Windows. The ``sources`` is a list of extra .c files compiled and linked together (the file -``module_name.c`` is always generated and automatically added as the +``module_name.c`` shown above is always generated and automatically added as the first argument to ``sources``). See the distutils documentations for `more information about the other arguments`__. @@ -309,7 +333,9 @@ ``source_extension``, defaulting to ``".c"``. The file generated will be actually called ``module_name + source_extension``. Example for C++ (but note that there are still a few known issues of C-versus-C++ -compatibility):: +compatibility): + +.. code-block:: python ffi.set_source("mymodule", ''' extern "C" { @@ -456,6 +482,12 @@ for large projects where one CFFI-based interface depends on some types declared in a different CFFI-based interface. +*Note that you should only use one ffi object per library; the intended +usage of ffi.include() is if you want to interface with several +inter-dependent libraries.* For only one library, make one ``ffi`` +object. (You can write several ``cdef()`` calls over the same ``ffi`` +from several Python files, if one file would be too large.) + For out-of-line modules, the ``ffi.include(other_ffi)`` line should occur in the build script, and the ``other_ffi`` argument should be another FFI that comes from another build script. When the two build @@ -474,11 +506,6 @@ In ABI mode, these must be accessed via the original ``other_lib`` object returned by the ``dlopen()`` method on ``other_ffi``. -*Note that you should only use one ffi object per library; the -intended usage of ffi.include() is if you want to interface with -several inter-dependent libraries.* For only one library, make one -``ffi`` object. - ffi.cdef() limitations ---------------------- @@ -571,7 +598,9 @@ One remaining use case for ``ffi.verify()`` would be the following hack to find explicitly the size of any type, in bytes, and have it available in Python immediately (e.g. because it is needed in order to -write the rest of the build script):: +write the rest of the build script): + +.. code-block:: python ffi = cffi.FFI() ffi.cdef("const int mysize;") @@ -652,7 +681,9 @@ consider moving to the out-of-line approach new in 1.0. Here are the steps. -**ABI mode:** if your CFFI project uses:: +**ABI mode** if your CFFI project uses ``ffi.dlopen()``: + +.. code-block:: python import cffi @@ -668,7 +699,9 @@ .. __: distutils-setuptools_ -**API mode:** if your CFFI project uses:: +**API mode** if your CFFI project uses ``ffi.verify()``: + +.. code-block:: python import cffi @@ -689,7 +722,9 @@ The following example should work both with old (pre-1.0) and new versions of CFFI---supporting both is important to run on PyPy, -because CFFI 1.0 does not work in PyPy < 2.6:: +because CFFI 1.0 does not work in PyPy < 2.6: + +.. code-block:: python # in a separate file "package/foo_build.py" import cffi @@ -710,7 +745,9 @@ if __name__ == "__main__": ffi.compile() -And in the main program:: +And in the main program: + +.. code-block:: python try: from package._foo import ffi, lib @@ -723,7 +760,9 @@ Writing a ``setup.py`` script that works both with CFFI 0.9 and 1.0 requires explicitly checking the version of CFFI that we can have---it -is hard-coded as a built-in module in PyPy:: +is hard-coded as a built-in module in PyPy: + +.. code-block:: python if '_cffi_backend' in sys.builtin_module_names: # PyPy import _cffi_backend @@ -732,7 +771,9 @@ requires_cffi = "cffi>=1.0.0" Then we use the ``requires_cffi`` variable to give different arguments to -``setup()`` as needed, e.g.:: +``setup()`` as needed, e.g.: + +.. code-block:: python if requires_cffi.startswith("cffi==0."): # backward compatibility: we have "cffi==0.*" diff --git a/doc/source/conf.py b/doc/source/conf.py --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -47,7 +47,7 @@ # The short X.Y version. version = '1.0' # The full version, including alpha/beta/rc tags. -release = '1.0.0' +release = '1.0.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -120,7 +120,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +#html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -51,13 +51,13 @@ Download and Installation: -* http://pypi.python.org/packages/source/c/cffi/cffi-1.0.0.tar.gz +* http://pypi.python.org/packages/source/c/cffi/cffi-1.0.1.tar.gz - Or grab the most current version by following the instructions below. - - MD5: e0a938e4880fe60b8d0200e8370f8940 + - MD5: ... - - SHA: c97ff6f3dfc41ba3a762feea8ac13cdafa76a475 + - SHA: ... * Or get it from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` diff --git a/doc/source/overview.rst b/doc/source/overview.rst --- a/doc/source/overview.rst +++ b/doc/source/overview.rst @@ -83,7 +83,9 @@ For distribution purposes, remember that there is a new ``_simple_example.py`` file generated. You can either include it statically within your project's source files, or, with Setuptools, -you can say in the ``setup.py``:: +you can say in the ``setup.py``: + +.. code-block:: python from setuptools import setup @@ -196,6 +198,53 @@ .. _array: http://docs.python.org/library/array.html +.. _performance: + +Purely for performance (API level, out-of-line) +----------------------------------------------- + +A variant of the `section above`__ where the goal is not to call an +existing C library, but to compile and call some C function written +directly in the build script: + +.. __: real-example_ + +.. code-block:: python + + # file "example_build.py" + + from cffi import FFI + ffi = FFI() + + ffi.cdef("int foo(int *, int *, int);") + + ffi.set_source("_example", + """ + static int foo(int *buffer_in, int *buffer_out, int x) + { + /* some algorithm that is seriously faster in C than in Python */ + } + """) + + if __name__ == "__main__": + ffi.compile() + +.. code-block:: python + + # file "example.py" + + from _example import ffi, lib + + buffer_in = ffi.new("int[]", 1000) + # initialize buffer_in here... + + # easier to do all buffer allocations in Python and pass them to C, + # even for output-only arguments + buffer_out = ffi.new("int[]", 1000) + + result = lib.foo(buffer_in, buffer_out, 1000) + + What actually happened? ----------------------- @@ -256,12 +305,20 @@ errors, as usual e.g. if you misdeclare some function's signature. Note that the ``C header`` part can contain arbitrary C code. You can -use it to declare some more helpers written in C. To export these -helpers to Python, put their signature in the ``cdef()`` too. This -can be used for example to wrap "crazy" macros into more standard C -functions. (If all you need is to call "non-crazy" macros, then you -can directly declare them in the ``cdef()`` as if they were -functions.) +use it to declare some more helper functions written in C. To export +these helpers to Python, put their signature in the ``cdef()`` too. +(You can use the ``static`` C keyword, as in ``static int +myhelper(int x) { real_code_here; }``, because these helpers are only +referenced from the "magic" C code that is generated afterwards in the +same C file.) + +This can be used for example to wrap "crazy" macros into more standard +C functions. The extra layer of C can be useful for other reasons +too, like calling functions that expect some complicated argument +structures that you prefer to build in C rather than in Python. On +the other hand, if all you need is to call "function-like" macros, +then you can directly declare them in the ``cdef()`` as if they were +functions. The generated piece of C code should be the same independently on the platform on which you run it, so in simple cases you can simply diff --git a/doc/source/using.rst b/doc/source/using.rst --- a/doc/source/using.rst +++ b/doc/source/using.rst @@ -57,7 +57,9 @@ ownership, so you must keep it alive. As soon as you forget it, then the casted pointer will point to garbage! In other words, the ownership rules are attached to the *wrapper* cdata objects: they are not, and -cannot, be attached to the underlying raw memory.) Example:: +cannot, be attached to the underlying raw memory.) Example: + +.. code-block:: python global_weakkeydict = weakref.WeakKeyDictionary() @@ -102,7 +104,9 @@ place to keep alive the original pointer object (returned by ``ffi.new()``). -Example:: +Example: + +.. code-block:: python # void somefunction(int *); @@ -184,7 +188,9 @@ it all the time. The C99 variable-sized structures are supported too, as long as the -initializer says how long the array should be:: +initializer says how long the array should be: + +.. code-block:: python # typedef struct { int x; int y[]; } foo_t; @@ -267,7 +273,9 @@ When calling C functions, passing arguments follows mostly the same rules as assigning to structure fields, and the return value follows the -same rules as reading a structure field. For example:: +same rules as reading a structure field. For example: + +.. code-block:: python # int foo(short a, int b); @@ -276,7 +284,9 @@ You can pass to ``char *`` arguments a normal Python string (but don't pass a normal Python string to functions that take a ``char *`` -argument and may mutate it!):: +argument and may mutate it!): + +.. code-block:: python # size_t strlen(const char *); @@ -286,14 +296,18 @@ in general, there is no difference between C argument declarations that use ``type *`` or ``type[]``. For example, ``int *`` is fully equivalent to ``int[]`` or ``int[5]``. So you can pass an ``int *`` as -a list of integers:: +a list of integers: + +.. code-block:: python # void do_something_with_array(int *array); lib.do_something_with_array([1, 2, 3, 4, 5]) CFFI supports passing and returning structs to functions and callbacks. -Example:: +Example: + +.. code-block:: python # struct foo_s { int a, b; }; # struct foo_s function_returning_a_struct(void); @@ -319,7 +333,9 @@ function>``). This means you cannot e.g. pass them to some other C function expecting a function pointer argument. Only ``ffi.typeof()`` works on them. If you really need a cdata pointer to the function, -use the following workaround:: +use the following workaround: + +.. code-block:: python ffi.cdef(""" int (*foo)(int a, int b); """) @@ -335,18 +351,22 @@ all the arguments passed in the variable part *must* be cdata objects. This is because it would not be possible to guess, if you wrote this:: - lib.printf("hello, %d\n", 42) + lib.printf("hello, %d\n", 42) # doesn't work! that you really meant the 42 to be passed as a C ``int``, and not a ``long`` or ``long long``. The same issue occurs with ``float`` versus ``double``. So you have to force cdata objects of the C type you want, -if necessary with ``ffi.cast()``:: +if necessary with ``ffi.cast()``: + +.. code-block:: python lib.printf("hello, %d\n", ffi.cast("int", 42)) lib.printf("hello, %ld\n", ffi.cast("long", 42)) lib.printf("hello, %f\n", ffi.cast("double", 42)) -But of course:: +But of course: + +.. code-block:: python lib.printf("hello, %s\n", ffi.new("char[]", "world")) @@ -400,7 +420,9 @@ Note that callbacks of a variadic function type are not supported. A workaround is to add custom C code. In the following example, a callback gets a first argument that counts how many extra ``int`` -arguments are passed:: +arguments are passed: + +.. code-block:: python # file "example_build.py" @@ -427,7 +449,7 @@ } """) -:: +.. code-block:: python # file "example.py" @@ -450,7 +472,9 @@ and the C-level callback is made to return a default value. The returned value in case of errors is 0 or null by default, but can be -specified with the ``error`` keyword argument to ``ffi.callback()``:: +specified with the ``error`` keyword argument to ``ffi.callback()``: + +.. code-block:: python @ffi.callback("int(int, int)", error=-1) @@ -588,7 +612,9 @@ accepts a C type can receive either a string or a pre-parsed ``ctype`` object (and because of caching of the string, there is no real performance difference). It can still be useful in writing typechecks, -e.g.:: +e.g.: + +.. code-block:: python def myfunction(ptr): assert ffi.typeof(ptr) is ffi.typeof("foo_t*") diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -3,6 +3,19 @@ ====================== +1.0.1 +===== + +* ``ffi.set_source()`` crashed if passed a ``sources=[..]`` argument. + Fixed by chrippa on pull request #60. + +* Issue #193: if we use a struct between the first cdef() where it is + declared and another cdef() where its fields are defined, then this + definition was ignored. + +* Enums were buggy if you used too many "..." in their definition. + + 1.0.0 ===== diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -143,7 +143,7 @@ `Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_ """, - version='1.0.0', + version='1.0.1', packages=['cffi'], package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h']}, zip_safe=False, diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py --- a/testing/cffi0/backend_tests.py +++ b/testing/cffi0/backend_tests.py @@ -1703,3 +1703,13 @@ assert lib.DOT_HEX == 0x100 assert lib.DOT_HEX2 == 0x10 assert lib.DOT_UL == 1000 + + def test_opaque_struct_becomes_nonopaque(self): + # Issue #193: if we use a struct between the first cdef() where it is + # declared and another cdef() where its fields are defined, then the + # definition was ignored. + ffi = FFI(backend=self.Backend()) + ffi.cdef("struct foo_s;") + py.test.raises(TypeError, ffi.new, "struct foo_s *") + ffi.cdef("struct foo_s { int x; };") + ffi.new("struct foo_s *") diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py --- a/testing/cffi0/test_verify.py +++ b/testing/cffi0/test_verify.py @@ -764,6 +764,11 @@ assert ffi.string(ffi.cast('enum ee2', -1239)) == 'EE4' assert ffi.string(ffi.cast('enum ee2', -1238)) == 'EE5' +def test_nonfull_enum_bug3(): + ffi = FFI() + ffi.cdef("enum ee2 { EE4=..., EE5=... };") + ffi.cdef("enum ee6 { EE7=10, EE8=..., EE9=... };") + def test_get_set_errno(): ffi = FFI() ffi.cdef("int foo(int);") _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit