Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
gh-84027: Add whl as an unpack format in shutil
Wheels are the most ubiquitous Python package format. Per [the wheel
format
specification](https://packaging.python.org/en/latest/specifications/binary-distribution-format/),
"A wheel is a ZIP-format archive with a specially formatted file name
and the .whl extension."

To enable unpacking of wheels for inspection or even installation, we
register the format in shutil using shutil._unpack_zipfile to do the
actual unpacking.
  • Loading branch information
emmatyping committed Oct 20, 2025
commit f46af2d14b7d51283fe3069e3ced825ccdbc9a66
1 change: 1 addition & 0 deletions Lib/shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,7 @@ def _unpack_tarfile(filename, extract_dir, *, filter=None):
_UNPACK_FORMATS = {
'tar': (['.tar'], _unpack_tarfile, [], "uncompressed tar file"),
'zip': (['.zip'], _unpack_zipfile, [], "ZIP file"),
'whl': (['.whl'], _unpack_zipfile, [], "Wheel file"),
}

if _ZLIB_SUPPORTED:
Expand Down
9 changes: 8 additions & 1 deletion Lib/test/test_shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -2115,12 +2115,13 @@ def check_unpack_archive(self, format, **kwargs):
self.check_unpack_archive_with_converter(format, FakePath, **kwargs)

def check_unpack_archive_with_converter(self, format, converter, **kwargs):
make_format = kwargs.pop("make_format", format)
root_dir, base_dir = self._create_files()
expected = rlistdir(root_dir)
expected.remove('outer')

base_name = os.path.join(self.mkdtemp(), 'archive')
filename = make_archive(base_name, format, root_dir, base_dir)
filename = make_archive(base_name, make_format, root_dir, base_dir)

# let's try to unpack it now
tmpdir2 = self.mkdtemp()
Expand Down Expand Up @@ -2168,6 +2169,12 @@ def test_unpack_archive_zip(self):
with self.assertRaises(TypeError):
self.check_unpack_archive('zip', filter='data')

@support.requires_zlib()
def test_unpack_archive_whl(self):
self.check_unpack_archive('whl', make_format='zip')
with self.assertRaises(TypeError):
self.check_unpack_archive('whl', filter='data', make_format='zip')

def test_unpack_registry(self):

formats = get_unpack_formats()
Expand Down
Loading