From d37615b8ca65046d48b294ed90faee1a29f213cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio?= Date: Sat, 13 Apr 2024 19:36:24 +0000 Subject: [PATCH 1/8] Add build-tag parameter --- src/pdm/backend/sdist.py | 4 ++++ src/pdm/backend/wheel.py | 37 +++++++++++++++++++++++++++++++++---- tests/test_wheel.py | 16 +++++++--------- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/pdm/backend/sdist.py b/src/pdm/backend/sdist.py index a08647f..a4e8ee2 100644 --- a/src/pdm/backend/sdist.py +++ b/src/pdm/backend/sdist.py @@ -68,6 +68,10 @@ def build_artifact( name = to_filename(canonicalize_name(context.config.metadata["name"])) dist_info = f"{name}-{version}" + if "--build-tag" in context.config_settings: + build_tag = context.config_settings["--build-tag"] + dist_info = f"{dist_info}-{build_tag}" + target = context.dist_dir / f"{dist_info}.tar.gz" with tarfile.open(target, mode="w:gz", format=tarfile.PAX_FORMAT) as tar: diff --git a/src/pdm/backend/wheel.py b/src/pdm/backend/wheel.py index d5f1adb..43cab69 100644 --- a/src/pdm/backend/wheel.py +++ b/src/pdm/backend/wheel.py @@ -46,6 +46,10 @@ Tag: {tag} """ +BUILD_TAG_FORMAT = """\ +Build: {build} +""" + # Fix the date time for reproducible builds try: _env_date = time.gmtime(int(os.environ["SOURCE_DATE_EPOCH"]))[:6] @@ -77,6 +81,7 @@ def __init__( ) -> None: super().__init__(location, config_settings) self.__tag: str | None = None + self.__build_tag: str | None = None def scheme_path(self, name: str, relative: str) -> str: if name not in SCHEME_NAMES: @@ -156,6 +161,10 @@ def build_artifact( records.append(self._add_file_to_zip(zf, rel_path, full_path)) self._write_record(zf, records) + name_version = self.name_version + if self.build_tag: + name_version = f"{name_version}-{self.build_tag}" + target = context.dist_dir / f"{self.name_version}-{self.tag}.whl" if target.exists(): target.unlink() @@ -168,6 +177,12 @@ def name_version(self) -> str: version = to_filename(safe_version(self.config.metadata["version"])) return f"{name}-{version}" + @property + def build_tag(self) -> str | None: + if not self.__build_tag: + self.__build_tag = self._get_build_tag() + return self.__build_tag + @property def dist_info_name(self) -> str: return f"{self.name_version}.dist-info" @@ -178,6 +193,15 @@ def tag(self) -> str: self.__tag = self._get_tag() return self.__tag + def _get_build_tag(self) -> str | None: + build_tag: str | None = None + if not self.config_settings: + return build_tag + if "--build-tag" in self.config_settings: + build_tag = self.config_settings["--build-tag"] + + return build_tag + def _get_tag(self) -> str: impl, abi, platform = self._get_platform_tags() is_purelib = self.config.build_config.is_purelib @@ -276,12 +300,17 @@ def _write_wheel_file(self, fp: IO[str], is_purelib: bool) -> None: except ModuleNotFoundError: version = "0.0.0+local" - fp.write( - WHEEL_FILE_FORMAT.format( - is_purelib=str(is_purelib).lower(), tag=self.tag, version=version - ) + wheel_metadata = WHEEL_FILE_FORMAT.format( + is_purelib=str(is_purelib).lower(), tag=self.tag, version=version ) + if self.build_tag: + wheel_metadata = ( + f"{wheel_metadata}\n{BUILD_TAG_FORMAT.format(build_tag=self.build_tag)}" + ) + + fp.write(wheel_metadata) + def _write_entry_points( self, fp: IO[str], entry_points: dict[str, dict[str, str]] ) -> None: diff --git a/tests/test_wheel.py b/tests/test_wheel.py index 9ad6907..9554ac9 100644 --- a/tests/test_wheel.py +++ b/tests/test_wheel.py @@ -16,17 +16,15 @@ ], ) def test_override_tags_in_wheel_filename( - python_tag: str, py_limited_api: str, plat_name: str, tag: str + python_tag: str, py_limited_api: str, plat_name: str, build_tag: str, tag: str ) -> None: project = FIXTURES / "projects/demo-cextension" - with wheel.WheelBuilder( - project, - config_settings={ - "--python-tag": python_tag, - "--py-limited-api": py_limited_api, - "--plat-name": plat_name, - }, - ) as builder: + config_settings = { + "--python-tag": python_tag, + "--py-limited-api": py_limited_api, + "--plat-name": plat_name, + } + with wheel.WheelBuilder(project, config_settings=config_settings) as builder: assert builder.tag == tag From cb40374dd4fe29b18fd7606791a9fc26b4bfc2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio?= Date: Sun, 14 Apr 2024 15:09:54 +0000 Subject: [PATCH 2/8] Revert changes in sdist.py --- src/pdm/backend/sdist.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pdm/backend/sdist.py b/src/pdm/backend/sdist.py index a4e8ee2..a08647f 100644 --- a/src/pdm/backend/sdist.py +++ b/src/pdm/backend/sdist.py @@ -68,10 +68,6 @@ def build_artifact( name = to_filename(canonicalize_name(context.config.metadata["name"])) dist_info = f"{name}-{version}" - if "--build-tag" in context.config_settings: - build_tag = context.config_settings["--build-tag"] - dist_info = f"{dist_info}-{build_tag}" - target = context.dist_dir / f"{dist_info}.tar.gz" with tarfile.open(target, mode="w:gz", format=tarfile.PAX_FORMAT) as tar: From e83ee1b97f4251458eed36718224ecc1917a3e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio?= Date: Sun, 14 Apr 2024 16:24:56 +0000 Subject: [PATCH 3/8] Add unit tests for build-number --- src/pdm/backend/wheel.py | 44 ++++++++++++++++++++-------------------- tests/test_api.py | 25 +++++++++++++++++++++++ tests/test_wheel.py | 2 +- 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/pdm/backend/wheel.py b/src/pdm/backend/wheel.py index 43cab69..bdad264 100644 --- a/src/pdm/backend/wheel.py +++ b/src/pdm/backend/wheel.py @@ -17,7 +17,7 @@ from pdm.backend._vendor.packaging import tags from pdm.backend._vendor.packaging.specifiers import SpecifierSet -from pdm.backend._vendor.packaging.utils import canonicalize_name +from pdm.backend._vendor.packaging.utils import _build_tag_regex, canonicalize_name from pdm.backend.base import Builder from pdm.backend.hooks import Context from pdm.backend.hooks.setuptools import SetuptoolsBuildHook @@ -46,9 +46,7 @@ Tag: {tag} """ -BUILD_TAG_FORMAT = """\ -Build: {build} -""" +BUILD_TAG_FORMAT = "Build: {build_number}" # Fix the date time for reproducible builds try: @@ -81,7 +79,7 @@ def __init__( ) -> None: super().__init__(location, config_settings) self.__tag: str | None = None - self.__build_tag: str | None = None + self.__build_number: str | None = None def scheme_path(self, name: str, relative: str) -> str: if name not in SCHEME_NAMES: @@ -162,10 +160,10 @@ def build_artifact( self._write_record(zf, records) name_version = self.name_version - if self.build_tag: - name_version = f"{name_version}-{self.build_tag}" + if self.build_number: + name_version = f"{name_version}-{self.build_number}" - target = context.dist_dir / f"{self.name_version}-{self.tag}.whl" + target = context.dist_dir / f"{name_version}-{self.tag}.whl" if target.exists(): target.unlink() shutil.move(temp_name, target) @@ -178,10 +176,10 @@ def name_version(self) -> str: return f"{name}-{version}" @property - def build_tag(self) -> str | None: - if not self.__build_tag: - self.__build_tag = self._get_build_tag() - return self.__build_tag + def build_number(self) -> str | None: + if not self.__build_number: + self.__build_number = self._get_build_number() + return self.__build_number @property def dist_info_name(self) -> str: @@ -193,14 +191,18 @@ def tag(self) -> str: self.__tag = self._get_tag() return self.__tag - def _get_build_tag(self) -> str | None: - build_tag: str | None = None + def _get_build_number(self) -> str | None: + build_number: str | None = None if not self.config_settings: - return build_tag - if "--build-tag" in self.config_settings: - build_tag = self.config_settings["--build-tag"] + return build_number + if (cmd := "--build-number") in self.config_settings: + build_number = self.config_settings[cmd] + if not _build_tag_regex.match(build_number): + raise ValueError( + f"Invalid build number: {build_number}, please refer to PEP 427" + ) - return build_tag + return build_number def _get_tag(self) -> str: impl, abi, platform = self._get_platform_tags() @@ -304,10 +306,8 @@ def _write_wheel_file(self, fp: IO[str], is_purelib: bool) -> None: is_purelib=str(is_purelib).lower(), tag=self.tag, version=version ) - if self.build_tag: - wheel_metadata = ( - f"{wheel_metadata}\n{BUILD_TAG_FORMAT.format(build_tag=self.build_tag)}" - ) + if self.build_number: + wheel_metadata = f"{wheel_metadata}{BUILD_TAG_FORMAT.format(build_number=self.build_number)}" fp.write(wheel_metadata) diff --git a/tests/test_api.py b/tests/test_api.py index 9d159e6..96bd829 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -42,6 +42,31 @@ def test_build_single_module(dist: Path) -> None: assert "demo_module-0.1.0.dist-info/licenses/LICENSE" in zip_names +@pytest.mark.parametrize("name", ["demo-module"]) +def test_build_single_module_with_build_number(dist: Path) -> None: + wheel_name = api.build_wheel( + dist.as_posix(), + config_settings={"--build-number": (build_number := "20231241")}, + ) + assert wheel_name == f"demo_module-0.1.0-{build_number}-py3-none-any.whl" + with zipfile.ZipFile(dist / wheel_name) as zf: + wheel_metadata = email.message_from_bytes( + zf.read("demo_module-0.1.0.dist-info/WHEEL") + ) + assert wheel_metadata["Build"] == build_number + + +@pytest.mark.parametrize("name", ["demo-module"]) +def test_build_single_module_without_build_number(dist: Path) -> None: + wheel_name = api.build_wheel(dist.as_posix()) + assert wheel_name == "demo_module-0.1.0-py3-none-any.whl" + with zipfile.ZipFile(dist / wheel_name) as zf: + wheel_metadata = email.message_from_bytes( + zf.read("demo_module-0.1.0.dist-info/WHEEL") + ) + assert "Build" not in wheel_metadata + + @pytest.mark.parametrize("name", ["demo-package"]) def test_build_package(dist: Path) -> None: wheel_name = api.build_wheel(dist.as_posix()) diff --git a/tests/test_wheel.py b/tests/test_wheel.py index 9554ac9..aedc075 100644 --- a/tests/test_wheel.py +++ b/tests/test_wheel.py @@ -16,7 +16,7 @@ ], ) def test_override_tags_in_wheel_filename( - python_tag: str, py_limited_api: str, plat_name: str, build_tag: str, tag: str + python_tag: str, py_limited_api: str, plat_name: str, tag: str ) -> None: project = FIXTURES / "projects/demo-cextension" config_settings = { From 7dbabfbe98deeea0dca365f2b04d6fdbad221507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio?= Date: Sun, 14 Apr 2024 16:51:32 +0000 Subject: [PATCH 4/8] Update documentation --- docs/build_config.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/build_config.md b/docs/build_config.md index 00bc423..6237f3b 100644 --- a/docs/build_config.md +++ b/docs/build_config.md @@ -328,6 +328,7 @@ Some build frontends such as [build] and [pdm] supports passing options from com - `--python-tag=` Override the python implementation compatibility tag(e.g. `cp37`, `py3`, `pp3`) - `--py-limited-api=` Python tag (`cp32`|`cp33`|`cpNN`) for abi3 wheel tag - `--plat-name=` Override the platform name(e.g. `win_amd64`, `manylinux2010_x86_64`) +- `--build-number=` Build number for this particular version. As specified in PEP-0427, this must start with a digit. - `no-clean-build` Don't clean the build directory before the build starts, this can also work by setting env var `PDM_BUILD_NO_CLEAN` to `1`. For example, you can supply these options with [build]: From 25a7c860e72cfb69ea0ceccbc243ca43af2f63ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio?= Date: Sun, 14 Apr 2024 17:02:45 +0000 Subject: [PATCH 5/8] Revert test_wheel.py --- tests/test_wheel.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/test_wheel.py b/tests/test_wheel.py index aedc075..9ad6907 100644 --- a/tests/test_wheel.py +++ b/tests/test_wheel.py @@ -19,12 +19,14 @@ def test_override_tags_in_wheel_filename( python_tag: str, py_limited_api: str, plat_name: str, tag: str ) -> None: project = FIXTURES / "projects/demo-cextension" - config_settings = { - "--python-tag": python_tag, - "--py-limited-api": py_limited_api, - "--plat-name": plat_name, - } - with wheel.WheelBuilder(project, config_settings=config_settings) as builder: + with wheel.WheelBuilder( + project, + config_settings={ + "--python-tag": python_tag, + "--py-limited-api": py_limited_api, + "--plat-name": plat_name, + }, + ) as builder: assert builder.tag == tag From 9ccaa2d74007dba619c63f4b9ac4e3c42d52bdc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio?= Date: Mon, 15 Apr 2024 11:49:45 +0200 Subject: [PATCH 6/8] Update src/pdm/backend/wheel.py Applied suggestions from review Co-authored-by: Frost Ming --- src/pdm/backend/wheel.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pdm/backend/wheel.py b/src/pdm/backend/wheel.py index bdad264..17070a9 100644 --- a/src/pdm/backend/wheel.py +++ b/src/pdm/backend/wheel.py @@ -192,10 +192,8 @@ def tag(self) -> str: return self.__tag def _get_build_number(self) -> str | None: - build_number: str | None = None - if not self.config_settings: - return build_number - if (cmd := "--build-number") in self.config_settings: + if (cmd := "--build-number") not in self.config_settings: + return None build_number = self.config_settings[cmd] if not _build_tag_regex.match(build_number): raise ValueError( From 8c719b7aafb6a947b143f394c49bfc719f4a3e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio?= Date: Mon, 15 Apr 2024 11:50:08 +0200 Subject: [PATCH 7/8] Update src/pdm/backend/wheel.py Applied suggestions from review Co-authored-by: Frost Ming --- src/pdm/backend/wheel.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pdm/backend/wheel.py b/src/pdm/backend/wheel.py index 17070a9..238023a 100644 --- a/src/pdm/backend/wheel.py +++ b/src/pdm/backend/wheel.py @@ -194,11 +194,11 @@ def tag(self) -> str: def _get_build_number(self) -> str | None: if (cmd := "--build-number") not in self.config_settings: return None - build_number = self.config_settings[cmd] - if not _build_tag_regex.match(build_number): - raise ValueError( - f"Invalid build number: {build_number}, please refer to PEP 427" - ) + build_number = self.config_settings[cmd] + if not _build_tag_regex.match(build_number): + raise ValueError( + f"Invalid build number: {build_number}, please refer to PEP 427" + ) return build_number From d6657693d91c340325c7a1db5994476051a9096a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio?= Date: Tue, 16 Apr 2024 07:27:36 +0000 Subject: [PATCH 8/8] Remove `:=` operator due to py3.7 support --- src/pdm/backend/wheel.py | 3 ++- tests/test_api.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pdm/backend/wheel.py b/src/pdm/backend/wheel.py index 238023a..5783efd 100644 --- a/src/pdm/backend/wheel.py +++ b/src/pdm/backend/wheel.py @@ -192,7 +192,8 @@ def tag(self) -> str: return self.__tag def _get_build_number(self) -> str | None: - if (cmd := "--build-number") not in self.config_settings: + cmd = "--build-number" + if cmd not in self.config_settings: return None build_number = self.config_settings[cmd] if not _build_tag_regex.match(build_number): diff --git a/tests/test_api.py b/tests/test_api.py index 96bd829..34e5d3f 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -44,9 +44,10 @@ def test_build_single_module(dist: Path) -> None: @pytest.mark.parametrize("name", ["demo-module"]) def test_build_single_module_with_build_number(dist: Path) -> None: + build_number = "20231241" wheel_name = api.build_wheel( dist.as_posix(), - config_settings={"--build-number": (build_number := "20231241")}, + config_settings={"--build-number": build_number}, ) assert wheel_name == f"demo_module-0.1.0-{build_number}-py3-none-any.whl" with zipfile.ZipFile(dist / wheel_name) as zf: