Author: Armin Rigo <[email protected]>
Branch: py3.5-eintr-pep475
Changeset: r88853:1ddec1199da6
Date: 2016-12-03 17:26 +0100
http://bitbucket.org/pypy/pypy/changeset/1ddec1199da6/

Log:    pep475-style test and fix for os.read()

diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -561,7 +561,19 @@
 
 @specialize.arg(3)
 def wrap_oserror2(space, e, w_filename=None, exception_name='w_OSError',
-                  w_exception_class=None, w_filename2=None):
+                  w_exception_class=None, w_filename2=None, eintr_retry=False):
+    """A double API here:
+
+        * if eintr_retry is False, always return the OperationError to
+          be raised by the caller, which might be about EINTR
+          (checksignals() is still called here).
+
+        * if eintr_retry is True (PEP 475 compliant API for retrying
+          system calls failing with EINTR), then this function raises
+          the OperationError directly, or for EINTR it calls
+          checksignals() and returns None in case the original
+          operation should be retried.
+    """
     assert isinstance(e, OSError)
 
     if _WINDOWS and isinstance(e, WindowsError):
@@ -571,6 +583,8 @@
 
     if errno == EINTR:
         space.getexecutioncontext().checksignals()
+        if eintr_retry:
+            return None
 
     try:
         msg = strerror(errno)
@@ -591,11 +605,14 @@
     else:
         w_error = space.call_function(exc, space.wrap(errno),
                                       space.wrap(msg))
-    return OperationError(exc, w_error)
+    operr = OperationError(exc, w_error)
+    if eintr_retry:
+        raise operr
+    return operr
 
 @specialize.arg(3)
 def wrap_oserror(space, e, filename=None, exception_name='w_OSError',
-                 w_exception_class=None, filename2=None):
+                 w_exception_class=None, filename2=None, eintr_retry=False):
     w_filename = None
     w_filename2 = None
     if filename is not None:
diff --git a/pypy/module/posix/interp_posix.py 
b/pypy/module/posix/interp_posix.py
--- a/pypy/module/posix/interp_posix.py
+++ b/pypy/module/posix/interp_posix.py
@@ -263,12 +263,13 @@
 @unwrap_spec(fd=c_int, length=int)
 def read(space, fd, length):
     """Read data from a file descriptor."""
-    try:
-        s = os.read(fd, length)
-    except OSError as e:
-        raise wrap_oserror(space, e)
-    else:
-        return space.newbytes(s)
+    while True:
+        try:
+            s = os.read(fd, length)
+        except OSError as e:
+            wrap_oserror(space, e, eintr_retry=True)
+        else:
+            return space.newbytes(s)
 
 @unwrap_spec(fd=c_int)
 def write(space, fd, w_data):
diff --git a/pypy/module/posix/test/test_posix2.py 
b/pypy/module/posix/test/test_posix2.py
--- a/pypy/module/posix/test/test_posix2.py
+++ b/pypy/module/posix/test/test_posix2.py
@@ -7,6 +7,7 @@
 
 from rpython.tool.udir import udir
 from pypy.tool.pytest.objspace import gettestobjspace
+from pypy.interpreter.gateway import interp2app
 from rpython.translator.c.test.test_extfunc import need_sparse_files
 from rpython.rlib import rposix
 
@@ -1365,3 +1366,40 @@
         if os.name == 'posix':
             assert os.open in os.supports_dir_fd  # openat()
 
+
+class AppTestPep475Retry:
+    spaceconfig = {'usemodules': USEMODULES}
+
+    def setup_class(cls):
+        if os.name != 'posix':
+            skip("xxx tests are posix-only")
+        if cls.runappdirect:
+            skip("xxx does not work with -A")
+
+        def fd_data_after_delay(space):
+            g = os.popen("sleep 5 && echo hello", "r")
+            cls._keepalive_g = g
+            return space.wrap(g.fileno())
+
+        cls.w_posix = space.appexec([], GET_POSIX)
+        cls.w_fd_data_after_delay = cls.space.wrap(
+            interp2app(fd_data_after_delay))
+
+    def test_pep475_retry_read(self):
+        import _signal as signal
+        signalled = []
+
+        def foo(*args):
+            signalled.append("ALARM")
+
+        signal.signal(signal.SIGALRM, foo)
+        try:
+            fd = self.fd_data_after_delay()
+            signal.alarm(1)
+            got = self.posix.read(fd, 100)
+            self.posix.close(fd)
+        finally:
+            signal.signal(signal.SIGALRM, signal.SIG_DFL)
+
+        assert signalled != []
+        assert got.startswith(b'h')
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to