Source code for ctypes

"""create and manipulate C data types in Python"""

import os as _os, sys as _sys

__version__ = "1.1.0"

from _ctypes import Union, Structure, Array
from _ctypes import _Pointer
from _ctypes import CFuncPtr as _CFuncPtr
from _ctypes import __version__ as _ctypes_version
from _ctypes import RTLD_LOCAL, RTLD_GLOBAL
from _ctypes import ArgumentError

from struct import calcsize as _calcsize

if __version__ != _ctypes_version:
    raise Exception("Version number mismatch", __version__, _ctypes_version)

if _os.name == "nt":
    from _ctypes import FormatError

DEFAULT_MODE = RTLD_LOCAL
if _os.name == "posix" and _sys.platform == "darwin":
    # On OS X 10.3, we use RTLD_GLOBAL as default mode
    # because RTLD_LOCAL does not work at least on some
    # libraries.  OS X 10.3 is Darwin 7, so we check for
    # that.

    if int(_os.uname().release.split('.')[0]) < 8:
        DEFAULT_MODE = RTLD_GLOBAL

from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \
     FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI, \
     FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \
     FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR

# WINOLEAPI -> HRESULT
# WINOLEAPI_(type)
#
# STDMETHODCALLTYPE
#
# STDMETHOD(name)
# STDMETHOD_(type, name)
#
# STDAPICALLTYPE

def create_string_buffer(init, size=None):
    """create_string_buffer(aBytes) -> character array
    create_string_buffer(anInteger) -> character array
    create_string_buffer(aBytes, anInteger) -> character array
    """
    if isinstance(init, bytes):
        if size is None:
            size = len(init)+1
        _sys.audit("ctypes.create_string_buffer", init, size)
        buftype = c_char * size
        buf = buftype()
        buf.value = init
        return buf
    elif isinstance(init, int):
        _sys.audit("ctypes.create_string_buffer", None, init)
        buftype = c_char * init
        buf = buftype()
        return buf
    raise TypeError(init)

def c_buffer(init, size=None):
##    "deprecated, use create_string_buffer instead"
##    import warnings
##    warnings.warn("c_buffer is deprecated, use create_string_buffer instead",
##                  DeprecationWarning, stacklevel=2)
    return create_string_buffer(init, size)

_c_functype_cache = {}
def CFUNCTYPE(restype, *argtypes, **kw):
    """CFUNCTYPE(restype, *argtypes,
                 use_errno=False, use_last_error=False) -> function prototype.

    restype: the result type
    argtypes: a sequence specifying the argument types

    The function prototype can be called in different ways to create a
    callable object:

    prototype(integer address) -> foreign function
    prototype(callable) -> create and return a C callable function from callable
    prototype(integer index, method name[, paramflags]) -> foreign function calling a COM method
    prototype((ordinal number, dll object)[, paramflags]) -> foreign function exported by ordinal
    prototype((function name, dll object)[, paramflags]) -> foreign function exported by name
    """
    flags = _FUNCFLAG_CDECL
    if kw.pop("use_errno", False):
        flags |= _FUNCFLAG_USE_ERRNO
    if kw.pop("use_last_error", False):
        flags |= _FUNCFLAG_USE_LASTERROR
    if kw:
        raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
    try:
        return _c_functype_cache[(restype, argtypes, flags)]
    except KeyError:
        class CFunctionType(_CFuncPtr):
            _argtypes_ = argtypes
            _restype_ = restype
            _flags_ = flags
        _c_functype_cache[(restype, argtypes, flags)] = CFunctionType
        return CFunctionType

if _os.name == "nt":
    from _ctypes import LoadLibrary as _dlopen
    from _ctypes import FUNCFLAG_STDCALL as _FUNCFLAG_STDCALL

    _win_functype_cache = {}
    def WINFUNCTYPE(restype, *argtypes, **kw):
        # docstring set later (very similar to CFUNCTYPE.__doc__)
        flags = _FUNCFLAG_STDCALL
        if kw.pop("use_errno", False):
            flags |= _FUNCFLAG_USE_ERRNO
        if kw.pop("use_last_error", False):
            flags |= _FUNCFLAG_USE_LASTERROR
        if kw:
            raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
        try:
            return _win_functype_cache[(restype, argtypes, flags)]
        except KeyError:
            class WinFunctionType(_CFuncPtr):
                _argtypes_ = argtypes
                _restype_ = restype
                _flags_ = flags
            _win_functype_cache[(restype, argtypes, flags)] = WinFunctionType
            return WinFunctionType
    if WINFUNCTYPE.__doc__:
        WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE")

elif _os.name == "posix":
    from _ctypes import dlopen as _dlopen

from _ctypes import sizeof, byref, addressof, alignment, resize
from _ctypes import get_errno, set_errno
from _ctypes import _SimpleCData

def _check_size(typ, typecode=None):
    # Check if sizeof(ctypes_type) against struct.calcsize.  This
    # should protect somewhat against a misconfigured libffi.
    from struct import calcsize
    if typecode is None:
        # Most _type_ codes are the same as used in struct
        typecode = typ._type_
    actual, required = sizeof(typ), calcsize(typecode)
    if actual != required:
        raise SystemError("sizeof(%s) wrong: %d instead of %d" % \
                          (typ, actual, required))

class py_object(_SimpleCData):
    _type_ = "O"
    def __repr__(self):
        try:
            return super().__repr__()
        except ValueError:
            return "%s(<NULL>)" % type(self).__name__
_check_size(py_object, "P")

class c_short(_SimpleCData):
    _type_ = "h"
_check_size(c_short)

class c_ushort(_SimpleCData):
    _type_ = "H"
_check_size(c_ushort)

class c_long(_SimpleCData):
    _type_ = "l"
_check_size(c_long)

class c_ulong(_SimpleCData):
    _type_ = "L"
_check_size(c_ulong)

if _calcsize("i") == _calcsize("l"):
    # if int and long have the same size, make c_int an alias for c_long
    c_int = c_long
    c_uint = c_ulong
else:
[docs] class c_int(_SimpleCData): _type_ = "i"
_check_size(c_int) class c_uint(_SimpleCData): _type_ = "I" _check_size(c_uint) class c_float(_SimpleCData): _type_ = "f" _check_size(c_float) class c_double(_SimpleCData): _type_ = "d" _check_size(c_double) class c_longdouble(_SimpleCData): _type_ = "g" if sizeof(c_longdouble) == sizeof(c_double): c_longdouble = c_double if _calcsize("l") == _calcsize("q"): # if long and long long have the same size, make c_longlong an alias for c_long c_longlong = c_long c_ulonglong = c_ulong else: class c_longlong(_SimpleCData): _type_ = "q" _check_size(c_longlong) class c_ulonglong(_SimpleCData): _type_ = "Q" ## def from_param(cls, val): ## return ('d', float(val), val) ## from_param = classmethod(from_param) _check_size(c_ulonglong)
[docs]class c_ubyte(_SimpleCData): _type_ = "B"
c_ubyte.__ctype_le__ = c_ubyte.__ctype_be__ = c_ubyte # backward compatibility: ##c_uchar = c_ubyte _check_size(c_ubyte) class c_byte(_SimpleCData): _type_ = "b" c_byte.__ctype_le__ = c_byte.__ctype_be__ = c_byte _check_size(c_byte) class c_char(_SimpleCData): _type_ = "c" c_char.__ctype_le__ = c_char.__ctype_be__ = c_char _check_size(c_char) class c_char_p(_SimpleCData): _type_ = "z" def __repr__(self): return "%s(%s)" % (self.__class__.__name__, c_void_p.from_buffer(self).value) _check_size(c_char_p, "P") class c_void_p(_SimpleCData): _type_ = "P" c_voidp = c_void_p # backwards compatibility (to a bug) _check_size(c_void_p) class c_bool(_SimpleCData): _type_ = "?" from _ctypes import POINTER, pointer, _pointer_type_cache class c_wchar_p(_SimpleCData): _type_ = "Z" def __repr__(self): return "%s(%s)" % (self.__class__.__name__, c_void_p.from_buffer(self).value) class c_wchar(_SimpleCData): _type_ = "u" def _reset_cache(): _pointer_type_cache.clear() _c_functype_cache.clear() if _os.name == "nt": _win_functype_cache.clear() # _SimpleCData.c_wchar_p_from_param POINTER(c_wchar).from_param = c_wchar_p.from_param # _SimpleCData.c_char_p_from_param POINTER(c_char).from_param = c_char_p.from_param _pointer_type_cache[None] = c_void_p def create_unicode_buffer(init, size=None): """create_unicode_buffer(aString) -> character array create_unicode_buffer(anInteger) -> character array create_unicode_buffer(aString, anInteger) -> character array """ if isinstance(init, str): if size is None: if sizeof(c_wchar) == 2: # UTF-16 requires a surrogate pair (2 wchar_t) for non-BMP # characters (outside [U+0000; U+FFFF] range). +1 for trailing # NUL character. size = sum(2 if ord(c) > 0xFFFF else 1 for c in init) + 1 else: # 32-bit wchar_t (1 wchar_t per Unicode character). +1 for # trailing NUL character. size = len(init) + 1 _sys.audit("ctypes.create_unicode_buffer", init, size) buftype = c_wchar * size buf = buftype() buf.value = init return buf elif isinstance(init, int): _sys.audit("ctypes.create_unicode_buffer", None, init) buftype = c_wchar * init buf = buftype() return buf raise TypeError(init) # XXX Deprecated def SetPointerType(pointer, cls): if _pointer_type_cache.get(cls, None) is not None: raise RuntimeError("This type already exists in the cache") if id(pointer) not in _pointer_type_cache: raise RuntimeError("What's this???") pointer.set_type(cls) _pointer_type_cache[cls] = pointer del _pointer_type_cache[id(pointer)] # XXX Deprecated def ARRAY(typ, len): return typ * len ################################################################ class CDLL(object): """An instance of this class represents a loaded dll/shared library, exporting functions using the standard C calling convention (named 'cdecl' on Windows). The exported functions can be accessed as attributes, or by indexing with the function name. Examples: <obj>.qsort -> callable object <obj>['qsort'] -> callable object Calling the functions releases the Python GIL during the call and reacquires it afterwards. """ _func_flags_ = _FUNCFLAG_CDECL _func_restype_ = c_int # default values for repr _name = '<uninitialized>' _handle = 0 _FuncPtr = None def __init__(self, name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None): self._name = name flags = self._func_flags_ if use_errno: flags |= _FUNCFLAG_USE_ERRNO if use_last_error: flags |= _FUNCFLAG_USE_LASTERROR if _sys.platform.startswith("aix"): """When the name contains ".a(" and ends with ")", e.g., "libFOO.a(libFOO.so)" - this is taken to be an archive(member) syntax for dlopen(), and the mode is adjusted. Otherwise, name is presented to dlopen() as a file argument. """ if name and name.endswith(")") and ".a(" in name: mode |= ( _os.RTLD_MEMBER | _os.RTLD_NOW ) if _os.name == "nt": if winmode is not None: mode = winmode else: import nt mode = nt._LOAD_LIBRARY_SEARCH_DEFAULT_DIRS if '/' in name or '\\' in name: self._name = nt._getfullpathname(self._name) mode |= nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR class _FuncPtr(_CFuncPtr): _flags_ = flags _restype_ = self._func_restype_ self._FuncPtr = _FuncPtr if handle is None: self._handle = _dlopen(self._name, mode) else: self._handle = handle def __repr__(self): return "<%s '%s', handle %x at %#x>" % \ (self.__class__.__name__, self._name, (self._handle & (_sys.maxsize*2 + 1)), id(self) & (_sys.maxsize*2 + 1)) def __getattr__(self, name): if name.startswith('__') and name.endswith('__'): raise AttributeError(name) func = self.__getitem__(name) setattr(self, name, func) return func def __getitem__(self, name_or_ordinal): func = self._FuncPtr((name_or_ordinal, self)) if not isinstance(name_or_ordinal, int): func.__name__ = name_or_ordinal return func class PyDLL(CDLL): """This class represents the Python library itself. It allows accessing Python API functions. The GIL is not released, and Python exceptions are handled correctly. """ _func_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI if _os.name == "nt": class WinDLL(CDLL): """This class represents a dll exporting functions using the Windows stdcall calling convention. """ _func_flags_ = _FUNCFLAG_STDCALL # XXX Hm, what about HRESULT as normal parameter? # Mustn't it derive from c_long then? from _ctypes import _check_HRESULT, _SimpleCData class HRESULT(_SimpleCData): _type_ = "l" # _check_retval_ is called with the function's result when it # is used as restype. It checks for the FAILED bit, and # raises an OSError if it is set. # # The _check_retval_ method is implemented in C, so that the # method definition itself is not included in the traceback # when it raises an error - that is what we want (and Python # doesn't have a way to raise an exception in the caller's # frame). _check_retval_ = _check_HRESULT class OleDLL(CDLL): """This class represents a dll exporting functions using the Windows stdcall calling convention, and returning HRESULT. HRESULT error values are automatically raised as OSError exceptions. """ _func_flags_ = _FUNCFLAG_STDCALL _func_restype_ = HRESULT class LibraryLoader(object): def __init__(self, dlltype): self._dlltype = dlltype def __getattr__(self, name): if name[0] == '_': raise AttributeError(name) dll = self._dlltype(name) setattr(self, name, dll) return dll def __getitem__(self, name): return getattr(self, name) def LoadLibrary(self, name): return self._dlltype(name) cdll = LibraryLoader(CDLL) pydll = LibraryLoader(PyDLL) if _os.name == "nt": pythonapi = PyDLL("python dll", None, _sys.dllhandle) elif _sys.platform == "cygwin": pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2]) else: pythonapi = PyDLL(None) if _os.name == "nt": windll = LibraryLoader(WinDLL) oledll = LibraryLoader(OleDLL) GetLastError = windll.kernel32.GetLastError from _ctypes import get_last_error, set_last_error def WinError(code=None, descr=None): if code is None: code = GetLastError() if descr is None: descr = FormatError(code).strip() return OSError(None, descr, None, code) if sizeof(c_uint) == sizeof(c_void_p): c_size_t = c_uint c_ssize_t = c_int elif sizeof(c_ulong) == sizeof(c_void_p): c_size_t = c_ulong c_ssize_t = c_long elif sizeof(c_ulonglong) == sizeof(c_void_p): c_size_t = c_ulonglong c_ssize_t = c_longlong # functions from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, _cast_addr ## void *memmove(void *, const void *, size_t); memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr) ## void *memset(void *, int, size_t) memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr) def PYFUNCTYPE(restype, *argtypes): class CFunctionType(_CFuncPtr): _argtypes_ = argtypes _restype_ = restype _flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI return CFunctionType _cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr) def cast(obj, typ): return _cast(obj, obj, typ) _string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr) def string_at(ptr, size=-1): """string_at(addr[, size]) -> string Return the string at addr.""" return _string_at(ptr, size) try: from _ctypes import _wstring_at_addr except ImportError: pass else: _wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr) def wstring_at(ptr, size=-1): """wstring_at(addr[, size]) -> string Return the string at addr.""" return _wstring_at(ptr, size) if _os.name == "nt": # COM stuff def DllGetClassObject(rclsid, riid, ppv): try: ccom = __import__("comtypes.server.inprocserver", globals(), locals(), ['*']) except ImportError: return -2147221231 # CLASS_E_CLASSNOTAVAILABLE else: return ccom.DllGetClassObject(rclsid, riid, ppv) def DllCanUnloadNow(): try: ccom = __import__("comtypes.server.inprocserver", globals(), locals(), ['*']) except ImportError: return 0 # S_OK return ccom.DllCanUnloadNow() from ctypes._endian import BigEndianStructure, LittleEndianStructure # Fill in specifically-sized types c_int8 = c_byte c_uint8 = c_ubyte for kind in [c_short, c_int, c_long, c_longlong]: if sizeof(kind) == 2: c_int16 = kind elif sizeof(kind) == 4: c_int32 = kind elif sizeof(kind) == 8: c_int64 = kind for kind in [c_ushort, c_uint, c_ulong, c_ulonglong]: if sizeof(kind) == 2: c_uint16 = kind elif sizeof(kind) == 4: c_uint32 = kind elif sizeof(kind) == 8: c_uint64 = kind del(kind) _reset_cache()