Skip to content

Commit 82996b1

Browse files
committed
gh-149760: Improve exception tracebacks from TarFile.next()
Keep the original traceback when TarFile.next() reraises non-zlib exceptions, including when zlib is unavailable. Preserve the existing conversion of zlib.error to ReadError.
1 parent 5ddd59f commit 82996b1

3 files changed

Lines changed: 33 additions & 4 deletions

File tree

Lib/tarfile.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2951,12 +2951,12 @@ def next(self):
29512951
except Exception as e:
29522952
try:
29532953
import zlib
2954+
except ImportError:
2955+
pass
2956+
else:
29542957
if isinstance(e, zlib.error):
29552958
raise ReadError(f'zlib error: {e}') from None
2956-
else:
2957-
raise e
2958-
except ImportError:
2959-
raise e
2959+
raise
29602960
break
29612961

29622962
if tarinfo is not None:

Lib/test/test_tarfile.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,33 @@ def test_zlib_error_does_not_leak(self):
877877
with self.assertRaises(tarfile.ReadError):
878878
tarfile.open(self.tarname)
879879

880+
def test_next_preserves_exception_traceback(self):
881+
class FailingFile(io.BytesIO):
882+
def read(self, *args):
883+
raise error
884+
885+
for error_type in (OSError, ImportError):
886+
for missing_zlib in (False, True):
887+
with self.subTest(error_type=error_type,
888+
missing_zlib=missing_zlib):
889+
error = error_type("read failed")
890+
modules = {"zlib": None} if missing_zlib else {}
891+
with unittest.mock.patch.dict(sys.modules, modules):
892+
try:
893+
tarfile.open(fileobj=FailingFile(), mode="r:")
894+
except error_type as exc:
895+
self.assertIs(exc, error)
896+
next_frames = 0
897+
tb = exc.__traceback__
898+
while tb is not None:
899+
code = tb.tb_frame.f_code
900+
if code is tarfile.TarFile.next.__code__:
901+
next_frames += 1
902+
tb = tb.tb_next
903+
self.assertEqual(next_frames, 1)
904+
else:
905+
self.fail(f"{error_type.__name__} not raised")
906+
880907
def test_next_on_empty_tarfile(self):
881908
fd = io.BytesIO()
882909
tf = tarfile.open(fileobj=fd, mode="w")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Preserve the original traceback when :meth:`tarfile.TarFile.next`
2+
reraises an exception.

0 commit comments

Comments
 (0)