[pypy-commit] cffi static-callback: Write one error message directly to stderr instead of sys.stderr. This

arigo noreply at buildbot.pypy.org
Wed Nov 18 06:27:42 EST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: static-callback
Changeset: r2416:1e8f6c41d71a
Date: 2015-11-18 12:28 +0100
http://bitbucket.org/cffi/cffi/changeset/1e8f6c41d71a/

Log:	Write one error message directly to stderr instead of sys.stderr.
	This lets us avoid taking the GIL, which might crash in case the
	Python interpreter is not initialized at all.

diff --git a/c/call_python.c b/c/call_python.c
--- a/c/call_python.c
+++ b/c/call_python.c
@@ -90,29 +90,21 @@
        at least 8 bytes in size.
     */
     save_errno();
-    {
-#ifdef WITH_THREAD
-    PyGILState_STATE state = PyGILState_Ensure();
-#endif
 
     if (externpy->reserved1 == NULL) {
         /* not initialized! */
-        PyObject *f = PySys_GetObject("stderr");
-        if (f != NULL) {
-            PyFile_WriteString("extern \"Python\": function ", f);
-            PyFile_WriteString(externpy->name, f);
-            PyFile_WriteString("() called, but no code was attached "
-                               "to it yet with @ffi.def_extern().  "
-                               "Returning 0.\n", f);
-        }
+        fprintf(stderr, "extern \"Python\": function %s() called, "
+                        "but no code was attached to it yet with "
+                        "@ffi.def_extern().  Returning 0.\n", externpy->name);
         memset(args, 0, externpy->size_of_result);
     }
     else {
+#ifdef WITH_THREAD
+        PyGILState_STATE state = PyGILState_Ensure();
+#endif
         general_invoke_callback(0, args, args, externpy->reserved1);
-    }
-
 #ifdef WITH_THREAD
-    PyGILState_Release(state);
+        PyGILState_Release(state);
 #endif
     }
     restore_errno();
diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py
--- a/testing/cffi0/test_function.py
+++ b/testing/cffi0/test_function.py
@@ -4,6 +4,7 @@
 import ctypes.util
 from cffi.backend_ctypes import CTypesBackend
 from testing.udir import udir
+from testing.support import FdWriteCapture
 
 try:
     from StringIO import StringIO
@@ -11,29 +12,6 @@
     from io import StringIO
 
 
-class FdWriteCapture(object):
-    """xxx limited to capture at most 512 bytes of output, according
-    to the Posix manual."""
-
-    def __init__(self, capture_fd):
-        self.capture_fd = capture_fd
-
-    def __enter__(self):
-        self.read_fd, self.write_fd = os.pipe()
-        self.copy_fd = os.dup(self.capture_fd)
-        os.dup2(self.write_fd, self.capture_fd)
-        return self
-
-    def __exit__(self, *args):
-        os.dup2(self.copy_fd, self.capture_fd)
-        os.close(self.copy_fd)
-        os.close(self.write_fd)
-        self._value = os.read(self.read_fd, 512)
-        os.close(self.read_fd)
-
-    def getvalue(self):
-        return self._value
-
 lib_m = 'm'
 if sys.platform == 'win32':
     #there is a small chance this fails on Mingw via environ $CC
@@ -135,7 +113,7 @@
         """)
         ffi.C = ffi.dlopen(None)
         ffi.C.fputs   # fetch before capturing, for easier debugging
-        with FdWriteCapture(2) as fd:
+        with FdWriteCapture() as fd:
             ffi.C.fputs(b"hello\n", ffi.C.stderr)
             ffi.C.fputs(b"  world\n", ffi.C.stderr)
         res = fd.getvalue()
@@ -151,7 +129,7 @@
         """)
         ffi.C = ffi.dlopen(None)
         ffi.C.fputs   # fetch before capturing, for easier debugging
-        with FdWriteCapture(2) as fd:
+        with FdWriteCapture() as fd:
             ffi.C.fputs(b"hello\n", ffi.C.stderr)
             ffi.C.fputs(b"  world\n", ffi.C.stderr)
         res = fd.getvalue()
@@ -166,7 +144,7 @@
            void *stderr;
         """)
         ffi.C = ffi.dlopen(None)
-        with FdWriteCapture(2) as fd:
+        with FdWriteCapture() as fd:
             ffi.C.fprintf(ffi.C.stderr, b"hello with no arguments\n")
             ffi.C.fprintf(ffi.C.stderr,
                           b"hello, %s!\n", ffi.new("char[]", b"world"))
@@ -228,7 +206,7 @@
         fptr = ffi.cast("int(*)(const char *txt, void *)", ffi.C.fputs)
         assert fptr == ffi.C.fputs
         assert repr(fptr).startswith("<cdata 'int(*)(char *, void *)' 0x")
-        with FdWriteCapture(2) as fd:
+        with FdWriteCapture() as fd:
             fptr(b"world\n", ffi.C.stderr)
         res = fd.getvalue()
         assert res == b'world\n'
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -4,7 +4,7 @@
 from cffi import recompiler
 from testing.udir import udir
 from testing.support import u
-from StringIO import StringIO
+from testing.support import FdWriteCapture, StdErrCapture
 
 
 def check_type_table(input, expected_output, included=None):
@@ -1486,14 +1486,6 @@
     pt = ptr_call2(ffi.addressof(lib, 'cb2'))
     assert (pt.x, pt.y) == (99*500*999, -99*500*999)
 
-class StdErrCapture(object):
-    def __enter__(self):
-        self.old_stderr = sys.stderr
-        sys.stderr = f = StringIO()
-        return f
-    def __exit__(self, *args):
-        sys.stderr = self.old_stderr
-
 def test_extern_python_1():
     ffi = FFI()
     ffi.cdef("""
@@ -1506,7 +1498,7 @@
     """)
     lib = verify(ffi, 'test_extern_python_1', "")
     assert ffi.typeof(lib.bar) == ffi.typeof("int(*)(int, int)")
-    with StdErrCapture() as f:
+    with FdWriteCapture() as f:
         res = lib.bar(4, 5)
     assert res == 0
     assert f.getvalue() == (
diff --git a/testing/support.py b/testing/support.py
--- a/testing/support.py
+++ b/testing/support.py
@@ -17,3 +17,40 @@
     u = ""
     unicode = str
     long = int
+
+
+class StdErrCapture(object):
+    """Capture writes to sys.stderr (not to the underlying file descriptor)."""
+    def __enter__(self):
+        import StringIO
+        self.old_stderr = sys.stderr
+        sys.stderr = f = StringIO.StringIO()
+        return f
+    def __exit__(self, *args):
+        sys.stderr = self.old_stderr
+
+
+class FdWriteCapture(object):
+    """xxx limited to capture at most 512 bytes of output, according
+    to the Posix manual."""
+
+    def __init__(self, capture_fd=2):    # stderr by default
+        self.capture_fd = capture_fd
+
+    def __enter__(self):
+        import os
+        self.read_fd, self.write_fd = os.pipe()
+        self.copy_fd = os.dup(self.capture_fd)
+        os.dup2(self.write_fd, self.capture_fd)
+        return self
+
+    def __exit__(self, *args):
+        import os
+        os.dup2(self.copy_fd, self.capture_fd)
+        os.close(self.copy_fd)
+        os.close(self.write_fd)
+        self._value = os.read(self.read_fd, 512)
+        os.close(self.read_fd)
+
+    def getvalue(self):
+        return self._value


More information about the pypy-commit mailing list