Skip to content

Commit 75f7782

Browse files
authored
Merge pull request pypa#4766 from di/fix/3777
Fix "Wheel naming is not following PEP 491 convention"
2 parents a443f76 + 8ba79d9 commit 75f7782

File tree

7 files changed

+25
-24
lines changed

7 files changed

+25
-24
lines changed

newsfragments/4766.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix wheel file naming to follow binary distribution specification -- by :user:`di`

setuptools/_normalization.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,13 @@ def filename_component_broken(value: str) -> str:
134134
def safer_name(value: str) -> str:
135135
"""Like ``safe_name`` but can be used as filename component for wheel"""
136136
# See bdist_wheel.safer_name
137-
return filename_component(safe_name(value))
137+
return (
138+
# Per https://packaging.python.org/en/latest/specifications/name-normalization/#name-normalization
139+
re.sub(r"[-_.]+", "-", safe_name(value))
140+
.lower()
141+
# Per https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode
142+
.replace("-", "_")
143+
)
138144

139145

140146
def safer_best_effort_version(value: str) -> str:

setuptools/command/bdist_wheel.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,13 @@
2323
from wheel.wheelfile import WheelFile
2424

2525
from .. import Command, __version__, _shutil
26+
from .._normalization import safer_name
2627
from ..warnings import SetuptoolsDeprecationWarning
2728
from .egg_info import egg_info as egg_info_cls
2829

2930
from distutils import log
3031

3132

32-
def safe_name(name: str) -> str:
33-
"""Convert an arbitrary string to a standard distribution name
34-
Any runs of non-alphanumeric/. characters are replaced with a single '-'.
35-
"""
36-
return re.sub("[^A-Za-z0-9.]+", "-", name)
37-
38-
3933
def safe_version(version: str) -> str:
4034
"""
4135
Convert an arbitrary string to a standard version string
@@ -133,10 +127,6 @@ def get_abi_tag() -> str | None:
133127
return abi
134128

135129

136-
def safer_name(name: str) -> str:
137-
return safe_name(name).replace("-", "_")
138-
139-
140130
def safer_version(version: str) -> str:
141131
return safe_version(version).replace("-", "_")
142132

setuptools/tests/test_bdist_wheel.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,9 @@ def test_no_scripts(wheel_paths):
246246

247247

248248
def test_unicode_record(wheel_paths):
249-
path = next(path for path in wheel_paths if "unicode.dist" in path)
249+
path = next(path for path in wheel_paths if "unicode_dist" in path)
250250
with ZipFile(path) as zf:
251-
record = zf.read("unicode.dist-0.1.dist-info/RECORD")
251+
record = zf.read("unicode_dist-0.1.dist-info/RECORD")
252252

253253
assert "åäö_日本語.py".encode() in record
254254

setuptools/tests/test_dist.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from setuptools import Distribution
99
from setuptools.dist import check_package_data, check_specifier
1010

11-
from .test_easy_install import make_nspkg_sdist
11+
from .test_easy_install import make_trivial_sdist
1212
from .test_find_packages import ensure_files
1313
from .textwrap import DALS
1414

@@ -25,7 +25,7 @@ def test_dist_fetch_build_egg(tmpdir):
2525
def sdist_with_index(distname, version):
2626
dist_dir = index.mkdir(distname)
2727
dist_sdist = f'{distname}-{version}.tar.gz'
28-
make_nspkg_sdist(str(dist_dir.join(dist_sdist)), distname, version)
28+
make_trivial_sdist(str(dist_dir.join(dist_sdist)), distname, version)
2929
with dist_dir.join('index.html').open('w') as fp:
3030
fp.write(
3131
DALS(

setuptools/tests/test_dist_info.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ def test_dist_info_is_the_same_as_in_wheel(
188188
dist_info = next(tmp_path.glob("dir_dist/*.dist-info"))
189189

190190
assert dist_info.name == wheel_dist_info.name
191-
assert dist_info.name.startswith(f"{name.replace('-', '_')}-{version}{suffix}")
191+
assert dist_info.name.startswith(f"my_proj-{version}{suffix}")
192192
for file in "METADATA", "entry_points.txt":
193193
assert read(dist_info / file) == read(wheel_dist_info / file)
194194

setuptools/tests/test_easy_install.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import setuptools.command.easy_install as ei
2727
from pkg_resources import Distribution as PRDistribution, normalize_path, working_set
2828
from setuptools import sandbox
29+
from setuptools._normalization import safer_name
2930
from setuptools.command.easy_install import PthDistributions
3031
from setuptools.dist import Distribution
3132
from setuptools.sandbox import run_setup
@@ -670,11 +671,11 @@ def test_setup_requires_override_nspkg(self, use_setup_cfg):
670671

671672
with contexts.save_pkg_resources_state():
672673
with contexts.tempdir() as temp_dir:
673-
foobar_1_archive = os.path.join(temp_dir, 'foo.bar-0.1.tar.gz')
674+
foobar_1_archive = os.path.join(temp_dir, 'foo_bar-0.1.tar.gz')
674675
make_nspkg_sdist(foobar_1_archive, 'foo.bar', '0.1')
675676
# Now actually go ahead an extract to the temp dir and add the
676677
# extracted path to sys.path so foo.bar v0.1 is importable
677-
foobar_1_dir = os.path.join(temp_dir, 'foo.bar-0.1')
678+
foobar_1_dir = os.path.join(temp_dir, 'foo_bar-0.1')
678679
os.mkdir(foobar_1_dir)
679680
with tarfile.open(foobar_1_archive) as tf:
680681
tf.extraction_filter = lambda member, path: member
@@ -697,7 +698,7 @@ def test_setup_requires_override_nspkg(self, use_setup_cfg):
697698
len(foo.__path__) == 2):
698699
print('FAIL')
699700
700-
if 'foo.bar-0.2' not in foo.__path__[0]:
701+
if 'foo_bar-0.2' not in foo.__path__[0]:
701702
print('FAIL')
702703
"""
703704
)
@@ -718,8 +719,8 @@ def test_setup_requires_override_nspkg(self, use_setup_cfg):
718719
# Don't even need to install the package, just
719720
# running the setup.py at all is sufficient
720721
run_setup(test_setup_py, ['--name'])
721-
except pkg_resources.VersionConflict:
722-
self.fail(
722+
except pkg_resources.VersionConflict: # pragma: nocover
723+
pytest.fail(
723724
'Installing setup.py requirements caused a VersionConflict'
724725
)
725726

@@ -1120,6 +1121,8 @@ def make_nspkg_sdist(dist_path, distname, version):
11201121
package with the same name as distname. The top-level package is
11211122
designated a namespace package).
11221123
"""
1124+
# Assert that the distname contains at least one period
1125+
assert '.' in distname
11231126

11241127
parts = distname.split('.')
11251128
nspackage = parts[0]
@@ -1207,10 +1210,11 @@ def create_setup_requires_package(
12071210
package itself is just 'test_pkg'.
12081211
"""
12091212

1213+
normalized_distname = safer_name(distname)
12101214
test_setup_attrs = {
12111215
'name': 'test_pkg',
12121216
'version': '0.0',
1213-
'setup_requires': [f'{distname}=={version}'],
1217+
'setup_requires': [f'{normalized_distname}=={version}'],
12141218
'dependency_links': [os.path.abspath(path)],
12151219
}
12161220
if setup_attrs:
@@ -1259,7 +1263,7 @@ def create_setup_requires_package(
12591263
with open(os.path.join(test_pkg, 'setup.py'), 'w', encoding="utf-8") as f:
12601264
f.write(setup_py_template % test_setup_attrs)
12611265

1262-
foobar_path = os.path.join(path, f'{distname}-{version}.tar.gz')
1266+
foobar_path = os.path.join(path, f'{normalized_distname}-{version}.tar.gz')
12631267
make_package(foobar_path, distname, version)
12641268

12651269
return test_pkg

0 commit comments

Comments
 (0)