mirror of https://github.com/python/cpython.git
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.
This commit is contained in:
parent
d593881505
commit
74fa9f723f
|
@ -1577,7 +1577,11 @@ def __init__(self, file, mode='r', closefd=True, opener=None):
|
||||||
# For consistent behaviour, we explicitly seek to the
|
# For consistent behaviour, we explicitly seek to the
|
||||||
# end of file (otherwise, it might be done only on the
|
# end of file (otherwise, it might be done only on the
|
||||||
# first write()).
|
# first write()).
|
||||||
|
try:
|
||||||
os.lseek(fd, 0, SEEK_END)
|
os.lseek(fd, 0, SEEK_END)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.ESPIPE:
|
||||||
|
raise
|
||||||
except:
|
except:
|
||||||
if owned_fd is not None:
|
if owned_fd is not None:
|
||||||
os.close(owned_fd)
|
os.close(owned_fd)
|
||||||
|
|
|
@ -3906,6 +3906,17 @@ def test_removed_u_mode(self):
|
||||||
self.open(support.TESTFN, mode)
|
self.open(support.TESTFN, mode)
|
||||||
self.assertIn('invalid mode', str(cm.exception))
|
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):
|
def test_io_after_close(self):
|
||||||
for kwargs in [
|
for kwargs in [
|
||||||
{"mode": "w"},
|
{"mode": "w"},
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Allow opening pipes and other non-seekable files in append mode with
|
||||||
|
:func:`open`.
|
|
@ -4,6 +4,7 @@
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "pycore_object.h"
|
#include "pycore_object.h"
|
||||||
#include "structmember.h"
|
#include "structmember.h"
|
||||||
|
#include <stdbool.h>
|
||||||
#ifdef HAVE_SYS_TYPES_H
|
#ifdef HAVE_SYS_TYPES_H
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -75,7 +76,7 @@ _Py_IDENTIFIER(name);
|
||||||
#define PyFileIO_Check(op) (PyObject_TypeCheck((op), &PyFileIO_Type))
|
#define PyFileIO_Check(op) (PyObject_TypeCheck((op), &PyFileIO_Type))
|
||||||
|
|
||||||
/* Forward declarations */
|
/* 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
|
int
|
||||||
_PyFileIO_closed(PyObject *self)
|
_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
|
/* For consistent behaviour, we explicitly seek to the
|
||||||
end of file (otherwise, it might be done only on the
|
end of file (otherwise, it might be done only on the
|
||||||
first write()). */
|
first write()). */
|
||||||
PyObject *pos = portable_lseek(self, NULL, 2);
|
PyObject *pos = portable_lseek(self, NULL, 2, true);
|
||||||
if (pos == NULL)
|
if (pos == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
Py_DECREF(pos);
|
Py_DECREF(pos);
|
||||||
|
@ -603,7 +604,7 @@ _io_FileIO_seekable_impl(fileio *self)
|
||||||
return err_closed();
|
return err_closed();
|
||||||
if (self->seekable < 0) {
|
if (self->seekable < 0) {
|
||||||
/* portable_lseek() sets the seekable attribute */
|
/* 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);
|
assert(self->seekable >= 0);
|
||||||
if (pos == NULL) {
|
if (pos == NULL) {
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
|
@ -870,7 +871,7 @@ _io_FileIO_write_impl(fileio *self, Py_buffer *b)
|
||||||
|
|
||||||
/* Cribbed from posix_lseek() */
|
/* Cribbed from posix_lseek() */
|
||||||
static PyObject *
|
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;
|
Py_off_t pos, res;
|
||||||
int fd = self->fd;
|
int fd = self->fd;
|
||||||
|
@ -921,8 +922,13 @@ portable_lseek(fileio *self, PyObject *posobj, int whence)
|
||||||
self->seekable = (res >= 0);
|
self->seekable = (res >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res < 0)
|
if (res < 0) {
|
||||||
|
if (suppress_pipe_error && errno == ESPIPE) {
|
||||||
|
res = 0;
|
||||||
|
} else {
|
||||||
return PyErr_SetFromErrno(PyExc_OSError);
|
return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(HAVE_LARGEFILE_SUPPORT)
|
#if defined(HAVE_LARGEFILE_SUPPORT)
|
||||||
return PyLong_FromLongLong(res);
|
return PyLong_FromLongLong(res);
|
||||||
|
@ -955,7 +961,7 @@ _io_FileIO_seek_impl(fileio *self, PyObject *pos, int whence)
|
||||||
if (self->fd < 0)
|
if (self->fd < 0)
|
||||||
return err_closed();
|
return err_closed();
|
||||||
|
|
||||||
return portable_lseek(self, pos, whence);
|
return portable_lseek(self, pos, whence, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
|
@ -973,7 +979,7 @@ _io_FileIO_tell_impl(fileio *self)
|
||||||
if (self->fd < 0)
|
if (self->fd < 0)
|
||||||
return err_closed();
|
return err_closed();
|
||||||
|
|
||||||
return portable_lseek(self, NULL, 1);
|
return portable_lseek(self, NULL, 1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_FTRUNCATE
|
#ifdef HAVE_FTRUNCATE
|
||||||
|
@ -1004,7 +1010,7 @@ _io_FileIO_truncate_impl(fileio *self, PyObject *posobj)
|
||||||
|
|
||||||
if (posobj == Py_None) {
|
if (posobj == Py_None) {
|
||||||
/* Get the current position. */
|
/* Get the current position. */
|
||||||
posobj = portable_lseek(self, NULL, 1);
|
posobj = portable_lseek(self, NULL, 1, false);
|
||||||
if (posobj == NULL)
|
if (posobj == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue