Skip to content

Commit 025fe55

Browse files
gh-84027: Add whl as an unpack format in shutil (#140385)
Co-authored-by: Emma Smith <emma@emmatyping.dev> Co-authored-by: Stan Ulbrych <stan@python.org>
1 parent 1374fde commit 025fe55

5 files changed

Lines changed: 32 additions & 5 deletions

File tree

‎Doc/library/shutil.rst‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules.
753753
unpacked. If not provided, the current working directory is used.
754754

755755
*format* is the archive format: one of "zip", "tar", "gztar", "bztar",
756-
"xztar", or "zstdtar". Or any other format registered with
756+
"xztar", "zstdtar", or "whl". Or any other format registered with
757757
:func:`register_unpack_format`. If not provided, :func:`unpack_archive`
758758
will use the archive file name extension and see if an unpacker was
759759
registered for that extension. In case none is found,
@@ -786,6 +786,9 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules.
786786
.. versionchanged:: 3.12
787787
Added the *filter* argument.
788788

789+
.. versionchanged:: next
790+
Added the "whl" format.
791+
789792
.. function:: register_unpack_format(name, extensions, function[, extra_args[, description]])
790793

791794
Registers an unpack format. *name* is the name of the format and
@@ -827,10 +830,16 @@ provided. They rely on the :mod:`zipfile` and :mod:`tarfile` modules.
827830
- *xztar*: xz'ed tar-file (if the :mod:`lzma` module is available).
828831
- *zstdtar*: Zstandard compressed tar-file (if the :mod:`compression.zstd`
829832
module is available).
833+
- *whl*: `wheel file <https://packaging.python.org/en/latest/discussions/package-formats/#what-is-a-wheel>`__
834+
(unpacking compressed files works only if the corresponding module is
835+
available).
830836

831837
You can register new formats or provide your own unpacker for any existing
832838
formats, by using :func:`register_unpack_format`.
833839

840+
.. versionchanged:: next
841+
Added the *whl* format.
842+
834843

835844
.. _shutil-archiving-example:
836845

‎Doc/whatsnew/3.16.rst‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,9 @@ shutil
681681
:func:`shutil.copystat`, e.g. when copying a read-only tree.
682682
(Contributed by Łukasz Langa in :gh:`89721`.)
683683

684+
* :func:`shutil.unpack_archive` can now unpack wheel files using the ``whl`` format.
685+
(Contributed by Emma Harper Smith and Stan Ulbrych in :gh:`84027`.)
686+
684687

685688
socket
686689
------

‎Lib/shutil.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,7 @@ def _unpack_tarfile(filename, extract_dir, *, filter=None):
13761376
_UNPACK_FORMATS = {
13771377
'tar': (['.tar'], _unpack_tarfile, [], "uncompressed tar file"),
13781378
'zip': (['.zip'], _unpack_zipfile, [], "ZIP file"),
1379+
'whl': (['.whl'], _unpack_zipfile, [], "Wheel file"),
13791380
}
13801381

13811382
if _ZLIB_SUPPORTED:
@@ -1410,9 +1411,9 @@ def unpack_archive(filename, extract_dir=None, format=None, *, filter=None):
14101411
is unpacked. If not provided, the current working directory is used.
14111412
14121413
`format` is the archive format: one of "zip", "tar", "gztar", "bztar",
1413-
"xztar", or "zstdtar". Or any other registered format. If not provided,
1414-
unpack_archive will use the filename extension and see if an unpacker
1415-
was registered for that extension.
1414+
"xztar", "zstdtar", or "whl". Or any other registered format. If not
1415+
provided, unpack_archive will use the filename extension and see if an
1416+
unpacker was registered for that extension.
14161417
14171418
In case none is found, a ValueError is raised.
14181419

‎Lib/test/test_shutil.py‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2287,12 +2287,18 @@ def check_unpack_archive(self, format, **kwargs):
22872287
self.check_unpack_archive_with_converter(format, FakePath, **kwargs)
22882288

22892289
def check_unpack_archive_with_converter(self, format, converter, **kwargs):
2290+
make_format = kwargs.pop("make_format", format)
2291+
extension = kwargs.pop("extension", None)
22902292
root_dir, base_dir = self._create_files()
22912293
expected = rlistdir(root_dir)
22922294
expected.remove('outer')
22932295

22942296
base_name = os.path.join(self.mkdtemp(), 'archive')
2295-
filename = make_archive(base_name, format, root_dir, base_dir)
2297+
filename = make_archive(base_name, make_format, root_dir, base_dir)
2298+
if extension is not None:
2299+
new_filename = os.path.splitext(filename)[0] + extension
2300+
os.rename(filename, new_filename)
2301+
filename = new_filename
22962302

22972303
# let's try to unpack it now
22982304
tmpdir2 = self.mkdtemp()
@@ -2340,6 +2346,13 @@ def test_unpack_archive_zip(self):
23402346
with self.assertRaises(TypeError):
23412347
self.check_unpack_archive('zip', filter='data')
23422348

2349+
@support.requires_zlib()
2350+
def test_unpack_archive_whl(self):
2351+
self.check_unpack_archive('whl', make_format='zip', extension='.whl')
2352+
with self.assertRaises(TypeError):
2353+
self.check_unpack_archive('whl', filter='data', make_format='zip',
2354+
extension='.whl')
2355+
23432356
def test_unpack_archive_zip_badpaths(self):
23442357
srcdir = self.mkdtemp()
23452358
zipname = os.path.join(srcdir, 'test.zip')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:mod:`shutil` now can unpack wheel packages using the ``whl`` format.

0 commit comments

Comments
 (0)