Commits

Anonymous committed 01d2d65 Merge

Comments (0)

Files changed (2)

c/_cffi_backend.c

     return convert_from_object(result, ctype, pyobj);
 }
 
+static void _my_PyErr_WriteUnraisable(PyObject *obj)
+{
+    /* like PyErr_WriteUnraisable(), but write a full traceback */
+    PyObject *f, *t, *v, *tb;
+    PyErr_Fetch(&t, &v, &tb);
+    f = PySys_GetObject("stderr");
+    if (f != NULL) {
+        PyFile_WriteString("From callback ", f);
+        PyFile_WriteObject(obj, f, 0);
+        PyFile_WriteString(":\n", f);
+        PyErr_Display(t, v, tb);
+    }
+    Py_XDECREF(t);
+    Py_XDECREF(v);
+    Py_XDECREF(tb);
+}
+
 static void invoke_callback(ffi_cif *cif, void *result, void **args,
                             void *userdata)
 {
     return;
 
  error:
-    PyErr_WriteUnraisable(py_ob);
+    _my_PyErr_WriteUnraisable(py_ob);
     if (SIGNATURE(1)->ct_size > 0) {
         py_rawerr = PyTuple_GET_ITEM(cb_args, 2);
         memcpy(result, PyBytes_AS_STRING(py_rawerr),
     e = py.test.raises(TypeError, f)
     assert str(e.value) == "'int(*)(int)' expects 1 arguments, got 0"
 
+def test_callback_exception():
+    import cStringIO, linecache
+    def matches(str, pattern):
+        while '$' in pattern:
+            i = pattern.index('$')
+            assert str[:i] == pattern[:i]
+            j = str.find(pattern[i+1], i)
+            assert i + 1 <= j <= str.find('\n', i)
+            str = str[j:]
+            pattern = pattern[i+1:]
+        assert str == pattern
+        return True
+    def check_value(x):
+        if x == 10000:
+            raise ValueError(42)
+    def cb1(x):
+        check_value(x)
+        return x * 3
+    BShort = new_primitive_type("short")
+    BFunc = new_function_type((BShort,), BShort, False)
+    f = callback(BFunc, cb1, -42)
+    orig_stderr = sys.stderr
+    orig_getline = linecache.getline
+    try:
+        linecache.getline = lambda *args: 'LINE'    # hack: speed up PyPy tests
+        sys.stderr = cStringIO.StringIO()
+        assert f(100) == 300
+        assert sys.stderr.getvalue() == ''
+        assert f(10000) == -42
+        assert matches(sys.stderr.getvalue(), """\
+From callback <function cb1 at 0x$>:
+Traceback (most recent call last):
+  File "$", line $, in cb1
+    $
+  File "$", line $, in check_value
+    $
+ValueError: 42
+""")
+        sys.stderr = cStringIO.StringIO()
+        bigvalue = 20000
+        assert f(bigvalue) == -42
+        assert matches(sys.stderr.getvalue(), """\
+From callback <function cb1 at 0x$>:
+OverflowError: integer 60000 does not fit 'short'
+""")
+    finally:
+        sys.stderr = orig_stderr
+        linecache.getline = orig_getline
+
 def test_callback_return_type():
     for rettype in ["signed char", "short", "int", "long", "long long",
                     "unsigned char", "unsigned short", "unsigned int",
     assert p.a1 == -1
 
 def test_weakref():
-    import weakref
+    import _weakref
     BInt = new_primitive_type("int")
     BPtr = new_pointer_type(BInt)
-    weakref.ref(BInt)
-    weakref.ref(newp(BPtr, 42))
-    weakref.ref(cast(BPtr, 42))
-    weakref.ref(cast(BInt, 42))
+    _weakref.ref(BInt)
+    _weakref.ref(newp(BPtr, 42))
+    _weakref.ref(cast(BPtr, 42))
+    _weakref.ref(cast(BInt, 42))
 
 def test_no_inheritance():
     BInt = new_primitive_type("int")
     a = newp(BArray, [u+'A', u+'B', u+'C'])
     assert type(string(a)) is unicode and string(a) == u+'ABC'
     if 'PY_DOT_PY' not in globals() and sys.version_info < (3,):
-        assert string(a, 8).startswith(u+'ABC') # may contain additional garbage
+        try:
+            # may contain additional garbage
+            assert string(a, 8).startswith(u+'ABC')
+        except ValueError:    # garbage contains values > 0x10FFFF
+            assert sizeof(BWChar) == 4
 
 def test_string_typeerror():
     BShort = new_primitive_type("short")
     data = posix.read(fdr, 256)
     assert data == b"Xhello\n"
     posix.close(fdr)
+
+def test_GetLastError():
+    if sys.platform != "win32":
+        py.test.skip("GetLastError(): only for Windows")
+    #
+    lib = find_and_load_library('KERNEL32')
+    BInt = new_primitive_type("int")
+    BVoid = new_void_type()
+    BFunc1 = new_function_type((BInt,), BVoid, False)
+    BFunc2 = new_function_type((), BInt, False)
+    SetLastError = lib.load_function(BFunc1, "SetLastError")
+    GetLastError = lib.load_function(BFunc2, "GetLastError")
+    #
+    SetLastError(42)
+    # a random function that will reset the real GetLastError() to 0
+    import nt; nt.stat('.')
+    #
+    res = GetLastError()
+    assert res == 42
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.