From 83d425d1efe433dccb56ee89a726f50e7e5e9536 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Mon, 9 Dec 2024 23:56:00 +0000 Subject: [PATCH 01/12] Fix wheel file naming --- setuptools/_normalization.py | 8 +++++++- setuptools/command/bdist_wheel.py | 14 ++++++++++---- setuptools/tests/test_bdist_wheel.py | 4 ++-- setuptools/tests/test_dist_info.py | 2 +- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/setuptools/_normalization.py b/setuptools/_normalization.py index 467b643d46..9541a55d6c 100644 --- a/setuptools/_normalization.py +++ b/setuptools/_normalization.py @@ -134,7 +134,13 @@ def filename_component_broken(value: str) -> str: def safer_name(value: str) -> str: """Like ``safe_name`` but can be used as filename component for wheel""" # See bdist_wheel.safer_name - return filename_component(safe_name(value)) + return ( + # Per https://packaging.python.org/en/latest/specifications/name-normalization/#name-normalization + re.sub(r"[-_.]+", "-", safe_name(value)) + .lower() + # Per https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode + .replace("-", "_") + ) def safer_best_effort_version(value: str) -> str: diff --git a/setuptools/command/bdist_wheel.py b/setuptools/command/bdist_wheel.py index 234df2a7c7..2f129481fa 100644 --- a/setuptools/command/bdist_wheel.py +++ b/setuptools/command/bdist_wheel.py @@ -134,7 +134,13 @@ def get_abi_tag() -> str | None: def safer_name(name: str) -> str: - return safe_name(name).replace("-", "_") + return ( + # Per https://packaging.python.org/en/latest/specifications/name-normalization/#name-normalization + re.sub(r"[-_.]+", "-", safe_name(name)) + .lower() + # Per https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode + .replace("-", "_") + ) def safer_version(version: str) -> str: @@ -364,9 +370,9 @@ def get_tag(self) -> tuple[str, str, str]: supported_tags = [ (t.interpreter, t.abi, plat_name) for t in tags.sys_tags() ] - assert tag in supported_tags, ( - f"would build wheel with unsupported tag {tag}" - ) + assert ( + tag in supported_tags + ), f"would build wheel with unsupported tag {tag}" return tag def run(self): diff --git a/setuptools/tests/test_bdist_wheel.py b/setuptools/tests/test_bdist_wheel.py index d51dfbeb6d..776d21d729 100644 --- a/setuptools/tests/test_bdist_wheel.py +++ b/setuptools/tests/test_bdist_wheel.py @@ -246,9 +246,9 @@ def test_no_scripts(wheel_paths): def test_unicode_record(wheel_paths): - path = next(path for path in wheel_paths if "unicode.dist" in path) + path = next(path for path in wheel_paths if "unicode_dist" in path) with ZipFile(path) as zf: - record = zf.read("unicode.dist-0.1.dist-info/RECORD") + record = zf.read("unicode_dist-0.1.dist-info/RECORD") assert "åäö_日本語.py".encode() in record diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py index 31e6e95a68..426694e019 100644 --- a/setuptools/tests/test_dist_info.py +++ b/setuptools/tests/test_dist_info.py @@ -188,7 +188,7 @@ def test_dist_info_is_the_same_as_in_wheel( dist_info = next(tmp_path.glob("dir_dist/*.dist-info")) assert dist_info.name == wheel_dist_info.name - assert dist_info.name.startswith(f"{name.replace('-', '_')}-{version}{suffix}") + assert dist_info.name.startswith(f"my_proj-{version}{suffix}") for file in "METADATA", "entry_points.txt": assert read(dist_info / file) == read(wheel_dist_info / file) From c1bf7c76ace9eec1de1a6fb807787eb3ab51022a Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Mon, 9 Dec 2024 23:59:43 +0000 Subject: [PATCH 02/12] Fix bug in test --- setuptools/tests/test_easy_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 586324be37..f7a39c8ca6 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -719,7 +719,7 @@ def test_setup_requires_override_nspkg(self, use_setup_cfg): # running the setup.py at all is sufficient run_setup(test_setup_py, ['--name']) except pkg_resources.VersionConflict: - self.fail( + pytest.fail( 'Installing setup.py requirements caused a VersionConflict' ) From 3aa2d57bc7e4afd4e665aa47c8aea073b779a240 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Thu, 9 Jan 2025 21:44:19 +0000 Subject: [PATCH 03/12] Fix pesky test --- setuptools/tests/test_easy_install.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 126efc7060..10c2b4f08a 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -670,11 +670,11 @@ def test_setup_requires_override_nspkg(self, use_setup_cfg): with contexts.save_pkg_resources_state(): with contexts.tempdir() as temp_dir: - foobar_1_archive = os.path.join(temp_dir, 'foo.bar-0.1.tar.gz') - make_nspkg_sdist(foobar_1_archive, 'foo.bar', '0.1') + foobar_1_archive = os.path.join(temp_dir, 'foo_bar-0.1.tar.gz') + make_nspkg_sdist(foobar_1_archive, 'foo_bar', '0.1') # Now actually go ahead an extract to the temp dir and add the # extracted path to sys.path so foo.bar v0.1 is importable - foobar_1_dir = os.path.join(temp_dir, 'foo.bar-0.1') + foobar_1_dir = os.path.join(temp_dir, 'foo_bar-0.1') os.mkdir(foobar_1_dir) with tarfile.open(foobar_1_archive) as tf: tf.extraction_filter = lambda member, path: member @@ -697,14 +697,14 @@ def test_setup_requires_override_nspkg(self, use_setup_cfg): len(foo.__path__) == 2): print('FAIL') - if 'foo.bar-0.2' not in foo.__path__[0]: + if 'foo_bar-0.2' not in foo.__path__[0]: print('FAIL') """ ) test_pkg = create_setup_requires_package( temp_dir, - 'foo.bar', + 'foo_bar', '0.2', make_nspkg_sdist, template, @@ -1120,8 +1120,10 @@ def make_nspkg_sdist(dist_path, distname, version): package with the same name as distname. The top-level package is designated a namespace package). """ - - parts = distname.split('.') + # The project name might not contain periods. Replace dashes and + # underscores with periods before constructing the namespace. + namespace = distname.replace('-', '.').replace('_', '.') + parts = namespace.split('.') nspackage = parts[0] packages = ['.'.join(parts[:idx]) for idx in range(1, len(parts) + 1)] From eb3ad58fd19c54a34abea70c7786b7b1f2dffe15 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Thu, 9 Jan 2025 22:00:36 +0000 Subject: [PATCH 04/12] Fix linting --- setuptools/command/bdist_wheel.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setuptools/command/bdist_wheel.py b/setuptools/command/bdist_wheel.py index d47f63c4d8..8e66e439d1 100644 --- a/setuptools/command/bdist_wheel.py +++ b/setuptools/command/bdist_wheel.py @@ -368,9 +368,9 @@ def get_tag(self) -> tuple[str, str, str]: supported_tags = [ (t.interpreter, t.abi, plat_name) for t in tags.sys_tags() ] - assert ( - tag in supported_tags - ), f"would build wheel with unsupported tag {tag}" + assert tag in supported_tags, ( + f"would build wheel with unsupported tag {tag}" + ) return tag def run(self): From 12109dfed0b665d8bc5d86bedd564022728faaf9 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Thu, 9 Jan 2025 22:03:25 +0000 Subject: [PATCH 05/12] Add newsfragment --- newsfragments/4766.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/4766.bugfix.rst diff --git a/newsfragments/4766.bugfix.rst b/newsfragments/4766.bugfix.rst new file mode 100644 index 0000000000..fcd54785d8 --- /dev/null +++ b/newsfragments/4766.bugfix.rst @@ -0,0 +1 @@ +Fix wheel file naming to follow binary distribution specification -- by :user:`di` From 2234be899ec1761c4a5bd4ae870d38b291ecf1df Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Thu, 9 Jan 2025 22:21:10 +0000 Subject: [PATCH 06/12] Ignore missing coverage in test --- setuptools/tests/test_easy_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 10c2b4f08a..692d8dbed9 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -718,7 +718,7 @@ def test_setup_requires_override_nspkg(self, use_setup_cfg): # Don't even need to install the package, just # running the setup.py at all is sufficient run_setup(test_setup_py, ['--name']) - except pkg_resources.VersionConflict: + except pkg_resources.VersionConflict: # pragma: nocover pytest.fail( 'Installing setup.py requirements caused a VersionConflict' ) From 8795306772ed9fac82826529eb8b334945c6205c Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Mon, 27 Jan 2025 15:07:28 +0000 Subject: [PATCH 07/12] Call make_nspkg_sdist with 'foo.bar' distname --- setuptools/tests/test_easy_install.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 692d8dbed9..19fffacadb 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -671,7 +671,7 @@ def test_setup_requires_override_nspkg(self, use_setup_cfg): with contexts.save_pkg_resources_state(): with contexts.tempdir() as temp_dir: foobar_1_archive = os.path.join(temp_dir, 'foo_bar-0.1.tar.gz') - make_nspkg_sdist(foobar_1_archive, 'foo_bar', '0.1') + make_nspkg_sdist(foobar_1_archive, 'foo.bar', '0.1') # Now actually go ahead an extract to the temp dir and add the # extracted path to sys.path so foo.bar v0.1 is importable foobar_1_dir = os.path.join(temp_dir, 'foo_bar-0.1') @@ -704,7 +704,7 @@ def test_setup_requires_override_nspkg(self, use_setup_cfg): test_pkg = create_setup_requires_package( temp_dir, - 'foo_bar', + 'foo.bar', '0.2', make_nspkg_sdist, template, From ae5df974d552eb0566b79a434d8a9fd7bdfd1081 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Mon, 27 Jan 2025 15:47:51 +0000 Subject: [PATCH 08/12] Don't normalize the distname in `make_nspkg_sdist` --- setuptools/tests/test_easy_install.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 19fffacadb..c554d03f83 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -1120,10 +1120,7 @@ def make_nspkg_sdist(dist_path, distname, version): package with the same name as distname. The top-level package is designated a namespace package). """ - # The project name might not contain periods. Replace dashes and - # underscores with periods before constructing the namespace. - namespace = distname.replace('-', '.').replace('_', '.') - parts = namespace.split('.') + parts = distname.split('.') nspackage = parts[0] packages = ['.'.join(parts[:idx]) for idx in range(1, len(parts) + 1)] From 1ec0c91ba05772a14f7c18747350b6b2ca7f81ab Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Mon, 27 Jan 2025 15:49:53 +0000 Subject: [PATCH 09/12] Require that `distname` contain at least one period --- setuptools/tests/test_easy_install.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index c554d03f83..e6e6383c6f 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -1120,6 +1120,9 @@ def make_nspkg_sdist(dist_path, distname, version): package with the same name as distname. The top-level package is designated a namespace package). """ + # Assert that the distname contains at least one period + assert '.' in distname + parts = distname.split('.') nspackage = parts[0] From 1836bfbdc1f1e16deb640dc143029e38ba3f9d5d Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Mon, 27 Jan 2025 15:51:30 +0000 Subject: [PATCH 10/12] Normalize distname in `create_setup_requires_package` --- setuptools/tests/test_easy_install.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index e6e6383c6f..b58b0b6666 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -26,6 +26,7 @@ import setuptools.command.easy_install as ei from pkg_resources import Distribution as PRDistribution, normalize_path, working_set from setuptools import sandbox +from setuptools._normalization import safer_name from setuptools.command.easy_install import PthDistributions from setuptools.dist import Distribution from setuptools.sandbox import run_setup @@ -1209,10 +1210,11 @@ def create_setup_requires_package( package itself is just 'test_pkg'. """ + normalized_distname = safer_name(distname) test_setup_attrs = { 'name': 'test_pkg', 'version': '0.0', - 'setup_requires': [f'{distname}=={version}'], + 'setup_requires': [f'{normalized_distname}=={version}'], 'dependency_links': [os.path.abspath(path)], } if setup_attrs: @@ -1261,7 +1263,7 @@ def create_setup_requires_package( with open(os.path.join(test_pkg, 'setup.py'), 'w', encoding="utf-8") as f: f.write(setup_py_template % test_setup_attrs) - foobar_path = os.path.join(path, f'{distname}-{version}.tar.gz') + foobar_path = os.path.join(path, f'{normalized_distname}-{version}.tar.gz') make_package(foobar_path, distname, version) return test_pkg From 9f764e8a6df19248cabd7225ed35bc342affbd95 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Mon, 27 Jan 2025 15:55:30 +0000 Subject: [PATCH 11/12] Consolidate `safe_name`/`safer_name` logic --- setuptools/command/bdist_wheel.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/setuptools/command/bdist_wheel.py b/setuptools/command/bdist_wheel.py index 8e66e439d1..b452097426 100644 --- a/setuptools/command/bdist_wheel.py +++ b/setuptools/command/bdist_wheel.py @@ -23,19 +23,13 @@ from wheel.wheelfile import WheelFile from .. import Command, __version__, _shutil +from .._normalization import safer_name from ..warnings import SetuptoolsDeprecationWarning from .egg_info import egg_info as egg_info_cls from distutils import log -def safe_name(name: str) -> str: - """Convert an arbitrary string to a standard distribution name - Any runs of non-alphanumeric/. characters are replaced with a single '-'. - """ - return re.sub("[^A-Za-z0-9.]+", "-", name) - - def safe_version(version: str) -> str: """ Convert an arbitrary string to a standard version string @@ -133,16 +127,6 @@ def get_abi_tag() -> str | None: return abi -def safer_name(name: str) -> str: - return ( - # Per https://packaging.python.org/en/latest/specifications/name-normalization/#name-normalization - re.sub(r"[-_.]+", "-", safe_name(name)) - .lower() - # Per https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode - .replace("-", "_") - ) - - def safer_version(version: str) -> str: return safe_version(version).replace("-", "_") From 27cc9091c718071f2b41a4c9921b1baa2c876ef3 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Mon, 27 Jan 2025 16:22:17 +0000 Subject: [PATCH 12/12] Remove unnecessary use of `make_nspkg_sdist` --- setuptools/tests/test_dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py index 533eb9f45e..e65ab310e7 100644 --- a/setuptools/tests/test_dist.py +++ b/setuptools/tests/test_dist.py @@ -8,7 +8,7 @@ from setuptools import Distribution from setuptools.dist import check_package_data, check_specifier -from .test_easy_install import make_nspkg_sdist +from .test_easy_install import make_trivial_sdist from .test_find_packages import ensure_files from .textwrap import DALS @@ -25,7 +25,7 @@ def test_dist_fetch_build_egg(tmpdir): def sdist_with_index(distname, version): dist_dir = index.mkdir(distname) dist_sdist = f'{distname}-{version}.tar.gz' - make_nspkg_sdist(str(dist_dir.join(dist_sdist)), distname, version) + make_trivial_sdist(str(dist_dir.join(dist_sdist)), distname, version) with dist_dir.join('index.html').open('w') as fp: fp.write( DALS(