[pypy-commit] cffi default: Add ffi.getwinerror().

arigo noreply at buildbot.pypy.org
Tue Nov 12 13:48:33 CET 2013


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r1412:8a16eff7850c
Date: 2013-11-12 13:48 +0100
http://bitbucket.org/cffi/cffi/changeset/8a16eff7850c/

Log:	Add ffi.getwinerror().

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -5227,6 +5227,9 @@
     {"set_errno", b_set_errno, METH_VARARGS},
     {"newp_handle", b_newp_handle, METH_VARARGS},
     {"from_handle", b_from_handle, METH_O},
+#ifdef MS_WIN32
+    {"getwinerror", b_getwinerror, METH_VARARGS},
+#endif
     {"_get_types", b__get_types, METH_NOARGS},
     {"_testfunc", b__testfunc, METH_VARARGS},
     {NULL,     NULL}    /* Sentinel */
diff --git a/c/misc_win32.h b/c/misc_win32.h
--- a/c/misc_win32.h
+++ b/c/misc_win32.h
@@ -80,6 +80,54 @@
     /* else: cannot report the error */
 }
 
+static PyObject *b_getwinerror(PyObject *self, PyObject *args)
+{
+    int err = -1;
+    int len;
+    char *s;
+    char *s_buf = NULL; /* Free via LocalFree */
+    char s_small_buf[28]; /* Room for "Windows Error 0xFFFFFFFF" */
+    PyObject *v;
+
+    if (!PyArg_ParseTuple(args, "|i", &err))
+        return NULL;
+
+    if (err == -1) {
+        struct cffi_errno_s *p;
+        p = _geterrno_object();
+        if (p == NULL)
+            return PyErr_NoMemory();
+        err = p->saved_lasterror;
+    }
+
+    len = FormatMessage(
+        /* Error API error */
+        FORMAT_MESSAGE_ALLOCATE_BUFFER |
+        FORMAT_MESSAGE_FROM_SYSTEM |
+        FORMAT_MESSAGE_IGNORE_INSERTS,
+        NULL,           /* no message source */
+        err,
+        MAKELANGID(LANG_NEUTRAL,
+        SUBLANG_DEFAULT), /* Default language */
+        (LPTSTR) &s_buf,
+        0,              /* size not used */
+        NULL);          /* no args */
+    if (len==0) {
+        /* Only seen this in out of mem situations */
+        sprintf(s_small_buf, "Windows Error 0x%X", err);
+        s = s_small_buf;
+        s_buf = NULL;
+    } else {
+        s = s_buf;
+        /* remove trailing cr/lf and dots */
+        while (len > 0 && (s[len-1] <= ' ' || s[len-1] == '.'))
+            s[--len] = '\0';
+    }
+    v = Py_BuildValue("(is)", err, s);
+    LocalFree(s_buf);
+    return v;
+}
+
 /************************************************************/
 /* Emulate dlopen()&co. from the Windows API */
 
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -2698,6 +2698,16 @@
     #
     res = GetLastError()
     assert res == 42
+    #
+    SetLastError(2)
+    code, message = getwinerror()
+    assert code == 2
+    assert message == "The system cannot find the file specified"
+    #
+    code, message = getwinerror(1155)
+    assert code == 1155
+    assert message == ("No application is associated with the "
+                       "specified file for this operation")
 
 def test_nonstandard_integer_types():
     for typename in ['int8_t', 'uint8_t', 'int16_t', 'uint16_t', 'int32_t',
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -347,6 +347,9 @@
     errno = property(_get_errno, _set_errno, None,
                      "the value of 'errno' from/to the C calls")
 
+    def getwinerror(self, code=-1):
+        return self._backend.getwinerror(code)
+
     def _pointer_to(self, ctype):
         from . import model
         with self._lock:
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -1122,9 +1122,18 @@
 
 ``ffi.errno``: the value of ``errno`` received from the most recent C call
 in this thread, and passed to the following C call, is available via
-reads and writes of the property ``ffi.errno``.  On Windows we also save
-and restore the ``GetLastError()`` value, but to access it you need to
-declare and call the ``GetLastError()`` function as usual.
+reads and writes of the property ``ffi.errno``.
+
+``ffi.getwinerror(code=-1)``: on Windows, in addition to ``errno`` we
+also save and restore the ``GetLastError()`` value across function
+calls.  This function returns this error code as a tuple ``(code,
+message)``, adding a readable message like Python does when raising
+WindowsError.  If the argument ``code`` is given, format that code into
+a message instead of using ``GetLastError()``.  *New in version 0.8.*
+(Note that it is also possible to declare and call the ``GetLastError()``
+function as usual.)
+
+.. "versionadded:: 0.8" --- inlined in the previous paragraph
 
 ``ffi.string(cdata, [maxlen])``: return a Python string (or unicode
 string) from the 'cdata'.  *New in version 0.3.*
diff --git a/testing/test_ffi_backend.py b/testing/test_ffi_backend.py
--- a/testing/test_ffi_backend.py
+++ b/testing/test_ffi_backend.py
@@ -194,3 +194,20 @@
         assert p.a[0] == 200
         assert p.a[1] == 300
         assert p.a[2] == 400
+
+    @pytest.mark.skipif("sys.platform != 'win32'")
+    def test_getwinerror(self):
+        ffi = FFI()
+        code, message = ffi.getwinerror(1155)
+        assert code == 1155
+        assert message == ("No application is associated with the "
+                           "specified file for this operation")
+        ffi.cdef("void SetLastError(int);")
+        lib = ffi.dlopen("Kernel32.dll")
+        lib.SetLastError(2)
+        code, message = ffi.getwinerror()
+        assert code == 2
+        assert message == "The system cannot find the file specified"
+        code, message = ffi.getwinerror(-1)
+        assert code == 2
+        assert message == "The system cannot find the file specified"


More information about the pypy-commit mailing list