From: Benjamin Peterson Date: Tue, 12 Nov 2019 23:34:43 +0000 (-0800) Subject: [3.7] closes bpo-27805: Ignore ESPIPE in initializing seek of append-mode files.... X-Git-Tag: v3.7.6rc1~57 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b8b3e4377ec38c7d64570afabbd0923a51f0666c;p=thirdparty%2FPython%2Fcpython.git [3.7] closes bpo-27805: Ignore ESPIPE in initializing seek of append-mode files. (GH-17137) This change, which follows the behavior of C stdio's fdopen and Python 2's file object, allows pipes to be opened in append mode.. (cherry picked from commit 74fa9f723f700a342e582b5ad4b51a2c4801cd1c) --- diff --git a/Lib/_pyio.py b/Lib/_pyio.py index e81cc5128881..d2197813450b 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -1543,7 +1543,11 @@ class FileIO(RawIOBase): # For consistent behaviour, we explicitly seek to the # end of file (otherwise, it might be done only on the # first write()). - os.lseek(fd, 0, SEEK_END) + try: + os.lseek(fd, 0, SEEK_END) + except OSError as e: + if e.errno != errno.ESPIPE: + raise except: if owned_fd is not None: os.close(owned_fd) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index d6bf43d0a0c1..42002b9acfb7 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3830,6 +3830,17 @@ class MiscIOTest(unittest.TestCase): f.close() g.close() + def test_open_pipe_with_append(self): + # bpo-27805: Ignore ESPIPE from lseek() in open(). + r, w = os.pipe() + self.addCleanup(os.close, r) + f = self.open(w, 'a') + self.addCleanup(f.close) + # Check that the file is marked non-seekable. On Windows, however, lseek + # somehow succeeds on pipes. + if sys.platform != 'win32': + self.assertFalse(f.seekable()) + def test_io_after_close(self): for kwargs in [ {"mode": "w"}, diff --git a/Misc/NEWS.d/next/Library/2019-11-11-21-43-06.bpo-27805.D3zl1_.rst b/Misc/NEWS.d/next/Library/2019-11-11-21-43-06.bpo-27805.D3zl1_.rst new file mode 100644 index 000000000000..37be6a5d0b22 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-11-11-21-43-06.bpo-27805.D3zl1_.rst @@ -0,0 +1,2 @@ +Allow opening pipes and other non-seekable files in append mode with +:func:`open`. diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 8bbe1ce367e2..ac670510f95d 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -3,6 +3,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" #include "structmember.h" +#include #ifdef HAVE_SYS_TYPES_H #include #endif @@ -74,7 +75,7 @@ _Py_IDENTIFIER(name); #define PyFileIO_Check(op) (PyObject_TypeCheck((op), &PyFileIO_Type)) /* Forward declarations */ -static PyObject* portable_lseek(fileio *self, PyObject *posobj, int whence); +static PyObject* portable_lseek(fileio *self, PyObject *posobj, int whence, bool suppress_pipe_error); int _PyFileIO_closed(PyObject *self) @@ -475,7 +476,7 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, /* For consistent behaviour, we explicitly seek to the end of file (otherwise, it might be done only on the first write()). */ - PyObject *pos = portable_lseek(self, NULL, 2); + PyObject *pos = portable_lseek(self, NULL, 2, true); if (pos == NULL) goto error; Py_DECREF(pos); @@ -598,7 +599,7 @@ _io_FileIO_seekable_impl(fileio *self) return err_closed(); if (self->seekable < 0) { /* portable_lseek() sets the seekable attribute */ - PyObject *pos = portable_lseek(self, NULL, SEEK_CUR); + PyObject *pos = portable_lseek(self, NULL, SEEK_CUR, false); assert(self->seekable >= 0); if (pos == NULL) { PyErr_Clear(); @@ -865,7 +866,7 @@ _io_FileIO_write_impl(fileio *self, Py_buffer *b) /* Cribbed from posix_lseek() */ static PyObject * -portable_lseek(fileio *self, PyObject *posobj, int whence) +portable_lseek(fileio *self, PyObject *posobj, int whence, bool suppress_pipe_error) { Py_off_t pos, res; int fd = self->fd; @@ -916,8 +917,13 @@ portable_lseek(fileio *self, PyObject *posobj, int whence) self->seekable = (res >= 0); } - if (res < 0) - return PyErr_SetFromErrno(PyExc_OSError); + if (res < 0) { + if (suppress_pipe_error && errno == ESPIPE) { + res = 0; + } else { + return PyErr_SetFromErrno(PyExc_OSError); + } + } #if defined(HAVE_LARGEFILE_SUPPORT) return PyLong_FromLongLong(res); @@ -950,7 +956,7 @@ _io_FileIO_seek_impl(fileio *self, PyObject *pos, int whence) if (self->fd < 0) return err_closed(); - return portable_lseek(self, pos, whence); + return portable_lseek(self, pos, whence, false); } /*[clinic input] @@ -968,7 +974,7 @@ _io_FileIO_tell_impl(fileio *self) if (self->fd < 0) return err_closed(); - return portable_lseek(self, NULL, 1); + return portable_lseek(self, NULL, 1, false); } #ifdef HAVE_FTRUNCATE @@ -999,7 +1005,7 @@ _io_FileIO_truncate_impl(fileio *self, PyObject *posobj) if (posobj == Py_None || posobj == NULL) { /* Get the current position. */ - posobj = portable_lseek(self, NULL, 1); + posobj = portable_lseek(self, NULL, 1, false); if (posobj == NULL) return NULL; }