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