Author: Armin Rigo <ar...@tunes.org> Branch: cffi-1.0 Changeset: r2022:33893ccb35f0 Date: 2015-05-17 10:21 +0200 http://bitbucket.org/cffi/cffi/changeset/33893ccb35f0/
Log: in-progress diff --git a/TODO b/TODO --- a/TODO +++ b/TODO @@ -1,18 +1,10 @@ -* write docs! also, remember to remove ``ext_package=".."`` from - setup.py, which was needed with verify() but is just confusion - with set_source(). - -* document distutils + setuptools + just distributing the C sources - -* version-1.0.0.diff - -* mention todo: cffi-runtime package - * mention todo: ffi.new("xyz") makes {"xyz": <ctype>} always immortal * mention todo: dlopen(), by "compiling" a cdef()-only FFI into a .py module * ffi.set_source() produces a C file that is entirely independent on the OS, what is installed, and the current Python version + +* cffi_modules, now with the *path as a filename*! diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst --- a/doc/source/cdef.rst +++ b/doc/source/cdef.rst @@ -98,7 +98,10 @@ The reason for this split of functionality is that a regular program using CFFI out-of-line does not need to import the ``cffi`` pure Python package at all. (Internally it still needs ``_cffi_backend``, -a C extension module that comes with CFFI.) +a C extension module that comes with CFFI; this is why CFFI is also +listed in ``install_requires=..`` above. In the future this might be +split into a different PyPI package that only installs +``_cffi_backend``.) ffi.cdef(): declaring types and functions @@ -176,6 +179,29 @@ Also, this has no effect on structs declared with ``"...;"``---next section.) +**ffi.set_unicode(enabled_flag)**: Windows: if ``enabled_flag`` is +True, enable the ``UNICODE`` and ``_UNICODE`` defines in C, and +declare the types ``TBYTE TCHAR LPCTSTR PCTSTR LPTSTR PTSTR PTBYTE +PTCHAR`` to be (pointers to) ``wchar_t``. If ``enabled_flag`` is +False, declare these types to be (pointers to) plain 8-bit characters. +(These types are not predeclared at all if you don't call +``set_unicode()``.) *New in version 0.9.* + +The reason behind this method is that a lot of standard functions have +two versions, like ``MessageBoxA()`` and ``MessageBoxW()``. The +official interface is ``MessageBox()`` with arguments like +``LPTCSTR``. Depending on whether ``UNICODE`` is defined or not, the +standard header renames the generic function name to one of the two +specialized versions, and declares the correct (unicode or not) types. + +Usually, the right thing to do is to call this method with True. Be +aware (particularly on Python 2) that, afterwards, you need to pass unicode +strings as arguments instead of not byte strings. (Before cffi version 0.9, +``TCHAR`` and friends where hard-coded as unicode, but ``UNICODE`` was, +inconsistently, not defined by default.) + +.. "versionadded:: 0.9" --- inlined in the previous paragraph + ffi.dlopen(): loading libraries in ABI mode ------------------------------------------- @@ -272,8 +298,8 @@ An extra keyword argument processed internally is ``source_extension``, defaulting to ``".c"``. The file generated will be actually called ``module_name + source_extension``. Example for -C++ (but note that there are a few known issues of C-versus-C++ -compatibility left):: +C++ (but note that there are still a few known issues of C-versus-C++ +compatibility):: ffi.set_source("mymodule", ''' extern "C" { @@ -285,7 +311,7 @@ Letting the C compiler fill the gaps ------------------------------------ -If you are using a C compiler (see `API-level`_), then: +If you are using a C compiler ("API mode"), then: * functions taking or returning integer or float-point arguments can be misdeclared: if e.g. a function is declared by ``cdef()`` as taking a @@ -378,35 +404,58 @@ mode (i.e. checks that ``ffi.compile()`` would have generated a Python file). The file to write is explicitly named. -**ffi.emit_c_code(filename):** generate the given .c file. +**ffi.emit_c_code(filename):** generate the given .c file (for API +mode) without compiling it. Can be used if you have some other method +to compile it, e.g. if you want to integrate with some larger build +system that will compile this file for you. +**ffi.distutils_extension(tmpdir='build', verbose=True):** for +distutils-based ``setup.py`` files. Calling this creates the .c file +if needed in the given ``tmpdir``, and returns a +``distutils.core.Extension`` instance. +For Setuptools, you use instead the line +``cffi_modules=["path/to/foo_build.py:ffi"]`` in ``setup.py``. This +line will internally cause Setuptools to call +``cffi.setuptools_ext.cffi_modules()``, which writes the .c file and +attaches an ``Extension`` instance automatically. +ffi.include(): combining multiple CFFI interfaces +------------------------------------------------- -**ffi.include(other_ffi)**: includes the typedefs, structs, unions, enums -and constants defined in another FFI instance. Usage is similar to a -``#include`` in C, where a part of the program might include types -defined in another part for its own usage. Note that the include() -method has no effect on functions, constants and global variables, which -must anyway be accessed directly from the ``lib`` object returned by the -original FFI instance. *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. (If the source becomes too large, -split it up e.g. by collecting the cdef/verify strings from multiple -Python modules, as long as you call ``ffi.verify()`` only once.) *New -in version 0.5.* +**ffi.include(other_ffi)**: includes the typedefs, structs, unions, +enums and constants defined in another FFI instance. This is meant +for large projects where one CFFI-based interface depends on some +types or functions declared in a different CFFI-based interface. -.. "versionadded:: 0.5" --- inlined in the previous paragraph +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 +scripts are turned into generated files, say ``_ffi.so`` and +``_other_ffi.so``, then importing ``_ffi.so`` will internally cause +``_other_ffi.so`` to be imported. +The usage of ``ffi.include()`` is the cdef-level equivalent of a +``#include`` in C, where a part of the program might include types and +functions defined in another part for its own usage. You can see on +the ``ffi`` object (and associated ``lib`` objects on the *including* +side) the types and constants declared on the included side. In API +mode, you can also see the functions and global variables directly. +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. -Unimplemented features +ffi.cdef() limitations ---------------------- -All of the ANSI C declarations should be supported, and some of C99. +All of the ANSI C *declarations* should be supported in ``cdef()``, +and some of C99. (This excludes any ``#include`` or ``#ifdef``.) Known missing features that are GCC or MSVC extensions: * Any ``__attribute__`` or ``#pragma pack(n)`` @@ -424,16 +473,12 @@ * Thread-local variables (access them via getter/setter functions) -.. _`variable-length array`: - -.. versionadded:: 0.8 - Now supported: variable-length structures, i.e. whose last field is - a variable-length array. - Note that since version 0.8, declarations like ``int field[];`` in -structures are interpreted as variable-length structures. When used for -structures that are not, in fact, variable-length, it works too; in this -case, the difference with using ``int field[...];`` is that, as CFFI +structures are interpreted as variable-length structures. Declarations +like ``int field[...];`` on the other hand are arrays whose length is +going to be completed by the compiler. You can use ``int field[];`` +for array fields that are not, in fact, variable-length; it works too, +but in this case, as CFFI believes it cannot ask the C compiler for the length of the array, you get reduced safety checks: for example, you risk overwriting the following fields by passing too many array items in the constructor. @@ -477,55 +522,31 @@ about unresolved symbols. +ffi.verify(): in-line API-mode +------------------------------ +**ffi.verify()** is supported for backward compatibility, but is +deprecated. ``ffi.verify(c_header_source, tmpdir=.., ext_package=.., +modulename=.., flags=.., **kwargs)`` makes and compiles a C file from +the ``ffi.cdef()``, like ``ffi.set_source()`` in API mode, and then +immediately loads and returns the dynamic library object. +The ``c_header_source`` and the extra keyword arguments have the +same meaning as in ``ffi.set_source()``. +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):: + ffi = cffi.FFI() + ffi.cdef("const int mysize;") + lib = ffi.verify("const int mysize = sizeof(THE_TYPE);") + print lib.mysize -**ffi.set_unicode(enabled_flag)**: Windows: if ``enabled_flag`` is -True, enable the ``UNICODE`` and ``_UNICODE`` defines in C, and -declare the types ``TBYTE TCHAR LPCTSTR PCTSTR LPTSTR PTSTR PTBYTE -PTCHAR`` to be (pointers to) ``wchar_t``. If ``enabled_flag`` is -False, declare these types to be (pointers to) plain 8-bit characters. -(These types are not predeclared at all if you don't call -``set_unicode()``.) *New in version 0.9.* - -The reason behind this method is that a lot of standard functions have -two versions, like ``MessageBoxA()`` and ``MessageBoxW()``. The -official interface is ``MessageBox()`` with arguments like -``LPTCSTR``. Depending on whether ``UNICODE`` is defined or not, the -standard header renames the generic function name to one of the two -specialized versions, and declares the correct (unicode or not) types. - -Usually, the right thing to do is to call this method with True. Be -aware (particularly on Python 2) that, afterwards, you need to pass unicode -strings as arguments instead of not byte strings. (Before cffi version 0.9, -``TCHAR`` and friends where hard-coded as unicode, but ``UNICODE`` was, -inconsistently, not defined by default.) - -.. "versionadded:: 0.9" --- inlined in the previous paragraph - - -Reference: verifier -------------------- - -missing - - - - -* ``source``: C code that is pasted verbatim in the generated code (it - is *not* parsed internally). It should contain at least the - necessary ``#include``. It can also contain the complete - implementation of some functions declared in ``cdef()``; this is - useful if you really need to write a piece of C code, e.g. to access - some advanced macros (see the example of ``getyx()`` in - `demo/_curses.py`_). - -.. _`demo/_curses.py`: https://bitbucket.org/cffi/cffi/src/default/demo/_curses.py - -.. versionadded:: 0.4 - The ``tmpdir`` argument to ``verify()`` controls where the C +Extra arguments to ``ffi.verify()``: + +* ``tmpdir`` controls where the C files are created and compiled. Unless the ``CFFI_TMPDIR`` environment variable is set, the default is ``directory_containing_the_py_file/__pycache__`` using the @@ -534,18 +555,15 @@ consistent with the location of the .pyc files for your library. The name ``__pycache__`` itself comes from Python 3.) - The ``ext_package`` argument controls in which package the +* ``ext_package`` controls in which package the compiled extension module should be looked from. This is - only useful after `distributing modules using CFFI`_. + only useful after distributing ffi.verify()-based modules. - The ``tag`` argument gives an extra string inserted in the +* The ``tag`` argument gives an extra string inserted in the middle of the extension module's name: ``_cffi_<tag>_<hash>``. Useful to give a bit more context, e.g. when debugging. -.. _`warning about modulename`: - -.. versionadded:: 0.5 - The ``modulename`` argument can be used to force a specific module +* The ``modulename`` argument can be used to force a specific module name, overriding the name ``_cffi_<tag>_<hash>``. Use with care, e.g. if you are passing variable information to ``verify()`` but still want the module name to be always the same (e.g. absolute @@ -554,51 +572,48 @@ check. Be sure to have other means of clearing the ``tmpdir`` whenever you change your sources. +* ``source_extension`` has the same meaning as in + ``ffi.set_source()``. -.. versionadded:: 0.9 - The optional ``flags`` argument has been added, see ``man dlopen`` (ignored - on Windows). It defaults to ``ffi.RTLD_NOW``. +* The optional ``flags`` argument has been added, see ``man dlopen`` + (ignored on Windows). It defaults to ``ffi.RTLD_NOW``. (With + ``ffi.set_source()``, you would use ``sys.setdlopenflags()``.) -.. versionadded:: 0.9 - The optional ``relative_to`` argument is useful if you need to list - local files passed to the C compiler: - -:: +* The optional ``relative_to`` argument is useful if you need to list + local files passed to the C compiler:: ext = ffi.verify(..., sources=['foo.c'], relative_to=__file__) -The line above is roughly the same as:: + The line above is roughly the same as:: ext = ffi.verify(..., sources=['/path/to/this/file/foo.c']) -except that the default name of the produced library is built from the -CRC checkum of the argument ``sources``, as well as most other arguments -you give to ``ffi.verify()`` -- but not ``relative_to``. So if you used -the second line, it would stop finding the already-compiled library -after your project is installed, because the ``'/path/to/this/file'`` -suddenly changed. The first line does not have this problem. + except that the default name of the produced library is built from + the CRC checkum of the argument ``sources``, as well as most other + arguments you give to ``ffi.verify()`` -- but not ``relative_to``. + So if you used the second line, it would stop finding the + already-compiled library after your project is installed, because + the ``'/path/to/this/file'`` suddenly changed. The first line does + not have this problem. +Note that during development, every time you change the C sources that +you pass to ``cdef()`` or ``verify()``, then the latter will create a +new module file name, based on two CRC32 hashes computed from these +strings. This creates more and more files in the ``__pycache__`` +directory. It is recommended that you clean it up from time to time. +A nice way to do that is to add, in your test suite, a call to +``cffi.verifier.cleanup_tmpdir()``. Alternatively, you can just +completely remove the ``__pycache__`` directory. +An alternative cache directory can be given as the ``tmpdir`` argument +to ``verify()``, via the environment variable ``CFFI_TMPDIR``, or by +calling ``cffi.verifier.set_tmpdir(path)`` prior to calling +``verify``. +Upgrading from CFFI 0.9 to CFFI 1.0 +----------------------------------- +xxx also, remember to remove ``ext_package=".."`` from setup.py, which +was needed with verify() but is just confusion with set_source(). -.. __: `Declaring types and functions`_ - -Note the following hack to find explicitly the size of any type, in -bytes:: - - ffi.cdef("const int mysize;") - lib = ffi.verify("const int mysize = sizeof(THE_TYPE);") - print lib.mysize - -Note that this approach is meant to call C libraries that are *not* using -``#include <Python.h>``. The C functions are called without the GIL, -and afterwards we don't check if they set a Python exception, for -example. You may work around it, but mixing CFFI with ``Python.h`` is -not recommended. - - - - -cffi_modules, now with the *path as a filename*! diff --git a/doc/source/using.rst b/doc/source/using.rst --- a/doc/source/using.rst +++ b/doc/source/using.rst @@ -822,6 +822,11 @@ `(**)` C function calls are done with the GIL released. + Note that we assume that the called functions are *not* using the + Python API from Python.h. For example, we don't check afterwards + if they set a Python exception. You may work around it, but mixing + CFFI with ``Python.h`` is not recommended. + `(***)` ``long double`` support: We keep ``long double`` values inside a cdata object to avoid _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit