• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""create and manipulate C data types in Python"""
2
3import os as _os, sys as _sys
4import types as _types
5
6__version__ = "1.1.0"
7
8from _ctypes import Union, Structure, Array
9from _ctypes import _Pointer
10from _ctypes import CFuncPtr as _CFuncPtr
11from _ctypes import __version__ as _ctypes_version
12from _ctypes import RTLD_LOCAL, RTLD_GLOBAL
13from _ctypes import ArgumentError
14from _ctypes import SIZEOF_TIME_T
15
16from struct import calcsize as _calcsize
17
18if __version__ != _ctypes_version:
19    raise Exception("Version number mismatch", __version__, _ctypes_version)
20
21if _os.name == "nt":
22    from _ctypes import FormatError
23
24DEFAULT_MODE = RTLD_LOCAL
25if _os.name == "posix" and _sys.platform == "darwin":
26    # On OS X 10.3, we use RTLD_GLOBAL as default mode
27    # because RTLD_LOCAL does not work at least on some
28    # libraries.  OS X 10.3 is Darwin 7, so we check for
29    # that.
30
31    if int(_os.uname().release.split('.')[0]) < 8:
32        DEFAULT_MODE = RTLD_GLOBAL
33
34from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \
35     FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI, \
36     FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \
37     FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR
38
39# WINOLEAPI -> HRESULT
40# WINOLEAPI_(type)
41#
42# STDMETHODCALLTYPE
43#
44# STDMETHOD(name)
45# STDMETHOD_(type, name)
46#
47# STDAPICALLTYPE
48
49def create_string_buffer(init, size=None):
50    """create_string_buffer(aBytes) -> character array
51    create_string_buffer(anInteger) -> character array
52    create_string_buffer(aBytes, anInteger) -> character array
53    """
54    if isinstance(init, bytes):
55        if size is None:
56            size = len(init)+1
57        _sys.audit("ctypes.create_string_buffer", init, size)
58        buftype = c_char * size
59        buf = buftype()
60        buf.value = init
61        return buf
62    elif isinstance(init, int):
63        _sys.audit("ctypes.create_string_buffer", None, init)
64        buftype = c_char * init
65        buf = buftype()
66        return buf
67    raise TypeError(init)
68
69# Alias to create_string_buffer() for backward compatibility
70c_buffer = create_string_buffer
71
72_c_functype_cache = {}
73def CFUNCTYPE(restype, *argtypes, **kw):
74    """CFUNCTYPE(restype, *argtypes,
75                 use_errno=False, use_last_error=False) -> function prototype.
76
77    restype: the result type
78    argtypes: a sequence specifying the argument types
79
80    The function prototype can be called in different ways to create a
81    callable object:
82
83    prototype(integer address) -> foreign function
84    prototype(callable) -> create and return a C callable function from callable
85    prototype(integer index, method name[, paramflags]) -> foreign function calling a COM method
86    prototype((ordinal number, dll object)[, paramflags]) -> foreign function exported by ordinal
87    prototype((function name, dll object)[, paramflags]) -> foreign function exported by name
88    """
89    flags = _FUNCFLAG_CDECL
90    if kw.pop("use_errno", False):
91        flags |= _FUNCFLAG_USE_ERRNO
92    if kw.pop("use_last_error", False):
93        flags |= _FUNCFLAG_USE_LASTERROR
94    if kw:
95        raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
96
97    try:
98        return _c_functype_cache[(restype, argtypes, flags)]
99    except KeyError:
100        pass
101
102    class CFunctionType(_CFuncPtr):
103        _argtypes_ = argtypes
104        _restype_ = restype
105        _flags_ = flags
106    _c_functype_cache[(restype, argtypes, flags)] = CFunctionType
107    return CFunctionType
108
109if _os.name == "nt":
110    from _ctypes import LoadLibrary as _dlopen
111    from _ctypes import FUNCFLAG_STDCALL as _FUNCFLAG_STDCALL
112
113    _win_functype_cache = {}
114    def WINFUNCTYPE(restype, *argtypes, **kw):
115        # docstring set later (very similar to CFUNCTYPE.__doc__)
116        flags = _FUNCFLAG_STDCALL
117        if kw.pop("use_errno", False):
118            flags |= _FUNCFLAG_USE_ERRNO
119        if kw.pop("use_last_error", False):
120            flags |= _FUNCFLAG_USE_LASTERROR
121        if kw:
122            raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
123
124        try:
125            return _win_functype_cache[(restype, argtypes, flags)]
126        except KeyError:
127            pass
128
129        class WinFunctionType(_CFuncPtr):
130            _argtypes_ = argtypes
131            _restype_ = restype
132            _flags_ = flags
133        _win_functype_cache[(restype, argtypes, flags)] = WinFunctionType
134        return WinFunctionType
135    if WINFUNCTYPE.__doc__:
136        WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE")
137
138elif _os.name == "posix":
139    from _ctypes import dlopen as _dlopen
140
141from _ctypes import sizeof, byref, addressof, alignment, resize
142from _ctypes import get_errno, set_errno
143from _ctypes import _SimpleCData
144
145def _check_size(typ, typecode=None):
146    # Check if sizeof(ctypes_type) against struct.calcsize.  This
147    # should protect somewhat against a misconfigured libffi.
148    from struct import calcsize
149    if typecode is None:
150        # Most _type_ codes are the same as used in struct
151        typecode = typ._type_
152    actual, required = sizeof(typ), calcsize(typecode)
153    if actual != required:
154        raise SystemError("sizeof(%s) wrong: %d instead of %d" % \
155                          (typ, actual, required))
156
157class py_object(_SimpleCData):
158    _type_ = "O"
159    def __repr__(self):
160        try:
161            return super().__repr__()
162        except ValueError:
163            return "%s(<NULL>)" % type(self).__name__
164_check_size(py_object, "P")
165
166class c_short(_SimpleCData):
167    _type_ = "h"
168_check_size(c_short)
169
170class c_ushort(_SimpleCData):
171    _type_ = "H"
172_check_size(c_ushort)
173
174class c_long(_SimpleCData):
175    _type_ = "l"
176_check_size(c_long)
177
178class c_ulong(_SimpleCData):
179    _type_ = "L"
180_check_size(c_ulong)
181
182if _calcsize("i") == _calcsize("l"):
183    # if int and long have the same size, make c_int an alias for c_long
184    c_int = c_long
185    c_uint = c_ulong
186else:
187    class c_int(_SimpleCData):
188        _type_ = "i"
189    _check_size(c_int)
190
191    class c_uint(_SimpleCData):
192        _type_ = "I"
193    _check_size(c_uint)
194
195class c_float(_SimpleCData):
196    _type_ = "f"
197_check_size(c_float)
198
199class c_double(_SimpleCData):
200    _type_ = "d"
201_check_size(c_double)
202
203class c_longdouble(_SimpleCData):
204    _type_ = "g"
205if sizeof(c_longdouble) == sizeof(c_double):
206    c_longdouble = c_double
207
208if _calcsize("l") == _calcsize("q"):
209    # if long and long long have the same size, make c_longlong an alias for c_long
210    c_longlong = c_long
211    c_ulonglong = c_ulong
212else:
213    class c_longlong(_SimpleCData):
214        _type_ = "q"
215    _check_size(c_longlong)
216
217    class c_ulonglong(_SimpleCData):
218        _type_ = "Q"
219    ##    def from_param(cls, val):
220    ##        return ('d', float(val), val)
221    ##    from_param = classmethod(from_param)
222    _check_size(c_ulonglong)
223
224class c_ubyte(_SimpleCData):
225    _type_ = "B"
226c_ubyte.__ctype_le__ = c_ubyte.__ctype_be__ = c_ubyte
227# backward compatibility:
228##c_uchar = c_ubyte
229_check_size(c_ubyte)
230
231class c_byte(_SimpleCData):
232    _type_ = "b"
233c_byte.__ctype_le__ = c_byte.__ctype_be__ = c_byte
234_check_size(c_byte)
235
236class c_char(_SimpleCData):
237    _type_ = "c"
238c_char.__ctype_le__ = c_char.__ctype_be__ = c_char
239_check_size(c_char)
240
241class c_char_p(_SimpleCData):
242    _type_ = "z"
243    def __repr__(self):
244        return "%s(%s)" % (self.__class__.__name__, c_void_p.from_buffer(self).value)
245_check_size(c_char_p, "P")
246
247class c_void_p(_SimpleCData):
248    _type_ = "P"
249c_voidp = c_void_p # backwards compatibility (to a bug)
250_check_size(c_void_p)
251
252class c_bool(_SimpleCData):
253    _type_ = "?"
254
255from _ctypes import POINTER, pointer, _pointer_type_cache
256
257class c_wchar_p(_SimpleCData):
258    _type_ = "Z"
259    def __repr__(self):
260        return "%s(%s)" % (self.__class__.__name__, c_void_p.from_buffer(self).value)
261
262class c_wchar(_SimpleCData):
263    _type_ = "u"
264
265def _reset_cache():
266    _pointer_type_cache.clear()
267    _c_functype_cache.clear()
268    if _os.name == "nt":
269        _win_functype_cache.clear()
270    # _SimpleCData.c_wchar_p_from_param
271    POINTER(c_wchar).from_param = c_wchar_p.from_param
272    # _SimpleCData.c_char_p_from_param
273    POINTER(c_char).from_param = c_char_p.from_param
274    _pointer_type_cache[None] = c_void_p
275
276def create_unicode_buffer(init, size=None):
277    """create_unicode_buffer(aString) -> character array
278    create_unicode_buffer(anInteger) -> character array
279    create_unicode_buffer(aString, anInteger) -> character array
280    """
281    if isinstance(init, str):
282        if size is None:
283            if sizeof(c_wchar) == 2:
284                # UTF-16 requires a surrogate pair (2 wchar_t) for non-BMP
285                # characters (outside [U+0000; U+FFFF] range). +1 for trailing
286                # NUL character.
287                size = sum(2 if ord(c) > 0xFFFF else 1 for c in init) + 1
288            else:
289                # 32-bit wchar_t (1 wchar_t per Unicode character). +1 for
290                # trailing NUL character.
291                size = len(init) + 1
292        _sys.audit("ctypes.create_unicode_buffer", init, size)
293        buftype = c_wchar * size
294        buf = buftype()
295        buf.value = init
296        return buf
297    elif isinstance(init, int):
298        _sys.audit("ctypes.create_unicode_buffer", None, init)
299        buftype = c_wchar * init
300        buf = buftype()
301        return buf
302    raise TypeError(init)
303
304
305def SetPointerType(pointer, cls):
306    import warnings
307    warnings._deprecated("ctypes.SetPointerType", remove=(3, 15))
308    if _pointer_type_cache.get(cls, None) is not None:
309        raise RuntimeError("This type already exists in the cache")
310    if id(pointer) not in _pointer_type_cache:
311        raise RuntimeError("What's this???")
312    pointer.set_type(cls)
313    _pointer_type_cache[cls] = pointer
314    del _pointer_type_cache[id(pointer)]
315
316def ARRAY(typ, len):
317    return typ * len
318
319################################################################
320
321
322class CDLL(object):
323    """An instance of this class represents a loaded dll/shared
324    library, exporting functions using the standard C calling
325    convention (named 'cdecl' on Windows).
326
327    The exported functions can be accessed as attributes, or by
328    indexing with the function name.  Examples:
329
330    <obj>.qsort -> callable object
331    <obj>['qsort'] -> callable object
332
333    Calling the functions releases the Python GIL during the call and
334    reacquires it afterwards.
335    """
336    _func_flags_ = _FUNCFLAG_CDECL
337    _func_restype_ = c_int
338    # default values for repr
339    _name = '<uninitialized>'
340    _handle = 0
341    _FuncPtr = None
342
343    def __init__(self, name, mode=DEFAULT_MODE, handle=None,
344                 use_errno=False,
345                 use_last_error=False,
346                 winmode=None):
347        if name:
348            name = _os.fspath(name)
349
350            # If the filename that has been provided is an iOS/tvOS/watchOS
351            # .fwork file, dereference the location to the true origin of the
352            # binary.
353            if name.endswith(".fwork"):
354                with open(name) as f:
355                    name = _os.path.join(
356                        _os.path.dirname(_sys.executable),
357                        f.read().strip()
358                    )
359
360        self._name = name
361        flags = self._func_flags_
362        if use_errno:
363            flags |= _FUNCFLAG_USE_ERRNO
364        if use_last_error:
365            flags |= _FUNCFLAG_USE_LASTERROR
366        if _sys.platform.startswith("aix"):
367            """When the name contains ".a(" and ends with ")",
368               e.g., "libFOO.a(libFOO.so)" - this is taken to be an
369               archive(member) syntax for dlopen(), and the mode is adjusted.
370               Otherwise, name is presented to dlopen() as a file argument.
371            """
372            if name and name.endswith(")") and ".a(" in name:
373                mode |= ( _os.RTLD_MEMBER | _os.RTLD_NOW )
374        if _os.name == "nt":
375            if winmode is not None:
376                mode = winmode
377            else:
378                import nt
379                mode = nt._LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
380                if '/' in name or '\\' in name:
381                    self._name = nt._getfullpathname(self._name)
382                    mode |= nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
383
384        class _FuncPtr(_CFuncPtr):
385            _flags_ = flags
386            _restype_ = self._func_restype_
387        self._FuncPtr = _FuncPtr
388
389        if handle is None:
390            self._handle = _dlopen(self._name, mode)
391        else:
392            self._handle = handle
393
394    def __repr__(self):
395        return "<%s '%s', handle %x at %#x>" % \
396               (self.__class__.__name__, self._name,
397                (self._handle & (_sys.maxsize*2 + 1)),
398                id(self) & (_sys.maxsize*2 + 1))
399
400    def __getattr__(self, name):
401        if name.startswith('__') and name.endswith('__'):
402            raise AttributeError(name)
403        func = self.__getitem__(name)
404        setattr(self, name, func)
405        return func
406
407    def __getitem__(self, name_or_ordinal):
408        func = self._FuncPtr((name_or_ordinal, self))
409        if not isinstance(name_or_ordinal, int):
410            func.__name__ = name_or_ordinal
411        return func
412
413class PyDLL(CDLL):
414    """This class represents the Python library itself.  It allows
415    accessing Python API functions.  The GIL is not released, and
416    Python exceptions are handled correctly.
417    """
418    _func_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
419
420if _os.name == "nt":
421
422    class WinDLL(CDLL):
423        """This class represents a dll exporting functions using the
424        Windows stdcall calling convention.
425        """
426        _func_flags_ = _FUNCFLAG_STDCALL
427
428    # XXX Hm, what about HRESULT as normal parameter?
429    # Mustn't it derive from c_long then?
430    from _ctypes import _check_HRESULT, _SimpleCData
431    class HRESULT(_SimpleCData):
432        _type_ = "l"
433        # _check_retval_ is called with the function's result when it
434        # is used as restype.  It checks for the FAILED bit, and
435        # raises an OSError if it is set.
436        #
437        # The _check_retval_ method is implemented in C, so that the
438        # method definition itself is not included in the traceback
439        # when it raises an error - that is what we want (and Python
440        # doesn't have a way to raise an exception in the caller's
441        # frame).
442        _check_retval_ = _check_HRESULT
443
444    class OleDLL(CDLL):
445        """This class represents a dll exporting functions using the
446        Windows stdcall calling convention, and returning HRESULT.
447        HRESULT error values are automatically raised as OSError
448        exceptions.
449        """
450        _func_flags_ = _FUNCFLAG_STDCALL
451        _func_restype_ = HRESULT
452
453class LibraryLoader(object):
454    def __init__(self, dlltype):
455        self._dlltype = dlltype
456
457    def __getattr__(self, name):
458        if name[0] == '_':
459            raise AttributeError(name)
460        try:
461            dll = self._dlltype(name)
462        except OSError:
463            raise AttributeError(name)
464        setattr(self, name, dll)
465        return dll
466
467    def __getitem__(self, name):
468        return getattr(self, name)
469
470    def LoadLibrary(self, name):
471        return self._dlltype(name)
472
473    __class_getitem__ = classmethod(_types.GenericAlias)
474
475cdll = LibraryLoader(CDLL)
476pydll = LibraryLoader(PyDLL)
477
478if _os.name == "nt":
479    pythonapi = PyDLL("python dll", None, _sys.dllhandle)
480elif _sys.platform == "android":
481    pythonapi = PyDLL("libpython%d.%d.so" % _sys.version_info[:2])
482elif _sys.platform == "cygwin":
483    pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2])
484else:
485    pythonapi = PyDLL(None)
486
487
488if _os.name == "nt":
489    windll = LibraryLoader(WinDLL)
490    oledll = LibraryLoader(OleDLL)
491
492    GetLastError = windll.kernel32.GetLastError
493    from _ctypes import get_last_error, set_last_error
494
495    def WinError(code=None, descr=None):
496        if code is None:
497            code = GetLastError()
498        if descr is None:
499            descr = FormatError(code).strip()
500        return OSError(None, descr, None, code)
501
502if sizeof(c_uint) == sizeof(c_void_p):
503    c_size_t = c_uint
504    c_ssize_t = c_int
505elif sizeof(c_ulong) == sizeof(c_void_p):
506    c_size_t = c_ulong
507    c_ssize_t = c_long
508elif sizeof(c_ulonglong) == sizeof(c_void_p):
509    c_size_t = c_ulonglong
510    c_ssize_t = c_longlong
511
512# functions
513
514from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, _cast_addr
515
516## void *memmove(void *, const void *, size_t);
517memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr)
518
519## void *memset(void *, int, size_t)
520memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr)
521
522def PYFUNCTYPE(restype, *argtypes):
523    class CFunctionType(_CFuncPtr):
524        _argtypes_ = argtypes
525        _restype_ = restype
526        _flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
527    return CFunctionType
528
529_cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
530def cast(obj, typ):
531    return _cast(obj, obj, typ)
532
533_string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
534def string_at(ptr, size=-1):
535    """string_at(ptr[, size]) -> string
536
537    Return the byte string at void *ptr."""
538    return _string_at(ptr, size)
539
540try:
541    from _ctypes import _wstring_at_addr
542except ImportError:
543    pass
544else:
545    _wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr)
546    def wstring_at(ptr, size=-1):
547        """wstring_at(ptr[, size]) -> string
548
549        Return the wide-character string at void *ptr."""
550        return _wstring_at(ptr, size)
551
552
553if _os.name == "nt": # COM stuff
554    def DllGetClassObject(rclsid, riid, ppv):
555        try:
556            ccom = __import__("comtypes.server.inprocserver", globals(), locals(), ['*'])
557        except ImportError:
558            return -2147221231 # CLASS_E_CLASSNOTAVAILABLE
559        else:
560            return ccom.DllGetClassObject(rclsid, riid, ppv)
561
562    def DllCanUnloadNow():
563        try:
564            ccom = __import__("comtypes.server.inprocserver", globals(), locals(), ['*'])
565        except ImportError:
566            return 0 # S_OK
567        return ccom.DllCanUnloadNow()
568
569from ctypes._endian import BigEndianStructure, LittleEndianStructure
570from ctypes._endian import BigEndianUnion, LittleEndianUnion
571
572# Fill in specifically-sized types
573c_int8 = c_byte
574c_uint8 = c_ubyte
575for kind in [c_short, c_int, c_long, c_longlong]:
576    if sizeof(kind) == 2: c_int16 = kind
577    elif sizeof(kind) == 4: c_int32 = kind
578    elif sizeof(kind) == 8: c_int64 = kind
579for kind in [c_ushort, c_uint, c_ulong, c_ulonglong]:
580    if sizeof(kind) == 2: c_uint16 = kind
581    elif sizeof(kind) == 4: c_uint32 = kind
582    elif sizeof(kind) == 8: c_uint64 = kind
583del(kind)
584
585if SIZEOF_TIME_T == 8:
586    c_time_t = c_int64
587elif SIZEOF_TIME_T == 4:
588    c_time_t = c_int32
589else:
590    raise SystemError(f"Unexpected sizeof(time_t): {SIZEOF_TIME_T=}")
591
592_reset_cache()
593