From 6055df1b9e8678af05961acea52f5394062fc47e Mon Sep 17 00:00:00 2001 From: henriquejsza Date: Fri, 21 Aug 2026 06:43:05 -0300 Subject: [PATCH] fix(pypi): handle revisions in uv.lock Git sources --- news/4084.fixed.md | 3 +++ python/private/pypi/parse_requirements.bzl | 6 +++++- .../pypi/parse_requirements/parse_requirements_tests.bzl | 8 ++++---- 3 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 news/4084.fixed.md diff --git a/news/4084.fixed.md b/news/4084.fixed.md new file mode 100644 index 0000000000..6e19ca3835 --- /dev/null +++ b/news/4084.fixed.md @@ -0,0 +1,3 @@ +(pypi) Fixed {obj}`pip.parse` repository names for Git sources in `uv.lock` +files by excluding URL query and fragment components +([#4084](https://github.com/bazel-contrib/rules_python/issues/4084)). diff --git a/python/private/pypi/parse_requirements.bzl b/python/private/pypi/parse_requirements.bzl index 4874c7ac79..580989e3ff 100644 --- a/python/private/pypi/parse_requirements.bzl +++ b/python/private/pypi/parse_requirements.bzl @@ -227,7 +227,11 @@ def _parse_uv_lock_json(uv_lock, all_platforms, logger, extra_pip_args = None, p git_struct = None if pkg.get("source", {}).get("git"): url = pkg["source"]["git"] - _, _, filename = url.rpartition("/") + + # Keep the revision in the URL, but exclude it from the repository filename. + url_path, _, _ = url.partition("?") + url_path, _, _ = url_path.partition("#") + _, _, filename = url_path.rpartition("/") git_struct = struct( filename = filename, url = url, diff --git a/tests/pypi/parse_requirements/parse_requirements_tests.bzl b/tests/pypi/parse_requirements/parse_requirements_tests.bzl index 54f906929a..f6b9d7e5a5 100644 --- a/tests/pypi/parse_requirements/parse_requirements_tests.bzl +++ b/tests/pypi/parse_requirements/parse_requirements_tests.bzl @@ -114,7 +114,7 @@ bar==0.0.1 --hash=sha256:deadb00f "uv_lock_foo_sha512": """{"package":[{"name":"foo","source":{"registry":"https://pypi.org/simple"},"version":"0.0.1","wheels":[{"hash":"sha512:deadbeef","url":"https://files.pythonhosted.org/packages/foo-0.0.1-py3-none-any.whl"}]}]}""", "uv_lock_foo_virtual": """{"package":[{"name":"foo","source":{"registry":"https://pypi.org/simple"},"version":"0.0.1","wheels":[{"hash":"sha256:deadbeef","url":"https://files.pythonhosted.org/packages/foo-0.0.1-py3-none-any.whl"}]},{"name":"virtual-pkg","source":{"virtual":true},"version":"0.0.0"}]}""", "uv_lock_foo_with_extras": """{"package":[{"name":"foo","provides-extras":["extra"],"source":{"registry":"https://pypi.org/simple"},"version":"0.0.1","wheels":[{"hash":"sha256:deadbeef","url":"https://files.pythonhosted.org/packages/foo-0.0.1-py3-none-any.whl"}]}]}""", - "uv_lock_git_vcs": """{"package":[{"name":"foo","source":{"git":"https://github.com/org/foo.git"},"version":"0.1.0"}]}""", + "uv_lock_git_vcs": """{"package":[{"name":"foo","source":{"git":"https://github.com/org/foo?rev=deadbeef#deadbeef"},"version":"0.1.0"}]}""", "uv_lock_rules_python_pkg": """{"package":[{"name":"rules_python","source":{"registry":"https://pypi.org/simple"},"version":"0.0.1","wheels":[{"hash":"sha256:deadbeef","url":"https://files.pythonhosted.org/packages/rules_python-0.0.1-py3-none-any.whl"}]}]}""", } @@ -1321,7 +1321,7 @@ def _test_uv_lock_cross_consistent(env): _tests.append(_test_uv_lock_cross_consistent) def _test_uv_lock_vcs_entry(env): - """Test that VCS entries in uv.lock are handled without crashing.""" + """Test that VCS entry filenames exclude URL query and fragment components.""" got = parse_requirements( uv_lock = "uv_lock_git_vcs", ) @@ -1337,9 +1337,9 @@ def _test_uv_lock_vcs_entry(env): extra_pip_args = [], requirement_line = "foo==0.1.0", target_platforms = ["linux_x86_64"], - filename = "foo.git", + filename = "foo", digest = "", - url = "https://github.com/org/foo.git", + url = "https://github.com/org/foo?rev=deadbeef#deadbeef", yanked = None, ), ],