Skip to content

Commit

Permalink
closes bpo-27805: Ignore ESPIPE in initializing seek of append-mode f…
Browse files Browse the repository at this point in the history
…iles. (pythonGH-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.
  • Loading branch information
benjaminp authored Nov 12, 2019
1 parent d593881 commit 74fa9f7
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 10 deletions.
6 changes: 5 additions & 1 deletion Lib/_pyio.py
Original file line number Diff line number Diff line change
Expand Up @@ -1577,7 +1577,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)
Expand Down
11 changes: 11 additions & 0 deletions Lib/test/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -3906,6 +3906,17 @@ def test_removed_u_mode(self):
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"},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Allow opening pipes and other non-seekable files in append mode with
:func:`open`.
24 changes: 15 additions & 9 deletions Modules/_io/fileio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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]
Expand All @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit 74fa9f7

Please sign in to comment.