]> granicus.if.org Git - python/commitdiff
bpo-30228: FileIO seek() and tell() set seekable (#1384)
authorVictor Stinner <victor.stinner@gmail.com>
Tue, 2 May 2017 13:10:39 +0000 (15:10 +0200)
committerGitHub <noreply@github.com>
Tue, 2 May 2017 13:10:39 +0000 (15:10 +0200)
FileIO.seek() and FileIO.tell() method now set the internal seekable
attribute to avoid one syscall on open() (in buffered or text mode).

The seekable property is now also more reliable since its value is
set correctly on memory allocation failure.

Misc/NEWS
Modules/_io/fileio.c

index 2a86c3419ae0f3b5fe917e9cc106eea66c3ad157..118483f9437ed4ad46fab4ea2811fa17be581b05 100644 (file)
--- 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')
index 922db3e4705ef9b9bceeb92c9f48445df6228483..a09c39f76ad3a2bcaa40a0544185745cf1cffe53 100644 (file)
@@ -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;
     }