summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Peterson <benjamin@python.org>2019-11-12 22:51:34 (GMT)
committerGitHub <noreply@github.com>2019-11-12 22:51:34 (GMT)
commit74fa9f723f700a342e582b5ad4b51a2c4801cd1c (patch)
tree0d34e5bbb923f72121243ba0c425a39c08d4d411
parentd593881505c1f4acfd17f41312b27cc898451816 (diff)
downloadcpython-74fa9f723f700a342e582b5ad4b51a2c4801cd1c.zip
cpython-74fa9f723f700a342e582b5ad4b51a2c4801cd1c.tar.gz
cpython-74fa9f723f700a342e582b5ad4b51a2c4801cd1c.tar.bz2
closes bpo-27805: Ignore ESPIPE in initializing seek of append-mode files. (GH-17112)
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.
-rw-r--r--Lib/_pyio.py6
-rw-r--r--Lib/test/test_io.py11
-rw-r--r--Misc/NEWS.d/next/Library/2019-11-11-21-43-06.bpo-27805.D3zl1_.rst2
-rw-r--r--Modules/_io/fileio.c24
4 files changed, 33 insertions, 10 deletions
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index e819b0a..4c24146 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -1577,7 +1577,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 ad22dfe..501e931 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -3906,6 +3906,17 @@ class MiscIOTest(unittest.TestCase):
self.open(support.TESTFN, mode)
self.assertIn('invalid mode', str(cm.exception))
+ 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 0000000..37be6a5
--- /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 fd32b19..1855b83 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;
}