Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
api.py968 linesDownload Raw Back to cffi
1import sys, types2from .lock import allocate_lock3from .error import CDefError4from . import model5 6try:7    callable8except NameError:9    # Python 3.110    from collections import Callable11    callable = lambda x: isinstance(x, Callable)12 13try:14    basestring15except NameError:16    # Python 3.x17    basestring = str18 19_unspecified = object()20 21 22 23class 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 library35        -or-36        C = ffi.verify()  # use a C compiler: verify the decl above is right37 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 to43        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) with47            # _cffi_backend.so compiled.48            import _cffi_backend as backend49            from . import __version__50            if backend.__version__ != __version__:51                # bad version!  Try to be as explicit as possible.52                if hasattr(backend, '__file__'):53                    # CPython54                    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                    # PyPy59                    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 option62            # 'backend=backend_ctypes.CTypesBackend()', but don't63            # rely on it!  It's probably not going to work well.)64 65        from . import cparser66        self._backend = backend67        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 = None77        self._init_once_cache = {}78        self._cdef_version = None79        self._embedding = None80        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 class92            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 instance97            self.NULL = self.cast(self.BVoidP, 0)98            self.CData, self.CType = backend._get_types()99        self.buffer = backend.buffer100 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 can104        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 this107        cdef are packed, i.e. laid out without any field alignment at all.108        Alternatively, 'pack' can be a small integer, and requests for109        alignment greater than that are ignored (pack=1 is equivalent to110        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 2121            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._recomplete132            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 not141        linked to a particular library, just like C headers; in the142        library we only look for the actual (untyped) symbols.143        """144        if not (isinstance(name, basestring) or145                name is None or146                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 lib154 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 fail158        (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 = cdecl165        if key in self._parsed_types:166            return self._parsed_types[key]167        #168        if not isinstance(cdecl, str):    # unicode, on Python 2169            cdecl = cdecl.encode('ascii')170        #171        type = self._parser.parse_type(cdecl)172        really_a_function_type = type.is_raw_function173        if really_a_function_type:174            type = type.as_function_pointer()175        btype = self._get_cached_btype(type)176        result = btype, really_a_function_type177        self._parsed_types[key] = result178        return result179 180    def _typeof(self, cdecl, consider_function_as_funcptr=False):181        # string -> ctype object182        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 = result189        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 btype193 194    def typeof(self, cdecl):195        """Parse the C type given as a string and return the196        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 res207        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 a215        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 type225        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 given233        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 array236        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 and244        return a pointer to it.  The specified C type must be either a245        pointer or an array: ``new('X *')`` allocates an X and returns246        a pointer to it, whereas ``new('X[n]')`` allocates an array of247        n X'es and returns an array referencing it (which works248        mostly like a pointer, like in C).  You can also use249        ``new('X[]', n)`` to allocate an array of a non-constant250        length n.251 252        The memory is initialized following the rules of declaring a253        global variable in C: by default it is zero-initialized, but254        an explicit initializer can be given which can be used to255        fill all or part of the memory.256 257        When the returned <cdata> object goes out of scope, the memory258        is freed.  In other words the returned <cdata> object has259        ownership of the value of type 'cdecl' that it points to.  This260        means that the raw data can be used as long as this object is261        kept alive, but must not be used for a longer time.  Be careful262        about that when copying the pointer to the memory somewhere263        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, a275        MemoryError is raised.  'free' is called with the result of 'alloc'276        as argument.  Both can be either Python function or directly C277        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 memory281        returned by 'alloc' is assumed to be already cleared (or you are282        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 allocate292 293    def cast(self, cdecl, source):294        """Similar to a C cast: returns an instance of the named C295        type initialized with the given 'source'.  The source is296        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, returns305        the null-terminated string.  The returned string extends until306        the first null character, or at most 'maxlen' characters.  If307        'cdata' is an array then 'maxlen' defaults to its length.308 309        If 'cdata' is a pointer or array of wchar_t, returns a unicode310        string following the same rules.311 312        If 'cdata' is a single character or byte or a wchar_t, returns313        it as a string or unicode string.314 315        If 'cdata' is an enum, returns the value of the enumerator as a316        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 of332        '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 data339   #    pointed to by the given 'cdata'.  The 'cdata' must be a pointer or340   #    an array.  Can be passed to functions expecting a buffer, or directly341   #    manipulated with:342   #343   #        buf[:]          get a copy of it in a regular string, or344   #        buf[idx]        as a single character345   #        buf[:] = ...346   #        buf[idx] = ...  change the content347   #    """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 the353        given Python object, which must support the buffer interface.354        Note that this is not meant to be used on the built-in types355        str or unicode (you can build 'char[]' arrays explicitly)356        but only on objects containing large quantities of raw data357        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, cdecl363        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 buffer376        object.  The size to copy, 'n', is always measured in bytes.377 378        Unlike other methods, this one supports all Python buffer including379        byte strings and bytearrays---but it still does not support380        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 a386        callback object.  'cdecl' must name a C function pointer type.387        The callback invokes the specified 'python_callable' (which may388        be provided either directly or via a decorator).  Important: the389        callback object must be manually kept alive for as long as the390        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 mode402        else:403            return callback_decorator_wrap(python_callable)  # direct mode404 405    def getctype(self, cdecl, replace_with=''):406        """Return a string giving the C type 'cdecl', which may be itself407        a string or a <ctype> object.  If 'replace_with' is given, it gives408        extra text to append (or insert for more complicated C types), like409        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_with417        elif replace_with and not replace_with[0] in '[(':418            replace_with = ' ' + replace_with419        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 same423        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 to427        trigger the garbage collection more eagerly.  So far only used428        on PyPy.  It tells the GC that the returned object keeps alive429        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 False435        # 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 BType444 445    def verify(self, source='', tmpdir=None, **kwargs):446        """Verify that the current ffi signatures compile on this447        machine, and return a dynamic library object.  The dynamic448        library can be used to call functions and access global449        variables declared in this 'ffi'.  The library is compiled450        by the C compiler: it gives you C-level API compatibility451        (including calling macros).  This is unlike 'ffi.dlopen()',452        which requires binary compatibility in the signatures.453        """454        from .verifier import Verifier, _caller_dir_pycache455        #456        # If set_unicode(True) was called, insert the UNICODE and457        # _UNICODE macro declarations458        if self._windows_unicode:459            self._apply_windows_unicode(kwargs)460        #461        # Set the tmpdir here, and not in Verifier.__init__: it picks462        # up the caller's directory, which we want to be the caller of463        # 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, even471        # if the caller doesn't keep it alive itself (it should).472        self._libraries.append(lib)473        return lib474 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 that492        field or array item in the structure or array, recursively in493        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            raise501        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 = 0507        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 += offset1515        return ctype, offset516 517    def include(self, ffi_to_include):518        """Includes the typedefs, structs, unions and enums defined519        in another FFI instance.  Usage is similar to a #include in C,520        where a part of the program might include types defined in521        another part for its own usage.  Note that the include()522        method has no effect on functions, constants and global523        variables, which must anyway be accessed directly from the524        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 and551        _UNICODE defines in C, and declare the types like TCHAR and LPTCSTR552        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_flag578 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'] = defmacros586 587    def _apply_embedding_fix(self, kwds):588        # must include an argument like "-lpython2.7" for the compiler589        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 os596            if sys.platform == "win32":597                # we need 'libpypy-c.lib'.  Current distributions of598                # 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 by604                # default located in 'sys.prefix/bin' for installed605                # 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 in613            # .../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 sysconfig624                except ImportError:    # 2.6625                    from cffi._shimmed_dist_utils import sysconfig626                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.abiflags633        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 os639        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 pkgconfig653        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 mkpath662        from .recompiler import recompile663        #664        if not hasattr(self, '_assigned_source'):665            if hasattr(self, 'verifier'):     # fallback, 'tmpdir' ignored666                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_source670        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 ext685 686    def emit_c_code(self, filename):687        from .recompiler import recompile688        #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_source692        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 recompile701        #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_source705        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 the714        compiled DLL.  Use '*' to force distutils' choice, suitable for715        regular CPython C API modules.  Use a file name ending in '.*'716        to ask for the system's default extension for dynamic libraries717        (.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 recompile723        #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_source727        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) if733        # we're calling the function now in some thread, or (True, result).734        # Don't call setdefault() in most cases, to avoid allocating and735        # immediately freeing a lock; but still use setdefaut() to avoid736        # 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 result754 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 indented761        # - check for SyntaxErrors762        import re763        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 = pysource780 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 806def _load_backend_lib(backend, name, flags):807    import os808    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 works812                      # on Python 2 (backward compatibility hack only)813    first_error = None814    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 = e819    import ctypes.util820    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 832def _make_ffi_library(ffi, libname, flags):833    backend = ffi._backend834    backendlib = _load_backend_lib(backend, libname, flags)835    #836    def accessor_function(name):837        key = 'function ' + name838        tp, _ = ffi._parser._declarations[key]839        BType = ffi._get_cached_btype(tp)840        value = backendlib.load_function(BType, name)841        library.__dict__[name] = value842    #843    def accessor_variable(name):844        key = 'variable ' + name845        tp, _ = ffi._parser._declarations[key]846        BType = ffi._get_cached_btype(tp)847        read_variable = backendlib.read_variable848        write_variable = backendlib.write_variable849        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 ' + name860                    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] = p866            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            return882        #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_function888                elif tag == 'variable':889                    accessors[name] = accessor_variable890                elif tag == 'constant':891                    accessors[name] = accessor_constant892            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_enum898        for name in ffi._parser._int_constants:899            accessors.setdefault(name, accessor_int_constant)900        accessors_version[0] = ffi._cdef_version901    #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 lock906            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 2947                libname = libname.encode('utf-8')948            FFILibrary.__name__ = 'FFILibrary_%s' % libname949        except UnicodeError:950            pass951    library = FFILibrary()952    return library, library.__dict__953 954def _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 sys958    try:959        module = sys.modules[func.__module__]960        ffi = module._cffi_original_ffi961        types_of_builtin_funcs = module._cffi_types_of_builtin_funcs962        tp = types_of_builtin_funcs[func]963    except (KeyError, AttributeError, TypeError):964        return None965    else:966        with ffi._lock:967            return ffi._get_cached_btype(tp)968 
codekingpro/portable-devtools · Team Ai