[Python-checkins] [3.8] closes bpo-27805: Ignore ESPIPE in initializing seek of append-mode files. (GH-17136)

Benjamin Peterson webhook-mailer at python.org
Tue Nov 12 18:54:28 EST 2019


https://github.com/python/cpython/commit/9788f97bf69230ec6b38a483c90e88828eba9a1b
commit: 9788f97bf69230ec6b38a483c90e88828eba9a1b
branch: 3.8
author: Benjamin Peterson <benjamin at python.org>
committer: GitHub <noreply at github.com>
date: 2019-11-12T15:54:19-08:00
summary:

[3.8] closes bpo-27805: Ignore ESPIPE in initializing seek of append-mode files. (GH-17136)

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)

files:
A Misc/NEWS.d/next/Library/2019-11-11-21-43-06.bpo-27805.D3zl1_.rst
M Lib/_pyio.py
M Lib/test/test_io.py
M Modules/_io/fileio.c

diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index eb4e6620fc65f..fd31b8ca9d83f 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -1587,7 +1587,11 @@ def __init__(self, file, mode='r', closefd=True, opener=None):
                 # 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 fc474c99053df..50459e0792962 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -3907,6 +3907,17 @@ def test_attributes(self):
         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 0000000000000..37be6a5d0b22b
--- /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 9166c607e737c..482d08f9f23b2 100644
--- a/Modules/_io/fileio.c
+++ b/Modules/_io/fileio.c
@@ -4,6 +4,7 @@
 #include "Python.h"
 #include "pycore_object.h"
 #include "structmember.h"
+#include <stdbool.h>
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
@@ -75,7 +76,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)
@@ -480,7 +481,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);
@@ -603,7 +604,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();
@@ -870,7 +871,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;
@@ -921,8 +922,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);
@@ -955,7 +961,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]
@@ -973,7 +979,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
@@ -1004,7 +1010,7 @@ _io_FileIO_truncate_impl(fileio *self, PyObject *posobj)
 
     if (posobj == Py_None) {
         /* Get the current position. */
-        posobj = portable_lseek(self, NULL, 1);
+        posobj = portable_lseek(self, NULL, 1, false);
         if (posobj == NULL)
             return NULL;
     }



More information about the Python-checkins mailing list