https://github.com/python/cpython/commit/3ccb1f8835fbd556cdb30a6be1411b04cc4f7c1a commit: 3ccb1f8835fbd556cdb30a6be1411b04cc4f7c1a branch: main author: Shadi Bahaa <[email protected]> committer: ethanfurman <[email protected]> date: 2026-09-01T10:37:22-07:00 summary:
gh-149578: Fix tarfile.open failing on PAX archives with only global headers (GH-149647) When a PAX format tar archive contains only global headers and no regular members, tarfile.open() raised ReadError because the EOFHeaderError from reaching end-of-archive after the global header was being caught and converted to SubsequentHeaderError in TarInfo._proc_pax(). This prevented the caller from handling end-of-archive normally. Fix by letting EOFHeaderError propagate when processing a global header (XGLTYPE), while still treating it as a SubsequentHeaderError for extended headers (XHDTYPE) where a following file entry is mandatory. Co-authored-by: Ethan Furman <[email protected]> files: A Misc/NEWS.d/next/Library/2026-05-10-12-00-00.gh-issue-149578.5cgq7iV.rst M Lib/tarfile.py M Lib/test/test_tarfile.py diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 451302715329fe..f46e938fd314dd 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -1601,6 +1601,14 @@ def _proc_pax(self, tarfile): # Fetch the next header. try: next = self._fromtarfile(tarfile, dircheck=False) + except EOFHeaderError: + if self.type == XGLTYPE: + # If this is a global header at the end of the archive + # (no regular members follow), let the EOFHeaderError + # propagate so the caller handles end-of-archive normally. + tarfile.offset = tarfile.fileobj.tell() - BLOCKSIZE + raise + raise SubsequentHeaderError("end of file header") from None except HeaderError as e: raise SubsequentHeaderError(str(e)) from None diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 3899eac6be3b3a..10106c3ada9ba5 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -2499,6 +2499,35 @@ def test_pax_global_header(self): finally: tar.close() + def test_pax_global_header_empty_archive(self): + # An archive that contains only a global header and no regular + # members should be opened successfully (gh-149578). + pax_headers = {"foo": "bar"} + + # Create a PAX archive with global headers but no file entries. + with tarfile.open(tmpname, "w", format=tarfile.PAX_FORMAT, + pax_headers=pax_headers): + pass + + # Reading the archive should work and preserve global headers. + with tarfile.open(tmpname) as tar: + self.assertEqual(tar.pax_headers, pax_headers) + self.assertEqual(tar.getmembers(), []) + + # Appending to the archive should work. + with tarfile.open(tmpname, "a") as tar: + self.assertEqual(tar.pax_headers, pax_headers) + self.assertEqual(tar.getmembers(), []) + tar.addfile(tarfile.TarInfo("test")) + + # Verify the appended member is present and global headers + # are preserved. + with tarfile.open(tmpname) as tar: + self.assertEqual(tar.pax_headers, pax_headers) + members = tar.getmembers() + self.assertEqual(len(members), 1) + self.assertEqual(members[0].name, "test") + def test_pax_extended_header(self): # The fields from the pax header have priority over the # TarInfo. diff --git a/Misc/NEWS.d/next/Library/2026-05-10-12-00-00.gh-issue-149578.5cgq7iV.rst b/Misc/NEWS.d/next/Library/2026-05-10-12-00-00.gh-issue-149578.5cgq7iV.rst new file mode 100644 index 00000000000000..eb738df5beb0c5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-10-12-00-00.gh-issue-149578.5cgq7iV.rst @@ -0,0 +1,3 @@ +Fix :func:`tarfile.open` failing with :exc:`~tarfile.ReadError` when +opening a PAX format tar archive that contains only global headers and +no regular members. _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
