2016-01-31 14:44:46 +00:00
import sys , types
from . lock import allocate_lock
2019-01-20 10:35:31 +00:00
from . error import CDefError
from . import model
2016-01-31 14:44:46 +00:00
try :
callable
except NameError :
# Python 3.1
from collections import Callable
callable = lambda x : isinstance ( x , Callable )
try :
basestring
except NameError :
# Python 3.x
basestring = str
class FFI ( object ) :
r '''
The main top - level class that you instantiate once , or once per module .
Example usage :
ffi = FFI ( )
ffi . cdef ( """
int printf ( const char * , . . . ) ;
""" )
C = ffi . dlopen ( 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 ) :
""" Create an FFI instance. The ' backend ' argument is used to
select a non - default backend , mostly for tests .
"""
if backend is None :
# You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with
# _cffi_backend.so compiled.
import _cffi_backend as backend
from . import __version__
2019-01-20 10:35:31 +00:00
if backend . __version__ != __version__ :
# bad version! Try to be as explicit as possible.
if hasattr ( backend , ' __file__ ' ) :
# CPython
raise Exception ( " Version mismatch: this is the ' cffi ' package version %s , located in %r . When we import the top-level ' _cffi_backend ' extension module, we get version %s , located in %r . The two versions should be equal; check your installation. " % (
__version__ , __file__ ,
backend . __version__ , backend . __file__ ) )
else :
# PyPy
raise Exception ( " Version mismatch: this is the ' cffi ' package version %s , located in %r . This interpreter comes with a built-in ' _cffi_backend ' module, which is version %s . The two versions should be equal; check your installation. " % (
__version__ , __file__ , backend . __version__ ) )
2016-01-31 14:44:46 +00:00
# (If you insist you can also try to pass the option
# 'backend=backend_ctypes.CTypesBackend()', but don't
# rely on it! It's probably not going to work well.)
2019-01-20 10:35:31 +00:00
from . import cparser
2016-01-31 14:44:46 +00:00
self . _backend = backend
self . _lock = allocate_lock ( )
self . _parser = cparser . Parser ( )
self . _cached_btypes = { }
self . _parsed_types = types . ModuleType ( ' parsed_types ' ) . __dict__
self . _new_types = types . ModuleType ( ' new_types ' ) . __dict__
self . _function_caches = [ ]
self . _libraries = [ ]
self . _cdefsources = [ ]
self . _included_ffis = [ ]
self . _windows_unicode = None
self . _init_once_cache = { }
self . _cdef_version = None
self . _embedding = None
2019-01-20 10:35:31 +00:00
self . _typecache = model . get_typecache ( backend )
2016-01-31 14:44:46 +00:00
if hasattr ( backend , ' set_ffi ' ) :
backend . set_ffi ( self )
2019-01-20 10:35:31 +00:00
for name in list ( backend . __dict__ ) :
2016-01-31 14:44:46 +00:00
if name . startswith ( ' RTLD_ ' ) :
setattr ( self , name , getattr ( backend , name ) )
#
with self . _lock :
self . BVoidP = self . _get_cached_btype ( model . voidp_type )
self . BCharA = self . _get_cached_btype ( model . char_array_type )
if isinstance ( backend , types . ModuleType ) :
# _cffi_backend: attach these constants to the class
if not hasattr ( FFI , ' NULL ' ) :
FFI . NULL = self . cast ( self . BVoidP , 0 )
FFI . CData , FFI . CType = backend . _get_types ( )
else :
# ctypes backend: attach these constants to the instance
self . NULL = self . cast ( self . BVoidP , 0 )
self . CData , self . CType = backend . _get_types ( )
2019-01-20 10:35:31 +00:00
self . buffer = backend . buffer
2016-01-31 14:44:46 +00:00
def cdef ( self , csource , override = False , packed = False ) :
""" Parse the given C source. This registers all declared functions,
types , and global variables . The functions and global variables can
then be accessed via either ' ffi.dlopen() ' or ' ffi.verify() ' .
The types can be used in ' ffi.new() ' and other functions .
If ' packed ' is specified as True , all structs declared inside this
cdef are packed , i . e . laid out without any field alignment at all .
"""
self . _cdef ( csource , override = override , packed = packed )
def embedding_api ( self , csource , packed = False ) :
self . _cdef ( csource , packed = packed , dllexport = True )
if self . _embedding is None :
self . _embedding = ' '
def _cdef ( self , csource , override = False , * * options ) :
if not isinstance ( csource , str ) : # unicode, on Python 2
if not isinstance ( csource , basestring ) :
raise TypeError ( " cdef() argument must be a string " )
csource = csource . encode ( ' ascii ' )
with self . _lock :
self . _cdef_version = object ( )
self . _parser . parse ( csource , override = override , * * options )
self . _cdefsources . append ( csource )
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 ' .
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 , basestring ) or name is None
with self . _lock :
lib , function_cache = _make_ffi_library ( self , name , flags )
self . _function_caches . append ( function_cache )
self . _libraries . append ( lib )
return lib
2019-01-20 10:35:31 +00:00
def dlclose ( self , lib ) :
""" Close a library obtained with ffi.dlopen(). After this call,
access to functions or variables from the library will fail
( possibly with a segmentation fault ) .
"""
type ( lib ) . __cffi_close__ ( lib )
2016-01-31 14:44:46 +00:00
def _typeof_locked ( self , cdecl ) :
# call me with the lock!
key = cdecl
if key in self . _parsed_types :
return self . _parsed_types [ key ]
#
if not isinstance ( cdecl , str ) : # unicode, on Python 2
cdecl = cdecl . encode ( ' ascii ' )
#
type = self . _parser . parse_type ( cdecl )
really_a_function_type = type . is_raw_function
if really_a_function_type :
type = type . as_function_pointer ( )
btype = self . _get_cached_btype ( type )
result = btype , really_a_function_type
self . _parsed_types [ key ] = result
return result
def _typeof ( self , cdecl , consider_function_as_funcptr = False ) :
# string -> ctype object
try :
result = self . _parsed_types [ cdecl ]
except KeyError :
with self . _lock :
result = self . _typeof_locked ( cdecl )
#
btype , really_a_function_type = result
if really_a_function_type and not consider_function_as_funcptr :
raise CDefError ( " the type %r is a function type, not a "
" pointer-to-function type " % ( cdecl , ) )
return btype
def typeof ( self , cdecl ) :
""" Parse the C type given as a string and return the
corresponding < ctype > object .
It can also be used on ' cdata ' instance to get its C type .
"""
if isinstance ( cdecl , basestring ) :
return self . _typeof ( cdecl )
if isinstance ( cdecl , self . CData ) :
return self . _backend . typeof ( cdecl )
if isinstance ( cdecl , types . BuiltinFunctionType ) :
res = _builtin_function_type ( cdecl )
if res is not None :
return res
if ( isinstance ( cdecl , types . FunctionType )
and hasattr ( cdecl , ' _cffi_base_type ' ) ) :
with self . _lock :
return self . _get_cached_btype ( cdecl . _cffi_base_type )
raise TypeError ( type ( cdecl ) )
def sizeof ( self , cdecl ) :
""" Return the size in bytes of the argument. It can be a
string naming a C type , or a ' cdata ' instance .
"""
if isinstance ( cdecl , basestring ) :
BType = self . _typeof ( cdecl )
return self . _backend . sizeof ( BType )
else :
return self . _backend . sizeof ( cdecl )
def alignof ( self , cdecl ) :
""" Return the natural alignment size in bytes of the C type
given as a string .
"""
if isinstance ( cdecl , basestring ) :
cdecl = self . _typeof ( cdecl )
return self . _backend . alignof ( cdecl )
def offsetof ( self , cdecl , * fields_or_indexes ) :
""" Return the offset of the named field inside the given
2019-01-20 10:35:31 +00:00
structure or array , which must be given as a C type name .
2016-01-31 14:44:46 +00:00
You can give several field names in case of nested structures .
You can also give numeric values which correspond to array
items , in case of an array type .
"""
if isinstance ( cdecl , basestring ) :
cdecl = self . _typeof ( cdecl )
return self . _typeoffsetof ( cdecl , * fields_or_indexes ) [ 1 ]
def new ( self , cdecl , init = None ) :
""" Allocate an instance according to the specified C type and
return a pointer to it . The specified C type must be either a
pointer or an array : ` ` new ( ' X * ' ) ` ` allocates an X and returns
a pointer to it , whereas ` ` new ( ' X[n] ' ) ` ` allocates an array of
n X ' es and returns an array referencing it (which works
mostly like a pointer , like in C ) . You can also use
` ` new ( ' X[] ' , n ) ` ` to allocate an array of a non - constant
length n .
The memory is initialized following the rules of declaring a
global variable in C : by default it is zero - initialized , but
an explicit initializer can be given which can be used to
fill all or part of the memory .
When the returned < cdata > object goes out of scope , the memory
is freed . In other words the returned < cdata > object has
ownership of the value of type ' cdecl ' that it points to . This
means that the raw data can be used as long as this object is
kept alive , but must not be used for a longer time . Be careful
about that when copying the pointer to the memory somewhere
else , e . g . into another structure .
"""
if isinstance ( cdecl , basestring ) :
cdecl = self . _typeof ( cdecl )
return self . _backend . newp ( cdecl , init )
def new_allocator ( self , alloc = None , free = None ,
should_clear_after_alloc = True ) :
""" Return a new allocator, i.e. a function that behaves like ffi.new()
but uses the provided low - level ' alloc ' and ' free ' functions .
' alloc ' is called with the size as argument . If it returns NULL , a
MemoryError is raised . ' free ' is called with the result of ' alloc '
as argument . Both can be either Python function or directly C
functions . If ' free ' is None , then no free function is called .
If both ' alloc ' and ' free ' are None , the default is used .
If ' should_clear_after_alloc ' is set to False , then the memory
returned by ' alloc ' is assumed to be already cleared ( or you are
fine with garbage ) ; otherwise CFFI will clear it .
"""
compiled_ffi = self . _backend . FFI ( )
allocator = compiled_ffi . new_allocator ( alloc , free ,
should_clear_after_alloc )
def allocate ( cdecl , init = None ) :
if isinstance ( cdecl , basestring ) :
cdecl = self . _typeof ( cdecl )
return allocator ( cdecl , init )
return allocate
def cast ( self , cdecl , source ) :
""" Similar to a C cast: returns an instance of the named C
type initialized with the given ' source ' . The source is
casted between integers or pointers of any type .
"""
if isinstance ( cdecl , basestring ) :
cdecl = self . _typeof ( cdecl )
return self . _backend . cast ( cdecl , source )
def string ( self , cdata , maxlen = - 1 ) :
""" Return a Python string (or unicode string) from the ' cdata ' .
If ' cdata ' is a pointer or array of characters or bytes , returns
the null - terminated string . The returned string extends until
the first null character , or at most ' maxlen ' characters . If
' cdata ' is an array then ' maxlen ' defaults to its length .
If ' cdata ' is a pointer or array of wchar_t , returns a unicode
string following the same rules .
If ' cdata ' is a single character or byte or a wchar_t , returns
it as a string or unicode string .
If ' cdata ' is an enum , returns the value of the enumerator as a
string , or ' NUMBER ' if the value is out of range .
"""
return self . _backend . string ( cdata , maxlen )
2019-01-20 10:35:31 +00:00
def unpack ( self , cdata , length ) :
""" Unpack an array of C data of the given length,
returning a Python string / unicode / list .
If ' cdata ' is a pointer to ' char ' , returns a byte string .
It does not stop at the first null . This is equivalent to :
ffi . buffer ( cdata , length ) [ : ]
2016-01-31 14:44:46 +00:00
2019-01-20 10:35:31 +00:00
If ' cdata ' is a pointer to ' wchar_t ' , returns a unicode string .
' length ' is measured in wchar_t ' s; it is not the size in bytes.
If ' cdata ' is a pointer to anything else , returns a list of
' length ' items . This is a faster equivalent to :
[ cdata [ i ] for i in range ( length ) ]
2016-01-31 14:44:46 +00:00
"""
2019-01-20 10:35:31 +00:00
return self . _backend . unpack ( cdata , length )
#def buffer(self, cdata, size=-1):
# """Return a read-write buffer object that references the raw C data
# pointed to by the given 'cdata'. The 'cdata' must be a pointer or
# an array. Can be passed to functions expecting a buffer, or directly
# manipulated with:
#
# buf[:] get a copy of it in a regular string, or
# buf[idx] as a single character
# buf[:] = ...
# buf[idx] = ... change the content
# """
# note that 'buffer' is a type, set on this instance by __init__
2016-01-31 14:44:46 +00:00
def from_buffer ( self , python_buffer ) :
""" Return a <cdata ' char[] ' > that points to the data of the
given Python object , which must support the buffer interface .
2019-01-20 10:35:31 +00:00
Note that this is not meant to be used on the built - in types
str or unicode ( you can build ' char[] ' arrays explicitly )
2016-01-31 14:44:46 +00:00
but only on objects containing large quantities of raw data
in some other format , like ' array.array ' or numpy arrays .
"""
return self . _backend . from_buffer ( self . BCharA , python_buffer )
def memmove ( self , dest , src , n ) :
""" ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.
Like the C function memmove ( ) , the memory areas may overlap ;
apart from that it behaves like the C function memcpy ( ) .
' src ' can be any cdata ptr or array , or any Python buffer object .
' dest ' can be any cdata ptr or array , or a writable Python buffer
object . The size to copy , ' n ' , is always measured in bytes .
Unlike other methods , this one supports all Python buffer including
byte strings and bytearrays - - - but it still does not support
non - contiguous buffers .
"""
return self . _backend . memmove ( dest , src , n )
def callback ( self , cdecl , python_callable = None , error = None , onerror = None ) :
""" Return a callback object or a decorator making such a
callback object . ' cdecl ' must name a C function pointer type .
The callback invokes the specified ' python_callable ' ( which may
be provided either directly or via a decorator ) . Important : the
callback object must be manually kept alive for as long as the
callback may be invoked from the C level .
"""
def callback_decorator_wrap ( python_callable ) :
if not callable ( python_callable ) :
raise TypeError ( " the ' python_callable ' argument "
" is not callable " )
return self . _backend . callback ( cdecl , python_callable ,
error , onerror )
if isinstance ( cdecl , basestring ) :
cdecl = self . _typeof ( cdecl , consider_function_as_funcptr = True )
if python_callable is None :
return callback_decorator_wrap # decorator mode
else :
return callback_decorator_wrap ( python_callable ) # direct mode
def getctype ( self , cdecl , replace_with = ' ' ) :
""" Return a string giving the C type ' cdecl ' , which may be itself
a string or a < ctype > object . If ' replace_with ' is given , it gives
extra text to append ( or insert for more complicated C types ) , like
a variable name , or ' * ' to get actually the C type ' pointer-to-cdecl ' .
"""
if isinstance ( cdecl , basestring ) :
cdecl = self . _typeof ( cdecl )
replace_with = replace_with . strip ( )
if ( replace_with . startswith ( ' * ' )
and ' &[ ' in self . _backend . getcname ( cdecl , ' & ' ) ) :
replace_with = ' ( %s ) ' % replace_with
elif replace_with and not replace_with [ 0 ] in ' [( ' :
replace_with = ' ' + replace_with
return self . _backend . getcname ( cdecl , replace_with )
2019-01-20 10:35:31 +00:00
def gc ( self , cdata , destructor , size = 0 ) :
2016-01-31 14:44:46 +00:00
""" Return a new cdata object that points to the same
data . Later , when this new cdata object is garbage - collected ,
' destructor(old_cdata_object) ' will be called .
2019-01-20 10:35:31 +00:00
The optional ' size ' gives an estimate of the size , used to
trigger the garbage collection more eagerly . So far only used
on PyPy . It tells the GC that the returned object keeps alive
roughly ' size ' bytes of external memory .
2016-01-31 14:44:46 +00:00
"""
2019-01-20 10:35:31 +00:00
return self . _backend . gcp ( cdata , destructor , size )
2016-01-31 14:44:46 +00:00
def _get_cached_btype ( self , type ) :
assert self . _lock . acquire ( False ) is False
# call me with the lock!
try :
BType = self . _cached_btypes [ type ]
except KeyError :
finishlist = [ ]
BType = type . get_cached_btype ( self , finishlist )
for type in finishlist :
type . finish_backend_type ( self , finishlist )
return BType
def verify ( self , source = ' ' , tmpdir = None , * * kwargs ) :
""" 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.dlopen() ' ,
which requires binary compatibility in the signatures .
"""
from . verifier import Verifier , _caller_dir_pycache
#
# If set_unicode(True) was called, insert the UNICODE and
# _UNICODE macro declarations
if self . _windows_unicode :
self . _apply_windows_unicode ( kwargs )
#
# Set the tmpdir here, and not in Verifier.__init__: it picks
# up the caller's directory, which we want to be the caller of
# ffi.verify(), as opposed to the caller of Veritier().
tmpdir = tmpdir or _caller_dir_pycache ( )
#
# Make a Verifier() and use it to load the library.
self . verifier = Verifier ( self , source , tmpdir , * * kwargs )
lib = self . verifier . load_library ( )
#
# Save the loaded library for keep-alive purposes, even
# if the caller doesn't keep it alive itself (it should).
self . _libraries . append ( lib )
return lib
def _get_errno ( self ) :
return self . _backend . get_errno ( )
def _set_errno ( self , errno ) :
self . _backend . set_errno ( errno )
errno = property ( _get_errno , _set_errno , None ,
" the value of ' errno ' from/to the C calls " )
def getwinerror ( self , code = - 1 ) :
return self . _backend . getwinerror ( code )
def _pointer_to ( self , ctype ) :
with self . _lock :
return model . pointer_cache ( self , ctype )
def addressof ( self , cdata , * fields_or_indexes ) :
""" Return the address of a <cdata ' struct-or-union ' >.
If ' fields_or_indexes ' are given , returns the address of that
field or array item in the structure or array , recursively in
case of nested structures .
"""
2019-01-20 10:35:31 +00:00
try :
ctype = self . _backend . typeof ( cdata )
except TypeError :
if ' __addressof__ ' in type ( cdata ) . __dict__ :
return type ( cdata ) . __addressof__ ( cdata , * fields_or_indexes )
raise
2016-01-31 14:44:46 +00:00
if fields_or_indexes :
ctype , offset = self . _typeoffsetof ( ctype , * fields_or_indexes )
else :
if ctype . kind == " pointer " :
raise TypeError ( " addressof(pointer) " )
offset = 0
ctypeptr = self . _pointer_to ( ctype )
return self . _backend . rawaddressof ( ctypeptr , cdata , offset )
def _typeoffsetof ( self , ctype , field_or_index , * fields_or_indexes ) :
ctype , offset = self . _backend . typeoffsetof ( ctype , field_or_index )
for field1 in fields_or_indexes :
ctype , offset1 = self . _backend . typeoffsetof ( ctype , field1 , 1 )
offset + = offset1
return ctype , offset
def include ( self , ffi_to_include ) :
""" Includes the typedefs, structs, unions and enums 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 .
"""
if not isinstance ( ffi_to_include , FFI ) :
raise TypeError ( " ffi.include() expects an argument that is also of "
" type cffi.FFI, not %r " % (
type ( ffi_to_include ) . __name__ , ) )
if ffi_to_include is self :
raise ValueError ( " self.include(self) " )
with ffi_to_include . _lock :
with self . _lock :
self . _parser . include ( ffi_to_include . _parser )
self . _cdefsources . append ( ' [ ' )
self . _cdefsources . extend ( ffi_to_include . _cdefsources )
self . _cdefsources . append ( ' ] ' )
self . _included_ffis . append ( ffi_to_include )
def new_handle ( self , x ) :
return self . _backend . newp_handle ( self . BVoidP , x )
def from_handle ( self , x ) :
return self . _backend . from_handle ( x )
def set_unicode ( self , enabled_flag ) :
""" Windows: if ' enabled_flag ' is True, enable the UNICODE and
_UNICODE defines in C , and declare the types like TCHAR and LPTCSTR
to be ( pointers to ) wchar_t . If ' enabled_flag ' is False ,
declare these types to be ( pointers to ) plain 8 - bit characters .
This is mostly for backward compatibility ; you usually want True .
"""
if self . _windows_unicode is not None :
raise ValueError ( " set_unicode() can only be called once " )
enabled_flag = bool ( enabled_flag )
if enabled_flag :
self . cdef ( " typedef wchar_t TBYTE; "
" typedef wchar_t TCHAR; "
" typedef const wchar_t *LPCTSTR; "
" typedef const wchar_t *PCTSTR; "
" typedef wchar_t *LPTSTR; "
" typedef wchar_t *PTSTR; "
" typedef TBYTE *PTBYTE; "
" typedef TCHAR *PTCHAR; " )
else :
self . cdef ( " typedef char TBYTE; "
" typedef char TCHAR; "
" typedef const char *LPCTSTR; "
" typedef const char *PCTSTR; "
" typedef char *LPTSTR; "
" typedef char *PTSTR; "
" typedef TBYTE *PTBYTE; "
" typedef TCHAR *PTCHAR; " )
self . _windows_unicode = enabled_flag
def _apply_windows_unicode ( self , kwds ) :
defmacros = kwds . get ( ' define_macros ' , ( ) )
if not isinstance ( defmacros , ( list , tuple ) ) :
raise TypeError ( " ' define_macros ' must be a list or tuple " )
defmacros = list ( defmacros ) + [ ( ' UNICODE ' , ' 1 ' ) ,
( ' _UNICODE ' , ' 1 ' ) ]
kwds [ ' define_macros ' ] = defmacros
def _apply_embedding_fix ( self , kwds ) :
# must include an argument like "-lpython2.7" for the compiler
def ensure ( key , value ) :
lst = kwds . setdefault ( key , [ ] )
if value not in lst :
lst . append ( value )
#
if ' __pypy__ ' in sys . builtin_module_names :
2019-01-20 10:35:31 +00:00
import os
if sys . platform == " win32 " :
# we need 'libpypy-c.lib'. Current distributions of
# pypy (>= 4.1) contain it as 'libs/python27.lib'.
pythonlib = " python27 "
if hasattr ( sys , ' prefix ' ) :
ensure ( ' library_dirs ' , os . path . join ( sys . prefix , ' libs ' ) )
else :
# we need 'libpypy-c.{so,dylib}', which should be by
# default located in 'sys.prefix/bin' for installed
# systems.
if sys . version_info < ( 3 , ) :
pythonlib = " pypy-c "
else :
pythonlib = " pypy3-c "
if hasattr ( sys , ' prefix ' ) :
ensure ( ' library_dirs ' , os . path . join ( sys . prefix , ' bin ' ) )
# On uninstalled pypy's, the libpypy-c is typically found in
# .../pypy/goal/.
2016-01-31 14:44:46 +00:00
if hasattr ( sys , ' prefix ' ) :
2019-01-20 10:35:31 +00:00
ensure ( ' library_dirs ' , os . path . join ( sys . prefix , ' pypy ' , ' goal ' ) )
2016-01-31 14:44:46 +00:00
else :
if sys . platform == " win32 " :
template = " python %d %d "
2019-01-20 10:35:31 +00:00
if hasattr ( sys , ' gettotalrefcount ' ) :
template + = ' _d '
2016-01-31 14:44:46 +00:00
else :
2019-01-20 10:35:31 +00:00
try :
import sysconfig
except ImportError : # 2.6
from distutils import sysconfig
2016-01-31 14:44:46 +00:00
template = " python %d . %d "
2019-01-20 10:35:31 +00:00
if sysconfig . get_config_var ( ' DEBUG_EXT ' ) :
template + = sysconfig . get_config_var ( ' DEBUG_EXT ' )
2016-01-31 14:44:46 +00:00
pythonlib = ( template %
( sys . hexversion >> 24 , ( sys . hexversion >> 16 ) & 0xff ) )
if hasattr ( sys , ' abiflags ' ) :
pythonlib + = sys . abiflags
ensure ( ' libraries ' , pythonlib )
if sys . platform == " win32 " :
ensure ( ' extra_link_args ' , ' /MANIFEST ' )
def set_source ( self , module_name , source , source_extension = ' .c ' , * * kwds ) :
2019-01-20 10:35:31 +00:00
import os
2016-01-31 14:44:46 +00:00
if hasattr ( self , ' _assigned_source ' ) :
raise ValueError ( " set_source() cannot be called several times "
" per ffi object " )
if not isinstance ( module_name , basestring ) :
raise TypeError ( " ' module_name ' must be a string " )
2019-01-20 10:35:31 +00:00
if os . sep in module_name or ( os . altsep and os . altsep in module_name ) :
raise ValueError ( " ' module_name ' must not contain ' / ' : use a dotted "
" name to make a ' package.module ' location " )
2016-01-31 14:44:46 +00:00
self . _assigned_source = ( str ( module_name ) , source ,
source_extension , kwds )
def distutils_extension ( self , tmpdir = ' build ' , verbose = True ) :
from distutils . dir_util import mkpath
from . recompiler import recompile
#
if not hasattr ( self , ' _assigned_source ' ) :
if hasattr ( self , ' verifier ' ) : # fallback, 'tmpdir' ignored
return self . verifier . get_extension ( )
raise ValueError ( " set_source() must be called before "
" distutils_extension() " )
module_name , source , source_extension , kwds = self . _assigned_source
if source is None :
raise TypeError ( " distutils_extension() is only for C extension "
" modules, not for dlopen()-style pure Python "
" modules " )
mkpath ( tmpdir )
ext , updated = recompile ( self , module_name ,
source , tmpdir = tmpdir , extradir = tmpdir ,
source_extension = source_extension ,
call_c_compiler = False , * * kwds )
if verbose :
if updated :
sys . stderr . write ( " regenerated: %r \n " % ( ext . sources [ 0 ] , ) )
else :
sys . stderr . write ( " not modified: %r \n " % ( ext . sources [ 0 ] , ) )
return ext
def emit_c_code ( self , filename ) :
from . recompiler import recompile
#
if not hasattr ( self , ' _assigned_source ' ) :
raise ValueError ( " set_source() must be called before emit_c_code() " )
module_name , source , source_extension , kwds = self . _assigned_source
if source is None :
raise TypeError ( " emit_c_code() is only for C extension modules, "
" not for dlopen()-style pure Python modules " )
recompile ( self , module_name , source ,
c_file = filename , call_c_compiler = False , * * kwds )
def emit_python_code ( self , filename ) :
from . recompiler import recompile
#
if not hasattr ( self , ' _assigned_source ' ) :
raise ValueError ( " set_source() must be called before emit_c_code() " )
module_name , source , source_extension , kwds = self . _assigned_source
if source is not None :
raise TypeError ( " emit_python_code() is only for dlopen()-style "
" pure Python modules, not for C extension modules " )
recompile ( self , module_name , source ,
c_file = filename , call_c_compiler = False , * * kwds )
2019-01-20 10:35:31 +00:00
def compile ( self , tmpdir = ' . ' , verbose = 0 , target = None , debug = None ) :
2016-01-31 14:44:46 +00:00
""" The ' target ' argument gives the final file name of the
compiled DLL . Use ' * ' to force distutils ' choice, suitable for
regular CPython C API modules . Use a file name ending in ' .* '
to ask for the system ' s default extension for dynamic libraries
( . so / . dll / . dylib ) .
The default is ' * ' when building a non - embedded C API extension ,
and ( module_name + ' .* ' ) when building an embedded library .
"""
from . recompiler import recompile
#
if not hasattr ( self , ' _assigned_source ' ) :
raise ValueError ( " set_source() must be called before compile() " )
module_name , source , source_extension , kwds = self . _assigned_source
return recompile ( self , module_name , source , tmpdir = tmpdir ,
target = target , source_extension = source_extension ,
2019-01-20 10:35:31 +00:00
compiler_verbose = verbose , debug = debug , * * kwds )
2016-01-31 14:44:46 +00:00
def init_once ( self , func , tag ) :
# Read _init_once_cache[tag], which is either (False, lock) if
# we're calling the function now in some thread, or (True, result).
# Don't call setdefault() in most cases, to avoid allocating and
# immediately freeing a lock; but still use setdefaut() to avoid
# races.
try :
x = self . _init_once_cache [ tag ]
except KeyError :
x = self . _init_once_cache . setdefault ( tag , ( False , allocate_lock ( ) ) )
# Common case: we got (True, result), so we return the result.
if x [ 0 ] :
return x [ 1 ]
# Else, it's a lock. Acquire it to serialize the following tests.
with x [ 1 ] :
# Read again from _init_once_cache the current status.
x = self . _init_once_cache [ tag ]
if x [ 0 ] :
return x [ 1 ]
# Call the function and store the result back.
result = func ( )
self . _init_once_cache [ tag ] = ( True , result )
return result
def embedding_init_code ( self , pysource ) :
if self . _embedding :
raise ValueError ( " embedding_init_code() can only be called once " )
# fix 'pysource' before it gets dumped into the C file:
# - remove empty lines at the beginning, so it starts at "line 1"
# - dedent, if all non-empty lines are indented
# - check for SyntaxErrors
import re
match = re . match ( r ' \ s* \ n ' , pysource )
if match :
pysource = pysource [ match . end ( ) : ]
lines = pysource . splitlines ( ) or [ ' ' ]
prefix = re . match ( r ' \ s* ' , lines [ 0 ] ) . group ( )
for i in range ( 1 , len ( lines ) ) :
line = lines [ i ]
if line . rstrip ( ) :
while not line . startswith ( prefix ) :
prefix = prefix [ : - 1 ]
i = len ( prefix )
lines = [ line [ i : ] + ' \n ' for line in lines ]
pysource = ' ' . join ( lines )
#
compile ( pysource , " cffi_init " , " exec " )
#
self . _embedding = pysource
2019-01-20 10:35:31 +00:00
def def_extern ( self , * args , * * kwds ) :
raise ValueError ( " ffi.def_extern() is only available on API-mode FFI "
" objects " )
def list_types ( self ) :
""" Returns the user type names known to this FFI instance.
This returns a tuple containing three lists of names :
( typedef_names , names_of_structs , names_of_unions )
"""
typedefs = [ ]
structs = [ ]
unions = [ ]
for key in self . _parser . _declarations :
if key . startswith ( ' typedef ' ) :
typedefs . append ( key [ 8 : ] )
elif key . startswith ( ' struct ' ) :
structs . append ( key [ 7 : ] )
elif key . startswith ( ' union ' ) :
unions . append ( key [ 6 : ] )
typedefs . sort ( )
structs . sort ( )
unions . sort ( )
return ( typedefs , structs , unions )
2016-01-31 14:44:46 +00:00
def _load_backend_lib ( backend , name , flags ) :
2019-01-20 10:35:31 +00:00
import os
2016-01-31 14:44:46 +00:00
if name is None :
if sys . platform != " win32 " :
return backend . load_library ( None , flags )
name = " c " # Windows: load_library(None) fails, but this works
2019-01-20 10:35:31 +00:00
# on Python 2 (backward compatibility hack only)
first_error = None
if ' . ' in name or ' / ' in name or os . sep in name :
try :
return backend . load_library ( name , flags )
except OSError as e :
first_error = e
import ctypes . util
path = ctypes . util . find_library ( name )
if path is None :
if name == " c " and sys . platform == " win32 " and sys . version_info > = ( 3 , ) :
raise OSError ( " dlopen(None) cannot work on Windows for Python 3 "
" (see http://bugs.python.org/issue23606) " )
msg = ( " ctypes.util.find_library() did not manage "
" to locate a library called %r " % ( name , ) )
if first_error is not None :
msg = " %s . Additionally, %s " % ( first_error , msg )
raise OSError ( msg )
return backend . load_library ( path , flags )
2016-01-31 14:44:46 +00:00
def _make_ffi_library ( ffi , libname , flags ) :
backend = ffi . _backend
backendlib = _load_backend_lib ( backend , libname , flags )
#
def accessor_function ( name ) :
key = ' function ' + name
tp , _ = ffi . _parser . _declarations [ key ]
BType = ffi . _get_cached_btype ( tp )
2019-01-20 10:35:31 +00:00
value = backendlib . load_function ( BType , name )
2016-01-31 14:44:46 +00:00
library . __dict__ [ name ] = value
#
def accessor_variable ( name ) :
key = ' variable ' + name
tp , _ = ffi . _parser . _declarations [ key ]
BType = ffi . _get_cached_btype ( tp )
read_variable = backendlib . read_variable
write_variable = backendlib . write_variable
setattr ( FFILibrary , name , property (
lambda self : read_variable ( BType , name ) ,
lambda self , value : write_variable ( BType , name , value ) ) )
#
2019-01-20 10:35:31 +00:00
def addressof_var ( name ) :
try :
return addr_variables [ name ]
except KeyError :
with ffi . _lock :
if name not in addr_variables :
key = ' variable ' + name
tp , _ = ffi . _parser . _declarations [ key ]
BType = ffi . _get_cached_btype ( tp )
if BType . kind != ' array ' :
BType = model . pointer_cache ( ffi , BType )
p = backendlib . load_function ( BType , name )
addr_variables [ name ] = p
return addr_variables [ name ]
#
2016-01-31 14:44:46 +00:00
def accessor_constant ( name ) :
raise NotImplementedError ( " non-integer constant ' %s ' cannot be "
" accessed from a dlopen() library " % ( name , ) )
#
def accessor_int_constant ( name ) :
library . __dict__ [ name ] = ffi . _parser . _int_constants [ name ]
#
accessors = { }
accessors_version = [ False ]
2019-01-20 10:35:31 +00:00
addr_variables = { }
2016-01-31 14:44:46 +00:00
#
def update_accessors ( ) :
if accessors_version [ 0 ] is ffi . _cdef_version :
return
#
for key , ( tp , _ ) in ffi . _parser . _declarations . items ( ) :
if not isinstance ( tp , model . EnumType ) :
tag , name = key . split ( ' ' , 1 )
if tag == ' function ' :
accessors [ name ] = accessor_function
elif tag == ' variable ' :
accessors [ name ] = accessor_variable
elif tag == ' constant ' :
accessors [ name ] = accessor_constant
else :
for i , enumname in enumerate ( tp . enumerators ) :
def accessor_enum ( name , tp = tp , i = i ) :
tp . check_not_partial ( )
library . __dict__ [ name ] = tp . enumvalues [ i ]
accessors [ enumname ] = accessor_enum
for name in ffi . _parser . _int_constants :
accessors . setdefault ( name , accessor_int_constant )
accessors_version [ 0 ] = ffi . _cdef_version
#
def make_accessor ( name ) :
with ffi . _lock :
if name in library . __dict__ or name in FFILibrary . __dict__ :
return # added by another thread while waiting for the lock
if name not in accessors :
update_accessors ( )
if name not in accessors :
raise AttributeError ( name )
accessors [ name ] ( name )
#
class FFILibrary ( object ) :
def __getattr__ ( self , name ) :
make_accessor ( name )
return getattr ( self , name )
def __setattr__ ( self , name , value ) :
try :
property = getattr ( self . __class__ , name )
except AttributeError :
make_accessor ( name )
setattr ( self , name , value )
else :
property . __set__ ( self , value )
def __dir__ ( self ) :
with ffi . _lock :
update_accessors ( )
return accessors . keys ( )
2019-01-20 10:35:31 +00:00
def __addressof__ ( self , name ) :
if name in library . __dict__ :
return library . __dict__ [ name ]
if name in FFILibrary . __dict__ :
return addressof_var ( name )
make_accessor ( name )
if name in library . __dict__ :
return library . __dict__ [ name ]
if name in FFILibrary . __dict__ :
return addressof_var ( name )
raise AttributeError ( " cffi library has no function or "
" global variable named ' %s ' " % ( name , ) )
def __cffi_close__ ( self ) :
backendlib . close_lib ( )
self . __dict__ . clear ( )
2016-01-31 14:44:46 +00:00
#
if libname is not None :
try :
if not isinstance ( libname , str ) : # unicode, on Python 2
libname = libname . encode ( ' utf-8 ' )
FFILibrary . __name__ = ' FFILibrary_ %s ' % libname
except UnicodeError :
pass
library = FFILibrary ( )
return library , library . __dict__
def _builtin_function_type ( func ) :
# a hack to make at least ffi.typeof(builtin_function) work,
# if the builtin function was obtained by 'vengine_cpy'.
import sys
try :
module = sys . modules [ func . __module__ ]
ffi = module . _cffi_original_ffi
types_of_builtin_funcs = module . _cffi_types_of_builtin_funcs
tp = types_of_builtin_funcs [ func ]
except ( KeyError , AttributeError , TypeError ) :
return None
else :
with ffi . _lock :
return ffi . _get_cached_btype ( tp )