Skip to content

Commit 0f4ff60

Browse files
martin4861aignas
andauthored
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>
1 parent 708b07b commit 0f4ff60

File tree

3 files changed

+66
-17
lines changed

3 files changed

+66
-17
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ END_UNRELEASED_TEMPLATE
102102
{obj}`PyExecutableInfo.interpreter_args`,
103103
{obj}`PyExecutableInfo.stage2_bootstrap`, and
104104
{obj}`PyExecutableInfo.venv_python_exe`.
105+
* (tools/wheelmaker.py) Added support for URL requirements according to PEP 508
106+
in Requires-Dist metadata. ([#3569](https://github.com/bazel-contrib/rules_python/pull/3569))
105107

106108
{#v1-8-3}
107109
## [1.8.3] - 2026-01-27

tests/tools/wheelmaker_test.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,41 @@ def test_arcname_from(self) -> None:
7171
self.assertEqual(got, want)
7272

7373

74+
class GetNewRequirementLineTest(unittest.TestCase):
75+
def test_requirement(self):
76+
result = wheelmaker.get_new_requirement_line("requests>=2.0", "")
77+
self.assertEqual(result, "Requires-Dist: requests>=2.0")
78+
79+
def test_requirement_and_extra(self):
80+
result = wheelmaker.get_new_requirement_line("requests>=2.0", "extra=='dev'")
81+
self.assertEqual(result, "Requires-Dist: requests>=2.0; extra=='dev'")
82+
83+
def test_requirement_with_url(self):
84+
result = wheelmaker.get_new_requirement_line(
85+
"requests @ git+https://github.com/psf/requests.git@3aa6386c3", ""
86+
)
87+
self.assertEqual(
88+
result,
89+
"Requires-Dist: requests @ git+https://github.com/psf/requests.git@3aa6386c3",
90+
)
91+
92+
def test_requirement_with_marker(self):
93+
result = wheelmaker.get_new_requirement_line(
94+
"requests>=2.0; python_version>='3.6'", ""
95+
)
96+
self.assertEqual(
97+
result, 'Requires-Dist: requests>=2.0; python_version >= "3.6"'
98+
)
99+
100+
def test_requirement_with_marker_and_extra(self):
101+
result = wheelmaker.get_new_requirement_line(
102+
"requests>=2.0; python_version>='3.6'", "extra=='dev'"
103+
)
104+
self.assertEqual(
105+
result,
106+
"Requires-Dist: requests>=2.0; (python_version >= \"3.6\") and extra=='dev'",
107+
)
108+
109+
74110
if __name__ == "__main__":
75111
unittest.main()

tools/wheelmaker.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -330,9 +330,7 @@ def add_wheelfile(self):
330330
Wheel-Version: 1.0
331331
Generator: bazel-wheelmaker 1.0
332332
Root-Is-Purelib: {}
333-
""".format(
334-
"true" if self._platform == "any" else "false"
335-
)
333+
""".format("true" if self._platform == "any" else "false")
336334
for tag in self.disttags():
337335
wheel_contents += "Tag: %s\n" % tag
338336
self._whlfile.add_string(self.distinfo_path("WHEEL"), wheel_contents)
@@ -365,6 +363,32 @@ def get_files_to_package(input_files):
365363
return files
366364

367365

366+
def get_new_requirement_line(reqs_text: str, extra: str) -> str:
367+
"""Formats a requirement text into a Requires-Dist metadata line."""
368+
from packaging.requirements import Requirement
369+
370+
req = Requirement(reqs_text.strip())
371+
req_extra_deps = f"[{','.join(req.extras)}]" if req.extras else ""
372+
373+
# Handle URL requirements (PEP 508)
374+
if req.url:
375+
req_spec = f" @ {req.url}"
376+
else:
377+
req_spec = str(req.specifier)
378+
379+
base = f"Requires-Dist: {req.name}{req_extra_deps}{req_spec}"
380+
381+
if req.marker:
382+
if extra:
383+
return f"{base}; ({req.marker}) and {extra}"
384+
else:
385+
return f"{base}; {req.marker}"
386+
elif extra:
387+
return f"{base}; {extra}"
388+
else:
389+
return base
390+
391+
368392
def resolve_argument_stamp(
369393
argument: str, volatile_status_stamp: Path, stable_status_stamp: Path
370394
) -> str:
@@ -430,7 +454,7 @@ def parse_args() -> argparse.Namespace:
430454
output_group.add_argument(
431455
"--name_file",
432456
type=Path,
433-
help="A file where the canonical name of the " "wheel will be written",
457+
help="A file where the canonical name of the wheel will be written",
434458
)
435459

436460
output_group.add_argument(
@@ -578,19 +602,6 @@ def main() -> None:
578602
# Search for any `Requires-Dist` entries that refer to other files and
579603
# expand them.
580604

581-
def get_new_requirement_line(reqs_text, extra):
582-
req = Requirement(reqs_text.strip())
583-
req_extra_deps = f"[{','.join(req.extras)}]" if req.extras else ""
584-
if req.marker:
585-
if extra:
586-
return f"Requires-Dist: {req.name}{req_extra_deps}{req.specifier}; ({req.marker}) and {extra}"
587-
else:
588-
return f"Requires-Dist: {req.name}{req_extra_deps}{req.specifier}; {req.marker}"
589-
else:
590-
return f"Requires-Dist: {req.name}{req_extra_deps}{req.specifier}; {extra}".strip(
591-
" ;"
592-
)
593-
594605
for meta_line in metadata.splitlines():
595606
if not meta_line.startswith("Requires-Dist: "):
596607
continue

0 commit comments

Comments
 (0)