Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Lib/test/test_io/test_fileio.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ def testSeekTell(self):
self.f.seek(-5, 2)
self.assertEqual(self.f.tell(), 15)

def testSeekableAfterFailedSeek(self) -> None:
self.assertRaises(OSError, self.f.seek, -1)
self.assertTrue(self.f.seekable())

def testAttributes(self):
# verify expected attributes exist
f = self.f
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix :meth:`io.FileIO.seekable` always returning ``False`` for a seekable file

@cmaloney cmaloney Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seekable and seek are only defined on IOBase in the docs

cpython/Doc/library/io.rst

Lines 422 to 425 in 7dbc797

.. method:: seekable()
Return ``True`` if the stream supports random access. If ``False``,
:meth:`seek`, :meth:`tell` and :meth:`truncate` will raise :exc:`OSError`.

I would probably do something like:

Fix :class:`io.FileIO` ``seekable`` being cached as false because of an invalid argument to :meth:`!io.FileIO.seek`

when the first call to :meth:`~io.FileIO.seek` failed.
2 changes: 1 addition & 1 deletion Modules/_io/fileio.c
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,7 @@ 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 == ESPIPE || posobj == NULL)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure across the set of OS + kernel + version which Python runs on this is correct... Can work on evaluating that thoroughly but my leaning would be just making it so specific returns we know about that indicate "bad argument" pass through.

For instance EBADF should probably still mark seekable as False / I could see code relying on that. Something like: EINVAL, ENXIO, EOVERFLOW for Linux; EBADF and EINVAL for OpenBSD.

self->seekable = (res >= 0);
}

Expand Down
Loading