Merged revisions 77895-77896 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/branches/py3k

................
  r77895 | antoine.pitrou | 2010-01-31 23:47:27 +0100 (dim., 31 janv. 2010) | 12 lines

  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.
  ........
................
  r77896 | antoine.pitrou | 2010-02-01 00:12:29 +0100 (lun., 01 févr. 2010) | 3 lines

  r77895 broke doctest.
................
This commit is contained in:
Antoine Pitrou 2010-01-31 23:20:26 +00:00
parent 1665a8d931
commit 66f9fea782
13 changed files with 99 additions and 48 deletions

View File

@ -851,7 +851,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
@ -1210,8 +1210,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:
@ -1712,8 +1711,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:

View File

@ -247,7 +247,8 @@ class _SpoofOut(StringIO):
return result
def truncate(self, size=None):
StringIO.truncate(self, size)
self.seek(size)
StringIO.truncate(self)
# Worst-case linear-time ellipsis matching.
def _ellipsis_match(want, got):

View File

@ -330,6 +330,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>

View File

@ -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)

View File

@ -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

View File

@ -72,7 +72,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")
@ -120,7 +120,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()

View File

@ -118,6 +118,7 @@ Terry Carroll
Donn Cave
Per Cederqvist
Octavian Cerna
Pascal Chambon
Hye-Shik Chang
Jeffrey Chang
Mitch Chapman

View File

@ -76,6 +76,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 #7785: Don't accept bytes in FileIO.write().

View File

@ -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);
}

View File

@ -761,8 +761,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;
@ -782,53 +784,81 @@ fileio_truncate(fileio *self, PyObject *args)
return NULL;
}
else {
/* Move to the position to be truncated. */
posobj = portable_lseek(fd, posobj, 0);
Py_INCREF(posobj);
}
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;
#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)

View File

@ -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)

View File

@ -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);
}

View File

@ -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 *