]> granicus.if.org Git - python/commitdiff
Issue #6939: Fix file I/O objects in the `io` module to keep the original
authorAntoine Pitrou <solipsis@pitrou.net>
Wed, 27 Jan 2010 21:48:46 +0000 (21:48 +0000)
committerAntoine Pitrou <solipsis@pitrou.net>
Wed, 27 Jan 2010 21:48:46 +0000 (21:48 +0000)
file position when calling `truncate()`.  It would previously change the
file position to the given argument, which goes against the tradition of
`ftruncate()` and other truncation APIs.  Patch by Pascal Chambon.

Lib/io.py
Lib/test/test_fileio.py
Lib/test/test_io.py
Lib/test/test_memoryio.py
Misc/NEWS
Modules/_bytesio.c
Modules/_fileio.c

index 70f2abaf57e2b8926aba8ad0d4e65f96c9133f02..d090eecff06266d0b421894cfdbd14b6a5fd0168 100644 (file)
--- a/Lib/io.py
+++ b/Lib/io.py
@@ -880,7 +880,7 @@ class _BytesIO(BufferedIOBase):
         elif pos < 0:
             raise ValueError("negative truncate position %r" % (pos,))
         del self._buffer[pos:]
-        return self.seek(pos)
+        return pos
 
     def readable(self):
         return True
@@ -1215,8 +1215,7 @@ class BufferedRandom(BufferedWriter, BufferedReader):
         if pos is None:
             pos = self.tell()
         # Use seek to flush the read buffer.
-        self.seek(pos)
-        return BufferedWriter.truncate(self)
+        return BufferedWriter.truncate(self, pos)
 
     def read(self, n=None):
         if n is None:
@@ -1667,8 +1666,7 @@ class TextIOWrapper(TextIOBase):
         self.flush()
         if pos is None:
             pos = self.tell()
-        self.seek(pos)
-        return self.buffer.truncate()
+        return self.buffer.truncate(pos)
 
     def seek(self, cookie, whence=0):
         if self.closed:
index 928fbecbf98a7f5bb92f397040ec95d507150236..0eea86b309eaae02527ca2b1ba3654e3c103b260 100644 (file)
@@ -197,6 +197,17 @@ class OtherFileTests(unittest.TestCase):
             f.close()
             self.fail("no error for invalid mode: %s" % bad_mode)
 
+    def testTruncate(self):
+        f = _fileio._FileIO(TESTFN, 'w')
+        f.write(bytes(bytearray(range(10))))
+        self.assertEqual(f.tell(), 10)
+        f.truncate(5)
+        self.assertEqual(f.tell(), 10)
+        self.assertEqual(f.seek(0, os.SEEK_END), 5)
+        f.truncate(15)
+        self.assertEqual(f.tell(), 5)
+        self.assertEqual(f.seek(0, os.SEEK_END), 15)
+
     def testTruncateOnWindows(self):
         def bug801631():
             # SF bug <http://www.python.org/sf/801631>
index f0b38b6f7a94b0b1bdfbbfb9354d50c15f459307..38c58c3687f711182dca363cef4606617a71f120 100644 (file)
@@ -87,6 +87,12 @@ class IOTest(unittest.TestCase):
         test_support.unlink(test_support.TESTFN)
 
     def write_ops(self, f):
+
+        self.assertEqual(f.write(b"blah."), 5)
+        f.truncate(0)
+        self.assertEqual(f.tell(), 5)
+        f.seek(0)
+
         self.assertEqual(f.write(b"blah."), 5)
         self.assertEqual(f.seek(0), 0)
         self.assertEqual(f.write(b"Hello."), 6)
@@ -98,8 +104,9 @@ class IOTest(unittest.TestCase):
         self.assertEqual(f.write(b"h"), 1)
         self.assertEqual(f.seek(-1, 2), 13)
         self.assertEqual(f.tell(), 13)
+
         self.assertEqual(f.truncate(12), 12)
-        self.assertEqual(f.tell(), 12)
+        self.assertEqual(f.tell(), 13)
         self.assertRaises(TypeError, f.seek, 0.0)
 
     def read_ops(self, f, buffered=False):
@@ -144,7 +151,7 @@ class IOTest(unittest.TestCase):
         self.assertEqual(f.tell(), self.LARGE + 2)
         self.assertEqual(f.seek(0, 2), self.LARGE + 2)
         self.assertEqual(f.truncate(self.LARGE + 1), self.LARGE + 1)
-        self.assertEqual(f.tell(), self.LARGE + 1)
+        self.assertEqual(f.tell(), self.LARGE + 2)
         self.assertEqual(f.seek(0, 2), self.LARGE + 1)
         self.assertEqual(f.seek(-1, 2), self.LARGE)
         self.assertEqual(f.read(2), b"x")
index d538a7eccc9b801b6a8b9b90c4bafd67081ae684..85b5e13cdd88ce22cc5c118887fce64469e13233 100644 (file)
@@ -32,7 +32,7 @@ class MemoryTestMixin:
         self.assertEqual(f.seek(0), 0)
         self.assertEqual(f.write(t("h")), 1)
         self.assertEqual(f.truncate(12), 12)
-        self.assertEqual(f.tell(), 12)
+        self.assertEqual(f.tell(), 1)
 
     def test_write(self):
         buf = self.buftype("hello world\n")
@@ -83,7 +83,8 @@ class MemoryTestMixin:
         # truncate() accepts long objects
         self.assertEqual(memio.truncate(4L), 4)
         self.assertEqual(memio.getvalue(), buf[:4])
-        self.assertEqual(memio.tell(), 4)
+        self.assertEqual(memio.tell(), 6)
+        memio.seek(0, 2)
         memio.write(buf)
         self.assertEqual(memio.getvalue(), buf[:4] + buf)
         pos = memio.tell()
index e2b45498667f38ac9c97d2cabf7522a9ec6b136a..6c15fe6c71d5b16284cb17198ff41d9592a07bb4 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -53,6 +53,11 @@ Core and Builtins
 Library
 -------
 
+- Issue #6939: Fix file I/O objects in the `io` module to keep the original
+  file position when calling `truncate()`.  It would previously change the
+  file position to the given argument, which goes against the tradition of
+  ftruncate() and other truncation APIs.  Patch by Pascal Chambon.
+
 - Issue #7792: Registering non-classes to ABCs raised an obscure error.
 
 - Issue #7773: Fix an UnboundLocalError in platform.linux_distribution() when
index d3428386dd04ff261532f96f46a716fbd0a08a5d..bbbb156146868c0b7b334ac7a8dc17f91205f205 100644 (file)
@@ -445,7 +445,6 @@ bytesio_truncate(BytesIOObject *self, PyObject *args)
         if (resize_buffer(self, size) < 0)
             return NULL;
     }
-    self->pos = size;
 
     return PyInt_FromSsize_t(size);
 }
index 5a3613ba375315ca392e51a64ed3eb927d2a39e9..a708906859b7e3b2b4daec2368a4ad8d666ca6df 100644 (file)
@@ -646,8 +646,10 @@ fileio_tell(PyFileIOObject *self, PyObject *args)
 static PyObject *
 fileio_truncate(PyFileIOObject *self, PyObject *args)
 {
-       PyObject *posobj = NULL;
+       PyObject *posobj = NULL; /* the new size wanted by the user */
+#ifndef MS_WINDOWS
        Py_off_t pos;
+#endif
        int ret;
        int fd;
 
@@ -662,56 +664,86 @@ fileio_truncate(PyFileIOObject *self, PyObject *args)
 
        if (posobj == Py_None || posobj == NULL) {
                /* Get the current position. */
-                posobj = portable_lseek(fd, NULL, 1);
-                if (posobj == NULL)
+               posobj = portable_lseek(fd, NULL, 1);
+               if (posobj == NULL)
                        return NULL;
-        }
-        else {
-               /* Move to the position to be truncated. */
-                posobj = portable_lseek(fd, posobj, 0);
-        }
-
-#if defined(HAVE_LARGEFILE_SUPPORT)
-       pos = PyLong_AsLongLong(posobj);
-#else
-       pos = PyLong_AsLong(posobj);
-#endif
-       if (PyErr_Occurred())
-               return NULL;
+       }
+       else {
+               Py_INCREF(posobj);
+       }
 
 #ifdef MS_WINDOWS
        /* MS _chsize doesn't work if newsize doesn't fit in 32 bits,
           so don't even try using it. */
        {
+               PyObject *oldposobj, *tempposobj;
                HANDLE hFile;
+       
+               /* we save the file pointer position */
+               oldposobj = portable_lseek(fd, NULL, 1); 
+               if (oldposobj == NULL) {
+                       Py_DECREF(posobj);
+                       return NULL;
+               }
+
+               /* we then move to the truncation position */
+               tempposobj = portable_lseek(fd, posobj, 0);
+               if (tempposobj == NULL) {
+                       Py_DECREF(oldposobj);
+                       Py_DECREF(posobj);
+                       return NULL;
+               }
+               Py_DECREF(tempposobj);
 
                /* Truncate.  Note that this may grow the file! */
                Py_BEGIN_ALLOW_THREADS
                errno = 0;
                hFile = (HANDLE)_get_osfhandle(fd);
-               ret = hFile == (HANDLE)-1;
+               ret = hFile == (HANDLE)-1; /* testing for INVALID_HANDLE value */
                if (ret == 0) {
                        ret = SetEndOfFile(hFile) == 0;
                        if (ret)
                                errno = EACCES;
                }
                Py_END_ALLOW_THREADS
+
+               /* we restore the file pointer position in any case */
+               tempposobj = portable_lseek(fd, oldposobj, 0);
+               Py_DECREF(oldposobj);
+               if (tempposobj == NULL) {
+                       Py_DECREF(posobj);
+                       return NULL;
+               }
+               Py_DECREF(tempposobj);
        }
 #else
+
+#if defined(HAVE_LARGEFILE_SUPPORT)
+       pos = PyLong_AsLongLong(posobj);
+#else
+       pos = PyLong_AsLong(posobj);
+#endif
+       if (PyErr_Occurred()){
+               Py_DECREF(posobj);
+               return NULL;
+       }
+
        Py_BEGIN_ALLOW_THREADS
        errno = 0;
        ret = ftruncate(fd, pos);
        Py_END_ALLOW_THREADS
+
 #endif /* !MS_WINDOWS */
 
        if (ret != 0) {
+               Py_DECREF(posobj);
                PyErr_SetFromErrno(PyExc_IOError);
                return NULL;
        }
 
        return posobj;
 }
-#endif
+#endif /* HAVE_FTRUNCATE */
 
 static char *
 mode_string(PyFileIOObject *self)