diff --git a/Lib/test/test_io/test_fileio.py b/Lib/test/test_io/test_fileio.py index 21a160904a7f1e0..92a8a98ce74bb51 100644 --- a/Lib/test/test_io/test_fileio.py +++ b/Lib/test/test_io/test_fileio.py @@ -50,6 +50,9 @@ def testWeakRefs(self): def testSeekTell(self): self.f.write(bytes(range(20))) + # An invalid argument must not mark the file as non-seekable. + self.assertRaises(OSError, self.f.seek, -1) + self.assertTrue(self.f.seekable()) self.assertEqual(self.f.tell(), 20) self.f.seek(0) self.assertEqual(self.f.tell(), 0) @@ -61,6 +64,7 @@ def testSeekTell(self): self.assertEqual(self.f.tell(), 10) self.f.seek(-5, 2) self.assertEqual(self.f.tell(), 15) + self.assertTrue(self.f.seekable()) def testAttributes(self): # verify expected attributes exist @@ -553,6 +557,13 @@ def testAbles(self): f.close() if sys.platform != "win32": + # On Windows, lseek somehow succeeds on pipes. + r, w = os.pipe() + os.close(w) + with self.FileIO(r, "r") as f: + self.assertRaises(OSError, f.seek, 0) + self.assertEqual(f.seekable(), False) + try: f = self.FileIO("/dev/tty", "a") except OSError: diff --git a/Misc/NEWS.d/next/Library/2026-10-18-17-26-00.gh-issue-159677.fIoSk1.rst b/Misc/NEWS.d/next/Library/2026-10-18-17-26-00.gh-issue-159677.fIoSk1.rst new file mode 100644 index 000000000000000..93feb6c41c545a0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-18-17-26-00.gh-issue-159677.fIoSk1.rst @@ -0,0 +1,2 @@ +Fix :class:`io.FileIO` ``seekable`` being cached as false because of an invalid +argument to :meth:`!io.FileIO.seek` diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index e5cd7aca5de2f17..94a51afa4335457 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -1013,7 +1013,13 @@ portable_lseek(fileio *self, PyObject *posobj, int whence, bool suppress_pipe_er _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS - if (self->seekable < 0) { + if (self->seekable < 0 + && !(res < 0 && (errno == EINVAL +#ifdef EOVERFLOW + || errno == EOVERFLOW +#endif + || errno == ENXIO))) + { self->seekable = (res >= 0); }