feat: handle url req in wheelmaker (#3569)
Adapt `wheelmaker` so that it now can also handle PEP 508 URL
requirements using the `req.url` attribute.
---------
Co-authored-by: Ignas Anikevicius <240938+aignas@users.noreply.github.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 15118f5..96e7dcd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -102,6 +102,8 @@
{obj}`PyExecutableInfo.interpreter_args`,
{obj}`PyExecutableInfo.stage2_bootstrap`, and
{obj}`PyExecutableInfo.venv_python_exe`.
+* (tools/wheelmaker.py) Added support for URL requirements according to PEP 508
+ in Requires-Dist metadata. ([#3569](https://github.com/bazel-contrib/rules_python/pull/3569))
{#v1-8-3}
## [1.8.3] - 2026-01-27
diff --git a/tests/tools/wheelmaker_test.py b/tests/tools/wheelmaker_test.py
index 288dde7..85094af 100644
--- a/tests/tools/wheelmaker_test.py
+++ b/tests/tools/wheelmaker_test.py
@@ -71,5 +71,41 @@
self.assertEqual(got, want)
+class GetNewRequirementLineTest(unittest.TestCase):
+ def test_requirement(self):
+ result = wheelmaker.get_new_requirement_line("requests>=2.0", "")
+ self.assertEqual(result, "Requires-Dist: requests>=2.0")
+
+ def test_requirement_and_extra(self):
+ result = wheelmaker.get_new_requirement_line("requests>=2.0", "extra=='dev'")
+ self.assertEqual(result, "Requires-Dist: requests>=2.0; extra=='dev'")
+
+ def test_requirement_with_url(self):
+ result = wheelmaker.get_new_requirement_line(
+ "requests @ git+https://github.com/psf/requests.git@3aa6386c3", ""
+ )
+ self.assertEqual(
+ result,
+ "Requires-Dist: requests @ git+https://github.com/psf/requests.git@3aa6386c3",
+ )
+
+ def test_requirement_with_marker(self):
+ result = wheelmaker.get_new_requirement_line(
+ "requests>=2.0; python_version>='3.6'", ""
+ )
+ self.assertEqual(
+ result, 'Requires-Dist: requests>=2.0; python_version >= "3.6"'
+ )
+
+ def test_requirement_with_marker_and_extra(self):
+ result = wheelmaker.get_new_requirement_line(
+ "requests>=2.0; python_version>='3.6'", "extra=='dev'"
+ )
+ self.assertEqual(
+ result,
+ "Requires-Dist: requests>=2.0; (python_version >= \"3.6\") and extra=='dev'",
+ )
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/tools/wheelmaker.py b/tools/wheelmaker.py
index 4390df3..7124ae7 100644
--- a/tools/wheelmaker.py
+++ b/tools/wheelmaker.py
@@ -330,9 +330,7 @@
Wheel-Version: 1.0
Generator: bazel-wheelmaker 1.0
Root-Is-Purelib: {}
-""".format(
- "true" if self._platform == "any" else "false"
- )
+""".format("true" if self._platform == "any" else "false")
for tag in self.disttags():
wheel_contents += "Tag: %s\n" % tag
self._whlfile.add_string(self.distinfo_path("WHEEL"), wheel_contents)
@@ -365,6 +363,32 @@
return files
+def get_new_requirement_line(reqs_text: str, extra: str) -> str:
+ """Formats a requirement text into a Requires-Dist metadata line."""
+ from packaging.requirements import Requirement
+
+ req = Requirement(reqs_text.strip())
+ req_extra_deps = f"[{','.join(req.extras)}]" if req.extras else ""
+
+ # Handle URL requirements (PEP 508)
+ if req.url:
+ req_spec = f" @ {req.url}"
+ else:
+ req_spec = str(req.specifier)
+
+ base = f"Requires-Dist: {req.name}{req_extra_deps}{req_spec}"
+
+ if req.marker:
+ if extra:
+ return f"{base}; ({req.marker}) and {extra}"
+ else:
+ return f"{base}; {req.marker}"
+ elif extra:
+ return f"{base}; {extra}"
+ else:
+ return base
+
+
def resolve_argument_stamp(
argument: str, volatile_status_stamp: Path, stable_status_stamp: Path
) -> str:
@@ -430,7 +454,7 @@
output_group.add_argument(
"--name_file",
type=Path,
- help="A file where the canonical name of the " "wheel will be written",
+ help="A file where the canonical name of the wheel will be written",
)
output_group.add_argument(
@@ -578,19 +602,6 @@
# Search for any `Requires-Dist` entries that refer to other files and
# expand them.
- def get_new_requirement_line(reqs_text, extra):
- req = Requirement(reqs_text.strip())
- req_extra_deps = f"[{','.join(req.extras)}]" if req.extras else ""
- if req.marker:
- if extra:
- return f"Requires-Dist: {req.name}{req_extra_deps}{req.specifier}; ({req.marker}) and {extra}"
- else:
- return f"Requires-Dist: {req.name}{req_extra_deps}{req.specifier}; {req.marker}"
- else:
- return f"Requires-Dist: {req.name}{req_extra_deps}{req.specifier}; {extra}".strip(
- " ;"
- )
-
for meta_line in metadata.splitlines():
if not meta_line.startswith("Requires-Dist: "):
continue