diff --git a/Lib/test/test_largefile.py b/Lib/test/test_largefile.py index 584a20623fd..d9326593a3f 100644 --- a/Lib/test/test_largefile.py +++ b/Lib/test/test_largefile.py @@ -133,6 +133,15 @@ class LargeFileTest(unittest.TestCase): f.seek(0) self.assertEqual(len(f.read()), 1) # else wasn't truncated + def test_seekable(self): + # Issue #5016; seekable() can return False when the current position + # is negative when truncated to an int. + for pos in (2**31-1, 2**31, 2**31+1): + with self.open(TESTFN, 'rb') as f: + f.seek(pos) + self.assert_(f.seekable()) + + def test_main(): # On Windows and Mac OSX this test comsumes large resources; It # takes a long time to build the >2GB file and takes >2GB of disk @@ -172,6 +181,7 @@ def test_main(): with _open(TESTFN, 'wb') as f: if hasattr(f, 'truncate'): suite.addTest(TestCase('test_truncate')) + suite.addTest(TestCase('test_seekable')) unlink(TESTFN) try: run_unittest(suite) diff --git a/Misc/NEWS b/Misc/NEWS index 21b4a9b993f..d1b1463aa22 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,9 @@ Library - The error detection code in FileIO.close() could fail to reflect the `errno` value, and report it as -1 instead. +- Issue #5016: FileIO.seekable() could return False if the file position + was negative when truncated to a C int. Patch by Victor Stinner. + What's New in Python 3.1 alpha 1 ================================ diff --git a/Modules/_fileio.c b/Modules/_fileio.c index 32f679007aa..88ee54c5f80 100644 --- a/Modules/_fileio.c +++ b/Modules/_fileio.c @@ -66,6 +66,8 @@ _PyFileIO_closed(PyObject *self) 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(PyFileIOObject *self) @@ -441,14 +443,14 @@ fileio_seekable(PyFileIOObject *self) if (self->fd < 0) return err_closed(); if (self->seekable < 0) { - int ret; - Py_BEGIN_ALLOW_THREADS - ret = lseek(self->fd, 0, SEEK_CUR); - Py_END_ALLOW_THREADS - if (ret < 0) + PyObject *pos = portable_lseek(self->fd, NULL, SEEK_CUR); + if (pos == NULL) { + PyErr_Clear(); self->seekable = 0; - else + } else { + Py_DECREF(pos); self->seekable = 1; + } } return PyBool_FromLong((long) self->seekable); }