Spaces:
Sleeping
Sleeping
Upload api.py
Browse files- cffi/api.py +967 -0
cffi/api.py
ADDED
@@ -0,0 +1,967 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sys, types
|
2 |
+
from .lock import allocate_lock
|
3 |
+
from .error import CDefError
|
4 |
+
from . import model
|
5 |
+
|
6 |
+
try:
|
7 |
+
callable
|
8 |
+
except NameError:
|
9 |
+
# Python 3.1
|
10 |
+
from collections import Callable
|
11 |
+
callable = lambda x: isinstance(x, Callable)
|
12 |
+
|
13 |
+
try:
|
14 |
+
basestring
|
15 |
+
except NameError:
|
16 |
+
# Python 3.x
|
17 |
+
basestring = str
|
18 |
+
|
19 |
+
_unspecified = object()
|
20 |
+
|
21 |
+
|
22 |
+
|
23 |
+
class FFI(object):
|
24 |
+
r'''
|
25 |
+
The main top-level class that you instantiate once, or once per module.
|
26 |
+
|
27 |
+
Example usage:
|
28 |
+
|
29 |
+
ffi = FFI()
|
30 |
+
ffi.cdef("""
|
31 |
+
int printf(const char *, ...);
|
32 |
+
""")
|
33 |
+
|
34 |
+
C = ffi.dlopen(None) # standard library
|
35 |
+
-or-
|
36 |
+
C = ffi.verify() # use a C compiler: verify the decl above is right
|
37 |
+
|
38 |
+
C.printf("hello, %s!\n", ffi.new("char[]", "world"))
|
39 |
+
'''
|
40 |
+
|
41 |
+
def __init__(self, backend=None):
|
42 |
+
"""Create an FFI instance. The 'backend' argument is used to
|
43 |
+
select a non-default backend, mostly for tests.
|
44 |
+
"""
|
45 |
+
if backend is None:
|
46 |
+
# You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with
|
47 |
+
# _cffi_backend.so compiled.
|
48 |
+
import _cffi_backend as backend
|
49 |
+
from . import __version__
|
50 |
+
if backend.__version__ != __version__:
|
51 |
+
# bad version! Try to be as explicit as possible.
|
52 |
+
if hasattr(backend, '__file__'):
|
53 |
+
# CPython
|
54 |
+
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." % (
|
55 |
+
__version__, __file__,
|
56 |
+
backend.__version__, backend.__file__))
|
57 |
+
else:
|
58 |
+
# PyPy
|
59 |
+
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." % (
|
60 |
+
__version__, __file__, backend.__version__))
|
61 |
+
# (If you insist you can also try to pass the option
|
62 |
+
# 'backend=backend_ctypes.CTypesBackend()', but don't
|
63 |
+
# rely on it! It's probably not going to work well.)
|
64 |
+
|
65 |
+
from . import cparser
|
66 |
+
self._backend = backend
|
67 |
+
self._lock = allocate_lock()
|
68 |
+
self._parser = cparser.Parser()
|
69 |
+
self._cached_btypes = {}
|
70 |
+
self._parsed_types = types.ModuleType('parsed_types').__dict__
|
71 |
+
self._new_types = types.ModuleType('new_types').__dict__
|
72 |
+
self._function_caches = []
|
73 |
+
self._libraries = []
|
74 |
+
self._cdefsources = []
|
75 |
+
self._included_ffis = []
|
76 |
+
self._windows_unicode = None
|
77 |
+
self._init_once_cache = {}
|
78 |
+
self._cdef_version = None
|
79 |
+
self._embedding = None
|
80 |
+
self._typecache = model.get_typecache(backend)
|
81 |
+
if hasattr(backend, 'set_ffi'):
|
82 |
+
backend.set_ffi(self)
|
83 |
+
for name in list(backend.__dict__):
|
84 |
+
if name.startswith('RTLD_'):
|
85 |
+
setattr(self, name, getattr(backend, name))
|
86 |
+
#
|
87 |
+
with self._lock:
|
88 |
+
self.BVoidP = self._get_cached_btype(model.voidp_type)
|
89 |
+
self.BCharA = self._get_cached_btype(model.char_array_type)
|
90 |
+
if isinstance(backend, types.ModuleType):
|
91 |
+
# _cffi_backend: attach these constants to the class
|
92 |
+
if not hasattr(FFI, 'NULL'):
|
93 |
+
FFI.NULL = self.cast(self.BVoidP, 0)
|
94 |
+
FFI.CData, FFI.CType = backend._get_types()
|
95 |
+
else:
|
96 |
+
# ctypes backend: attach these constants to the instance
|
97 |
+
self.NULL = self.cast(self.BVoidP, 0)
|
98 |
+
self.CData, self.CType = backend._get_types()
|
99 |
+
self.buffer = backend.buffer
|
100 |
+
|
101 |
+
def cdef(self, csource, override=False, packed=False, pack=None):
|
102 |
+
"""Parse the given C source. This registers all declared functions,
|
103 |
+
types, and global variables. The functions and global variables can
|
104 |
+
then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'.
|
105 |
+
The types can be used in 'ffi.new()' and other functions.
|
106 |
+
If 'packed' is specified as True, all structs declared inside this
|
107 |
+
cdef are packed, i.e. laid out without any field alignment at all.
|
108 |
+
Alternatively, 'pack' can be a small integer, and requests for
|
109 |
+
alignment greater than that are ignored (pack=1 is equivalent to
|
110 |
+
packed=True).
|
111 |
+
"""
|
112 |
+
self._cdef(csource, override=override, packed=packed, pack=pack)
|
113 |
+
|
114 |
+
def embedding_api(self, csource, packed=False, pack=None):
|
115 |
+
self._cdef(csource, packed=packed, pack=pack, dllexport=True)
|
116 |
+
if self._embedding is None:
|
117 |
+
self._embedding = ''
|
118 |
+
|
119 |
+
def _cdef(self, csource, override=False, **options):
|
120 |
+
if not isinstance(csource, str): # unicode, on Python 2
|
121 |
+
if not isinstance(csource, basestring):
|
122 |
+
raise TypeError("cdef() argument must be a string")
|
123 |
+
csource = csource.encode('ascii')
|
124 |
+
with self._lock:
|
125 |
+
self._cdef_version = object()
|
126 |
+
self._parser.parse(csource, override=override, **options)
|
127 |
+
self._cdefsources.append(csource)
|
128 |
+
if override:
|
129 |
+
for cache in self._function_caches:
|
130 |
+
cache.clear()
|
131 |
+
finishlist = self._parser._recomplete
|
132 |
+
if finishlist:
|
133 |
+
self._parser._recomplete = []
|
134 |
+
for tp in finishlist:
|
135 |
+
tp.finish_backend_type(self, finishlist)
|
136 |
+
|
137 |
+
def dlopen(self, name, flags=0):
|
138 |
+
"""Load and return a dynamic library identified by 'name'.
|
139 |
+
The standard C library can be loaded by passing None.
|
140 |
+
Note that functions and types declared by 'ffi.cdef()' are not
|
141 |
+
linked to a particular library, just like C headers; in the
|
142 |
+
library we only look for the actual (untyped) symbols.
|
143 |
+
"""
|
144 |
+
if not (isinstance(name, basestring) or
|
145 |
+
name is None or
|
146 |
+
isinstance(name, self.CData)):
|
147 |
+
raise TypeError("dlopen(name): name must be a file name, None, "
|
148 |
+
"or an already-opened 'void *' handle")
|
149 |
+
with self._lock:
|
150 |
+
lib, function_cache = _make_ffi_library(self, name, flags)
|
151 |
+
self._function_caches.append(function_cache)
|
152 |
+
self._libraries.append(lib)
|
153 |
+
return lib
|
154 |
+
|
155 |
+
def dlclose(self, lib):
|
156 |
+
"""Close a library obtained with ffi.dlopen(). After this call,
|
157 |
+
access to functions or variables from the library will fail
|
158 |
+
(possibly with a segmentation fault).
|
159 |
+
"""
|
160 |
+
type(lib).__cffi_close__(lib)
|
161 |
+
|
162 |
+
def _typeof_locked(self, cdecl):
|
163 |
+
# call me with the lock!
|
164 |
+
key = cdecl
|
165 |
+
if key in self._parsed_types:
|
166 |
+
return self._parsed_types[key]
|
167 |
+
#
|
168 |
+
if not isinstance(cdecl, str): # unicode, on Python 2
|
169 |
+
cdecl = cdecl.encode('ascii')
|
170 |
+
#
|
171 |
+
type = self._parser.parse_type(cdecl)
|
172 |
+
really_a_function_type = type.is_raw_function
|
173 |
+
if really_a_function_type:
|
174 |
+
type = type.as_function_pointer()
|
175 |
+
btype = self._get_cached_btype(type)
|
176 |
+
result = btype, really_a_function_type
|
177 |
+
self._parsed_types[key] = result
|
178 |
+
return result
|
179 |
+
|
180 |
+
def _typeof(self, cdecl, consider_function_as_funcptr=False):
|
181 |
+
# string -> ctype object
|
182 |
+
try:
|
183 |
+
result = self._parsed_types[cdecl]
|
184 |
+
except KeyError:
|
185 |
+
with self._lock:
|
186 |
+
result = self._typeof_locked(cdecl)
|
187 |
+
#
|
188 |
+
btype, really_a_function_type = result
|
189 |
+
if really_a_function_type and not consider_function_as_funcptr:
|
190 |
+
raise CDefError("the type %r is a function type, not a "
|
191 |
+
"pointer-to-function type" % (cdecl,))
|
192 |
+
return btype
|
193 |
+
|
194 |
+
def typeof(self, cdecl):
|
195 |
+
"""Parse the C type given as a string and return the
|
196 |
+
corresponding <ctype> object.
|
197 |
+
It can also be used on 'cdata' instance to get its C type.
|
198 |
+
"""
|
199 |
+
if isinstance(cdecl, basestring):
|
200 |
+
return self._typeof(cdecl)
|
201 |
+
if isinstance(cdecl, self.CData):
|
202 |
+
return self._backend.typeof(cdecl)
|
203 |
+
if isinstance(cdecl, types.BuiltinFunctionType):
|
204 |
+
res = _builtin_function_type(cdecl)
|
205 |
+
if res is not None:
|
206 |
+
return res
|
207 |
+
if (isinstance(cdecl, types.FunctionType)
|
208 |
+
and hasattr(cdecl, '_cffi_base_type')):
|
209 |
+
with self._lock:
|
210 |
+
return self._get_cached_btype(cdecl._cffi_base_type)
|
211 |
+
raise TypeError(type(cdecl))
|
212 |
+
|
213 |
+
def sizeof(self, cdecl):
|
214 |
+
"""Return the size in bytes of the argument. It can be a
|
215 |
+
string naming a C type, or a 'cdata' instance.
|
216 |
+
"""
|
217 |
+
if isinstance(cdecl, basestring):
|
218 |
+
BType = self._typeof(cdecl)
|
219 |
+
return self._backend.sizeof(BType)
|
220 |
+
else:
|
221 |
+
return self._backend.sizeof(cdecl)
|
222 |
+
|
223 |
+
def alignof(self, cdecl):
|
224 |
+
"""Return the natural alignment size in bytes of the C type
|
225 |
+
given as a string.
|
226 |
+
"""
|
227 |
+
if isinstance(cdecl, basestring):
|
228 |
+
cdecl = self._typeof(cdecl)
|
229 |
+
return self._backend.alignof(cdecl)
|
230 |
+
|
231 |
+
def offsetof(self, cdecl, *fields_or_indexes):
|
232 |
+
"""Return the offset of the named field inside the given
|
233 |
+
structure or array, which must be given as a C type name.
|
234 |
+
You can give several field names in case of nested structures.
|
235 |
+
You can also give numeric values which correspond to array
|
236 |
+
items, in case of an array type.
|
237 |
+
"""
|
238 |
+
if isinstance(cdecl, basestring):
|
239 |
+
cdecl = self._typeof(cdecl)
|
240 |
+
return self._typeoffsetof(cdecl, *fields_or_indexes)[1]
|
241 |
+
|
242 |
+
def new(self, cdecl, init=None):
|
243 |
+
"""Allocate an instance according to the specified C type and
|
244 |
+
return a pointer to it. The specified C type must be either a
|
245 |
+
pointer or an array: ``new('X *')`` allocates an X and returns
|
246 |
+
a pointer to it, whereas ``new('X[n]')`` allocates an array of
|
247 |
+
n X'es and returns an array referencing it (which works
|
248 |
+
mostly like a pointer, like in C). You can also use
|
249 |
+
``new('X[]', n)`` to allocate an array of a non-constant
|
250 |
+
length n.
|
251 |
+
|
252 |
+
The memory is initialized following the rules of declaring a
|
253 |
+
global variable in C: by default it is zero-initialized, but
|
254 |
+
an explicit initializer can be given which can be used to
|
255 |
+
fill all or part of the memory.
|
256 |
+
|
257 |
+
When the returned <cdata> object goes out of scope, the memory
|
258 |
+
is freed. In other words the returned <cdata> object has
|
259 |
+
ownership of the value of type 'cdecl' that it points to. This
|
260 |
+
means that the raw data can be used as long as this object is
|
261 |
+
kept alive, but must not be used for a longer time. Be careful
|
262 |
+
about that when copying the pointer to the memory somewhere
|
263 |
+
else, e.g. into another structure.
|
264 |
+
"""
|
265 |
+
if isinstance(cdecl, basestring):
|
266 |
+
cdecl = self._typeof(cdecl)
|
267 |
+
return self._backend.newp(cdecl, init)
|
268 |
+
|
269 |
+
def new_allocator(self, alloc=None, free=None,
|
270 |
+
should_clear_after_alloc=True):
|
271 |
+
"""Return a new allocator, i.e. a function that behaves like ffi.new()
|
272 |
+
but uses the provided low-level 'alloc' and 'free' functions.
|
273 |
+
|
274 |
+
'alloc' is called with the size as argument. If it returns NULL, a
|
275 |
+
MemoryError is raised. 'free' is called with the result of 'alloc'
|
276 |
+
as argument. Both can be either Python function or directly C
|
277 |
+
functions. If 'free' is None, then no free function is called.
|
278 |
+
If both 'alloc' and 'free' are None, the default is used.
|
279 |
+
|
280 |
+
If 'should_clear_after_alloc' is set to False, then the memory
|
281 |
+
returned by 'alloc' is assumed to be already cleared (or you are
|
282 |
+
fine with garbage); otherwise CFFI will clear it.
|
283 |
+
"""
|
284 |
+
compiled_ffi = self._backend.FFI()
|
285 |
+
allocator = compiled_ffi.new_allocator(alloc, free,
|
286 |
+
should_clear_after_alloc)
|
287 |
+
def allocate(cdecl, init=None):
|
288 |
+
if isinstance(cdecl, basestring):
|
289 |
+
cdecl = self._typeof(cdecl)
|
290 |
+
return allocator(cdecl, init)
|
291 |
+
return allocate
|
292 |
+
|
293 |
+
def cast(self, cdecl, source):
|
294 |
+
"""Similar to a C cast: returns an instance of the named C
|
295 |
+
type initialized with the given 'source'. The source is
|
296 |
+
casted between integers or pointers of any type.
|
297 |
+
"""
|
298 |
+
if isinstance(cdecl, basestring):
|
299 |
+
cdecl = self._typeof(cdecl)
|
300 |
+
return self._backend.cast(cdecl, source)
|
301 |
+
|
302 |
+
def string(self, cdata, maxlen=-1):
|
303 |
+
"""Return a Python string (or unicode string) from the 'cdata'.
|
304 |
+
If 'cdata' is a pointer or array of characters or bytes, returns
|
305 |
+
the null-terminated string. The returned string extends until
|
306 |
+
the first null character, or at most 'maxlen' characters. If
|
307 |
+
'cdata' is an array then 'maxlen' defaults to its length.
|
308 |
+
|
309 |
+
If 'cdata' is a pointer or array of wchar_t, returns a unicode
|
310 |
+
string following the same rules.
|
311 |
+
|
312 |
+
If 'cdata' is a single character or byte or a wchar_t, returns
|
313 |
+
it as a string or unicode string.
|
314 |
+
|
315 |
+
If 'cdata' is an enum, returns the value of the enumerator as a
|
316 |
+
string, or 'NUMBER' if the value is out of range.
|
317 |
+
"""
|
318 |
+
return self._backend.string(cdata, maxlen)
|
319 |
+
|
320 |
+
def unpack(self, cdata, length):
|
321 |
+
"""Unpack an array of C data of the given length,
|
322 |
+
returning a Python string/unicode/list.
|
323 |
+
|
324 |
+
If 'cdata' is a pointer to 'char', returns a byte string.
|
325 |
+
It does not stop at the first null. This is equivalent to:
|
326 |
+
ffi.buffer(cdata, length)[:]
|
327 |
+
|
328 |
+
If 'cdata' is a pointer to 'wchar_t', returns a unicode string.
|
329 |
+
'length' is measured in wchar_t's; it is not the size in bytes.
|
330 |
+
|
331 |
+
If 'cdata' is a pointer to anything else, returns a list of
|
332 |
+
'length' items. This is a faster equivalent to:
|
333 |
+
[cdata[i] for i in range(length)]
|
334 |
+
"""
|
335 |
+
return self._backend.unpack(cdata, length)
|
336 |
+
|
337 |
+
#def buffer(self, cdata, size=-1):
|
338 |
+
# """Return a read-write buffer object that references the raw C data
|
339 |
+
# pointed to by the given 'cdata'. The 'cdata' must be a pointer or
|
340 |
+
# an array. Can be passed to functions expecting a buffer, or directly
|
341 |
+
# manipulated with:
|
342 |
+
#
|
343 |
+
# buf[:] get a copy of it in a regular string, or
|
344 |
+
# buf[idx] as a single character
|
345 |
+
# buf[:] = ...
|
346 |
+
# buf[idx] = ... change the content
|
347 |
+
# """
|
348 |
+
# note that 'buffer' is a type, set on this instance by __init__
|
349 |
+
|
350 |
+
def from_buffer(self, cdecl, python_buffer=_unspecified,
|
351 |
+
require_writable=False):
|
352 |
+
"""Return a cdata of the given type pointing to the data of the
|
353 |
+
given Python object, which must support the buffer interface.
|
354 |
+
Note that this is not meant to be used on the built-in types
|
355 |
+
str or unicode (you can build 'char[]' arrays explicitly)
|
356 |
+
but only on objects containing large quantities of raw data
|
357 |
+
in some other format, like 'array.array' or numpy arrays.
|
358 |
+
|
359 |
+
The first argument is optional and default to 'char[]'.
|
360 |
+
"""
|
361 |
+
if python_buffer is _unspecified:
|
362 |
+
cdecl, python_buffer = self.BCharA, cdecl
|
363 |
+
elif isinstance(cdecl, basestring):
|
364 |
+
cdecl = self._typeof(cdecl)
|
365 |
+
return self._backend.from_buffer(cdecl, python_buffer,
|
366 |
+
require_writable)
|
367 |
+
|
368 |
+
def memmove(self, dest, src, n):
|
369 |
+
"""ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.
|
370 |
+
|
371 |
+
Like the C function memmove(), the memory areas may overlap;
|
372 |
+
apart from that it behaves like the C function memcpy().
|
373 |
+
|
374 |
+
'src' can be any cdata ptr or array, or any Python buffer object.
|
375 |
+
'dest' can be any cdata ptr or array, or a writable Python buffer
|
376 |
+
object. The size to copy, 'n', is always measured in bytes.
|
377 |
+
|
378 |
+
Unlike other methods, this one supports all Python buffer including
|
379 |
+
byte strings and bytearrays---but it still does not support
|
380 |
+
non-contiguous buffers.
|
381 |
+
"""
|
382 |
+
return self._backend.memmove(dest, src, n)
|
383 |
+
|
384 |
+
def callback(self, cdecl, python_callable=None, error=None, onerror=None):
|
385 |
+
"""Return a callback object or a decorator making such a
|
386 |
+
callback object. 'cdecl' must name a C function pointer type.
|
387 |
+
The callback invokes the specified 'python_callable' (which may
|
388 |
+
be provided either directly or via a decorator). Important: the
|
389 |
+
callback object must be manually kept alive for as long as the
|
390 |
+
callback may be invoked from the C level.
|
391 |
+
"""
|
392 |
+
def callback_decorator_wrap(python_callable):
|
393 |
+
if not callable(python_callable):
|
394 |
+
raise TypeError("the 'python_callable' argument "
|
395 |
+
"is not callable")
|
396 |
+
return self._backend.callback(cdecl, python_callable,
|
397 |
+
error, onerror)
|
398 |
+
if isinstance(cdecl, basestring):
|
399 |
+
cdecl = self._typeof(cdecl, consider_function_as_funcptr=True)
|
400 |
+
if python_callable is None:
|
401 |
+
return callback_decorator_wrap # decorator mode
|
402 |
+
else:
|
403 |
+
return callback_decorator_wrap(python_callable) # direct mode
|
404 |
+
|
405 |
+
def getctype(self, cdecl, replace_with=''):
|
406 |
+
"""Return a string giving the C type 'cdecl', which may be itself
|
407 |
+
a string or a <ctype> object. If 'replace_with' is given, it gives
|
408 |
+
extra text to append (or insert for more complicated C types), like
|
409 |
+
a variable name, or '*' to get actually the C type 'pointer-to-cdecl'.
|
410 |
+
"""
|
411 |
+
if isinstance(cdecl, basestring):
|
412 |
+
cdecl = self._typeof(cdecl)
|
413 |
+
replace_with = replace_with.strip()
|
414 |
+
if (replace_with.startswith('*')
|
415 |
+
and '&[' in self._backend.getcname(cdecl, '&')):
|
416 |
+
replace_with = '(%s)' % replace_with
|
417 |
+
elif replace_with and not replace_with[0] in '[(':
|
418 |
+
replace_with = ' ' + replace_with
|
419 |
+
return self._backend.getcname(cdecl, replace_with)
|
420 |
+
|
421 |
+
def gc(self, cdata, destructor, size=0):
|
422 |
+
"""Return a new cdata object that points to the same
|
423 |
+
data. Later, when this new cdata object is garbage-collected,
|
424 |
+
'destructor(old_cdata_object)' will be called.
|
425 |
+
|
426 |
+
The optional 'size' gives an estimate of the size, used to
|
427 |
+
trigger the garbage collection more eagerly. So far only used
|
428 |
+
on PyPy. It tells the GC that the returned object keeps alive
|
429 |
+
roughly 'size' bytes of external memory.
|
430 |
+
"""
|
431 |
+
return self._backend.gcp(cdata, destructor, size)
|
432 |
+
|
433 |
+
def _get_cached_btype(self, type):
|
434 |
+
assert self._lock.acquire(False) is False
|
435 |
+
# call me with the lock!
|
436 |
+
try:
|
437 |
+
BType = self._cached_btypes[type]
|
438 |
+
except KeyError:
|
439 |
+
finishlist = []
|
440 |
+
BType = type.get_cached_btype(self, finishlist)
|
441 |
+
for type in finishlist:
|
442 |
+
type.finish_backend_type(self, finishlist)
|
443 |
+
return BType
|
444 |
+
|
445 |
+
def verify(self, source='', tmpdir=None, **kwargs):
|
446 |
+
"""Verify that the current ffi signatures compile on this
|
447 |
+
machine, and return a dynamic library object. The dynamic
|
448 |
+
library can be used to call functions and access global
|
449 |
+
variables declared in this 'ffi'. The library is compiled
|
450 |
+
by the C compiler: it gives you C-level API compatibility
|
451 |
+
(including calling macros). This is unlike 'ffi.dlopen()',
|
452 |
+
which requires binary compatibility in the signatures.
|
453 |
+
"""
|
454 |
+
from .verifier import Verifier, _caller_dir_pycache
|
455 |
+
#
|
456 |
+
# If set_unicode(True) was called, insert the UNICODE and
|
457 |
+
# _UNICODE macro declarations
|
458 |
+
if self._windows_unicode:
|
459 |
+
self._apply_windows_unicode(kwargs)
|
460 |
+
#
|
461 |
+
# Set the tmpdir here, and not in Verifier.__init__: it picks
|
462 |
+
# up the caller's directory, which we want to be the caller of
|
463 |
+
# ffi.verify(), as opposed to the caller of Veritier().
|
464 |
+
tmpdir = tmpdir or _caller_dir_pycache()
|
465 |
+
#
|
466 |
+
# Make a Verifier() and use it to load the library.
|
467 |
+
self.verifier = Verifier(self, source, tmpdir, **kwargs)
|
468 |
+
lib = self.verifier.load_library()
|
469 |
+
#
|
470 |
+
# Save the loaded library for keep-alive purposes, even
|
471 |
+
# if the caller doesn't keep it alive itself (it should).
|
472 |
+
self._libraries.append(lib)
|
473 |
+
return lib
|
474 |
+
|
475 |
+
def _get_errno(self):
|
476 |
+
return self._backend.get_errno()
|
477 |
+
def _set_errno(self, errno):
|
478 |
+
self._backend.set_errno(errno)
|
479 |
+
errno = property(_get_errno, _set_errno, None,
|
480 |
+
"the value of 'errno' from/to the C calls")
|
481 |
+
|
482 |
+
def getwinerror(self, code=-1):
|
483 |
+
return self._backend.getwinerror(code)
|
484 |
+
|
485 |
+
def _pointer_to(self, ctype):
|
486 |
+
with self._lock:
|
487 |
+
return model.pointer_cache(self, ctype)
|
488 |
+
|
489 |
+
def addressof(self, cdata, *fields_or_indexes):
|
490 |
+
"""Return the address of a <cdata 'struct-or-union'>.
|
491 |
+
If 'fields_or_indexes' are given, returns the address of that
|
492 |
+
field or array item in the structure or array, recursively in
|
493 |
+
case of nested structures.
|
494 |
+
"""
|
495 |
+
try:
|
496 |
+
ctype = self._backend.typeof(cdata)
|
497 |
+
except TypeError:
|
498 |
+
if '__addressof__' in type(cdata).__dict__:
|
499 |
+
return type(cdata).__addressof__(cdata, *fields_or_indexes)
|
500 |
+
raise
|
501 |
+
if fields_or_indexes:
|
502 |
+
ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes)
|
503 |
+
else:
|
504 |
+
if ctype.kind == "pointer":
|
505 |
+
raise TypeError("addressof(pointer)")
|
506 |
+
offset = 0
|
507 |
+
ctypeptr = self._pointer_to(ctype)
|
508 |
+
return self._backend.rawaddressof(ctypeptr, cdata, offset)
|
509 |
+
|
510 |
+
def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes):
|
511 |
+
ctype, offset = self._backend.typeoffsetof(ctype, field_or_index)
|
512 |
+
for field1 in fields_or_indexes:
|
513 |
+
ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1)
|
514 |
+
offset += offset1
|
515 |
+
return ctype, offset
|
516 |
+
|
517 |
+
def include(self, ffi_to_include):
|
518 |
+
"""Includes the typedefs, structs, unions and enums defined
|
519 |
+
in another FFI instance. Usage is similar to a #include in C,
|
520 |
+
where a part of the program might include types defined in
|
521 |
+
another part for its own usage. Note that the include()
|
522 |
+
method has no effect on functions, constants and global
|
523 |
+
variables, which must anyway be accessed directly from the
|
524 |
+
lib object returned by the original FFI instance.
|
525 |
+
"""
|
526 |
+
if not isinstance(ffi_to_include, FFI):
|
527 |
+
raise TypeError("ffi.include() expects an argument that is also of"
|
528 |
+
" type cffi.FFI, not %r" % (
|
529 |
+
type(ffi_to_include).__name__,))
|
530 |
+
if ffi_to_include is self:
|
531 |
+
raise ValueError("self.include(self)")
|
532 |
+
with ffi_to_include._lock:
|
533 |
+
with self._lock:
|
534 |
+
self._parser.include(ffi_to_include._parser)
|
535 |
+
self._cdefsources.append('[')
|
536 |
+
self._cdefsources.extend(ffi_to_include._cdefsources)
|
537 |
+
self._cdefsources.append(']')
|
538 |
+
self._included_ffis.append(ffi_to_include)
|
539 |
+
|
540 |
+
def new_handle(self, x):
|
541 |
+
return self._backend.newp_handle(self.BVoidP, x)
|
542 |
+
|
543 |
+
def from_handle(self, x):
|
544 |
+
return self._backend.from_handle(x)
|
545 |
+
|
546 |
+
def release(self, x):
|
547 |
+
self._backend.release(x)
|
548 |
+
|
549 |
+
def set_unicode(self, enabled_flag):
|
550 |
+
"""Windows: if 'enabled_flag' is True, enable the UNICODE and
|
551 |
+
_UNICODE defines in C, and declare the types like TCHAR and LPTCSTR
|
552 |
+
to be (pointers to) wchar_t. If 'enabled_flag' is False,
|
553 |
+
declare these types to be (pointers to) plain 8-bit characters.
|
554 |
+
This is mostly for backward compatibility; you usually want True.
|
555 |
+
"""
|
556 |
+
if self._windows_unicode is not None:
|
557 |
+
raise ValueError("set_unicode() can only be called once")
|
558 |
+
enabled_flag = bool(enabled_flag)
|
559 |
+
if enabled_flag:
|
560 |
+
self.cdef("typedef wchar_t TBYTE;"
|
561 |
+
"typedef wchar_t TCHAR;"
|
562 |
+
"typedef const wchar_t *LPCTSTR;"
|
563 |
+
"typedef const wchar_t *PCTSTR;"
|
564 |
+
"typedef wchar_t *LPTSTR;"
|
565 |
+
"typedef wchar_t *PTSTR;"
|
566 |
+
"typedef TBYTE *PTBYTE;"
|
567 |
+
"typedef TCHAR *PTCHAR;")
|
568 |
+
else:
|
569 |
+
self.cdef("typedef char TBYTE;"
|
570 |
+
"typedef char TCHAR;"
|
571 |
+
"typedef const char *LPCTSTR;"
|
572 |
+
"typedef const char *PCTSTR;"
|
573 |
+
"typedef char *LPTSTR;"
|
574 |
+
"typedef char *PTSTR;"
|
575 |
+
"typedef TBYTE *PTBYTE;"
|
576 |
+
"typedef TCHAR *PTCHAR;")
|
577 |
+
self._windows_unicode = enabled_flag
|
578 |
+
|
579 |
+
def _apply_windows_unicode(self, kwds):
|
580 |
+
defmacros = kwds.get('define_macros', ())
|
581 |
+
if not isinstance(defmacros, (list, tuple)):
|
582 |
+
raise TypeError("'define_macros' must be a list or tuple")
|
583 |
+
defmacros = list(defmacros) + [('UNICODE', '1'),
|
584 |
+
('_UNICODE', '1')]
|
585 |
+
kwds['define_macros'] = defmacros
|
586 |
+
|
587 |
+
def _apply_embedding_fix(self, kwds):
|
588 |
+
# must include an argument like "-lpython2.7" for the compiler
|
589 |
+
def ensure(key, value):
|
590 |
+
lst = kwds.setdefault(key, [])
|
591 |
+
if value not in lst:
|
592 |
+
lst.append(value)
|
593 |
+
#
|
594 |
+
if '__pypy__' in sys.builtin_module_names:
|
595 |
+
import os
|
596 |
+
if sys.platform == "win32":
|
597 |
+
# we need 'libpypy-c.lib'. Current distributions of
|
598 |
+
# pypy (>= 4.1) contain it as 'libs/python27.lib'.
|
599 |
+
pythonlib = "python{0[0]}{0[1]}".format(sys.version_info)
|
600 |
+
if hasattr(sys, 'prefix'):
|
601 |
+
ensure('library_dirs', os.path.join(sys.prefix, 'libs'))
|
602 |
+
else:
|
603 |
+
# we need 'libpypy-c.{so,dylib}', which should be by
|
604 |
+
# default located in 'sys.prefix/bin' for installed
|
605 |
+
# systems.
|
606 |
+
if sys.version_info < (3,):
|
607 |
+
pythonlib = "pypy-c"
|
608 |
+
else:
|
609 |
+
pythonlib = "pypy3-c"
|
610 |
+
if hasattr(sys, 'prefix'):
|
611 |
+
ensure('library_dirs', os.path.join(sys.prefix, 'bin'))
|
612 |
+
# On uninstalled pypy's, the libpypy-c is typically found in
|
613 |
+
# .../pypy/goal/.
|
614 |
+
if hasattr(sys, 'prefix'):
|
615 |
+
ensure('library_dirs', os.path.join(sys.prefix, 'pypy', 'goal'))
|
616 |
+
else:
|
617 |
+
if sys.platform == "win32":
|
618 |
+
template = "python%d%d"
|
619 |
+
if hasattr(sys, 'gettotalrefcount'):
|
620 |
+
template += '_d'
|
621 |
+
else:
|
622 |
+
try:
|
623 |
+
import sysconfig
|
624 |
+
except ImportError: # 2.6
|
625 |
+
from cffi._shimmed_dist_utils import sysconfig
|
626 |
+
template = "python%d.%d"
|
627 |
+
if sysconfig.get_config_var('DEBUG_EXT'):
|
628 |
+
template += sysconfig.get_config_var('DEBUG_EXT')
|
629 |
+
pythonlib = (template %
|
630 |
+
(sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff))
|
631 |
+
if hasattr(sys, 'abiflags'):
|
632 |
+
pythonlib += sys.abiflags
|
633 |
+
ensure('libraries', pythonlib)
|
634 |
+
if sys.platform == "win32":
|
635 |
+
ensure('extra_link_args', '/MANIFEST')
|
636 |
+
|
637 |
+
def set_source(self, module_name, source, source_extension='.c', **kwds):
|
638 |
+
import os
|
639 |
+
if hasattr(self, '_assigned_source'):
|
640 |
+
raise ValueError("set_source() cannot be called several times "
|
641 |
+
"per ffi object")
|
642 |
+
if not isinstance(module_name, basestring):
|
643 |
+
raise TypeError("'module_name' must be a string")
|
644 |
+
if os.sep in module_name or (os.altsep and os.altsep in module_name):
|
645 |
+
raise ValueError("'module_name' must not contain '/': use a dotted "
|
646 |
+
"name to make a 'package.module' location")
|
647 |
+
self._assigned_source = (str(module_name), source,
|
648 |
+
source_extension, kwds)
|
649 |
+
|
650 |
+
def set_source_pkgconfig(self, module_name, pkgconfig_libs, source,
|
651 |
+
source_extension='.c', **kwds):
|
652 |
+
from . import pkgconfig
|
653 |
+
if not isinstance(pkgconfig_libs, list):
|
654 |
+
raise TypeError("the pkgconfig_libs argument must be a list "
|
655 |
+
"of package names")
|
656 |
+
kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs)
|
657 |
+
pkgconfig.merge_flags(kwds, kwds2)
|
658 |
+
self.set_source(module_name, source, source_extension, **kwds)
|
659 |
+
|
660 |
+
def distutils_extension(self, tmpdir='build', verbose=True):
|
661 |
+
from cffi._shimmed_dist_utils import mkpath
|
662 |
+
from .recompiler import recompile
|
663 |
+
#
|
664 |
+
if not hasattr(self, '_assigned_source'):
|
665 |
+
if hasattr(self, 'verifier'): # fallback, 'tmpdir' ignored
|
666 |
+
return self.verifier.get_extension()
|
667 |
+
raise ValueError("set_source() must be called before"
|
668 |
+
" distutils_extension()")
|
669 |
+
module_name, source, source_extension, kwds = self._assigned_source
|
670 |
+
if source is None:
|
671 |
+
raise TypeError("distutils_extension() is only for C extension "
|
672 |
+
"modules, not for dlopen()-style pure Python "
|
673 |
+
"modules")
|
674 |
+
mkpath(tmpdir)
|
675 |
+
ext, updated = recompile(self, module_name,
|
676 |
+
source, tmpdir=tmpdir, extradir=tmpdir,
|
677 |
+
source_extension=source_extension,
|
678 |
+
call_c_compiler=False, **kwds)
|
679 |
+
if verbose:
|
680 |
+
if updated:
|
681 |
+
sys.stderr.write("regenerated: %r\n" % (ext.sources[0],))
|
682 |
+
else:
|
683 |
+
sys.stderr.write("not modified: %r\n" % (ext.sources[0],))
|
684 |
+
return ext
|
685 |
+
|
686 |
+
def emit_c_code(self, filename):
|
687 |
+
from .recompiler import recompile
|
688 |
+
#
|
689 |
+
if not hasattr(self, '_assigned_source'):
|
690 |
+
raise ValueError("set_source() must be called before emit_c_code()")
|
691 |
+
module_name, source, source_extension, kwds = self._assigned_source
|
692 |
+
if source is None:
|
693 |
+
raise TypeError("emit_c_code() is only for C extension modules, "
|
694 |
+
"not for dlopen()-style pure Python modules")
|
695 |
+
recompile(self, module_name, source,
|
696 |
+
c_file=filename, call_c_compiler=False,
|
697 |
+
uses_ffiplatform=False, **kwds)
|
698 |
+
|
699 |
+
def emit_python_code(self, filename):
|
700 |
+
from .recompiler import recompile
|
701 |
+
#
|
702 |
+
if not hasattr(self, '_assigned_source'):
|
703 |
+
raise ValueError("set_source() must be called before emit_c_code()")
|
704 |
+
module_name, source, source_extension, kwds = self._assigned_source
|
705 |
+
if source is not None:
|
706 |
+
raise TypeError("emit_python_code() is only for dlopen()-style "
|
707 |
+
"pure Python modules, not for C extension modules")
|
708 |
+
recompile(self, module_name, source,
|
709 |
+
c_file=filename, call_c_compiler=False,
|
710 |
+
uses_ffiplatform=False, **kwds)
|
711 |
+
|
712 |
+
def compile(self, tmpdir='.', verbose=0, target=None, debug=None):
|
713 |
+
"""The 'target' argument gives the final file name of the
|
714 |
+
compiled DLL. Use '*' to force distutils' choice, suitable for
|
715 |
+
regular CPython C API modules. Use a file name ending in '.*'
|
716 |
+
to ask for the system's default extension for dynamic libraries
|
717 |
+
(.so/.dll/.dylib).
|
718 |
+
|
719 |
+
The default is '*' when building a non-embedded C API extension,
|
720 |
+
and (module_name + '.*') when building an embedded library.
|
721 |
+
"""
|
722 |
+
from .recompiler import recompile
|
723 |
+
#
|
724 |
+
if not hasattr(self, '_assigned_source'):
|
725 |
+
raise ValueError("set_source() must be called before compile()")
|
726 |
+
module_name, source, source_extension, kwds = self._assigned_source
|
727 |
+
return recompile(self, module_name, source, tmpdir=tmpdir,
|
728 |
+
target=target, source_extension=source_extension,
|
729 |
+
compiler_verbose=verbose, debug=debug, **kwds)
|
730 |
+
|
731 |
+
def init_once(self, func, tag):
|
732 |
+
# Read _init_once_cache[tag], which is either (False, lock) if
|
733 |
+
# we're calling the function now in some thread, or (True, result).
|
734 |
+
# Don't call setdefault() in most cases, to avoid allocating and
|
735 |
+
# immediately freeing a lock; but still use setdefaut() to avoid
|
736 |
+
# races.
|
737 |
+
try:
|
738 |
+
x = self._init_once_cache[tag]
|
739 |
+
except KeyError:
|
740 |
+
x = self._init_once_cache.setdefault(tag, (False, allocate_lock()))
|
741 |
+
# Common case: we got (True, result), so we return the result.
|
742 |
+
if x[0]:
|
743 |
+
return x[1]
|
744 |
+
# Else, it's a lock. Acquire it to serialize the following tests.
|
745 |
+
with x[1]:
|
746 |
+
# Read again from _init_once_cache the current status.
|
747 |
+
x = self._init_once_cache[tag]
|
748 |
+
if x[0]:
|
749 |
+
return x[1]
|
750 |
+
# Call the function and store the result back.
|
751 |
+
result = func()
|
752 |
+
self._init_once_cache[tag] = (True, result)
|
753 |
+
return result
|
754 |
+
|
755 |
+
def embedding_init_code(self, pysource):
|
756 |
+
if self._embedding:
|
757 |
+
raise ValueError("embedding_init_code() can only be called once")
|
758 |
+
# fix 'pysource' before it gets dumped into the C file:
|
759 |
+
# - remove empty lines at the beginning, so it starts at "line 1"
|
760 |
+
# - dedent, if all non-empty lines are indented
|
761 |
+
# - check for SyntaxErrors
|
762 |
+
import re
|
763 |
+
match = re.match(r'\s*\n', pysource)
|
764 |
+
if match:
|
765 |
+
pysource = pysource[match.end():]
|
766 |
+
lines = pysource.splitlines() or ['']
|
767 |
+
prefix = re.match(r'\s*', lines[0]).group()
|
768 |
+
for i in range(1, len(lines)):
|
769 |
+
line = lines[i]
|
770 |
+
if line.rstrip():
|
771 |
+
while not line.startswith(prefix):
|
772 |
+
prefix = prefix[:-1]
|
773 |
+
i = len(prefix)
|
774 |
+
lines = [line[i:]+'\n' for line in lines]
|
775 |
+
pysource = ''.join(lines)
|
776 |
+
#
|
777 |
+
compile(pysource, "cffi_init", "exec")
|
778 |
+
#
|
779 |
+
self._embedding = pysource
|
780 |
+
|
781 |
+
def def_extern(self, *args, **kwds):
|
782 |
+
raise ValueError("ffi.def_extern() is only available on API-mode FFI "
|
783 |
+
"objects")
|
784 |
+
|
785 |
+
def list_types(self):
|
786 |
+
"""Returns the user type names known to this FFI instance.
|
787 |
+
This returns a tuple containing three lists of names:
|
788 |
+
(typedef_names, names_of_structs, names_of_unions)
|
789 |
+
"""
|
790 |
+
typedefs = []
|
791 |
+
structs = []
|
792 |
+
unions = []
|
793 |
+
for key in self._parser._declarations:
|
794 |
+
if key.startswith('typedef '):
|
795 |
+
typedefs.append(key[8:])
|
796 |
+
elif key.startswith('struct '):
|
797 |
+
structs.append(key[7:])
|
798 |
+
elif key.startswith('union '):
|
799 |
+
unions.append(key[6:])
|
800 |
+
typedefs.sort()
|
801 |
+
structs.sort()
|
802 |
+
unions.sort()
|
803 |
+
return (typedefs, structs, unions)
|
804 |
+
|
805 |
+
|
806 |
+
def _load_backend_lib(backend, name, flags):
|
807 |
+
import os
|
808 |
+
if not isinstance(name, basestring):
|
809 |
+
if sys.platform != "win32" or name is not None:
|
810 |
+
return backend.load_library(name, flags)
|
811 |
+
name = "c" # Windows: load_library(None) fails, but this works
|
812 |
+
# on Python 2 (backward compatibility hack only)
|
813 |
+
first_error = None
|
814 |
+
if '.' in name or '/' in name or os.sep in name:
|
815 |
+
try:
|
816 |
+
return backend.load_library(name, flags)
|
817 |
+
except OSError as e:
|
818 |
+
first_error = e
|
819 |
+
import ctypes.util
|
820 |
+
path = ctypes.util.find_library(name)
|
821 |
+
if path is None:
|
822 |
+
if name == "c" and sys.platform == "win32" and sys.version_info >= (3,):
|
823 |
+
raise OSError("dlopen(None) cannot work on Windows for Python 3 "
|
824 |
+
"(see http://bugs.python.org/issue23606)")
|
825 |
+
msg = ("ctypes.util.find_library() did not manage "
|
826 |
+
"to locate a library called %r" % (name,))
|
827 |
+
if first_error is not None:
|
828 |
+
msg = "%s. Additionally, %s" % (first_error, msg)
|
829 |
+
raise OSError(msg)
|
830 |
+
return backend.load_library(path, flags)
|
831 |
+
|
832 |
+
def _make_ffi_library(ffi, libname, flags):
|
833 |
+
backend = ffi._backend
|
834 |
+
backendlib = _load_backend_lib(backend, libname, flags)
|
835 |
+
#
|
836 |
+
def accessor_function(name):
|
837 |
+
key = 'function ' + name
|
838 |
+
tp, _ = ffi._parser._declarations[key]
|
839 |
+
BType = ffi._get_cached_btype(tp)
|
840 |
+
value = backendlib.load_function(BType, name)
|
841 |
+
library.__dict__[name] = value
|
842 |
+
#
|
843 |
+
def accessor_variable(name):
|
844 |
+
key = 'variable ' + name
|
845 |
+
tp, _ = ffi._parser._declarations[key]
|
846 |
+
BType = ffi._get_cached_btype(tp)
|
847 |
+
read_variable = backendlib.read_variable
|
848 |
+
write_variable = backendlib.write_variable
|
849 |
+
setattr(FFILibrary, name, property(
|
850 |
+
lambda self: read_variable(BType, name),
|
851 |
+
lambda self, value: write_variable(BType, name, value)))
|
852 |
+
#
|
853 |
+
def addressof_var(name):
|
854 |
+
try:
|
855 |
+
return addr_variables[name]
|
856 |
+
except KeyError:
|
857 |
+
with ffi._lock:
|
858 |
+
if name not in addr_variables:
|
859 |
+
key = 'variable ' + name
|
860 |
+
tp, _ = ffi._parser._declarations[key]
|
861 |
+
BType = ffi._get_cached_btype(tp)
|
862 |
+
if BType.kind != 'array':
|
863 |
+
BType = model.pointer_cache(ffi, BType)
|
864 |
+
p = backendlib.load_function(BType, name)
|
865 |
+
addr_variables[name] = p
|
866 |
+
return addr_variables[name]
|
867 |
+
#
|
868 |
+
def accessor_constant(name):
|
869 |
+
raise NotImplementedError("non-integer constant '%s' cannot be "
|
870 |
+
"accessed from a dlopen() library" % (name,))
|
871 |
+
#
|
872 |
+
def accessor_int_constant(name):
|
873 |
+
library.__dict__[name] = ffi._parser._int_constants[name]
|
874 |
+
#
|
875 |
+
accessors = {}
|
876 |
+
accessors_version = [False]
|
877 |
+
addr_variables = {}
|
878 |
+
#
|
879 |
+
def update_accessors():
|
880 |
+
if accessors_version[0] is ffi._cdef_version:
|
881 |
+
return
|
882 |
+
#
|
883 |
+
for key, (tp, _) in ffi._parser._declarations.items():
|
884 |
+
if not isinstance(tp, model.EnumType):
|
885 |
+
tag, name = key.split(' ', 1)
|
886 |
+
if tag == 'function':
|
887 |
+
accessors[name] = accessor_function
|
888 |
+
elif tag == 'variable':
|
889 |
+
accessors[name] = accessor_variable
|
890 |
+
elif tag == 'constant':
|
891 |
+
accessors[name] = accessor_constant
|
892 |
+
else:
|
893 |
+
for i, enumname in enumerate(tp.enumerators):
|
894 |
+
def accessor_enum(name, tp=tp, i=i):
|
895 |
+
tp.check_not_partial()
|
896 |
+
library.__dict__[name] = tp.enumvalues[i]
|
897 |
+
accessors[enumname] = accessor_enum
|
898 |
+
for name in ffi._parser._int_constants:
|
899 |
+
accessors.setdefault(name, accessor_int_constant)
|
900 |
+
accessors_version[0] = ffi._cdef_version
|
901 |
+
#
|
902 |
+
def make_accessor(name):
|
903 |
+
with ffi._lock:
|
904 |
+
if name in library.__dict__ or name in FFILibrary.__dict__:
|
905 |
+
return # added by another thread while waiting for the lock
|
906 |
+
if name not in accessors:
|
907 |
+
update_accessors()
|
908 |
+
if name not in accessors:
|
909 |
+
raise AttributeError(name)
|
910 |
+
accessors[name](name)
|
911 |
+
#
|
912 |
+
class FFILibrary(object):
|
913 |
+
def __getattr__(self, name):
|
914 |
+
make_accessor(name)
|
915 |
+
return getattr(self, name)
|
916 |
+
def __setattr__(self, name, value):
|
917 |
+
try:
|
918 |
+
property = getattr(self.__class__, name)
|
919 |
+
except AttributeError:
|
920 |
+
make_accessor(name)
|
921 |
+
setattr(self, name, value)
|
922 |
+
else:
|
923 |
+
property.__set__(self, value)
|
924 |
+
def __dir__(self):
|
925 |
+
with ffi._lock:
|
926 |
+
update_accessors()
|
927 |
+
return accessors.keys()
|
928 |
+
def __addressof__(self, name):
|
929 |
+
if name in library.__dict__:
|
930 |
+
return library.__dict__[name]
|
931 |
+
if name in FFILibrary.__dict__:
|
932 |
+
return addressof_var(name)
|
933 |
+
make_accessor(name)
|
934 |
+
if name in library.__dict__:
|
935 |
+
return library.__dict__[name]
|
936 |
+
if name in FFILibrary.__dict__:
|
937 |
+
return addressof_var(name)
|
938 |
+
raise AttributeError("cffi library has no function or "
|
939 |
+
"global variable named '%s'" % (name,))
|
940 |
+
def __cffi_close__(self):
|
941 |
+
backendlib.close_lib()
|
942 |
+
self.__dict__.clear()
|
943 |
+
#
|
944 |
+
if isinstance(libname, basestring):
|
945 |
+
try:
|
946 |
+
if not isinstance(libname, str): # unicode, on Python 2
|
947 |
+
libname = libname.encode('utf-8')
|
948 |
+
FFILibrary.__name__ = 'FFILibrary_%s' % libname
|
949 |
+
except UnicodeError:
|
950 |
+
pass
|
951 |
+
library = FFILibrary()
|
952 |
+
return library, library.__dict__
|
953 |
+
|
954 |
+
def _builtin_function_type(func):
|
955 |
+
# a hack to make at least ffi.typeof(builtin_function) work,
|
956 |
+
# if the builtin function was obtained by 'vengine_cpy'.
|
957 |
+
import sys
|
958 |
+
try:
|
959 |
+
module = sys.modules[func.__module__]
|
960 |
+
ffi = module._cffi_original_ffi
|
961 |
+
types_of_builtin_funcs = module._cffi_types_of_builtin_funcs
|
962 |
+
tp = types_of_builtin_funcs[func]
|
963 |
+
except (KeyError, AttributeError, TypeError):
|
964 |
+
return None
|
965 |
+
else:
|
966 |
+
with ffi._lock:
|
967 |
+
return ffi._get_cached_btype(tp)
|