fix: requires_file preserves extras that package depends on (#2807)
When requirements are passed in through `requires_file` the extras are
not preserved.
eg if the contents of requires file is `example[extras]==1.1.1`, bazel
will currently write to the METADATA file `Requires-Dist:
example==1.1.1`. This PR attempts to fix that by adding that back if
there are any extras.
The expected output should be `Requires-Dist: example[extras]==1.1.1`
diff --git a/.bazelrc b/.bazelrc
index 4e6f2fa..d2e0721 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -4,8 +4,8 @@
# (Note, we cannot use `common --deleted_packages` because the bazel version command doesn't support it)
# To update these lines, execute
# `bazel run @rules_bazel_integration_test//tools:update_deleted_packages`
-build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma
-query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma
+build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/python/private,gazelle/pythonconfig,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma
+query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/python/private,gazelle/pythonconfig,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma
test --test_output=errors
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a8cac4c..19fe636 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -74,6 +74,8 @@
* The {obj}`//python/runtime_env_toolchains:all` toolchain now works with it.
* (rules) Better handle flakey platform.win32_ver() calls by calling them
multiple times.
+* (tools/wheelmaker.py) Extras are now preserved in Requires-Dist metadata when using requires_file
+ to specify the requirements.
{#v0-0-0-added}
### Added
diff --git a/examples/wheel/BUILD.bazel b/examples/wheel/BUILD.bazel
index b434e67..e52e0fc 100644
--- a/examples/wheel/BUILD.bazel
+++ b/examples/wheel/BUILD.bazel
@@ -313,6 +313,17 @@
""".splitlines(),
)
+write_file(
+ name = "requires_dist_depends_on_extras_file",
+ out = "requires_dist_depends_on_extras.txt",
+ content = """\
+# Requirements file
+--index-url https://pypi.com
+
+extra_requires[example]==0.0.1
+""".splitlines(),
+)
+
# py_wheel can use text files to specify their requirements. This
# can be convenient for users of `compile_pip_requirements` who have
# granular `requirements.in` files per package. This target shows
@@ -374,6 +385,22 @@
deps = [":example_pkg"],
)
+py_wheel(
+ name = "requires_dist_depends_on_extras",
+ distribution = "requires_dist_depends_on_extras",
+ requires = [
+ "extra_requires[example]==0.0.1",
+ ],
+ version = "0.0.1",
+)
+
+py_wheel(
+ name = "requires_dist_depends_on_extras_using_file",
+ distribution = "requires_dist_depends_on_extras_using_file",
+ requires_file = ":requires_dist_depends_on_extras.txt",
+ version = "0.0.1",
+)
+
py_test(
name = "wheel_test",
srcs = ["wheel_test.py"],
@@ -391,6 +418,8 @@
":minimal_with_py_package",
":python_abi3_binary_wheel",
":python_requires_in_a_package",
+ ":requires_dist_depends_on_extras",
+ ":requires_dist_depends_on_extras_using_file",
":requires_files",
":use_rule_with_dir_in_outs",
],
diff --git a/examples/wheel/wheel_test.py b/examples/wheel/wheel_test.py
index 35803da..43e56cf 100644
--- a/examples/wheel/wheel_test.py
+++ b/examples/wheel/wheel_test.py
@@ -565,6 +565,56 @@
requires,
)
+ def test_requires_dist_depends_on_extras(self):
+ filename = self._get_path("requires_dist_depends_on_extras-0.0.1-py3-none-any.whl")
+
+ with zipfile.ZipFile(filename) as zf:
+ self.assertAllEntriesHasReproducibleMetadata(zf)
+ metadata_file = None
+ for f in zf.namelist():
+ if os.path.basename(f) == "METADATA":
+ metadata_file = f
+ self.assertIsNotNone(metadata_file)
+
+ requires = []
+ with zf.open(metadata_file) as fp:
+ for line in fp:
+ if line.startswith(b"Requires-Dist:"):
+ requires.append(line.decode("utf-8").strip())
+
+ print(requires)
+ self.assertEqual(
+ [
+ "Requires-Dist: extra_requires[example]==0.0.1",
+ ],
+ requires,
+ )
+
+ def test_requires_dist_depends_on_extras_file(self):
+ filename = self._get_path("requires_dist_depends_on_extras_using_file-0.0.1-py3-none-any.whl")
+
+ with zipfile.ZipFile(filename) as zf:
+ self.assertAllEntriesHasReproducibleMetadata(zf)
+ metadata_file = None
+ for f in zf.namelist():
+ if os.path.basename(f) == "METADATA":
+ metadata_file = f
+ self.assertIsNotNone(metadata_file)
+
+ requires = []
+ with zf.open(metadata_file) as fp:
+ for line in fp:
+ if line.startswith(b"Requires-Dist:"):
+ requires.append(line.decode("utf-8").strip())
+
+ print(requires)
+ self.assertEqual(
+ [
+ "Requires-Dist: extra_requires[example]==0.0.1",
+ ],
+ requires,
+ )
+
if __name__ == "__main__":
unittest.main()
diff --git a/tools/wheelmaker.py b/tools/wheelmaker.py
index 28ec039..de58465 100644
--- a/tools/wheelmaker.py
+++ b/tools/wheelmaker.py
@@ -562,13 +562,14 @@
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.specifier}; ({req.marker}) and {extra}"
+ return f"Requires-Dist: {req.name}{req_extra_deps}{req.specifier}; ({req.marker}) and {extra}"
else:
- return f"Requires-Dist: {req.name}{req.specifier}; {req.marker}"
+ return f"Requires-Dist: {req.name}{req_extra_deps}{req.specifier}; {req.marker}"
else:
- return f"Requires-Dist: {req.name}{req.specifier}; {extra}".strip(" ;")
+ 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: "):