Merged revisions 77890 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r77890 | antoine.pitrou | 2010-01-31 23:26:04 +0100 (dim., 31 janv. 2010) | 7 lines - 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. ........
This commit is contained in:
parent
9b661e6ef6
commit
905a2ffe3e
|
@ -856,7 +856,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:
|
||||
|
@ -1717,8 +1716,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 detach(self):
|
||||
if self.buffer is None:
|
||||
|
|
|
@ -328,6 +328,17 @@ class OtherFileTests(unittest.TestCase):
|
|||
f.close()
|
||||
self.fail("no error for invalid mode: %s" % bad_mode)
|
||||
|
||||
def testTruncate(self):
|
||||
f = _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>
|
||||
|
|
|
@ -228,6 +228,11 @@ class IOTest(unittest.TestCase):
|
|||
support.unlink(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)
|
||||
|
@ -239,8 +244,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):
|
||||
|
@ -285,7 +291,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")
|
||||
|
@ -980,7 +986,7 @@ class BufferedWriterTest(unittest.TestCase, CommonBufferedTests):
|
|||
bufio = self.tp(raw, 8)
|
||||
bufio.write(b"abcdef")
|
||||
self.assertEqual(bufio.truncate(3), 3)
|
||||
self.assertEqual(bufio.tell(), 3)
|
||||
self.assertEqual(bufio.tell(), 6)
|
||||
with self.open(support.TESTFN, "rb", buffering=0) as f:
|
||||
self.assertEqual(f.read(), b"abc")
|
||||
|
||||
|
@ -1366,6 +1372,14 @@ class BufferedRandomTest(BufferedReaderTest, BufferedWriterTest):
|
|||
self.assertEqual(s,
|
||||
b"A" + b"B" * overwrite_size + b"A" * (9 - overwrite_size))
|
||||
|
||||
def test_truncate_after_read_or_write(self):
|
||||
raw = self.BytesIO(b"A" * 10)
|
||||
bufio = self.tp(raw, 100)
|
||||
self.assertEqual(bufio.read(2), b"AA") # the read buffer gets filled
|
||||
self.assertEqual(bufio.truncate(), 2)
|
||||
self.assertEqual(bufio.write(b"BB"), 2) # the write buffer increases
|
||||
self.assertEqual(bufio.truncate(), 4)
|
||||
|
||||
def test_misbehaved_io(self):
|
||||
BufferedReaderTest.test_misbehaved_io(self)
|
||||
BufferedWriterTest.test_misbehaved_io(self)
|
||||
|
|
|
@ -122,14 +122,14 @@ class LargeFileTest(unittest.TestCase):
|
|||
newsize -= 1
|
||||
f.seek(42)
|
||||
f.truncate(newsize)
|
||||
self.assertEqual(f.tell(), newsize) # else wasn't truncated
|
||||
self.assertEqual(f.tell(), 42)
|
||||
f.seek(0, 2)
|
||||
self.assertEqual(f.tell(), newsize)
|
||||
# XXX truncate(larger than true size) is ill-defined
|
||||
# across platform; cut it waaaaay back
|
||||
f.seek(0)
|
||||
f.truncate(1)
|
||||
self.assertEqual(f.tell(), 1) # else pointer moved
|
||||
self.assertEqual(f.tell(), 0) # else pointer moved
|
||||
f.seek(0)
|
||||
self.assertEqual(len(f.read()), 1) # else wasn't truncated
|
||||
|
||||
|
|
|
@ -73,7 +73,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")
|
||||
|
@ -121,7 +121,8 @@ class MemoryTestMixin:
|
|||
self.assertEqual(memio.getvalue(), buf[:6])
|
||||
self.assertEqual(memio.truncate(4), 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()
|
||||
|
|
|
@ -123,6 +123,7 @@ Donn Cave
|
|||
Charles Cazabon
|
||||
Per Cederqvist
|
||||
Octavian Cerna
|
||||
Pascal Chambon
|
||||
Hye-Shik Chang
|
||||
Jeffrey Chang
|
||||
Mitch Chapman
|
||||
|
|
|
@ -239,6 +239,11 @@ C-API
|
|||
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 #7610: Reworked implementation of the internal
|
||||
:class:`zipfile.ZipExtFile` class used to represent files stored inside
|
||||
an archive. The new implementation is significantly faster and can
|
||||
|
|
|
@ -412,7 +412,7 @@ PyDoc_STRVAR(truncate_doc,
|
|||
"truncate([size]) -> int. Truncate the file to at most size bytes.\n"
|
||||
"\n"
|
||||
"Size defaults to the current file position, as returned by tell().\n"
|
||||
"Returns the new size. Imply an absolute seek to the position size.");
|
||||
"The current file position is unchanged. Returns the new size.\n");
|
||||
|
||||
static PyObject *
|
||||
bytesio_truncate(bytesio *self, PyObject *args)
|
||||
|
@ -451,7 +451,6 @@ bytesio_truncate(bytesio *self, PyObject *args)
|
|||
if (resize_buffer(self, size) < 0)
|
||||
return NULL;
|
||||
}
|
||||
self->pos = size;
|
||||
|
||||
return PyLong_FromSsize_t(size);
|
||||
}
|
||||
|
|
|
@ -758,8 +758,10 @@ fileio_tell(fileio *self, PyObject *args)
|
|||
static PyObject *
|
||||
fileio_truncate(fileio *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;
|
||||
|
||||
|
@ -774,58 +776,86 @@ fileio_truncate(fileio *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 (posobj == NULL)
|
||||
return NULL;
|
||||
|
||||
#if defined(HAVE_LARGEFILE_SUPPORT)
|
||||
pos = PyLong_AsLongLong(posobj);
|
||||
#else
|
||||
pos = PyLong_AsLong(posobj);
|
||||
#endif
|
||||
if (pos == -1 && 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(fileio *self)
|
||||
|
|
|
@ -102,8 +102,8 @@ iobase_tell(PyObject *self, PyObject *args)
|
|||
PyDoc_STRVAR(iobase_truncate_doc,
|
||||
"Truncate file to size bytes.\n"
|
||||
"\n"
|
||||
"Size defaults to the current IO position as reported by tell(). Return\n"
|
||||
"the new size.");
|
||||
"File pointer is left unchanged. Size defaults to the current IO\n"
|
||||
"position as reported by tell(). Returns the new size.");
|
||||
|
||||
static PyObject *
|
||||
iobase_truncate(PyObject *self, PyObject *args)
|
||||
|
|
|
@ -350,7 +350,7 @@ PyDoc_STRVAR(stringio_truncate_doc,
|
|||
"Truncate size to pos.\n"
|
||||
"\n"
|
||||
"The pos argument defaults to the current file position, as\n"
|
||||
"returned by tell(). Imply an absolute seek to pos.\n"
|
||||
"returned by tell(). The current file position is unchanged.\n"
|
||||
"Returns the new absolute position.\n");
|
||||
|
||||
static PyObject *
|
||||
|
@ -390,7 +390,6 @@ stringio_truncate(stringio *self, PyObject *args)
|
|||
return NULL;
|
||||
self->string_size = size;
|
||||
}
|
||||
self->pos = size;
|
||||
|
||||
return PyLong_FromSsize_t(size);
|
||||
}
|
||||
|
|
|
@ -2318,15 +2318,7 @@ textiowrapper_truncate(textio *self, PyObject *args)
|
|||
return NULL;
|
||||
Py_DECREF(res);
|
||||
|
||||
if (pos != Py_None) {
|
||||
res = PyObject_CallMethodObjArgs((PyObject *) self,
|
||||
_PyIO_str_seek, pos, NULL);
|
||||
if (res == NULL)
|
||||
return NULL;
|
||||
Py_DECREF(res);
|
||||
}
|
||||
|
||||
return PyObject_CallMethodObjArgs(self->buffer, _PyIO_str_truncate, NULL);
|
||||
return PyObject_CallMethodObjArgs(self->buffer, _PyIO_str_truncate, pos, NULL);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
|
|
Loading…
Reference in New Issue