diff options
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/ctypes/__init__.py | 72 | ||||
-rw-r--r-- | Lib/ctypes/test/test_errno.py | 76 |
2 files changed, 125 insertions, 23 deletions
diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 41d39dc..d3e11dc 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -33,7 +33,9 @@ if _os.name == "posix" and _sys.platform == "darwin": DEFAULT_MODE = RTLD_GLOBAL from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \ - FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI + FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI, \ + FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \ + FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR """ WINOLEAPI -> HRESULT @@ -73,8 +75,9 @@ def c_buffer(init, size=None): return create_string_buffer(init, size) _c_functype_cache = {} -def CFUNCTYPE(restype, *argtypes): - """CFUNCTYPE(restype, *argtypes) -> function prototype. +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 @@ -88,14 +91,21 @@ def CFUNCTYPE(restype, *argtypes): 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)] + return _c_functype_cache[(restype, argtypes, flags)] except KeyError: class CFunctionType(_CFuncPtr): _argtypes_ = argtypes _restype_ = restype - _flags_ = _FUNCFLAG_CDECL - _c_functype_cache[(restype, argtypes)] = CFunctionType + _flags_ = flags + _c_functype_cache[(restype, argtypes, flags)] = CFunctionType return CFunctionType if _os.name in ("nt", "ce"): @@ -106,16 +116,23 @@ if _os.name in ("nt", "ce"): _FUNCFLAG_STDCALL = _FUNCFLAG_CDECL _win_functype_cache = {} - def WINFUNCTYPE(restype, *argtypes): + 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)] + return _win_functype_cache[(restype, argtypes, flags)] except KeyError: class WinFunctionType(_CFuncPtr): _argtypes_ = argtypes _restype_ = restype - _flags_ = _FUNCFLAG_STDCALL - _win_functype_cache[(restype, argtypes)] = WinFunctionType + _flags_ = flags + _win_functype_cache[(restype, argtypes, flags)] = WinFunctionType return WinFunctionType if WINFUNCTYPE.__doc__: WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE") @@ -124,6 +141,7 @@ 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): @@ -313,12 +331,24 @@ class CDLL(object): Calling the functions releases the Python GIL during the call and reacquires it afterwards. """ - class _FuncPtr(_CFuncPtr): - _flags_ = _FUNCFLAG_CDECL - _restype_ = c_int # default, can be overridden in instances + _func_flags_ = _FUNCFLAG_CDECL + _func_restype_ = c_int - def __init__(self, name, mode=DEFAULT_MODE, handle=None): + def __init__(self, name, mode=DEFAULT_MODE, handle=None, + use_errno=False, + use_last_error=False): self._name = name + flags = self._func_flags_ + if use_errno: + flags |= _FUNCFLAG_USE_ERRNO + if use_last_error: + flags |= _FUNCFLAG_USE_LASTERROR + + class _FuncPtr(_CFuncPtr): + _flags_ = flags + _restype_ = self._func_restype_ + self._FuncPtr = _FuncPtr + if handle is None: self._handle = _dlopen(self._name, mode) else: @@ -348,9 +378,7 @@ class PyDLL(CDLL): access Python API functions. The GIL is not released, and Python exceptions are handled correctly. """ - class _FuncPtr(_CFuncPtr): - _flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI - _restype_ = c_int # default, can be overridden in instances + _func_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI if _os.name in ("nt", "ce"): @@ -358,9 +386,7 @@ if _os.name in ("nt", "ce"): """This class represents a dll exporting functions using the Windows stdcall calling convention. """ - class _FuncPtr(_CFuncPtr): - _flags_ = _FUNCFLAG_STDCALL - _restype_ = c_int # default, can be overridden in instances + _func_flags_ = _FUNCFLAG_STDCALL # XXX Hm, what about HRESULT as normal parameter? # Mustn't it derive from c_long then? @@ -384,9 +410,8 @@ if _os.name in ("nt", "ce"): HRESULT error values are automatically raised as WindowsError exceptions. """ - class _FuncPtr(_CFuncPtr): - _flags_ = _FUNCFLAG_STDCALL - _restype_ = HRESULT + _func_flags_ = _FUNCFLAG_STDCALL + _func_restype_ = HRESULT class LibraryLoader(object): def __init__(self, dlltype): @@ -424,6 +449,7 @@ if _os.name in ("nt", "ce"): GetLastError = windll.kernel32.GetLastError else: GetLastError = windll.coredll.GetLastError + from _ctypes import get_last_error, set_last_error def WinError(code=None, descr=None): if code is None: diff --git a/Lib/ctypes/test/test_errno.py b/Lib/ctypes/test/test_errno.py new file mode 100644 index 0000000..b80656b --- /dev/null +++ b/Lib/ctypes/test/test_errno.py @@ -0,0 +1,76 @@ +import unittest, os, errno +from ctypes import * +from ctypes.util import find_library +import threading + +class Test(unittest.TestCase): + def test_open(self): + libc_name = find_library("c") + if libc_name is not None: + libc = CDLL(libc_name, use_errno=True) + if os.name == "nt": + libc_open = libc._open + else: + libc_open = libc.open + + libc_open.argtypes = c_char_p, c_int + + self.failUnlessEqual(libc_open("", 0), -1) + self.failUnlessEqual(get_errno(), errno.ENOENT) + + self.failUnlessEqual(set_errno(32), errno.ENOENT) + self.failUnlessEqual(get_errno(), 32) + + + def _worker(): + set_errno(0) + + libc = CDLL(libc_name, use_errno=False) + if os.name == "nt": + libc_open = libc._open + else: + libc_open = libc.open + libc_open.argtypes = c_char_p, c_int + self.failUnlessEqual(libc_open("", 0), -1) + self.failUnlessEqual(get_errno(), 0) + + t = threading.Thread(target=_worker) + t.start() + t.join() + + self.failUnlessEqual(get_errno(), 32) + set_errno(0) + + if os.name == "nt": + + def test_GetLastError(self): + dll = WinDLL("kernel32", use_last_error=True) + GetModuleHandle = dll.GetModuleHandleA + GetModuleHandle.argtypes = [c_wchar_p] + + self.failUnlessEqual(0, GetModuleHandle("foo")) + self.failUnlessEqual(get_last_error(), 126) + + self.failUnlessEqual(set_last_error(32), 126) + self.failUnlessEqual(get_last_error(), 32) + + def _worker(): + set_last_error(0) + + dll = WinDLL("kernel32", use_last_error=False) + GetModuleHandle = dll.GetModuleHandleW + GetModuleHandle.argtypes = [c_wchar_p] + GetModuleHandle("bar") + + self.failUnlessEqual(get_last_error(), 0) + + t = threading.Thread(target=_worker) + t.start() + t.join() + + self.failUnlessEqual(get_last_error(), 32) + + set_last_error(0) + +if __name__ == "__main__": + unittest.main() |