mirror of https://github.com/python/cpython
Merged revisions 63977 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r63977 | thomas.heller | 2008-06-06 10:33:46 +0200 (Fri, 06 Jun 2008) | 31 lines Issue #1798: Add ctypes calling convention that allows safe access of errno. ctypes maintains thread-local storage that has space for two error numbers: private copies of the system 'errno' value and, on Windows, the system error code accessed by the GetLastError() and SetLastError() api functions. Foreign functions created with CDLL(..., use_errno=True), when called, swap the system 'errno' value with the private copy just before the actual function call, and swapped again immediately afterwards. The 'use_errno' parameter defaults to False, in this case 'ctypes_errno' is not touched. On Windows, foreign functions created with CDLL(..., use_last_error=True) or WinDLL(..., use_last_error=True) swap the system LastError value with the ctypes private copy. The values are also swapped immeditately before and after ctypes callback functions are called, if the callbacks are constructed using the new optional use_errno parameter set to True: CFUNCTYPE(..., use_errno=TRUE) or WINFUNCTYPE(..., use_errno=True). New ctypes functions are provided to access the ctypes private copies from Python: - ctypes.set_errno(value) and ctypes.set_last_error(value) store 'value' in the private copy and returns the previous value. - ctypes.get_errno() and ctypes.get_last_error() returns the current ctypes private copies value. ........
This commit is contained in:
parent
311c16a2a9
commit
9cac7b6117
|
@ -30,7 +30,9 @@ if _os.name == "posix" and _sys.platform == "darwin":
|
||||||
DEFAULT_MODE = RTLD_GLOBAL
|
DEFAULT_MODE = RTLD_GLOBAL
|
||||||
|
|
||||||
from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \
|
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
|
WINOLEAPI -> HRESULT
|
||||||
|
@ -70,8 +72,9 @@ def c_buffer(init, size=None):
|
||||||
return create_string_buffer(init, size)
|
return create_string_buffer(init, size)
|
||||||
|
|
||||||
_c_functype_cache = {}
|
_c_functype_cache = {}
|
||||||
def CFUNCTYPE(restype, *argtypes):
|
def CFUNCTYPE(restype, *argtypes, **kw):
|
||||||
"""CFUNCTYPE(restype, *argtypes) -> function prototype.
|
"""CFUNCTYPE(restype, *argtypes,
|
||||||
|
use_errno=False, use_last_error=False) -> function prototype.
|
||||||
|
|
||||||
restype: the result type
|
restype: the result type
|
||||||
argtypes: a sequence specifying the argument types
|
argtypes: a sequence specifying the argument types
|
||||||
|
@ -85,14 +88,21 @@ def CFUNCTYPE(restype, *argtypes):
|
||||||
prototype((ordinal number, dll object)[, paramflags]) -> foreign function exported by ordinal
|
prototype((ordinal number, dll object)[, paramflags]) -> foreign function exported by ordinal
|
||||||
prototype((function name, dll object)[, paramflags]) -> foreign function exported by name
|
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:
|
try:
|
||||||
return _c_functype_cache[(restype, argtypes)]
|
return _c_functype_cache[(restype, argtypes, flags)]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
class CFunctionType(_CFuncPtr):
|
class CFunctionType(_CFuncPtr):
|
||||||
_argtypes_ = argtypes
|
_argtypes_ = argtypes
|
||||||
_restype_ = restype
|
_restype_ = restype
|
||||||
_flags_ = _FUNCFLAG_CDECL
|
_flags_ = flags
|
||||||
_c_functype_cache[(restype, argtypes)] = CFunctionType
|
_c_functype_cache[(restype, argtypes, flags)] = CFunctionType
|
||||||
return CFunctionType
|
return CFunctionType
|
||||||
|
|
||||||
if _os.name in ("nt", "ce"):
|
if _os.name in ("nt", "ce"):
|
||||||
|
@ -103,16 +113,23 @@ if _os.name in ("nt", "ce"):
|
||||||
_FUNCFLAG_STDCALL = _FUNCFLAG_CDECL
|
_FUNCFLAG_STDCALL = _FUNCFLAG_CDECL
|
||||||
|
|
||||||
_win_functype_cache = {}
|
_win_functype_cache = {}
|
||||||
def WINFUNCTYPE(restype, *argtypes):
|
def WINFUNCTYPE(restype, *argtypes, **kw):
|
||||||
# docstring set later (very similar to CFUNCTYPE.__doc__)
|
# 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:
|
try:
|
||||||
return _win_functype_cache[(restype, argtypes)]
|
return _win_functype_cache[(restype, argtypes, flags)]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
class WinFunctionType(_CFuncPtr):
|
class WinFunctionType(_CFuncPtr):
|
||||||
_argtypes_ = argtypes
|
_argtypes_ = argtypes
|
||||||
_restype_ = restype
|
_restype_ = restype
|
||||||
_flags_ = _FUNCFLAG_STDCALL
|
_flags_ = flags
|
||||||
_win_functype_cache[(restype, argtypes)] = WinFunctionType
|
_win_functype_cache[(restype, argtypes, flags)] = WinFunctionType
|
||||||
return WinFunctionType
|
return WinFunctionType
|
||||||
if WINFUNCTYPE.__doc__:
|
if WINFUNCTYPE.__doc__:
|
||||||
WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE")
|
WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE")
|
||||||
|
@ -121,6 +138,7 @@ elif _os.name == "posix":
|
||||||
from _ctypes import dlopen as _dlopen
|
from _ctypes import dlopen as _dlopen
|
||||||
|
|
||||||
from _ctypes import sizeof, byref, addressof, alignment, resize
|
from _ctypes import sizeof, byref, addressof, alignment, resize
|
||||||
|
from _ctypes import get_errno, set_errno
|
||||||
from _ctypes import _SimpleCData
|
from _ctypes import _SimpleCData
|
||||||
|
|
||||||
def _check_size(typ, typecode=None):
|
def _check_size(typ, typecode=None):
|
||||||
|
@ -310,12 +328,24 @@ class CDLL(object):
|
||||||
Calling the functions releases the Python GIL during the call and
|
Calling the functions releases the Python GIL during the call and
|
||||||
reacquires it afterwards.
|
reacquires it afterwards.
|
||||||
"""
|
"""
|
||||||
class _FuncPtr(_CFuncPtr):
|
_func_flags_ = _FUNCFLAG_CDECL
|
||||||
_flags_ = _FUNCFLAG_CDECL
|
_func_restype_ = c_int
|
||||||
_restype_ = c_int # default, can be overridden in instances
|
|
||||||
|
|
||||||
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
|
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:
|
if handle is None:
|
||||||
self._handle = _dlopen(self._name, mode)
|
self._handle = _dlopen(self._name, mode)
|
||||||
else:
|
else:
|
||||||
|
@ -345,9 +375,7 @@ class PyDLL(CDLL):
|
||||||
access Python API functions. The GIL is not released, and
|
access Python API functions. The GIL is not released, and
|
||||||
Python exceptions are handled correctly.
|
Python exceptions are handled correctly.
|
||||||
"""
|
"""
|
||||||
class _FuncPtr(_CFuncPtr):
|
_func_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
|
||||||
_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
|
|
||||||
_restype_ = c_int # default, can be overridden in instances
|
|
||||||
|
|
||||||
if _os.name in ("nt", "ce"):
|
if _os.name in ("nt", "ce"):
|
||||||
|
|
||||||
|
@ -355,9 +383,7 @@ if _os.name in ("nt", "ce"):
|
||||||
"""This class represents a dll exporting functions using the
|
"""This class represents a dll exporting functions using the
|
||||||
Windows stdcall calling convention.
|
Windows stdcall calling convention.
|
||||||
"""
|
"""
|
||||||
class _FuncPtr(_CFuncPtr):
|
_func_flags_ = _FUNCFLAG_STDCALL
|
||||||
_flags_ = _FUNCFLAG_STDCALL
|
|
||||||
_restype_ = c_int # default, can be overridden in instances
|
|
||||||
|
|
||||||
# XXX Hm, what about HRESULT as normal parameter?
|
# XXX Hm, what about HRESULT as normal parameter?
|
||||||
# Mustn't it derive from c_long then?
|
# Mustn't it derive from c_long then?
|
||||||
|
@ -381,9 +407,8 @@ if _os.name in ("nt", "ce"):
|
||||||
HRESULT error values are automatically raised as WindowsError
|
HRESULT error values are automatically raised as WindowsError
|
||||||
exceptions.
|
exceptions.
|
||||||
"""
|
"""
|
||||||
class _FuncPtr(_CFuncPtr):
|
_func_flags_ = _FUNCFLAG_STDCALL
|
||||||
_flags_ = _FUNCFLAG_STDCALL
|
_func_restype_ = HRESULT
|
||||||
_restype_ = HRESULT
|
|
||||||
|
|
||||||
class LibraryLoader(object):
|
class LibraryLoader(object):
|
||||||
def __init__(self, dlltype):
|
def __init__(self, dlltype):
|
||||||
|
@ -421,6 +446,7 @@ if _os.name in ("nt", "ce"):
|
||||||
GetLastError = windll.kernel32.GetLastError
|
GetLastError = windll.kernel32.GetLastError
|
||||||
else:
|
else:
|
||||||
GetLastError = windll.coredll.GetLastError
|
GetLastError = windll.coredll.GetLastError
|
||||||
|
from _ctypes import get_last_error, set_last_error
|
||||||
|
|
||||||
def WinError(code=None, descr=None):
|
def WinError(code=None, descr=None):
|
||||||
if code is None:
|
if code is None:
|
||||||
|
|
|
@ -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()
|
|
@ -3340,7 +3340,7 @@ CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
thunk = AllocFunctionCallback(callable,
|
thunk = AllocFunctionCallback(callable,
|
||||||
dict->argtypes,
|
dict->argtypes,
|
||||||
dict->restype,
|
dict->restype,
|
||||||
dict->flags & FUNCFLAG_CDECL);
|
dict->flags);
|
||||||
if (!thunk)
|
if (!thunk)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -5333,6 +5333,8 @@ init_ctypes(void)
|
||||||
PyModule_AddObject(m, "FUNCFLAG_STDCALL", PyLong_FromLong(FUNCFLAG_STDCALL));
|
PyModule_AddObject(m, "FUNCFLAG_STDCALL", PyLong_FromLong(FUNCFLAG_STDCALL));
|
||||||
#endif
|
#endif
|
||||||
PyModule_AddObject(m, "FUNCFLAG_CDECL", PyLong_FromLong(FUNCFLAG_CDECL));
|
PyModule_AddObject(m, "FUNCFLAG_CDECL", PyLong_FromLong(FUNCFLAG_CDECL));
|
||||||
|
PyModule_AddObject(m, "FUNCFLAG_USE_ERRNO", PyLong_FromLong(FUNCFLAG_USE_ERRNO));
|
||||||
|
PyModule_AddObject(m, "FUNCFLAG_USE_LASTERROR", PyLong_FromLong(FUNCFLAG_USE_LASTERROR));
|
||||||
PyModule_AddObject(m, "FUNCFLAG_PYTHONAPI", PyLong_FromLong(FUNCFLAG_PYTHONAPI));
|
PyModule_AddObject(m, "FUNCFLAG_PYTHONAPI", PyLong_FromLong(FUNCFLAG_PYTHONAPI));
|
||||||
PyModule_AddStringConstant(m, "__version__", "1.1.0");
|
PyModule_AddStringConstant(m, "__version__", "1.1.0");
|
||||||
|
|
||||||
|
|
|
@ -185,12 +185,15 @@ static void _CallPythonObject(void *mem,
|
||||||
SETFUNC setfunc,
|
SETFUNC setfunc,
|
||||||
PyObject *callable,
|
PyObject *callable,
|
||||||
PyObject *converters,
|
PyObject *converters,
|
||||||
|
int flags,
|
||||||
void **pArgs)
|
void **pArgs)
|
||||||
{
|
{
|
||||||
Py_ssize_t i;
|
Py_ssize_t i;
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
PyObject *arglist = NULL;
|
PyObject *arglist = NULL;
|
||||||
Py_ssize_t nArgs;
|
Py_ssize_t nArgs;
|
||||||
|
PyObject *error_object = NULL;
|
||||||
|
int *space;
|
||||||
#ifdef WITH_THREAD
|
#ifdef WITH_THREAD
|
||||||
PyGILState_STATE state = PyGILState_Ensure();
|
PyGILState_STATE state = PyGILState_Ensure();
|
||||||
#endif
|
#endif
|
||||||
|
@ -267,8 +270,41 @@ static void _CallPythonObject(void *mem,
|
||||||
#define CHECK(what, x) \
|
#define CHECK(what, x) \
|
||||||
if (x == NULL) _AddTraceback(what, "_ctypes/callbacks.c", __LINE__ - 1), PyErr_Print()
|
if (x == NULL) _AddTraceback(what, "_ctypes/callbacks.c", __LINE__ - 1), PyErr_Print()
|
||||||
|
|
||||||
|
if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
|
||||||
|
error_object = get_error_object(&space);
|
||||||
|
if (error_object == NULL)
|
||||||
|
goto Done;
|
||||||
|
if (flags & FUNCFLAG_USE_ERRNO) {
|
||||||
|
int temp = space[0];
|
||||||
|
space[0] = errno;
|
||||||
|
errno = temp;
|
||||||
|
}
|
||||||
|
#ifdef MS_WIN32
|
||||||
|
if (flags & FUNCFLAG_USE_LASTERROR) {
|
||||||
|
int temp = space[1];
|
||||||
|
space[1] = GetLastError();
|
||||||
|
SetLastError(temp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
result = PyObject_CallObject(callable, arglist);
|
result = PyObject_CallObject(callable, arglist);
|
||||||
CHECK("'calling callback function'", result);
|
CHECK("'calling callback function'", result);
|
||||||
|
|
||||||
|
#ifdef MS_WIN32
|
||||||
|
if (flags & FUNCFLAG_USE_LASTERROR) {
|
||||||
|
int temp = space[1];
|
||||||
|
space[1] = GetLastError();
|
||||||
|
SetLastError(temp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (flags & FUNCFLAG_USE_ERRNO) {
|
||||||
|
int temp = space[0];
|
||||||
|
space[0] = errno;
|
||||||
|
errno = temp;
|
||||||
|
}
|
||||||
|
Py_XDECREF(error_object);
|
||||||
|
|
||||||
if ((restype != &ffi_type_void) && result) {
|
if ((restype != &ffi_type_void) && result) {
|
||||||
PyObject *keep;
|
PyObject *keep;
|
||||||
assert(setfunc);
|
assert(setfunc);
|
||||||
|
@ -319,6 +355,7 @@ static void closure_fcn(ffi_cif *cif,
|
||||||
p->setfunc,
|
p->setfunc,
|
||||||
p->callable,
|
p->callable,
|
||||||
p->converters,
|
p->converters,
|
||||||
|
p->flags,
|
||||||
args);
|
args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,7 +385,7 @@ static CThunkObject* CThunkObject_new(Py_ssize_t nArgs)
|
||||||
CThunkObject *AllocFunctionCallback(PyObject *callable,
|
CThunkObject *AllocFunctionCallback(PyObject *callable,
|
||||||
PyObject *converters,
|
PyObject *converters,
|
||||||
PyObject *restype,
|
PyObject *restype,
|
||||||
int is_cdecl)
|
int flags)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
CThunkObject *p;
|
CThunkObject *p;
|
||||||
|
@ -368,6 +405,7 @@ CThunkObject *AllocFunctionCallback(PyObject *callable,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p->flags = flags;
|
||||||
for (i = 0; i < nArgs; ++i) {
|
for (i = 0; i < nArgs; ++i) {
|
||||||
PyObject *cnv = PySequence_GetItem(converters, i);
|
PyObject *cnv = PySequence_GetItem(converters, i);
|
||||||
if (cnv == NULL)
|
if (cnv == NULL)
|
||||||
|
@ -395,7 +433,7 @@ CThunkObject *AllocFunctionCallback(PyObject *callable,
|
||||||
|
|
||||||
cc = FFI_DEFAULT_ABI;
|
cc = FFI_DEFAULT_ABI;
|
||||||
#if defined(MS_WIN32) && !defined(_WIN32_WCE) && !defined(MS_WIN64)
|
#if defined(MS_WIN32) && !defined(_WIN32_WCE) && !defined(MS_WIN64)
|
||||||
if (is_cdecl == 0)
|
if ((flags & FUNCFLAG_CDECL) == 0)
|
||||||
cc = FFI_STDCALL;
|
cc = FFI_STDCALL;
|
||||||
#endif
|
#endif
|
||||||
result = ffi_prep_cif(&p->cif, cc,
|
result = ffi_prep_cif(&p->cif, cc,
|
||||||
|
|
|
@ -78,7 +78,129 @@
|
||||||
#define DONT_USE_SEH
|
#define DONT_USE_SEH
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
ctypes maintains thread-local storage that has space for two error numbers:
|
||||||
|
private copies of the system 'errno' value and, on Windows, the system error code
|
||||||
|
accessed by the GetLastError() and SetLastError() api functions.
|
||||||
|
|
||||||
|
Foreign functions created with CDLL(..., use_errno=True), when called, swap
|
||||||
|
the system 'errno' value with the private copy just before the actual
|
||||||
|
function call, and swapped again immediately afterwards. The 'use_errno'
|
||||||
|
parameter defaults to False, in this case 'ctypes_errno' is not touched.
|
||||||
|
|
||||||
|
On Windows, foreign functions created with CDLL(..., use_last_error=True) or
|
||||||
|
WinDLL(..., use_last_error=True) swap the system LastError value with the
|
||||||
|
ctypes private copy.
|
||||||
|
|
||||||
|
The values are also swapped immeditately before and after ctypes callback
|
||||||
|
functions are called, if the callbacks are constructed using the new
|
||||||
|
optional use_errno parameter set to True: CFUNCTYPE(..., use_errno=TRUE) or
|
||||||
|
WINFUNCTYPE(..., use_errno=True).
|
||||||
|
|
||||||
|
New ctypes functions are provided to access the ctypes private copies from
|
||||||
|
Python:
|
||||||
|
|
||||||
|
- ctypes.set_errno(value) and ctypes.set_last_error(value) store 'value' in
|
||||||
|
the private copy and returns the previous value.
|
||||||
|
|
||||||
|
- ctypes.get_errno() and ctypes.get_last_error() returns the current ctypes
|
||||||
|
private copies value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This function creates and returns a thread-local Python object that has
|
||||||
|
space to store two integer error numbers; once created the Python object is
|
||||||
|
kept alive in the thread state dictionary as long as the thread itself.
|
||||||
|
*/
|
||||||
|
PyObject *
|
||||||
|
get_error_object(int **pspace)
|
||||||
|
{
|
||||||
|
PyObject *dict = PyThreadState_GetDict();
|
||||||
|
PyObject *errobj;
|
||||||
|
if (dict == 0) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError,
|
||||||
|
"cannot get thread state");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
errobj = PyDict_GetItemString(dict, "ctypes.error_object");
|
||||||
|
if (errobj)
|
||||||
|
Py_INCREF(errobj);
|
||||||
|
else {
|
||||||
|
void *space = PyMem_Malloc(sizeof(int) * 2);
|
||||||
|
if (space == NULL)
|
||||||
|
return NULL;
|
||||||
|
memset(space, 0, sizeof(int) * 2);
|
||||||
|
errobj = PyCObject_FromVoidPtr(space, PyMem_Free);
|
||||||
|
if (errobj == NULL)
|
||||||
|
return NULL;
|
||||||
|
if (-1 == PyDict_SetItemString(dict, "ctypes.error_object",
|
||||||
|
errobj)) {
|
||||||
|
Py_DECREF(errobj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*pspace = (int *)PyCObject_AsVoidPtr(errobj);
|
||||||
|
return errobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
get_error_internal(PyObject *self, PyObject *args, int index)
|
||||||
|
{
|
||||||
|
int *space;
|
||||||
|
PyObject *errobj = get_error_object(&space);
|
||||||
|
PyObject *result;
|
||||||
|
|
||||||
|
if (errobj == NULL)
|
||||||
|
return NULL;
|
||||||
|
result = PyLong_FromLong(space[index]);
|
||||||
|
Py_DECREF(errobj);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
set_error_internal(PyObject *self, PyObject *args, int index)
|
||||||
|
{
|
||||||
|
int new_errno, old_errno;
|
||||||
|
PyObject *errobj;
|
||||||
|
int *space;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "i", &new_errno))
|
||||||
|
return NULL;
|
||||||
|
errobj = get_error_object(&space);
|
||||||
|
if (errobj == NULL)
|
||||||
|
return NULL;
|
||||||
|
old_errno = space[index];
|
||||||
|
space[index] = new_errno;
|
||||||
|
Py_DECREF(errobj);
|
||||||
|
return PyLong_FromLong(old_errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
get_errno(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
return get_error_internal(self, args, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
set_errno(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
return set_error_internal(self, args, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef MS_WIN32
|
#ifdef MS_WIN32
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
get_last_error(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
return get_error_internal(self, args, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
set_last_error(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
return set_error_internal(self, args, 1);
|
||||||
|
}
|
||||||
|
|
||||||
PyObject *ComError;
|
PyObject *ComError;
|
||||||
|
|
||||||
static TCHAR *FormatError(DWORD code)
|
static TCHAR *FormatError(DWORD code)
|
||||||
|
@ -614,6 +736,8 @@ static int _call_function_pointer(int flags,
|
||||||
#ifdef WITH_THREAD
|
#ifdef WITH_THREAD
|
||||||
PyThreadState *_save = NULL; /* For Py_BLOCK_THREADS and Py_UNBLOCK_THREADS */
|
PyThreadState *_save = NULL; /* For Py_BLOCK_THREADS and Py_UNBLOCK_THREADS */
|
||||||
#endif
|
#endif
|
||||||
|
PyObject *error_object = NULL;
|
||||||
|
int *space;
|
||||||
ffi_cif cif;
|
ffi_cif cif;
|
||||||
int cc;
|
int cc;
|
||||||
#ifdef MS_WIN32
|
#ifdef MS_WIN32
|
||||||
|
@ -645,11 +769,26 @@ static int _call_function_pointer(int flags,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
|
||||||
|
error_object = get_error_object(&space);
|
||||||
|
if (error_object == NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
#ifdef WITH_THREAD
|
#ifdef WITH_THREAD
|
||||||
if ((flags & FUNCFLAG_PYTHONAPI) == 0)
|
if ((flags & FUNCFLAG_PYTHONAPI) == 0)
|
||||||
Py_UNBLOCK_THREADS
|
Py_UNBLOCK_THREADS
|
||||||
#endif
|
#endif
|
||||||
|
if (flags & FUNCFLAG_USE_ERRNO) {
|
||||||
|
int temp = space[0];
|
||||||
|
space[0] = errno;
|
||||||
|
errno = temp;
|
||||||
|
}
|
||||||
#ifdef MS_WIN32
|
#ifdef MS_WIN32
|
||||||
|
if (flags & FUNCFLAG_USE_LASTERROR) {
|
||||||
|
int temp = space[1];
|
||||||
|
space[1] = GetLastError();
|
||||||
|
SetLastError(temp);
|
||||||
|
}
|
||||||
#ifndef DONT_USE_SEH
|
#ifndef DONT_USE_SEH
|
||||||
__try {
|
__try {
|
||||||
#endif
|
#endif
|
||||||
|
@ -664,7 +803,18 @@ static int _call_function_pointer(int flags,
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (flags & FUNCFLAG_USE_LASTERROR) {
|
||||||
|
int temp = space[1];
|
||||||
|
space[1] = GetLastError();
|
||||||
|
SetLastError(temp);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (flags & FUNCFLAG_USE_ERRNO) {
|
||||||
|
int temp = space[0];
|
||||||
|
space[0] = errno;
|
||||||
|
errno = temp;
|
||||||
|
}
|
||||||
|
Py_XDECREF(error_object);
|
||||||
#ifdef WITH_THREAD
|
#ifdef WITH_THREAD
|
||||||
if ((flags & FUNCFLAG_PYTHONAPI) == 0)
|
if ((flags & FUNCFLAG_PYTHONAPI) == 0)
|
||||||
Py_BLOCK_THREADS
|
Py_BLOCK_THREADS
|
||||||
|
@ -1658,6 +1808,8 @@ buffer_info(PyObject *self, PyObject *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
PyMethodDef module_methods[] = {
|
PyMethodDef module_methods[] = {
|
||||||
|
{"get_errno", get_errno, METH_NOARGS},
|
||||||
|
{"set_errno", set_errno, METH_VARARGS},
|
||||||
{"POINTER", POINTER, METH_O },
|
{"POINTER", POINTER, METH_O },
|
||||||
{"pointer", pointer, METH_O },
|
{"pointer", pointer, METH_O },
|
||||||
{"_unpickle", unpickle, METH_VARARGS },
|
{"_unpickle", unpickle, METH_VARARGS },
|
||||||
|
@ -1667,6 +1819,8 @@ PyMethodDef module_methods[] = {
|
||||||
{"set_conversion_mode", set_conversion_mode, METH_VARARGS, set_conversion_mode_doc},
|
{"set_conversion_mode", set_conversion_mode, METH_VARARGS, set_conversion_mode_doc},
|
||||||
#endif
|
#endif
|
||||||
#ifdef MS_WIN32
|
#ifdef MS_WIN32
|
||||||
|
{"get_last_error", get_last_error, METH_NOARGS},
|
||||||
|
{"set_last_error", set_last_error, METH_VARARGS},
|
||||||
{"CopyComPointer", copy_com_pointer, METH_VARARGS, copy_com_pointer_doc},
|
{"CopyComPointer", copy_com_pointer, METH_VARARGS, copy_com_pointer_doc},
|
||||||
{"FormatError", format_error, METH_VARARGS, format_error_doc},
|
{"FormatError", format_error, METH_VARARGS, format_error_doc},
|
||||||
{"LoadLibrary", load_library, METH_VARARGS, load_library_doc},
|
{"LoadLibrary", load_library, METH_VARARGS, load_library_doc},
|
||||||
|
|
|
@ -56,6 +56,7 @@ typedef struct {
|
||||||
PyObject_VAR_HEAD
|
PyObject_VAR_HEAD
|
||||||
ffi_closure *pcl; /* the C callable */
|
ffi_closure *pcl; /* the C callable */
|
||||||
ffi_cif cif;
|
ffi_cif cif;
|
||||||
|
int flags;
|
||||||
PyObject *converters;
|
PyObject *converters;
|
||||||
PyObject *callable;
|
PyObject *callable;
|
||||||
PyObject *restype;
|
PyObject *restype;
|
||||||
|
@ -154,7 +155,7 @@ extern PyMethodDef module_methods[];
|
||||||
extern CThunkObject *AllocFunctionCallback(PyObject *callable,
|
extern CThunkObject *AllocFunctionCallback(PyObject *callable,
|
||||||
PyObject *converters,
|
PyObject *converters,
|
||||||
PyObject *restype,
|
PyObject *restype,
|
||||||
int stdcall);
|
int flags);
|
||||||
/* a table entry describing a predefined ctypes type */
|
/* a table entry describing a predefined ctypes type */
|
||||||
struct fielddesc {
|
struct fielddesc {
|
||||||
char code;
|
char code;
|
||||||
|
@ -280,6 +281,8 @@ PyObject *_CallProc(PPROC pProc,
|
||||||
#define FUNCFLAG_CDECL 0x1
|
#define FUNCFLAG_CDECL 0x1
|
||||||
#define FUNCFLAG_HRESULT 0x2
|
#define FUNCFLAG_HRESULT 0x2
|
||||||
#define FUNCFLAG_PYTHONAPI 0x4
|
#define FUNCFLAG_PYTHONAPI 0x4
|
||||||
|
#define FUNCFLAG_USE_ERRNO 0x8
|
||||||
|
#define FUNCFLAG_USE_LASTERROR 0x10
|
||||||
|
|
||||||
#define TYPEFLAG_ISPOINTER 0x100
|
#define TYPEFLAG_ISPOINTER 0x100
|
||||||
#define TYPEFLAG_HASPOINTER 0x200
|
#define TYPEFLAG_HASPOINTER 0x200
|
||||||
|
@ -356,6 +359,7 @@ extern char *alloc_format_string(const char *prefix, const char *suffix);
|
||||||
extern int IsSimpleSubType(PyObject *obj);
|
extern int IsSimpleSubType(PyObject *obj);
|
||||||
|
|
||||||
extern PyObject *_pointer_type_cache;
|
extern PyObject *_pointer_type_cache;
|
||||||
|
PyObject *get_error_object(int **pspace);
|
||||||
|
|
||||||
#ifdef MS_WIN32
|
#ifdef MS_WIN32
|
||||||
extern PyObject *ComError;
|
extern PyObject *ComError;
|
||||||
|
|
Loading…
Reference in New Issue