Index: Lib/ctypes/__init__.py =================================================================== --- Lib/ctypes/__init__.py (revision 59887) +++ Lib/ctypes/__init__.py (working copy) @@ -32,8 +32,15 @@ if int(_os.uname()[2].split('.')[0]) < 8: DEFAULT_MODE = RTLD_GLOBAL +def __py2exe_hint(): + # The _ctypes extension imports the threading module. This is a + # hint to executable packers that 'threading' is required. + import threading + from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \ - FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI + FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI, \ + FUNCFLAG_ERRNO as _FUNCFLAG_ERRNO, \ + FUNCFLAG_LASTERROR as _FUNCFLAG_LASTERROR """ WINOLEAPI -> HRESULT @@ -339,12 +346,22 @@ 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, errno=False, GetLastError=False): self._name = name + flags = self._func_flags_ + if errno: + flags |= _FUNCFLAG_ERRNO + if GetLastError: + flags |= _FUNCFLAG_LASTERROR + + class _FuncPtr(_CFuncPtr): + _flags_ = flags + _restype_ = self._func_restype_ + self._FuncPtr = _FuncPtr + if handle is None: self._handle = _dlopen(self._name, mode) else: @@ -374,9 +391,7 @@ 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"): @@ -384,9 +399,7 @@ """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? @@ -410,9 +423,8 @@ 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): Index: Lib/ctypes/test/test_errno.py =================================================================== --- Lib/ctypes/test/test_errno.py (revision 0) +++ Lib/ctypes/test/test_errno.py (revision 0) @@ -0,0 +1,37 @@ +import unittest, os, errno +from ctypes import * +from ctypes.util import find_library + +class Test(unittest.TestCase): + def test_open(self): + libc_name = find_library("c") + if libc_name is not None: + libc = CDLL(libc_name, errno=True) + if os.name == "nt": + libc_open = libc._open + else: + libc_open = libc.open + + libc_open.argtypes = c_char_p, c_int + + self.failUnlessRaises(ValueError, lambda: libc_open.errno) + + self.failUnlessEqual(libc_open(".", 0), -1) + self.failUnlessEqual(libc_open.errno, errno.EACCES) + + self.failUnlessEqual(libc_open("", 0), -1) + self.failUnlessEqual(libc_open.errno, errno.ENOENT) + + if os.name == "nt": + + def test_GetLastError(self): + dll = WinDLL("kernel32", GetLastError=True) + GetModuleHandle = dll.GetModuleHandleW + GetModuleHandle.argtypes = [c_wchar_p] + + self.failUnlessRaises(ValueError, lambda: GetModuleHandle.LastError) + self.failUnlessEqual(0, GetModuleHandle("foo")) + self.failUnlessEqual(GetModuleHandle.LastError, 126) + +if __name__ == "__main__": + unittest.main() Property changes on: Lib\ctypes\test\test_errno.py ___________________________________________________________________ Name: svn:eol-style + native Index: Lib/ctypes/util.py =================================================================== --- Lib/ctypes/util.py (revision 59887) +++ Lib/ctypes/util.py (working copy) @@ -5,7 +5,20 @@ # find_library(name) returns the pathname of a library, or None. if os.name == "nt": + import re + def find_msvcrt(): + # To access functions of the C runtime library the same dll + # that Python itself is linked to must be used. A not very + # robust but working hack is to scan the executable for the + # string (better would be to locate the real import table and + # parse that, but I'm too lazy for this now). + bytes = open(sys.executable, "rb").read() + match = re.search("msvcr([0-9]+|t)d?.dll", bytes, re.IGNORECASE) + return match.group(0) + def find_library(name): + if name in ("c", "m"): + return find_msvcrt() # See MSDN for the REAL search order. for directory in os.environ['PATH'].split(os.pathsep): fname = os.path.join(directory, name) Index: Modules/_ctypes/_ctypes.c =================================================================== --- Modules/_ctypes/_ctypes.c (revision 59887) +++ Modules/_ctypes/_ctypes.c (working copy) @@ -127,6 +127,8 @@ PyObject *PyExc_ArgError; static PyTypeObject Simple_Type; +static PyObject* pyerrno_name; +static PyObject* pylasterror_name; char *conversion_mode_encoding = NULL; char *conversion_mode_errors = NULL; @@ -2584,6 +2586,40 @@ } } +static PyObject * +CFuncPtr_get_errno(CFuncPtrObject *self) +{ + StgDictObject *dict = PyObject_stgdict((PyObject *)self); + if ((dict->flags & FUNCFLAG_ERRNO) == 0) { + PyErr_SetString(PyExc_AttributeError, "errno"); + return NULL; + } + if (self->tls == NULL) { + PyErr_SetString(PyExc_ValueError, + "errno not yet set"); + return NULL; + } + return PyObject_GetAttr(self->tls, pyerrno_name); +} + +#ifdef MS_WIN32 +static PyObject * +CFuncPtr_get_LastError(CFuncPtrObject *self) +{ + StgDictObject *dict = PyObject_stgdict((PyObject *)self); + if ((dict->flags & FUNCFLAG_LASTERROR) == 0) { + PyErr_SetString(PyExc_AttributeError, "LastError"); + return NULL; + } + if (self->tls == NULL) { + PyErr_SetString(PyExc_ValueError, + "LastError not yet set"); + return NULL; + } + return PyObject_GetAttr(self->tls, pylasterror_name); +} +#endif + static PyGetSetDef CFuncPtr_getsets[] = { { "errcheck", (getter)CFuncPtr_get_errcheck, (setter)CFuncPtr_set_errcheck, "a function to check for errors", NULL }, @@ -2592,6 +2628,12 @@ { "argtypes", (getter)CFuncPtr_get_argtypes, (setter)CFuncPtr_set_argtypes, "specify the argument types", NULL }, + { "errno", (getter)CFuncPtr_get_errno, NULL, + "errno of the last call", NULL }, +#ifdef MS_WIN32 + { "LastError", (getter)CFuncPtr_get_LastError, NULL, + "GetLastError() value of the last call", NULL }, +#endif { NULL, NULL } }; @@ -3310,6 +3352,31 @@ /* later, we probably want to have an errcheck field in stgdict */ errcheck = self->errcheck /* ? self->errcheck : dict->errcheck */; + if (0 != (dict->flags & (FUNCFLAG_ERRNO|FUNCFLAG_LASTERROR))) { + if (self->tls == NULL) { + PyObject *mod = PyImport_ImportModule("threading"); + PyObject *tls; + if (mod == NULL) { + _AddTraceback("CFuncPtr_call", + "_ctypes/_ctypes.c", __LINE__-4); + return NULL; + } + tls = PyObject_CallMethod(mod, "local", NULL); + Py_DECREF(mod); + if (tls == NULL) { + _AddTraceback("CFuncPtr_call", + "_ctypes/_ctypes.c", __LINE__-4); + return NULL; + } + self->tls = tls; + } + if (dict->flags & FUNCFLAG_ERRNO) + errno = 0; +#ifdef MS_WIN32 + if (dict->flags & FUNCFLAG_LASTERROR) + SetLastError(0); +#endif + } pProc = *(void **)self->b_ptr; #ifdef MS_WIN32 @@ -3389,6 +3456,34 @@ converters, restype, checker); + + if (self->tls) { + if (dict->flags & FUNCFLAG_ERRNO) { + PyObject *pyerr = PyInt_FromLong(errno); + if ((pyerr == NULL) || + (-1 == PyObject_SetAttr(self->tls, pyerrno_name, pyerr))) { + Py_DECREF(callargs); + Py_XDECREF(result); + Py_XDECREF(pyerr); + return NULL; + } + Py_DECREF(pyerr); + } +#ifdef MS_WIN32 + if (dict->flags & FUNCFLAG_LASTERROR) { + PyObject *pyerr = PyInt_FromLong(GetLastError()); + if ((pyerr == NULL) || + (-1 == PyObject_SetAttr(self->tls, pylasterror_name, pyerr))) { + Py_DECREF(callargs); + Py_XDECREF(result); + Py_XDECREF(pyerr); + return NULL; + } + Py_DECREF(pyerr); + } +#endif + } + /* The 'errcheck' protocol */ if (result != NULL && errcheck) { PyObject *v = PyObject_CallFunctionObjArgs(errcheck, @@ -3424,6 +3519,7 @@ Py_VISIT(self->argtypes); Py_VISIT(self->converters); Py_VISIT(self->paramflags); + Py_VISIT(self->tls); return CData_traverse((CDataObject *)self, visit, arg); } @@ -3437,6 +3533,7 @@ Py_CLEAR(self->argtypes); Py_CLEAR(self->converters); Py_CLEAR(self->paramflags); + Py_CLEAR(self->tls); if (self->thunk) { FreeClosure(self->thunk->pcl); @@ -4958,6 +5055,14 @@ if (!m) return; + pyerrno_name = PyString_FromString("errno"); + if (pyerrno_name == NULL) + return; + + pylasterror_name = PyString_FromString("LastError"); + if (pylasterror_name == NULL) + return; + if (PyType_Ready(&PyCArg_Type) < 0) return; @@ -5063,6 +5168,8 @@ #endif PyModule_AddObject(m, "FUNCFLAG_CDECL", PyInt_FromLong(FUNCFLAG_CDECL)); PyModule_AddObject(m, "FUNCFLAG_PYTHONAPI", PyInt_FromLong(FUNCFLAG_PYTHONAPI)); + PyModule_AddObject(m, "FUNCFLAG_ERRNO", PyInt_FromLong(FUNCFLAG_ERRNO)); + PyModule_AddObject(m, "FUNCFLAG_LASTERROR", PyInt_FromLong(FUNCFLAG_LASTERROR)); PyModule_AddStringConstant(m, "__version__", "1.1.0"); PyModule_AddObject(m, "_memmove_addr", PyLong_FromVoidPtr(memmove)); Index: Modules/_ctypes/ctypes.h =================================================================== --- Modules/_ctypes/ctypes.h (revision 59887) +++ Modules/_ctypes/ctypes.h (working copy) @@ -108,6 +108,7 @@ GUID *iid; #endif PyObject *paramflags; + PyObject *tls; } CFuncPtrObject; extern PyTypeObject StgDict_Type; @@ -285,6 +286,8 @@ #define FUNCFLAG_CDECL 0x1 #define FUNCFLAG_HRESULT 0x2 #define FUNCFLAG_PYTHONAPI 0x4 +#define FUNCFLAG_ERRNO 0x8 +#define FUNCFLAG_LASTERROR 0x10 #define DICTFLAG_FINAL 0x1000