8000 bpo-8978: improve tarfile.open error message when lzma / bz2 are missing by asottile · Pull Request #24850 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-8978: improve tarfile.open error message wh 8000 en lzma / bz2 are missing #24850

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
7 changes: 5 additions & 2 deletions Lib/tarfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1604,17 +1604,20 @@ def open(cls, name=None, mode="r", fileobj=None, bufsize=RECORDSIZE, **kwargs):
# Find out which *open() is appropriate for opening the file.
def not_compressed(comptype):
return cls.OPEN_METH[comptype] == 'taropen'
error_msgs = []
for comptype in sorted(cls.OPEN_METH, key=not_compressed):
func = getattr(cls, cls.OPEN_METH[comptype])
if fileobj is not None:
saved_pos = fileobj.tell()
try:
return func(name, "r", fileobj, **kwargs)
except (ReadError, CompressionError):
except (ReadError, CompressionError) as e:
Copy link
Member

Choose a reason for hiding this comment

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

I am ok with this, but this is going to create a hell of a lot of cycles, so I prefer to manually delete the list with all the exceptions once error_msgs is done to alleviate this a bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I actually thought about the reference cycle problem -- that's why these are strings and not exception objects -- I don't think there's actually any cycles added by this PR ? unless you're hinting at something I don't know about!

Copy link
Member

Choose a reason for hiding this comment

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

I don't think there's actually any cycles added by this PR ?

Oh, my bad, I was reading this as keeping the errors in the list instead of the string representation. Maybe renaming errors to error_messages? If you prefer not to do that is fine, I think we can go as it is as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah yeah that would be better -- but then what to call the joined variable 🤔 (to avoid having a variable change type)

Copy link
Member

Choose a reason for hiding this comment

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

the joined variable

final_error_msg ?

Copy link
Member

Choose a reason for hiding this comment

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

Notice that even if it contains the summary of multiple error messages is a single error message at the end.

Maybe you can call it summary_error_msg?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

went with error_msgs and error_msgs_summary -- thanks for the suggestion!

error_msgs.append(f'- method {comptype}: {e!r}')
if fileobj is not None:
fileobj.seek(saved_pos)
continue
raise ReadError("file could not be opened successfully")
error_msgs_summary = '\n'.join(error_msgs)
raise ReadError(f"file could not be opened successfully:\n{error_msgs_summary}")

elif ":" in mode:
filemode, comptype = mode.split(":", 1)
Expand Down
12 changes: 12 additions & 0 deletions Lib/test/test_tarfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2283,6 +2283,18 @@ def test__all__(self):
'SubsequentHeaderError', 'ExFileObject', 'main'}
support.check__all__(self, tarfile, not_exported=not_exported)

def test_useful_error_message_when_modules_missing(self):
fname = os.path.join(os.path.dirname(__file__), 'testtar.tar.xz')
with self.assertRaises(tarfile.ReadError) as excinfo:
error = tarfile.CompressionError('lzma module is not available'),
with unittest.mock.patch.object(tarfile.TarFile, 'xzopen', side_effect=error):
tarfile.open(fname)

self.assertIn(
"\n- method xz: CompressionError('lzma module is not available')\n",
str(excinfo.exception),
)


class CommandLineTest(unittest.TestCase):

Expand Down
Binary file added Lib/test/testtar.tar.xz
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improve error message for :func:`tarfile.open` when :mod:`lzma` / :mod:`bz2`
are unavailable. Patch by Anthony Sottile.
0