diff --git a/Lib/tarfile.py b/Lib/tarfile.py index c0b9f3f429c10e..16d3c60cd1b488 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -593,6 +593,8 @@ def _read(self, size): else: # Other decompressors have needs_input. # decompress() can buffer data internally. + if self.cmp.eof: + break if self.cmp.needs_input: cbuf = self.fileobj.read(self.bufsize) if not cbuf: diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 2b4cf748dffc1a..479772b5c8a89a 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -1060,6 +1060,26 @@ def test_compare_members(self): finally: tar1.close() + def test_ignore_zeros_concatenated(self): + # gh-159643: reading past the end of the compressed data + # must not raise EOFError. + with self.open(tmpname, "wb") as fobj: + for name in ("a", "b"): + with tarfile.open(fileobj=fobj, mode="w") as tar: + tar.addfile(tarfile.TarInfo(name)) + with tarfile.open(tmpname, self.mode, ignore_zeros=True) as tar: + self.assertEqual(tar.getnames(), ["a", "b"]) + + def test_no_end_of_archive_blocks(self): + # gh-159643 + tarinfo = tarfile.TarInfo("a") + tarinfo.size = 5 + with self.open(tmpname, "wb") as fobj: + fobj.write(tarinfo.tobuf()) + fobj.write(b"hello".ljust(tarfile.BLOCKSIZE, b"\0")) + with tarfile.open(tmpname, self.mode) as tar: + self.assertEqual(tar.getnames(), ["a"]) + class GzipStreamReadTest(GzipTest, GzipReadTestBase, StreamReadTest): pass diff --git a/Misc/NEWS.d/next/Library/2026-10-17-19-33-13.gh-issue-159643.EJfe0IXetV.rst b/Misc/NEWS.d/next/Library/2026-10-17-19-33-13.gh-issue-159643.EJfe0IXetV.rst new file mode 100644 index 00000000000000..a76e54de38cc4e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-17-19-33-13.gh-issue-159643.EJfe0IXetV.rst @@ -0,0 +1,3 @@ +Fix :exc:`EOFError` raised by :mod:`tarfile` in stream mode with bz2, xz or +zstd compression when reading past the end of the compressed data, for +example with *ignore_zeros* or an archive without end-of-archive blocks.