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