Skip to content

Commit 394cd4e

Browse files
committed
Added packaging.utils.create_wheel_filename and create_sdist_filename functions
1 parent a71c833 commit 394cd4e

4 files changed

Lines changed: 122 additions & 1 deletion

File tree

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Changelog
55
~~~~~~~~~~~~
66

77
* `packaging` is now only compatible with Python 3.6 and above.
8+
* Added ``packaging.utils.create_wheel_filename()`` and ``create_sdist_filename()`` (:issue:`408`)
89

910
20.9 - 2021-01-29
1011
~~~~~~~~~~~~~~~~~

docs/utils.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,33 @@ Reference
4141
>>> canonicalize_version('1.4.0.0.0')
4242
'1.4'
4343

44+
.. function:: create_wheel_filename(name, version, build, tags)
45+
46+
Combines a project name, version, build tag, and tag set
47+
to make a properly formatted wheel filename.
48+
49+
The project name is normalized such that the non-alphanumeric
50+
characters are replaced with ``_``. The version is an instance of
51+
:class:`~packaging.version.Version`. The build tag can be None,
52+
an empty tuple or a two-item tuple of an integer and a string.
53+
The tags is set of tags that will be compressed into a wheel
54+
tag string.
55+
56+
:param str name: The project name
57+
:param ~packaging.version.Version version: The project version
58+
:param Optional[(),(int,str)] build: An optional two-item tuple of an integer and string
59+
:param set[~packaging.tags.Tag] tags: The set of tags that apply to the wheel
60+
61+
.. doctest::
62+
63+
>>> from packaging.utils import create_wheel_filename
64+
>>> from packaging.tags import Tag
65+
>>> from packaging.version import Version
66+
>>> version = Version("1.0")
67+
>>> tags = {Tag("py3", "none", "any")}
68+
>>> "foo_bar-1.0-py3-none-any.whl" == create_wheel_filename("foo-bar", version, None, tags)
69+
True
70+
4471
.. function:: parse_wheel_filename(filename)
4572

4673
This function takes the filename of a wheel file, and parses it,
@@ -70,6 +97,20 @@ Reference
7097
>>> not build
7198
True
7299

100+
.. function:: create_sdist_filename(name, version)
101+
102+
Combines the project name and a version to make a valid sdist filename.
103+
104+
:param str name: The project name
105+
:param ~packaging.version.Version version: The project version
106+
107+
.. doctest::
108+
109+
>>> from packaging.utils import create_sdist_filename
110+
>>> from packaging.version import Version
111+
>>> "foo_bar-1.0.tar.gz" == create_sdist_filename("foo-bar", Version("1.0"))
112+
True
113+
73114
.. function:: parse_sdist_filename(filename)
74115

75116
This function takes the filename of a sdist file (as specified

packaging/utils.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# for complete details.
44

55
import re
6-
from typing import FrozenSet, NewType, Tuple, Union, cast
6+
from typing import AbstractSet, FrozenSet, NewType, Optional, Tuple, Union, cast
77

88
from .tags import Tag, parse_tag
99
from .version import InvalidVersion, Version
@@ -24,6 +24,7 @@ class InvalidSdistFilename(ValueError):
2424
"""
2525

2626

27+
_distribution_regex = re.compile(r"[^\w\d.]+")
2728
_canonicalize_regex = re.compile(r"[-_.]+")
2829
# PEP 427: The build number must start with a digit.
2930
_build_tag_regex = re.compile(r"(\d+)(.*)")
@@ -78,6 +79,30 @@ def canonicalize_version(version: Union[Version, str]) -> str:
7879
return "".join(parts)
7980

8081

82+
def _join_tag_attr(tags: AbstractSet[Tag], field: str) -> str:
83+
return ".".join(sorted({getattr(tag, field) for tag in tags}))
84+
85+
86+
def _compress_tag_set(tags: AbstractSet[Tag]) -> str:
87+
return "-".join(_join_tag_attr(tags, x) for x in ("interpreter", "abi", "platform"))
88+
89+
90+
def create_wheel_filename(
91+
name: str, version: Version, build: Optional[BuildTag], tags: AbstractSet[Tag]
92+
) -> str:
93+
norm_name = _distribution_regex.sub("_", name)
94+
compressed_tag = _compress_tag_set(tags)
95+
96+
parts: Tuple[str, ...]
97+
98+
if build:
99+
parts = norm_name, str(version), "".join(map(str, build)), compressed_tag
100+
else:
101+
parts = norm_name, str(version), compressed_tag
102+
103+
return "-".join(parts) + ".whl"
104+
105+
81106
def parse_wheel_filename(
82107
filename: str
83108
) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]:
@@ -114,6 +139,10 @@ def parse_wheel_filename(
114139
return (name, version, build, tags)
115140

116141

142+
def create_sdist_filename(name: str, version: Version) -> str:
143+
return f"{_distribution_regex.sub('_', name)}-{version}.tar.gz"
144+
145+
117146
def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]:
118147
if not filename.endswith(".tar.gz"):
119148
raise InvalidSdistFilename(

tests/test_utils.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
InvalidWheelFilename,
1111
canonicalize_name,
1212
canonicalize_version,
13+
create_sdist_filename,
14+
create_wheel_filename,
1315
parse_sdist_filename,
1416
parse_wheel_filename,
1517
)
@@ -56,6 +58,43 @@ def test_canonicalize_version(version, expected):
5658
assert canonicalize_version(version) == expected
5759

5860

61+
@pytest.mark.parametrize(
62+
("filename", "name", "version", "build", "tags"),
63+
[
64+
(
65+
"foo-1.0-py3-none-any.whl",
66+
"foo",
67+
Version("1.0"),
68+
(),
69+
{Tag("py3", "none", "any")},
70+
),
71+
(
72+
"foo-1.0-1000-py3-none-any.whl",
73+
"foo",
74+
Version("1.0"),
75+
(1000, ""),
76+
{Tag("py3", "none", "any")},
77+
),
78+
(
79+
"foo-1.0-1000abc-py3-none-any.whl",
80+
"foo",
81+
Version("1.0"),
82+
(1000, "abc"),
83+
{Tag("py3", "none", "any")},
84+
),
85+
(
86+
"foo_bar-1.0-42-py2.py3-none-any.whl",
87+
"foo-bar",
88+
Version("1.0"),
89+
(42, ""),
90+
{Tag("py2", "none", "any"), Tag("py3", "none", "any")},
91+
),
92+
],
93+
)
94+
def test_create_wheel_filename(filename, name, version, build, tags):
95+
assert create_wheel_filename(name, version, build, tags) == filename
96+
97+
5998
@pytest.mark.parametrize(
6099
("filename", "name", "version", "build", "tags"),
61100
[
@@ -103,6 +142,17 @@ def test_parse_wheel_invalid_filename(filename):
103142
parse_wheel_filename(filename)
104143

105144

145+
@pytest.mark.parametrize(
146+
("filename", "name", "version"),
147+
[
148+
("foo-1.0.tar.gz", "foo", Version("1.0")),
149+
("foo_bar-1.0.tar.gz", "foo-bar", Version("1.0")),
150+
],
151+
)
152+
def test_create_sdist_filename(filename, name, version):
153+
assert create_sdist_filename(name, version) == filename
154+
155+
106156
@pytest.mark.parametrize(
107157
("filename", "name", "version"), [("foo-1.0.tar.gz", "foo", Version("1.0"))]
108158
)

0 commit comments

Comments
 (0)