From 9cfcbca01861e8de0a6cdd4f750de0e0ee140a70 Mon Sep 17 00:00:00 2001 From: Jake Wallin Date: Tue, 20 Oct 2026 03:03:03 +0000 Subject: [PATCH] gh-124293: Support seekability checks on tar streams --- Lib/tarfile.py | 3 +++ Lib/test/test_tarfile.py | 19 +++++++++++-------- ...-10-20-04-10-00.gh-issue-124293.K9sW3e.rst | 2 ++ 3 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-10-20-04-10-00.gh-issue-124293.K9sW3e.rst diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 3c3375ca77cb41..21e0b9cd582cbd 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -543,6 +543,9 @@ def tell(self): """ return self.pos + def seekable(self): + return True + def seek(self, pos=0): """Set the stream's file pointer to pos. Negative seeking is forbidden. diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 219a563a193be3..759317e8d4cebd 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -547,10 +547,7 @@ def test_extractfile_attrs(self): self.assertEqual(fobj.mode, 'rb') self.assertIs(fobj.readable(), True) self.assertIs(fobj.writable(), False) - if self.is_stream: - self.assertRaises(AttributeError, fobj.seekable) - else: - self.assertIs(fobj.seekable(), True) + self.assertIs(fobj.seekable(), True) self.assertIs(fobj.closed, False) self.assertIs(fobj.closed, True) self.assertEqual(fobj.name, 'ustar/regtype') @@ -558,10 +555,7 @@ def test_extractfile_attrs(self): self.assertEqual(fobj.mode, 'rb') self.assertIs(fobj.readable(), True) self.assertIs(fobj.writable(), False) - if self.is_stream: - self.assertRaises(AttributeError, fobj.seekable) - else: - self.assertIs(fobj.seekable(), True) + self.assertIs(fobj.seekable(), True) class ReadSizeRecorder(io.BytesIO): @@ -1003,6 +997,15 @@ class StreamReadTest(CommonReadTest, unittest.TestCase): prefix="r|" is_stream = True + def test_extractfile_seek_forward(self): + tarinfo = self.tar.next() + with tarfile.open(tarname) as reference: + expected = reference.extractfile(tarinfo.name).read() + with self.tar.extractfile(tarinfo) as file: + self.assertIs(file.seekable(), True) + self.assertEqual(file.seek(10), 10) + self.assertEqual(file.read(20), expected[10:30]) + def test_read_through(self): # Issue #11224: A poorly designed _FileInFile.read() method # caused seeking errors with stream tar files. diff --git a/Misc/NEWS.d/next/Library/2026-10-20-04-10-00.gh-issue-124293.K9sW3e.rst b/Misc/NEWS.d/next/Library/2026-10-20-04-10-00.gh-issue-124293.K9sW3e.rst new file mode 100644 index 00000000000000..6070eee63a8c7f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-20-04-10-00.gh-issue-124293.K9sW3e.rst @@ -0,0 +1,2 @@ +Fix :exc:`AttributeError` from ``seekable()`` and forward ``seek()`` calls on +file objects returned by :meth:`tarfile.TarFile.extractfile` in stream modes.