Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9cac7b6

Browse files
author
Thomas Heller
committed
Merged revisions 63977 via svnmerge from
svn+ssh://[email protected]/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. ........
1 parent 311c16a commit 9cac7b6

6 files changed

Lines changed: 327 additions & 27 deletions

File tree

Lib/ctypes/__init__.py

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
DEFAULT_MODE = RTLD_GLOBAL
3131

3232
from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \
33-
FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI
33+
FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI, \
34+
FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \
35+
FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR
3436

3537
"""
3638
WINOLEAPI -> HRESULT
@@ -70,8 +72,9 @@ def c_buffer(init, size=None):
7072
return create_string_buffer(init, size)
7173

7274
_c_functype_cache = {}
73-
def CFUNCTYPE(restype, *argtypes):
74-
"""CFUNCTYPE(restype, *argtypes) -> function prototype.
75+
def CFUNCTYPE(restype, *argtypes, **kw):
76+
"""CFUNCTYPE(restype, *argtypes,
77+
use_errno=False, use_last_error=False) -> function prototype.
7578
7679
restype: the result type
7780
argtypes: a sequence specifying the argument types
@@ -85,14 +88,21 @@ def CFUNCTYPE(restype, *argtypes):
8588
prototype((ordinal number, dll object)[, paramflags]) -> foreign function exported by ordinal
8689
prototype((function name, dll object)[, paramflags]) -> foreign function exported by name
8790
"""
91+
flags = _FUNCFLAG_CDECL
92+
if kw.pop("use_errno", False):
93+
flags |= _FUNCFLAG_USE_ERRNO
94+
if kw.pop("use_last_error", False):
95+
flags |= _FUNCFLAG_USE_LASTERROR
96+
if kw:
97+
raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
8898
try:
89-
return _c_functype_cache[(restype, argtypes)]
99+
return _c_functype_cache[(restype, argtypes, flags)]
90100
except KeyError:
91101
class CFunctionType(_CFuncPtr):
92102
_argtypes_ = argtypes
93103
_restype_ = restype
94-
_flags_ = _FUNCFLAG_CDECL
95-
_c_functype_cache[(restype, argtypes)] = CFunctionType
104+
_flags_ = flags
105+
_c_functype_cache[(restype, argtypes, flags)] = CFunctionType
96106
return CFunctionType
97107

98108
if _os.name in ("nt", "ce"):
@@ -103,16 +113,23 @@ class CFunctionType(_CFuncPtr):
103113
_FUNCFLAG_STDCALL = _FUNCFLAG_CDECL
104114

105115
_win_functype_cache = {}
106-
def WINFUNCTYPE(restype, *argtypes):
116+
def WINFUNCTYPE(restype, *argtypes, **kw):
107117
# docstring set later (very similar to CFUNCTYPE.__doc__)
118+
flags = _FUNCFLAG_STDCALL
119+
if kw.pop("use_errno", False):
120+
flags |= _FUNCFLAG_USE_ERRNO
121+
if kw.pop("use_last_error", False):
122+
flags |= _FUNCFLAG_USE_LASTERROR
123+
if kw:
124+
raise ValueError("unexpected keyword argument(s) %s" % kw.keys())
108125
try:
109-
return _win_functype_cache[(restype, argtypes)]
126+
return _win_functype_cache[(restype, argtypes, flags)]
110127
except KeyError:
111128
class WinFunctionType(_CFuncPtr):
112129
_argtypes_ = argtypes
113130
_restype_ = restype
114-
_flags_ = _FUNCFLAG_STDCALL
115-
_win_functype_cache[(restype, argtypes)] = WinFunctionType
131+
_flags_ = flags
132+
_win_functype_cache[(restype, argtypes, flags)] = WinFunctionType
116133
return WinFunctionType
117134
if WINFUNCTYPE.__doc__:
118135
WINFUNCTYPE.__doc__ = CFUNCTYPE.__doc__.replace("CFUNCTYPE", "WINFUNCTYPE")
@@ -121,6 +138,7 @@ class WinFunctionType(_CFuncPtr):
121138
from _ctypes import dlopen as _dlopen
122139

123140
from _ctypes import sizeof, byref, addressof, alignment, resize
141+
from _ctypes import get_errno, set_errno
124142
from _ctypes import _SimpleCData
125143

126144
def _check_size(typ, typecode=None):
@@ -310,12 +328,24 @@ class CDLL(object):
310328
Calling the functions releases the Python GIL during the call and
311329
reacquires it afterwards.
312330
"""
313-
class _FuncPtr(_CFuncPtr):
314-
_flags_ = _FUNCFLAG_CDECL
315-
_restype_ = c_int # default, can be overridden in instances
331+
_func_flags_ = _FUNCFLAG_CDECL
332+
_func_restype_ = c_int
316333

317-
def __init__(self, name, mode=DEFAULT_MODE, handle=None):
334+
def __init__(self, name, mode=DEFAULT_MODE, handle=None,
335+
use_errno=False,
336+
use_last_error=False):
318337
self._name = name
338+
flags = self._func_flags_
339+
if use_errno:
340+
flags |= _FUNCFLAG_USE_ERRNO
341+
if use_last_error:
342+
flags |= _FUNCFLAG_USE_LASTERROR
343+
344+
class _FuncPtr(_CFuncPtr):
345+
_flags_ = flags
346+
_restype_ = self._func_restype_
347+
self._FuncPtr = _FuncPtr
348+
319349
if handle is None:
320350
self._handle = _dlopen(self._name, mode)
321351
else:
@@ -345,19 +375,15 @@ class PyDLL(CDLL):
345375
access Python API functions. The GIL is not released, and
346376
Python exceptions are handled correctly.
347377
"""
348-
class _FuncPtr(_CFuncPtr):
349-
_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
350-
_restype_ = c_int # default, can be overridden in instances
378+
_func_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
351379

352380
if _os.name in ("nt", "ce"):
353381

354382
class WinDLL(CDLL):
355383
"""This class represents a dll exporting functions using the
356384
Windows stdcall calling convention.
357385
"""
358-
class _FuncPtr(_CFuncPtr):
359-
_flags_ = _FUNCFLAG_STDCALL
360-
_restype_ = c_int # default, can be overridden in instances
386+
_func_flags_ = _FUNCFLAG_STDCALL
361387

362388
# XXX Hm, what about HRESULT as normal parameter?
363389
# Mustn't it derive from c_long then?
@@ -381,9 +407,8 @@ class OleDLL(CDLL):
381407
HRESULT error values are automatically raised as WindowsError
382408
exceptions.
383409
"""
384-
class _FuncPtr(_CFuncPtr):
385-
_flags_ = _FUNCFLAG_STDCALL
386-
_restype_ = HRESULT
410+
_func_flags_ = _FUNCFLAG_STDCALL
411+
_func_restype_ = HRESULT
387412

388413
class LibraryLoader(object):
389414
def __init__(self, dlltype):
@@ -421,6 +446,7 @@ def LoadLibrary(self, name):
421446
GetLastError = windll.kernel32.GetLastError
422447
else:
423448
GetLastError = windll.coredll.GetLastError
449+
from _ctypes import get_last_error, set_last_error
424450

425451
def WinError(code=None, descr=None):
426452
if code is None:

Lib/ctypes/test/test_errno.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import unittest, os, errno
2+
from ctypes import *
3+
from ctypes.util import find_library
4+
import threading
5+
6+
class Test(unittest.TestCase):
7+
def test_open(self):
8+
libc_name = find_library("c")
9+
if libc_name is not None:
10+
libc = CDLL(libc_name, use_errno=True)
11+
if os.name == "nt":
12+
libc_open = libc._open
13+
else:
14+
libc_open = libc.open
15+
16+
libc_open.argtypes = c_char_p, c_int
17+
18+
self.failUnlessEqual(libc_open("", 0), -1)
19+
self.failUnlessEqual(get_errno(), errno.ENOENT)
20+
21+
self.failUnlessEqual(set_errno(32), errno.ENOENT)
22+
self.failUnlessEqual(get_errno(), 32)
23+
24+
25+
def _worker():
26+
set_errno(0)
27+
28+
libc = CDLL(libc_name, use_errno=False)
29+
if os.name == "nt":
30+
libc_open = libc._open
31+
else:
32+
libc_open = libc.open
33+
libc_open.argtypes = c_char_p, c_int
34+
self.failUnlessEqual(libc_open("", 0), -1)
35+
self.failUnlessEqual(get_errno(), 0)
36+
37+
t = threading.Thread(target=_worker)
38+
t.start()
39+
t.join()
40+
41+
self.failUnlessEqual(get_errno(), 32)
42+
set_errno(0)
43+
44+
if os.name == "nt":
45+
46+
def test_GetLastError(self):
47+
dll = WinDLL("kernel32", use_last_error=True)
48+
GetModuleHandle = dll.GetModuleHandleA
49+
GetModuleHandle.argtypes = [c_wchar_p]
50+
51+
self.failUnlessEqual(0, GetModuleHandle("foo"))
52+
self.failUnlessEqual(get_last_error(), 126)
53+
54+
self.failUnlessEqual(set_last_error(32), 126)
55+
self.failUnlessEqual(get_last_error(), 32)
56+
57+
def _worker():
58+
set_last_error(0)
59+
60+
dll = WinDLL("kernel32", use_last_error=False)
61+
GetModuleHandle = dll.GetModuleHandleW
62+
GetModuleHandle.argtypes = [c_wchar_p]
63+
GetModuleHandle("bar")
64+
65+
self.failUnlessEqual(get_last_error(), 0)
66+
67+
t = threading.Thread(target=_worker)
68+
t.start()
69+
t.join()
70+
71+
self.failUnlessEqual(get_last_error(), 32)
72+
73+
set_last_error(0)
74+
75+
if __name__ == "__main__":
76+
unittest.main()

Modules/_ctypes/_ctypes.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3340,7 +3340,7 @@ CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
33403340
thunk = AllocFunctionCallback(callable,
33413341
dict->argtypes,
33423342
dict->restype,
3343-
dict->flags & FUNCFLAG_CDECL);
3343+
dict->flags);
33443344
if (!thunk)
33453345
return NULL;
33463346

@@ -5333,6 +5333,8 @@ init_ctypes(void)
53335333
PyModule_AddObject(m, "FUNCFLAG_STDCALL", PyLong_FromLong(FUNCFLAG_STDCALL));
53345334
#endif
53355335
PyModule_AddObject(m, "FUNCFLAG_CDECL", PyLong_FromLong(FUNCFLAG_CDECL));
5336+
PyModule_AddObject(m, "FUNCFLAG_USE_ERRNO", PyLong_FromLong(FUNCFLAG_USE_ERRNO));
5337+
PyModule_AddObject(m, "FUNCFLAG_USE_LASTERROR", PyLong_FromLong(FUNCFLAG_USE_LASTERROR));
53365338
PyModule_AddObject(m, "FUNCFLAG_PYTHONAPI", PyLong_FromLong(FUNCFLAG_PYTHONAPI));
53375339
PyModule_AddStringConstant(m, "__version__", "1.1.0");
53385340

Modules/_ctypes/callbacks.c

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,15 @@ static void _CallPythonObject(void *mem,
185185
SETFUNC setfunc,
186186
PyObject *callable,
187187
PyObject *converters,
188+
int flags,
188189
void **pArgs)
189190
{
190191
Py_ssize_t i;
191192
PyObject *result;
192193
PyObject *arglist = NULL;
193194
Py_ssize_t nArgs;
195+
PyObject *error_object = NULL;
196+
int *space;
194197
#ifdef WITH_THREAD
195198
PyGILState_STATE state = PyGILState_Ensure();
196199
#endif
@@ -267,8 +270,41 @@ static void _CallPythonObject(void *mem,
267270
#define CHECK(what, x) \
268271
if (x == NULL) _AddTraceback(what, "_ctypes/callbacks.c", __LINE__ - 1), PyErr_Print()
269272

273+
if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
274+
error_object = get_error_object(&space);
275+
if (error_object == NULL)
276+
goto Done;
277+
if (flags & FUNCFLAG_USE_ERRNO) {
278+
int temp = space[0];
279+
space[0] = errno;
280+
errno = temp;
281+
}
282+
#ifdef MS_WIN32
283+
if (flags & FUNCFLAG_USE_LASTERROR) {
284+
int temp = space[1];
285+
space[1] = GetLastError();
286+
SetLastError(temp);
287+
}
288+
#endif
289+
}
290+
270291
result = PyObject_CallObject(callable, arglist);
271292
CHECK("'calling callback function'", result);
293+
294+
#ifdef MS_WIN32
295+
if (flags & FUNCFLAG_USE_LASTERROR) {
296+
int temp = space[1];
297+
space[1] = GetLastError();
298+
SetLastError(temp);
299+
}
300+
#endif
301+
if (flags & FUNCFLAG_USE_ERRNO) {
302+
int temp = space[0];
303+
space[0] = errno;
304+
errno = temp;
305+
}
306+
Py_XDECREF(error_object);
307+
272308
if ((restype != &ffi_type_void) && result) {
273309
PyObject *keep;
274310
assert(setfunc);
@@ -319,6 +355,7 @@ static void closure_fcn(ffi_cif *cif,
319355
p->setfunc,
320356
p->callable,
321357
p->converters,
358+
p->flags,
322359
args);
323360
}
324361

@@ -348,7 +385,7 @@ static CThunkObject* CThunkObject_new(Py_ssize_t nArgs)
348385
CThunkObject *AllocFunctionCallback(PyObject *callable,
349386
PyObject *converters,
350387
PyObject *restype,
351-
int is_cdecl)
388+
int flags)
352389
{
353390
int result;
354391
CThunkObject *p;
@@ -368,6 +405,7 @@ CThunkObject *AllocFunctionCallback(PyObject *callable,
368405
goto error;
369406
}
370407

408+
p->flags = flags;
371409
for (i = 0; i < nArgs; ++i) {
372410
PyObject *cnv = PySequence_GetItem(converters, i);
373411
if (cnv == NULL)
@@ -395,7 +433,7 @@ CThunkObject *AllocFunctionCallback(PyObject *callable,
395433

396434
cc = FFI_DEFAULT_ABI;
397435
#if defined(MS_WIN32) && !defined(_WIN32_WCE) && !defined(MS_WIN64)
398-
if (is_cdecl == 0)
436+
if ((flags & FUNCFLAG_CDECL) == 0)
399437
cc = FFI_STDCALL;
400438
#endif
401439
result = ffi_prep_cif(&p->cif, cc,

0 commit comments

Comments
 (0)