Author: Armin Rigo <[email protected]>
Branch: py3.5
Changeset: r88885:b5eaea946a6b
Date: 2016-12-04 21:44 +0100
http://bitbucket.org/pypy/pypy/changeset/b5eaea946a6b/
Log: hg merge py3.5-eintr-pep475
Implement PEP475, which makes a number of os-, file-, select- and
socket-related functions no longer raise OSError/IOError on getting
EINTR, but instead automatically retry.
There are a few functions mentioned in the PEP too which are not
present in PyPy so far.
diff too long, truncating to 2000 out of 2118 lines
diff --git a/lib_pypy/_pypy_wait.py b/lib_pypy/_pypy_wait.py
--- a/lib_pypy/_pypy_wait.py
+++ b/lib_pypy/_pypy_wait.py
@@ -1,3 +1,5 @@
+import os
+from errno import EINTR
from resource import ffi, lib, _make_struct_rusage
__all__ = ["wait3", "wait4"]
@@ -6,7 +8,13 @@
def wait3(options):
status = ffi.new("int *")
ru = ffi.new("struct rusage *")
- pid = lib.wait3(status, options, ru)
+ while True:
+ pid = lib.wait3(status, options, ru)
+ if pid != -1:
+ break
+ errno = ffi.errno
+ if errno != EINTR:
+ raise OSError(errno, os.strerror(errno))
rusage = _make_struct_rusage(ru)
@@ -15,7 +23,13 @@
def wait4(pid, options):
status = ffi.new("int *")
ru = ffi.new("struct rusage *")
- pid = lib.wait4(pid, status, options, ru)
+ while True:
+ pid = lib.wait4(pid, status, options, ru)
+ if pid != -1:
+ break
+ errno = ffi.errno
+ if errno != EINTR:
+ raise OSError(errno, os.strerror(errno))
rusage = _make_struct_rusage(ru)
diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -543,6 +543,7 @@
_WINDOWS = True
def wrap_windowserror(space, e, w_filename=None):
+ XXX # WindowsError no longer exists in Py3.5
from rpython.rlib import rwin32
winerror = e.winerror
@@ -559,43 +560,72 @@
space.wrap(msg))
return OperationError(exc, w_error)
[email protected](3)
[email protected](3, 6)
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. It can possibly 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):
return wrap_windowserror(space, e, w_filename)
+ if w_exception_class is None:
+ w_exc = getattr(space, exception_name)
+ else:
+ w_exc = w_exception_class
+ operror = _wrap_oserror2_impl(space, e, w_filename, w_filename2, w_exc,
+ eintr_retry)
+ if eintr_retry:
+ assert operror is None # otherwise, _wrap_oserror2_impl() has raised
+ else:
+ assert operror is not None # tell the annotator we don't return None
+ return operror
+
+def _wrap_oserror2_impl(space, e, w_filename, w_filename2, w_exc, eintr_retry):
+ # move the common logic in its own function, instead of having it
+ # duplicated 4 times in all 4 specialized versions of wrap_oserror2()
errno = e.errno
if errno == EINTR:
space.getexecutioncontext().checksignals()
+ if eintr_retry:
+ return None
try:
msg = strerror(errno)
except ValueError:
msg = u'error %d' % errno
- if w_exception_class is None:
- exc = getattr(space, exception_name)
- else:
- exc = w_exception_class
if w_filename is not None:
if w_filename2 is not None:
- w_error = space.call_function(exc, space.wrap(errno),
+ w_error = space.call_function(w_exc, space.wrap(errno),
space.wrap(msg), w_filename,
space.w_None, w_filename2)
else:
- w_error = space.call_function(exc, space.wrap(errno),
+ w_error = space.call_function(w_exc, space.wrap(errno),
space.wrap(msg), w_filename)
else:
- w_error = space.call_function(exc, space.wrap(errno),
+ w_error = space.call_function(w_exc, space.wrap(errno),
space.wrap(msg))
- return OperationError(exc, w_error)
+ operror = OperationError(w_exc, w_error)
+ if eintr_retry:
+ raise operror
+ return operror
+_wrap_oserror2_impl._dont_inline_ = True
[email protected](3)
[email protected](3, 6)
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:
@@ -605,7 +635,9 @@
return wrap_oserror2(space, e, w_filename,
exception_name=exception_name,
w_exception_class=w_exception_class,
- w_filename2=w_filename2)
+ w_filename2=w_filename2,
+ eintr_retry=eintr_retry)
+wrap_oserror._dont_inline_ = True
def exception_from_saved_errno(space, w_type):
from rpython.rlib.rposix import get_saved_errno
diff --git a/pypy/interpreter/timeutils.py b/pypy/interpreter/timeutils.py
new file mode 100644
--- /dev/null
+++ b/pypy/interpreter/timeutils.py
@@ -0,0 +1,11 @@
+"""
+Access to the time module's high-resolution monotonic clock
+"""
+
+def monotonic(space):
+ from pypy.module.time import interp_time
+ if interp_time.HAS_MONOTONIC:
+ w_res = interp_time.monotonic(space)
+ else:
+ w_res = interp_time.gettimeofday(space)
+ return space.float_w(w_res) # xxx back and forth
diff --git a/pypy/module/_io/interp_fileio.py b/pypy/module/_io/interp_fileio.py
--- a/pypy/module/_io/interp_fileio.py
+++ b/pypy/module/_io/interp_fileio.py
@@ -175,46 +175,48 @@
"Cannot use closefd=False with file name")
from pypy.module.posix.interp_posix import dispatch_filename
- try:
- self.fd = dispatch_filename(rposix.open)(
- space, w_name, flags, 0666)
- except OSError as e:
- raise wrap_oserror2(space, e, w_name,
- exception_name='w_IOError')
- finally:
- fd_is_own = True
+ while True:
+ try:
+ self.fd = dispatch_filename(rposix.open)(
+ space, w_name, flags, 0666)
+ fd_is_own = True
+ break
+ except OSError as e:
+ wrap_oserror2(space, e, w_name,
+ exception_name='w_IOError',
+ eintr_retry=True)
if not rposix._WIN32:
try:
_open_inhcache.set_non_inheritable(self.fd)
except OSError as e:
- raise wrap_oserror2(space, e, w_name)
+ raise wrap_oserror2(space, e, w_name,
eintr_retry=False)
else:
w_fd = space.call_function(w_opener, w_name, space.wrap(flags))
try:
self.fd = space.int_w(w_fd)
+ fd_is_own = True
except OperationError as e:
if not e.match(space, space.w_TypeError):
raise
raise oefmt(space.w_TypeError,
"expected integer from opener")
- finally:
- fd_is_own = True
if not rposix._WIN32:
try:
rposix.set_inheritable(self.fd, False)
except OSError as e:
- raise wrap_oserror2(space, e, w_name)
+ raise wrap_oserror2(space, e, w_name,
eintr_retry=False)
try:
st = os.fstat(self.fd)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
# On Unix, fopen will succeed for directories.
# In Python, there should be no file objects referring to
# directories, so we need a check.
if stat.S_ISDIR(st.st_mode):
raise wrap_oserror2(space, OSError(errno.EISDIR, "fstat"),
- w_name, exception_name='w_IOError')
+ w_name, exception_name='w_IOError',
+ eintr_retry=False)
self.blksize = DEFAULT_BUFFER_SIZE
if HAS_BLKSIZE and st.st_blksize > 1:
self.blksize = st.st_blksize
@@ -227,7 +229,8 @@
try:
os.lseek(self.fd, 0, os.SEEK_END)
except OSError as e:
- raise wrap_oserror(space, e, exception_name='w_IOError')
+ raise wrap_oserror(space, e, exception_name='w_IOError',
+ eintr_retry=False)
except:
if not fd_is_own:
self.fd = -1
@@ -285,7 +288,8 @@
os.close(fd)
except OSError as e:
raise wrap_oserror(space, e,
- exception_name='w_IOError')
+ exception_name='w_IOError',
+ eintr_retry=False)
def close_w(self, space):
try:
@@ -319,7 +323,8 @@
pos = os.lseek(self.fd, pos, whence)
except OSError as e:
raise wrap_oserror(space, e,
- exception_name='w_IOError')
+ exception_name='w_IOError',
+ eintr_retry=False)
return space.wrap(pos)
def tell_w(self, space):
@@ -328,7 +333,8 @@
pos = os.lseek(self.fd, 0, 1)
except OSError as e:
raise wrap_oserror(space, e,
- exception_name='w_IOError')
+ exception_name='w_IOError',
+ eintr_retry=False)
return space.wrap(pos)
def readable_w(self, space):
@@ -361,7 +367,8 @@
try:
res = os.isatty(self.fd)
except OSError as e:
- raise wrap_oserror(space, e, exception_name='w_IOError')
+ raise wrap_oserror(space, e, exception_name='w_IOError',
+ eintr_retry=False)
return space.wrap(res)
def repr_w(self, space):
@@ -387,13 +394,16 @@
self._check_writable(space)
data = space.getarg_w('y*', w_data).as_str()
- try:
- n = os.write(self.fd, data)
- except OSError as e:
- if e.errno == errno.EAGAIN:
- return space.w_None
- raise wrap_oserror(space, e,
- exception_name='w_IOError')
+ while True:
+ try:
+ n = os.write(self.fd, data)
+ break
+ except OSError as e:
+ if e.errno == errno.EAGAIN:
+ return space.w_None
+ wrap_oserror(space, e,
+ exception_name='w_IOError',
+ eintr_retry=True)
return space.wrap(n)
@@ -405,13 +415,16 @@
if size < 0:
return self.readall_w(space)
- try:
- s = os.read(self.fd, size)
- except OSError as e:
- if e.errno == errno.EAGAIN:
- return space.w_None
- raise wrap_oserror(space, e,
- exception_name='w_IOError')
+ while True:
+ try:
+ s = os.read(self.fd, size)
+ break
+ except OSError as e:
+ if e.errno == errno.EAGAIN:
+ return space.w_None
+ wrap_oserror(space, e,
+ exception_name='w_IOError',
+ eintr_retry=True)
return space.newbytes(s)
@@ -420,13 +433,16 @@
self._check_readable(space)
rwbuffer = space.getarg_w('w*', w_buffer)
length = rwbuffer.getlength()
- try:
- buf = os.read(self.fd, length)
- except OSError as e:
- if e.errno == errno.EAGAIN:
- return space.w_None
- raise wrap_oserror(space, e,
- exception_name='w_IOError')
+ while True:
+ try:
+ buf = os.read(self.fd, length)
+ break
+ except OSError as e:
+ if e.errno == errno.EAGAIN:
+ return space.w_None
+ wrap_oserror(space, e,
+ exception_name='w_IOError',
+ eintr_retry=True)
rwbuffer.setslice(0, buf)
return space.wrap(len(buf))
@@ -442,17 +458,13 @@
try:
chunk = os.read(self.fd, newsize - total)
except OSError as e:
- if e.errno == errno.EINTR:
- space.getexecutioncontext().checksignals()
- continue
- if total > 0:
- # return what we've got so far
- break
if e.errno == errno.EAGAIN:
+ if total > 0:
+ break # return what we've got so far
return space.w_None
- raise wrap_oserror(space, e,
- exception_name='w_IOError')
-
+ wrap_oserror(space, e, exception_name='w_IOError',
+ eintr_retry=True)
+ continue
if not chunk:
break
builder.append(chunk)
@@ -476,7 +488,8 @@
try:
self._truncate(space.r_longlong_w(w_size))
except OSError as e:
- raise wrap_oserror(space, e, exception_name='w_IOError')
+ raise wrap_oserror(space, e, exception_name='w_IOError',
+ eintr_retry=False)
return w_size
diff --git a/pypy/module/_socket/interp_socket.py
b/pypy/module/_socket/interp_socket.py
--- a/pypy/module/_socket/interp_socket.py
+++ b/pypy/module/_socket/interp_socket.py
@@ -1,5 +1,6 @@
-import sys
+import sys, errno
from rpython.rlib import rsocket, rweaklist
+from rpython.rlib.objectmodel import specialize
from rpython.rlib.rarithmetic import intmask
from rpython.rlib.rsocket import (
RSocket, AF_INET, SOCK_STREAM, SocketError, SocketErrorWithErrno,
@@ -227,12 +228,13 @@
representing the connection, and the address of the client.
For IP sockets, the address info is a pair (hostaddr, port).
"""
- try:
- fd, addr = self.sock.accept(inheritable=False)
- return space.newtuple([space.wrap(fd),
- addr_as_object(addr, fd, space)])
- except SocketError as e:
- raise converted_error(space, e)
+ while True:
+ try:
+ fd, addr = self.sock.accept(inheritable=False)
+ return space.newtuple([space.wrap(fd),
+ addr_as_object(addr, fd, space)])
+ except SocketError as e:
+ converted_error(space, e, eintr_retry=True)
# convert an Address into an app-level object
def addr_as_object(self, space, address):
@@ -274,10 +276,12 @@
Connect the socket to a remote address. For IP sockets, the address
is a pair (host, port).
"""
- try:
- self.sock.connect(self.addr_from_object(space, w_addr))
- except SocketError as e:
- raise converted_error(space, e)
+ while True:
+ try:
+ self.sock.connect(self.addr_from_object(space, w_addr))
+ break
+ except SocketError as e:
+ converted_error(space, e, eintr_retry=True)
def connect_ex_w(self, space, w_addr):
"""connect_ex(address) -> errno
@@ -289,7 +293,11 @@
addr = self.addr_from_object(space, w_addr)
except SocketError as e:
raise converted_error(space, e)
- error = self.sock.connect_ex(addr)
+ while True:
+ error = self.sock.connect_ex(addr)
+ if error != errno.EINTR:
+ break
+ space.getexecutioncontext().checksignals()
return space.wrap(error)
def fileno_w(self, space):
@@ -384,10 +392,12 @@
at least one byte is available or until the remote end is closed. When
the remote end is closed and all data is read, return the empty string.
"""
- try:
- data = self.sock.recv(buffersize, flags)
- except SocketError as e:
- raise converted_error(space, e)
+ while True:
+ try:
+ data = self.sock.recv(buffersize, flags)
+ break
+ except SocketError as e:
+ converted_error(space, e, eintr_retry=True)
return space.newbytes(data)
@unwrap_spec(buffersize='nonnegint', flags=int)
@@ -396,15 +406,17 @@
Like recv(buffersize, flags) but also return the sender's address info.
"""
- try:
- data, addr = self.sock.recvfrom(buffersize, flags)
- if addr:
- w_addr = addr_as_object(addr, self.sock.fd, space)
- else:
- w_addr = space.w_None
- return space.newtuple([space.newbytes(data), w_addr])
- except SocketError as e:
- raise converted_error(space, e)
+ while True:
+ try:
+ data, addr = self.sock.recvfrom(buffersize, flags)
+ if addr:
+ w_addr = addr_as_object(addr, self.sock.fd, space)
+ else:
+ w_addr = space.w_None
+ break
+ except SocketError as e:
+ converted_error(space, e, eintr_retry=True)
+ return space.newtuple([space.newbytes(data), w_addr])
@unwrap_spec(data='bufferstr', flags=int)
def send_w(self, space, data, flags=0):
@@ -414,10 +426,12 @@
argument, see the Unix manual. Return the number of bytes
sent; this may be less than len(data) if the network is busy.
"""
- try:
- count = self.sock.send(data, flags)
- except SocketError as e:
- raise converted_error(space, e)
+ while True:
+ try:
+ count = self.sock.send(data, flags)
+ break
+ except SocketError as e:
+ converted_error(space, e, eintr_retry=True)
return space.wrap(count)
@unwrap_spec(data='bufferstr', flags=int)
@@ -450,11 +464,13 @@
# 3 args version
flags = space.int_w(w_param2)
w_addr = w_param3
- try:
- addr = self.addr_from_object(space, w_addr)
- count = self.sock.sendto(data, flags, addr)
- except SocketError as e:
- raise converted_error(space, e)
+ while True:
+ try:
+ addr = self.addr_from_object(space, w_addr)
+ count = self.sock.sendto(data, flags, addr)
+ break
+ except SocketError as e:
+ converted_error(space, e, eintr_retry=True)
return space.wrap(count)
@unwrap_spec(flag=int)
@@ -520,10 +536,13 @@
lgt = rwbuffer.getlength()
if nbytes == 0 or nbytes > lgt:
nbytes = lgt
- try:
- return space.wrap(self.sock.recvinto(rwbuffer, nbytes, flags))
- except SocketError as e:
- raise converted_error(space, e)
+ while True:
+ try:
+ nbytes_read = self.sock.recvinto(rwbuffer, nbytes, flags)
+ break
+ except SocketError as e:
+ converted_error(space, e, eintr_retry=True)
+ return space.wrap(nbytes_read)
@unwrap_spec(nbytes=int, flags=int)
def recvfrom_into_w(self, space, w_buffer, nbytes=0, flags=0):
@@ -538,15 +557,20 @@
elif nbytes > lgt:
raise oefmt(space.w_ValueError,
"nbytes is greater than the length of the buffer")
- try:
- readlgt, addr = self.sock.recvfrom_into(rwbuffer, nbytes, flags)
- if addr:
+ while True:
+ try:
+ readlgt, addr = self.sock.recvfrom_into(rwbuffer, nbytes,
flags)
+ break
+ except SocketError as e:
+ converted_error(space, e, eintr_retry=True)
+ if addr:
+ try:
w_addr = addr_as_object(addr, self.sock.fd, space)
- else:
- w_addr = space.w_None
- return space.newtuple([space.wrap(readlgt), w_addr])
- except SocketError as e:
- raise converted_error(space, e)
+ except SocketError as e:
+ raise converted_error(space, e)
+ else:
+ w_addr = space.w_None
+ return space.newtuple([space.wrap(readlgt), w_addr])
@unwrap_spec(cmd=int)
def ioctl_w(self, space, cmd, w_option):
@@ -690,15 +714,20 @@
def get_error(space, name):
return space.fromcache(SocketAPI).get_exception(name)
-def converted_error(space, e):
[email protected](2)
+def converted_error(space, e, eintr_retry=False):
message = e.get_msg()
w_exception_class = get_error(space, e.applevelerrcls)
if isinstance(e, SocketErrorWithErrno):
+ if e.errno == errno.EINTR:
+ space.getexecutioncontext().checksignals()
+ if eintr_retry:
+ return # only return None if eintr_retry==True
w_exception = space.call_function(w_exception_class,
space.wrap(e.errno),
space.wrap(message))
else:
w_exception = space.call_function(w_exception_class,
space.wrap(message))
- return OperationError(w_exception_class, w_exception)
+ raise OperationError(w_exception_class, w_exception)
# ____________________________________________________________
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
@@ -226,15 +226,21 @@
If it is unavailable, using it will raise a NotImplementedError."""
if rposix.O_CLOEXEC is not None:
flags |= rposix.O_CLOEXEC
+ while True:
+ try:
+ if rposix.HAVE_OPENAT and dir_fd != DEFAULT_DIR_FD:
+ path = space.fsencode_w(w_path)
+ fd = rposix.openat(path, flags, mode, dir_fd)
+ else:
+ fd = dispatch_filename(rposix.open)(space, w_path, flags, mode)
+ break
+ except OSError as e:
+ wrap_oserror2(space, e, w_path, eintr_retry=True)
try:
- if rposix.HAVE_OPENAT and dir_fd != DEFAULT_DIR_FD:
- path = space.fsencode_w(w_path)
- fd = rposix.openat(path, flags, mode, dir_fd)
- else:
- fd = dispatch_filename(rposix.open)(space, w_path, flags, mode)
_open_inhcache.set_non_inheritable(fd)
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ rposix.c_close(fd)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
return space.wrap(fd)
@unwrap_spec(fd=c_int, position=r_longlong, how=c_int)
@@ -245,7 +251,7 @@
try:
pos = os.lseek(fd, position, how)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
else:
return space.wrap(pos)
@@ -256,39 +262,45 @@
try:
res = os.isatty(fd)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
else:
return space.wrap(res)
@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):
"""Write a string to a file descriptor. Return the number of bytes
actually written, which may be smaller than len(data)."""
data = space.getarg_w('y*', w_data)
- try:
- res = os.write(fd, data.as_str())
- except OSError as e:
- raise wrap_oserror(space, e)
- else:
- return space.wrap(res)
+ while True:
+ try:
+ res = os.write(fd, data.as_str())
+ except OSError as e:
+ wrap_oserror(space, e, eintr_retry=True)
+ else:
+ return space.wrap(res)
@unwrap_spec(fd=c_int)
def close(space, fd):
"""Close a file descriptor (for low level IO)."""
+ # PEP 475 note: os.close() must not retry upon EINTR. Like in
+ # previous versions of Python it raises OSError in this case.
+ # The text of PEP 475 seems to suggest that EINTR is eaten and
+ # hidden from app-level, but it is not the case in CPython 3.5.2.
try:
os.close(fd)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@unwrap_spec(fd_low=c_int, fd_high=c_int)
def closerange(fd_low, fd_high):
@@ -298,10 +310,12 @@
@unwrap_spec(fd=c_int, length=r_longlong)
def ftruncate(space, fd, length):
"""Truncate a file (by file descriptor) to a specified length."""
- try:
- os.ftruncate(fd, length)
- except OSError as e:
- raise wrap_oserror(space, e)
+ while True:
+ try:
+ os.ftruncate(fd, length)
+ break
+ except OSError as e:
+ wrap_oserror(space, e, eintr_retry=True)
def truncate(space, w_path, w_length):
"""Truncate a file to a specified length."""
@@ -325,19 +339,23 @@
def fsync(space, w_fd):
"""Force write of file with filedescriptor to disk."""
fd = space.c_filedescriptor_w(w_fd)
- try:
- os.fsync(fd)
- except OSError as e:
- raise wrap_oserror(space, e)
+ while True:
+ try:
+ os.fsync(fd)
+ break
+ except OSError as e:
+ wrap_oserror(space, e, eintr_retry=True)
def fdatasync(space, w_fd):
"""Force write of file with filedescriptor to disk.
Does not force update of metadata."""
fd = space.c_filedescriptor_w(w_fd)
- try:
- os.fdatasync(fd)
- except OSError as e:
- raise wrap_oserror(space, e)
+ while True:
+ try:
+ os.fdatasync(fd)
+ break
+ except OSError as e:
+ wrap_oserror(space, e, eintr_retry=True)
def sync(space):
"""Force write of everything to disk."""
@@ -347,10 +365,12 @@
"""Change to the directory of the given file descriptor. fildes must be
opened on a directory, not a file."""
fd = space.c_filedescriptor_w(w_fd)
- try:
- os.fchdir(fd)
- except OSError as e:
- raise wrap_oserror(space, e)
+ while True:
+ try:
+ os.fchdir(fd)
+ break
+ except OSError as e:
+ wrap_oserror(space, e, eintr_retry=True)
# ____________________________________________________________
@@ -415,12 +435,13 @@
def fstat(space, fd):
"""Perform a stat system call on the file referenced to by an open
file descriptor."""
- try:
- st = rposix_stat.fstat(fd)
- except OSError as e:
- raise wrap_oserror(space, e)
- else:
- return build_stat_result(space, st)
+ while True:
+ try:
+ st = rposix_stat.fstat(fd)
+ except OSError as e:
+ wrap_oserror(space, e, eintr_retry=True)
+ else:
+ return build_stat_result(space, st)
@unwrap_spec(
path=path_or_fd(allow_fd=True),
@@ -466,7 +487,7 @@
raise oefmt(space.w_NotImplementedError,
"%s: unsupported argument combination", funcname)
except OSError as e:
- raise wrap_oserror2(space, e, path.w_path)
+ raise wrap_oserror2(space, e, path.w_path, eintr_retry=False)
else:
return build_stat_result(space, st)
@@ -503,12 +524,13 @@
@unwrap_spec(fd=c_int)
def fstatvfs(space, fd):
- try:
- st = rposix_stat.fstatvfs(fd)
- except OSError as e:
- raise wrap_oserror(space, e)
- else:
- return build_statvfs_result(space, st)
+ while True:
+ try:
+ st = rposix_stat.fstatvfs(fd)
+ except OSError as e:
+ wrap_oserror(space, e, eintr_retry=True)
+ else:
+ return build_statvfs_result(space, st)
def statvfs(space, w_path):
@@ -524,7 +546,7 @@
rposix_stat.statvfs,
allow_fd_fn=rposix_stat.fstatvfs)(space, w_path)
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
else:
return build_statvfs_result(space, st)
@@ -536,17 +558,19 @@
try:
newfd = rposix.dup(fd, inheritable=False)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
else:
return space.wrap(newfd)
@unwrap_spec(fd=c_int, fd2=c_int, inheritable=bool)
def dup2(space, fd, fd2, inheritable=1):
"""Duplicate a file descriptor."""
+ # like os.close(), this can still raise EINTR to app-level in
+ # CPython 3.5.2
try:
rposix.dup2(fd, fd2, inheritable)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@unwrap_spec(mode=c_int,
dir_fd=DirFD(rposix.HAVE_FACCESSAT), effective_ids=bool,
@@ -591,7 +615,7 @@
else:
ok = dispatch_filename(rposix.access)(space, w_path, mode)
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
else:
return space.wrap(ok)
@@ -605,7 +629,7 @@
try:
times = os.times()
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
else:
return space.newtuple([space.wrap(times[0]),
space.wrap(times[1]),
@@ -619,7 +643,7 @@
try:
rc = os.system(command)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
else:
return space.wrap(rc)
@@ -640,7 +664,7 @@
else:
dispatch_filename(rposix.unlink)(space, w_path)
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
@unwrap_spec(dir_fd=DirFD(rposix.HAVE_UNLINKAT))
def remove(space, w_path, __kwonly__, dir_fd=DEFAULT_DIR_FD):
@@ -659,7 +683,7 @@
else:
dispatch_filename(rposix.unlink)(space, w_path)
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
def _getfullpathname(space, w_path):
"""helper for ntpath.abspath """
@@ -673,7 +697,7 @@
fullpath = rposix.getfullpathname(path)
w_fullpath = space.newbytes(fullpath)
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
else:
return w_fullpath
@@ -682,7 +706,7 @@
try:
cur = os.getcwd()
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
else:
return space.newbytes(cur)
@@ -692,7 +716,7 @@
try:
cur = os.getcwdu()
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
else:
return space.wrap(cur)
else:
@@ -709,7 +733,7 @@
else:
dispatch_filename(rposix.chdir)(space, w_path)
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
@unwrap_spec(mode=c_int, dir_fd=DirFD(rposix.HAVE_MKDIRAT))
def mkdir(space, w_path, mode=0o777, __kwonly__=None, dir_fd=DEFAULT_DIR_FD):
@@ -730,7 +754,7 @@
else:
dispatch_filename(rposix.mkdir)(space, w_path, mode)
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
@unwrap_spec(dir_fd=DirFD(rposix.HAVE_UNLINKAT))
def rmdir(space, w_path, __kwonly__, dir_fd=DEFAULT_DIR_FD):
@@ -749,7 +773,7 @@
else:
dispatch_filename(rposix.rmdir)(space, w_path)
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
@unwrap_spec(code=c_int)
def strerror(space, code):
@@ -764,7 +788,7 @@
try:
cur = os.getlogin()
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.wrap_fsdecoded(cur)
# ____________________________________________________________
@@ -817,7 +841,7 @@
try:
rwin32._wputenv(name, value)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
else:
def _convertenviron(space, w_env):
for key, value in os.environ.items():
@@ -828,7 +852,7 @@
try:
dispatch_filename_2(rposix.putenv)(space, w_name, w_value)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
def unsetenv(space, w_name):
"""Delete an environment variable."""
@@ -837,7 +861,7 @@
except KeyError:
pass
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
def listdir(space, w_path=None):
@@ -860,7 +884,7 @@
try:
result = rposix.listdir(dirname)
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
return space.newlist_bytes(result)
try:
path = space.fsencode_w(w_path)
@@ -874,13 +898,13 @@
try:
result = rposix.fdlistdir(os.dup(fd))
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
else:
dirname = FileEncoder(space, w_path)
try:
result = rposix.listdir(dirname)
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
len_result = len(result)
result_w = [None] * len_result
for i in range(len_result):
@@ -895,14 +919,14 @@
try:
return space.wrap(rposix.get_inheritable(fd))
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@unwrap_spec(fd=c_int, inheritable=int)
def set_inheritable(space, fd, inheritable):
try:
rposix.set_inheritable(fd, inheritable)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
_pipe_inhcache = rposix.SetNonInheritableCache()
@@ -910,10 +934,15 @@
"Create a pipe. Returns (read_end, write_end)."
try:
fd1, fd2 = rposix.pipe(rposix.O_CLOEXEC or 0)
+ except OSError as e:
+ raise wrap_oserror(space, e, eintr_retry=False)
+ try:
_pipe_inhcache.set_non_inheritable(fd1)
_pipe_inhcache.set_non_inheritable(fd2)
except OSError as e:
- raise wrap_oserror(space, e)
+ rposix.c_close(fd2)
+ rposix.c_close(fd1)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.newtuple([space.wrap(fd1), space.wrap(fd2)])
@unwrap_spec(flags=c_int)
@@ -921,7 +950,7 @@
try:
fd1, fd2 = rposix.pipe2(flags)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.newtuple([space.wrap(fd1), space.wrap(fd2)])
@unwrap_spec(mode=c_int, dir_fd=DirFD(rposix.HAVE_FCHMODAT),
@@ -951,7 +980,7 @@
dispatch_filename(rposix.chmod)(space, w_path, mode)
return
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
try:
path = space.fsencode_w(w_path)
@@ -960,7 +989,12 @@
raise oefmt(space.w_TypeError,
"argument should be string, bytes or integer, not %T", w_path)
fd = unwrap_fd(space, w_path)
- _chmod_fd(space, fd, mode)
+ # NB. CPython 3.5.2: unclear why os.chmod(fd) propagates EINTR
+ # to app-level, but os.fchmod(fd) retries automatically
+ try:
+ os.fchmod(fd, mode)
+ except OSError as e:
+ raise wrap_oserror(space, e, eintr_retry=False)
else:
try:
_chmod_path(path, mode, dir_fd, follow_symlinks)
@@ -969,7 +1003,7 @@
# fchmodat() doesn't actually implement follow_symlinks=False
# so raise NotImplementedError in this case
raise argument_unavailable(space, "chmod", "follow_symlinks")
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
def _chmod_path(path, mode, dir_fd, follow_symlinks):
if dir_fd != DEFAULT_DIR_FD or not follow_symlinks:
@@ -977,19 +1011,19 @@
else:
rposix.chmod(path, mode)
-def _chmod_fd(space, fd, mode):
- try:
- os.fchmod(fd, mode)
- except OSError as e:
- raise wrap_oserror(space, e)
-
-
@unwrap_spec(fd=c_int, mode=c_int)
def fchmod(space, fd, mode):
"""\
Change the access permissions of the file given by file descriptor fd.
"""
- _chmod_fd(space, fd, mode)
+ # NB. CPython 3.5.2: unclear why os.chmod(fd) propagates EINTR
+ # to app-level, but os.fchmod(fd) retries automatically
+ while True:
+ try:
+ os.fchmod(fd, mode)
+ break
+ except OSError as e:
+ wrap_oserror(space, e, eintr_retry=True)
@unwrap_spec(src_dir_fd=DirFD(rposix.HAVE_RENAMEAT),
dst_dir_fd=DirFD(rposix.HAVE_RENAMEAT))
@@ -1013,7 +1047,8 @@
else:
dispatch_filename_2(rposix.rename)(space, w_src, w_dst)
except OSError as e:
- raise wrap_oserror2(space, e, w_filename=w_src, w_filename2=w_dst)
+ raise wrap_oserror2(space, e, w_filename=w_src, w_filename2=w_dst,
+ eintr_retry=False)
@unwrap_spec(src_dir_fd=DirFD(rposix.HAVE_RENAMEAT),
dst_dir_fd=DirFD(rposix.HAVE_RENAMEAT))
@@ -1037,7 +1072,8 @@
else:
dispatch_filename_2(rposix.replace)(space, w_src, w_dst)
except OSError as e:
- raise wrap_oserror2(space, e, w_filename=w_src, w_filename2=w_dst)
+ raise wrap_oserror2(space, e, w_filename=w_src, w_filename2=w_dst,
+ eintr_retry=False)
@unwrap_spec(mode=c_int, dir_fd=DirFD(rposix.HAVE_MKFIFOAT))
def mkfifo(space, w_path, mode=0666, __kwonly__=None, dir_fd=DEFAULT_DIR_FD):
@@ -1049,14 +1085,18 @@
and path should be relative; path will then be relative to that directory.
dir_fd may not be implemented on your platform.
If it is unavailable, using it will raise a NotImplementedError."""
- try:
- if rposix.HAVE_MKFIFOAT and dir_fd != DEFAULT_DIR_FD:
- path = space.fsencode_w(w_path)
- rposix.mkfifoat(path, mode, dir_fd)
- else:
- dispatch_filename(rposix.mkfifo)(space, w_path, mode)
- except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ # CPython 3.5.2: why does os.mkfifo() retry automatically if it
+ # gets EINTR, but not os.mkdir()?
+ while True:
+ try:
+ if rposix.HAVE_MKFIFOAT and dir_fd != DEFAULT_DIR_FD:
+ path = space.fsencode_w(w_path)
+ rposix.mkfifoat(path, mode, dir_fd)
+ else:
+ dispatch_filename(rposix.mkfifo)(space, w_path, mode)
+ break
+ except OSError as e:
+ wrap_oserror2(space, e, w_path, eintr_retry=True)
@unwrap_spec(mode=c_int, device=c_int, dir_fd=DirFD(rposix.HAVE_MKNODAT))
def mknod(space, w_path, mode=0600, device=0,
@@ -1074,14 +1114,16 @@
and path should be relative; path will then be relative to that directory.
dir_fd may not be implemented on your platform.
If it is unavailable, using it will raise a NotImplementedError."""
- try:
- if rposix.HAVE_MKNODAT and dir_fd != DEFAULT_DIR_FD:
- fname = space.fsencode_w(w_path)
- rposix.mknodat(fname, mode, device, dir_fd)
- else:
- dispatch_filename(rposix.mknod)(space, w_path, mode, device)
- except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ while True:
+ try:
+ if rposix.HAVE_MKNODAT and dir_fd != DEFAULT_DIR_FD:
+ fname = space.fsencode_w(w_path)
+ rposix.mknodat(fname, mode, device, dir_fd)
+ else:
+ dispatch_filename(rposix.mknod)(space, w_path, mode, device)
+ break
+ except OSError as e:
+ wrap_oserror2(space, e, w_path, eintr_retry=True)
@unwrap_spec(mask=c_int)
def umask(space, mask):
@@ -1094,7 +1136,7 @@
try:
pid = os.getpid()
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.wrap(pid)
@unwrap_spec(pid=c_int, signal=c_int)
@@ -1103,7 +1145,7 @@
try:
rposix.kill(pid, signal)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@unwrap_spec(pgid=c_int, signal=c_int)
def killpg(space, pgid, signal):
@@ -1111,7 +1153,7 @@
try:
os.killpg(pgid, signal)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
def abort(space):
"""Abort the interpreter immediately. This 'dumps core' or otherwise fails
@@ -1149,7 +1191,8 @@
else:
rposix.link(src, dst)
except OSError as e:
- raise wrap_oserror(space, e, filename=src, filename2=dst)
+ raise wrap_oserror(space, e, filename=src, filename2=dst,
+ eintr_retry=False)
@unwrap_spec(dir_fd=DirFD(rposix.HAVE_SYMLINKAT))
@@ -1176,7 +1219,8 @@
else:
dispatch_filename_2(rposix.symlink)(space, w_src, w_dst)
except OSError as e:
- raise wrap_oserror2(space, e, w_filename=w_src, w_filename2=w_dst)
+ raise wrap_oserror2(space, e, w_filename=w_src, w_filename2=w_dst,
+ eintr_retry=False)
@unwrap_spec(
@@ -1197,7 +1241,7 @@
else:
result = call_rposix(rposix.readlink, path)
except OSError as e:
- raise wrap_oserror2(space, e, path.w_path)
+ raise wrap_oserror2(space, e, path.w_path, eintr_retry=False)
w_result = space.newbytes(result)
if space.isinstance_w(path.w_path, space.w_unicode):
return space.fsdecode(w_result)
@@ -1245,7 +1289,7 @@
except:
# Don't clobber the OSError if the fork failed
pass
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
if pid == 0:
run_fork_hooks('child', space)
else:
@@ -1258,12 +1302,17 @@
def openpty(space):
"Open a pseudo-terminal, returning open fd's for both master and slave
end."
+ master_fd = slave_fd = -1
try:
master_fd, slave_fd = os.openpty()
rposix.set_inheritable(master_fd, False)
rposix.set_inheritable(slave_fd, False)
except OSError as e:
- raise wrap_oserror(space, e)
+ if master_fd >= 0:
+ rposix.c_close(master_fd)
+ if slave_fd >= 0:
+ rposix.c_close(slave_fd)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.newtuple([space.wrap(master_fd), space.wrap(slave_fd)])
def forkpty(space):
@@ -1277,12 +1326,16 @@
Wait for completion of a given child process.
"""
- try:
- pid, status = os.waitpid(pid, options)
- except OSError as e:
- raise wrap_oserror(space, e)
+ while True:
+ try:
+ pid, status = os.waitpid(pid, options)
+ break
+ except OSError as e:
+ wrap_oserror(space, e, eintr_retry=True)
return space.newtuple([space.wrap(pid), space.wrap(status)])
+# missing: waitid()
+
@unwrap_spec(status=c_int)
def _exit(space, status):
os._exit(status)
@@ -1310,7 +1363,7 @@
try:
os.execv(command, args)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
def _env2interp(space, w_env):
@@ -1355,12 +1408,12 @@
try:
rposix.fexecve(fd, args, env)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
else:
try:
os.execve(path, args, env)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@unwrap_spec(mode=int, path='fsencode')
def spawnv(space, mode, path, w_argv):
@@ -1368,7 +1421,7 @@
try:
ret = os.spawnv(mode, path, args)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.wrap(ret)
@unwrap_spec(mode=int, path='fsencode')
@@ -1378,7 +1431,7 @@
try:
ret = os.spawnve(mode, path, args, env)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.wrap(ret)
@@ -1486,7 +1539,7 @@
# something is wrong with the file, when it also
# could be the time stamp that gives a problem. */
# so we use wrap_oserror() instead of wrap_oserror2() here
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@specialize.arg(1)
def do_utimes(space, func, arg, utime):
@@ -1503,7 +1556,7 @@
func(arg, (atime, mtime))
except OSError as e:
# see comment above: don't use wrap_oserror2()
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@specialize.argtype(1)
def _dispatch_utime(path, times):
@@ -1546,7 +1599,7 @@
try:
r = os.uname()
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
l_w = [space.wrap_fsdecoded(i)
for i in [r[0], r[1], r[2], r[3], r[4]]]
w_tuple = space.newtuple(l_w)
@@ -1570,7 +1623,7 @@
try:
os.setuid(uid)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@unwrap_spec(euid=c_uid_t)
def seteuid(space, euid):
@@ -1581,7 +1634,7 @@
try:
os.seteuid(euid)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@unwrap_spec(gid=c_gid_t)
def setgid(space, gid):
@@ -1592,7 +1645,7 @@
try:
os.setgid(gid)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@unwrap_spec(egid=c_gid_t)
def setegid(space, egid):
@@ -1603,7 +1656,7 @@
try:
os.setegid(egid)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@unwrap_spec(path='fsencode')
def chroot(space, path):
@@ -1614,7 +1667,7 @@
try:
os.chroot(path)
except OSError as e:
- raise wrap_oserror(space, e, path)
+ raise wrap_oserror(space, e, path, eintr_retry=False)
return space.w_None
def getgid(space):
@@ -1646,7 +1699,7 @@
try:
list = os.getgroups()
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.newlist([wrap_gid(space, e) for e in list])
def setgroups(space, w_groups):
@@ -1660,7 +1713,7 @@
try:
os.setgroups(list[:])
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@unwrap_spec(username=str, gid=c_gid_t)
def initgroups(space, username, gid):
@@ -1673,7 +1726,7 @@
try:
os.initgroups(username, gid)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
def getpgrp(space):
""" getpgrp() -> pgrp
@@ -1690,7 +1743,7 @@
try:
os.setpgrp()
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.w_None
def getppid(space):
@@ -1709,7 +1762,7 @@
try:
pgid = os.getpgid(pid)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.wrap(pgid)
@unwrap_spec(pid=c_int, pgrp=c_int)
@@ -1721,7 +1774,7 @@
try:
os.setpgid(pid, pgrp)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.w_None
@unwrap_spec(ruid=c_uid_t, euid=c_uid_t)
@@ -1733,7 +1786,7 @@
try:
os.setreuid(ruid, euid)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@unwrap_spec(rgid=c_gid_t, egid=c_gid_t)
def setregid(space, rgid, egid):
@@ -1744,7 +1797,7 @@
try:
os.setregid(rgid, egid)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@unwrap_spec(pid=c_int)
def getsid(space, pid):
@@ -1755,7 +1808,7 @@
try:
sid = os.getsid(pid)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.wrap(sid)
def setsid(space):
@@ -1766,7 +1819,7 @@
try:
os.setsid()
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.w_None
@unwrap_spec(fd=c_int)
@@ -1778,7 +1831,7 @@
try:
pgid = os.tcgetpgrp(fd)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.wrap(pgid)
@unwrap_spec(fd=c_int, pgid=c_gid_t)
@@ -1790,7 +1843,7 @@
try:
os.tcsetpgrp(fd, pgid)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
def getresuid(space):
""" getresuid() -> (ruid, euid, suid)
@@ -1800,7 +1853,7 @@
try:
(ruid, euid, suid) = os.getresuid()
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.newtuple([wrap_uid(space, ruid),
wrap_uid(space, euid),
wrap_uid(space, suid)])
@@ -1813,7 +1866,7 @@
try:
(rgid, egid, sgid) = os.getresgid()
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.newtuple([wrap_gid(space, rgid),
wrap_gid(space, egid),
wrap_gid(space, sgid)])
@@ -1827,7 +1880,7 @@
try:
os.setresuid(ruid, euid, suid)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
@unwrap_spec(rgid=c_gid_t, egid=c_gid_t, sgid=c_gid_t)
def setresgid(space, rgid, egid, sgid):
@@ -1838,7 +1891,7 @@
try:
os.setresgid(rgid, egid, sgid)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
def declare_new_w_star(name):
if name in ('WEXITSTATUS', 'WSTOPSIG', 'WTERMSIG'):
@@ -1864,7 +1917,7 @@
try:
return space.wrap_fsdecoded(os.ttyname(fd))
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
def confname_w(space, w_name, namespace):
@@ -1883,7 +1936,7 @@
try:
res = os.sysconf(num)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.wrap(res)
@unwrap_spec(fd=c_int)
@@ -1892,7 +1945,7 @@
try:
res = os.fpathconf(fd, num)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.wrap(res)
@unwrap_spec(path=path_or_fd(allow_fd=hasattr(os, 'fpathconf')))
@@ -1902,12 +1955,12 @@
try:
res = os.fpathconf(path.as_fd, num)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
else:
try:
res = os.pathconf(path.as_bytes, num)
except OSError as e:
- raise wrap_oserror2(space, e, path.w_path)
+ raise wrap_oserror2(space, e, path.w_path, eintr_retry=False)
return space.wrap(res)
def confstr(space, w_name):
@@ -1915,7 +1968,7 @@
try:
res = os.confstr(num)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.wrap(res)
@unwrap_spec(
@@ -1959,7 +2012,7 @@
try:
os.fchown(fd, uid, gid)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
else:
# String case
try:
@@ -1974,7 +2027,7 @@
assert dir_fd == DEFAULT_DIR_FD
os.chown(path, uid, gid)
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
@unwrap_spec(path='fsencode', uid=c_uid_t, gid=c_gid_t)
@@ -1987,7 +2040,7 @@
try:
os.lchown(path, uid, gid)
except OSError as e:
- raise wrap_oserror(space, e, path)
+ raise wrap_oserror(space, e, path, eintr_retry=False)
@unwrap_spec(uid=c_uid_t, gid=c_gid_t)
def fchown(space, w_fd, uid, gid):
@@ -1995,11 +2048,14 @@
Change the owner and group id of the file given by file descriptor
fd to the numeric uid and gid. Equivalent to os.chown(fd, uid, gid)."""
+ # same comment than about os.chmod(fd) vs. os.fchmod(fd)
fd = space.c_filedescriptor_w(w_fd)
- try:
- os.fchown(fd, uid, gid)
- except OSError as e:
- raise wrap_oserror(space, e)
+ while True:
+ try:
+ os.fchown(fd, uid, gid)
+ break
+ except OSError as e:
+ wrap_oserror(space, e, eintr_retry=True)
def getloadavg(space):
try:
@@ -2032,7 +2088,7 @@
try:
res = os.nice(increment)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.wrap(res)
@unwrap_spec(size=int)
@@ -2045,7 +2101,9 @@
try:
return space.newbytes(rurandom.urandom(context, size))
except OSError as e:
- raise wrap_oserror(space, e)
+ # 'rurandom' should catch and retry internally if it gets EINTR
+ # (at least in os.read(), which is probably enough in practice)
+ raise wrap_oserror(space, e, eintr_retry=False)
def ctermid(space):
"""ctermid() -> string
@@ -2083,7 +2141,7 @@
try:
info = nt._getfileinformation(fd)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.newtuple([space.wrap(info[0]),
space.wrap(info[1]),
space.wrap(info[2])])
@@ -2096,7 +2154,7 @@
raise OperationError(space.w_NotImplementedError,
space.wrap(e.msg))
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
return space.wrap(result)
@@ -2228,7 +2286,7 @@
try:
flags = rposix.get_status_flags(fd)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
return space.newbool(flags & rposix.O_NONBLOCK == 0)
@unwrap_spec(fd=c_int, blocking=int)
@@ -2241,4 +2299,4 @@
flags |= rposix.O_NONBLOCK
rposix.set_status_flags(fd, flags)
except OSError as e:
- raise wrap_oserror(space, e)
+ raise wrap_oserror(space, e, eintr_retry=False)
diff --git a/pypy/module/posix/interp_scandir.py
b/pypy/module/posix/interp_scandir.py
--- a/pypy/module/posix/interp_scandir.py
+++ b/pypy/module/posix/interp_scandir.py
@@ -28,7 +28,7 @@
try:
dirp = rposix_scandir.opendir(path_bytes)
except OSError as e:
- raise wrap_oserror2(space, e, w_path)
+ raise wrap_oserror2(space, e, w_path, eintr_retry=False)
path_prefix = path_bytes
if len(path_prefix) > 0 and path_prefix[-1] != '/':
path_prefix += '/'
@@ -85,7 +85,8 @@
try:
entry = rposix_scandir.nextentry(self.dirp)
except OSError as e:
- raise self.fail(wrap_oserror2(space, e,
self.w_path_prefix))
+ raise self.fail(wrap_oserror2(space, e, self.w_path_prefix,
+ eintr_retry=False))
if not entry:
raise self.fail()
assert rposix_scandir.has_name_bytes(entry)
@@ -235,7 +236,8 @@
except OSError as e:
if e.errno == ENOENT: # not found
return -1
- raise wrap_oserror2(self.space, e, self.fget_path(self.space))
+ raise wrap_oserror2(self.space, e, self.fget_path(self.space),
+ eintr_retry=False)
return stat.S_IFMT(st.st_mode)
def is_dir(self, follow_symlinks):
@@ -287,7 +289,8 @@
try:
st = self.get_stat_or_lstat(follow_symlinks)
except OSError as e:
- raise wrap_oserror2(space, e, self.fget_path(space))
+ raise wrap_oserror2(space, e, self.fget_path(space),
+ eintr_retry=False)
return build_stat_result(space, st)
def descr_inode(self, space):
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')
diff --git a/pypy/module/select/interp_epoll.py
b/pypy/module/select/interp_epoll.py
--- a/pypy/module/select/interp_epoll.py
+++ b/pypy/module/select/interp_epoll.py
@@ -7,6 +7,7 @@
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.error import exception_from_saved_errno
from pypy.interpreter.typedef import TypeDef, GetSetProperty
+from pypy.interpreter import timeutils
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rtyper.tool import rffi_platform
from rpython.rlib._rsocket_rffi import socketclose, FD_SETSIZE
@@ -156,9 +157,11 @@
def descr_poll(self, space, timeout=-1.0, maxevents=-1):
self.check_closed(space)
if timeout < 0:
- timeout = -1.0
+ end_time = 0.0
+ itimeout = -1
else:
- timeout *= 1000.0
+ end_time = timeutils.monotonic(space) + timeout
+ itimeout = int(timeout * 1000.0 + 0.999)
if maxevents == -1:
maxevents = FD_SETSIZE - 1
@@ -167,9 +170,18 @@
"maxevents must be greater than 0, not %d", maxevents)
with lltype.scoped_alloc(rffi.CArray(epoll_event), maxevents) as evs:
- nfds = epoll_wait(self.epfd, evs, maxevents, int(timeout))
- if nfds < 0:
- raise exception_from_saved_errno(space, space.w_IOError)
+ while True:
+ nfds = epoll_wait(self.epfd, evs, maxevents, itimeout)
+ if nfds < 0:
+ if get_saved_errno() == errno.EINTR:
+ space.getexecutioncontext().checksignals()
+ if itimeout >= 0:
+ timeout = end_time - timeutils.monotonic(space)
+ timeout = max(timeout, 0.0)
+ itimeout = int(timeout * 1000.0 + 0.999)
+ continue
+ raise exception_from_saved_errno(space, space.w_IOError)
+ break
elist_w = [None] * nfds
for i in xrange(nfds):
diff --git a/pypy/module/select/interp_kqueue.py
b/pypy/module/select/interp_kqueue.py
--- a/pypy/module/select/interp_kqueue.py
+++ b/pypy/module/select/interp_kqueue.py
@@ -180,6 +180,7 @@
raise oefmt(space.w_ValueError,
"Timeout must be None or >= 0, got %s",
str(_timeout))
+ XXX # fix test_select_signal.py first, for PEP475!
sec = int(_timeout)
nsec = int(1e9 * (_timeout - sec))
rffi.setintfield(timeout, 'c_tv_sec', sec)
diff --git a/pypy/module/select/interp_select.py
b/pypy/module/select/interp_select.py
--- a/pypy/module/select/interp_select.py
+++ b/pypy/module/select/interp_select.py
@@ -10,6 +10,7 @@
from pypy.interpreter.gateway import (
Unwrapper, WrappedDefault, interp2app, unwrap_spec)
from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter import timeutils
defaultevents = rpoll.POLLIN | rpoll.POLLOUT | rpoll.POLLPRI
@@ -49,8 +50,10 @@
@unwrap_spec(w_timeout=WrappedDefault(None))
def poll(self, space, w_timeout):
+ """WARNING: the timeout parameter is in **milliseconds**!"""
if space.is_w(w_timeout, space.w_None):
timeout = -1
+ end_time = 0
else:
# we want to be compatible with cpython and also accept things
# that can be casted to integer (I think)
@@ -61,19 +64,29 @@
raise oefmt(space.w_TypeError,
"timeout must be an integer or None")
timeout = space.c_int_w(w_timeout)
+ end_time = timeutils.monotonic(space) + timeout * 0.001
if self.running:
raise oefmt(space.w_RuntimeError, "concurrent poll() invocation")
- self.running = True
- try:
- retval = rpoll.poll(self.fddict, timeout)
- except rpoll.PollError as e:
- message = e.get_msg()
- raise OperationError(space.w_OSError,
- space.newtuple([space.wrap(e.errno),
- space.wrap(message)]))
- finally:
- self.running = False
+ while True:
+ self.running = True
+ try:
+ retval = rpoll.poll(self.fddict, timeout)
+ except rpoll.PollError as e:
+ if e.errno == errno.EINTR:
+ space.getexecutioncontext().checksignals()
+ timeout = int((end_time - timeutils.monotonic(space))
+ * 1000.0 + 0.999) # round up
+ if timeout < 0:
+ timeout = 0
+ continue
+ message = e.get_msg()
+ raise OperationError(space.w_OSError,
+ space.newtuple([space.wrap(e.errno),
+ space.wrap(message)]))
+ finally:
+ self.running = False
+ break
retval_w = []
for fd, revents in retval:
@@ -112,7 +125,7 @@
def _call_select(space, iwtd_w, owtd_w, ewtd_w,
- ll_inl, ll_outl, ll_errl, ll_timeval):
+ ll_inl, ll_outl, ll_errl, ll_timeval, timeout):
fdlistin = fdlistout = fdlisterr = None
nfds = -1
if ll_inl:
@@ -122,13 +135,32 @@
if ll_errl:
fdlisterr, nfds = _build_fd_set(space, ewtd_w, ll_errl, nfds)
- res = _c.select(nfds + 1, ll_inl, ll_outl, ll_errl, ll_timeval)
+ if ll_timeval:
+ end_time = timeutils.monotonic(space) + timeout
+ else:
+ end_time = 0.0
- if res < 0:
- errno = _c.geterrno()
- msg = _c.socket_strerror_str(errno)
- raise OperationError(space.w_OSError, space.newtuple([
- space.wrap(errno), space.wrap(msg)]))
+ while True:
+ if ll_timeval:
+ i = int(timeout)
+ rffi.setintfield(ll_timeval, 'c_tv_sec', i)
+ rffi.setintfield(ll_timeval, 'c_tv_usec', int((timeout-i)*1000000))
+
+ res = _c.select(nfds + 1, ll_inl, ll_outl, ll_errl, ll_timeval)
+
+ if res >= 0:
+ break # normal path
+ err = _c.geterrno()
+ if err != errno.EINTR:
+ msg = _c.socket_strerror_str(err)
+ raise OperationError(space.w_OSError, space.newtuple([
+ space.wrap(err), space.wrap(msg)]))
+ # got EINTR, automatic retry
+ space.getexecutioncontext().checksignals()
+ if timeout > 0.0:
+ timeout = end_time - timeutils.monotonic(space)
+ if timeout < 0.0:
+ timeout = 0.0
resin_w = []
resout_w = []
@@ -193,15 +225,12 @@
ll_errl = lltype.malloc(_c.fd_set.TO, flavor='raw')
if timeout >= 0.0:
ll_timeval = rffi.make(_c.timeval)
- i = int(timeout)
- rffi.setintfield(ll_timeval, 'c_tv_sec', i)
- rffi.setintfield(ll_timeval, 'c_tv_usec', int((timeout-i)*1000000))
# Call this as a separate helper to avoid a large piece of code
# in try:finally:. Needed for calling further _always_inline_
# helpers like _build_fd_set().
return _call_select(space, iwtd_w, owtd_w, ewtd_w,
- ll_inl, ll_outl, ll_errl, ll_timeval)
+ ll_inl, ll_outl, ll_errl, ll_timeval, timeout)
finally:
if ll_timeval:
lltype.free(ll_timeval, flavor='raw')
diff --git a/pypy/module/select/test/test_select_signal.py
b/pypy/module/select/test/test_select_signal.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/select/test/test_select_signal.py
@@ -0,0 +1,48 @@
+
+class AppTestSelectSignal:
+ spaceconfig = {
+ "usemodules": ['select', 'time', 'signal'],
+ }
+
+ def test_pep475_retry(self):
+ import select, time
+ import _signal as signal
+
+ def foo(*args):
+ signalled.append("ALARM")
+
+ # a list of functions that will do nothing more than sleep for 3
+ # seconds
+ cases = [(select.select, [], [], [], 3.0)]
+
+ if hasattr(select, 'poll'):
+ import posix
+ poll = select.poll()
+ cases.append((poll.poll, 3000)) # milliseconds
+
+ if hasattr(select, 'epoll'):
+ epoll = select.epoll()
+ cases.append((epoll.poll, 3.0))
+
+ if hasattr(select, 'kqueue'):
+ kqueue = select.kqueue()
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit