github.com/whamcloud/lemur@v0.0.0-20190827193804-4655df8a52af/packaging/ci/lambda/GitPullS3/cffi/api.py (about)

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