diff --git a/Misc/NEWS b/Misc/NEWS index 2a86c3419ae..118483f9437 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -317,6 +317,9 @@ Extension Modules Library ------- +- bpo-30228: The seek() and tell() methods of io.FileIO now set the internal + seekable attribute to avoid one syscall on open() (in buffered or text mode). + - bpo-30190: unittest's assertAlmostEqual and assertNotAlmostEqual provide a better message in case of failure which includes the difference between left and right arguments. (patch by Giampaolo Rodola') diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 922db3e4705..a09c39f76ad 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -73,6 +73,9 @@ _Py_IDENTIFIER(name); #define PyFileIO_Check(op) (PyObject_TypeCheck((op), &PyFileIO_Type)) +/* Forward declarations */ +static PyObject* portable_lseek(fileio *self, PyObject *posobj, int whence); + int _PyFileIO_closed(PyObject *self) { @@ -98,11 +101,6 @@ fileio_dealloc_warn(fileio *self, PyObject *source) Py_RETURN_NONE; } -static PyObject * -portable_lseek(int fd, PyObject *posobj, int whence); - -static PyObject *portable_lseek(int fd, PyObject *posobj, int whence); - /* Returns 0 on success, -1 with exception set on failure. */ static int internal_close(fileio *self) @@ -478,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->fd, NULL, 2); + PyObject *pos = portable_lseek(self, NULL, 2); if (pos == NULL) goto error; Py_DECREF(pos); @@ -600,13 +598,14 @@ _io_FileIO_seekable_impl(fileio *self) if (self->fd < 0) return err_closed(); if (self->seekable < 0) { - PyObject *pos = portable_lseek(self->fd, NULL, SEEK_CUR); + /* portable_lseek() sets the seekable attribute */ + PyObject *pos = portable_lseek(self, NULL, SEEK_CUR); + assert(self->seekable >= 0); if (pos == NULL) { PyErr_Clear(); - self->seekable = 0; - } else { + } + else { Py_DECREF(pos); - self->seekable = 1; } } return PyBool_FromLong((long) self->seekable); @@ -865,9 +864,10 @@ _io_FileIO_write_impl(fileio *self, Py_buffer *b) /* Cribbed from posix_lseek() */ static PyObject * -portable_lseek(int fd, PyObject *posobj, int whence) +portable_lseek(fileio *self, PyObject *posobj, int whence) { Py_off_t pos, res; + int fd = self->fd; #ifdef SEEK_SET /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */ @@ -884,8 +884,9 @@ portable_lseek(int fd, PyObject *posobj, int whence) } #endif /* SEEK_SET */ - if (posobj == NULL) + if (posobj == NULL) { pos = 0; + } else { if(PyFloat_Check(posobj)) { PyErr_SetString(PyExc_TypeError, "an integer is required"); @@ -909,6 +910,11 @@ portable_lseek(int fd, PyObject *posobj, int whence) #endif _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS + + if (self->seekable < 0) { + self->seekable = (res >= 0); + } + if (res < 0) return PyErr_SetFromErrno(PyExc_OSError); @@ -943,7 +949,7 @@ _io_FileIO_seek_impl(fileio *self, PyObject *pos, int whence) if (self->fd < 0) return err_closed(); - return portable_lseek(self->fd, pos, whence); + return portable_lseek(self, pos, whence); } /*[clinic input] @@ -961,7 +967,7 @@ _io_FileIO_tell_impl(fileio *self) if (self->fd < 0) return err_closed(); - return portable_lseek(self->fd, NULL, 1); + return portable_lseek(self, NULL, 1); } #ifdef HAVE_FTRUNCATE @@ -992,7 +998,7 @@ _io_FileIO_truncate_impl(fileio *self, PyObject *posobj) if (posobj == Py_None || posobj == NULL) { /* Get the current position. */ - posobj = portable_lseek(fd, NULL, 1); + posobj = portable_lseek(self, NULL, 1); if (posobj == NULL) return NULL; }