From f0c491e3e8725ea85e0d3c169a42c0673e8271fe Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:08:45 +0900 Subject: [PATCH 1/2] test(pypi): port rules_py uv e2e cases to parse_requirements unit tests Cover uv.lock shapes exercised by the rules_py uv e2e cases that were not yet tested here: a single version split into per-platform records, conflicting dependency groups pinning different versions, hashless wheels, platform-specific wheels without an sdist, local and epoch versions, a root extra enabled through a dev group, bare linux_ wheel tags, and abi3 wheel preference on newer interpreters. --- .../parse_requirements_tests.bzl | 262 +++++++++++++++++- 1 file changed, 249 insertions(+), 13 deletions(-) diff --git a/tests/pypi/parse_requirements/parse_requirements_tests.bzl b/tests/pypi/parse_requirements/parse_requirements_tests.bzl index 3838ad4c73..95c0175ab7 100644 --- a/tests/pypi/parse_requirements/parse_requirements_tests.bzl +++ b/tests/pypi/parse_requirements/parse_requirements_tests.bzl @@ -1754,24 +1754,33 @@ def _uv_graph_package(name, version = "0.0.1", **kwargs): "wheels": [{"url": "https://example.org/{}-{}-py3-none-any.whl".format(name, version)}], }, **kwargs) -def _parse_uv_graph(packages): +def _uv_platform(os, arch, whl_platform_tags, python_version = "3.11.0"): + major, minor = python_version.split(".")[:2] + return struct( + env = pep508_env(python_version = python_version, os = os, arch = arch), + whl_abi_tags = ["none", "abi3", "cp{}{}".format(major, minor)], + whl_platform_tags = ["any"] + whl_platform_tags, + ) + +_UV_GRAPH_PLATFORMS = { + "linux_x86_64": _uv_platform("linux", "x86_64", ["manylinux_*_x86_64"]), + "osx_aarch64": _uv_platform("osx", "aarch64", ["macosx_*_arm64"]), +} + +def _parse_uv_graph(packages, platforms = _UV_GRAPH_PLATFORMS): return {pkg.name: pkg for pkg in parse_requirements( ctx = struct(read = lambda _: json.encode({"package": packages})), uv_lock = "uv.lock", - platforms = { - "linux_x86_64": struct( - env = pep508_env(python_version = "3.11.0", os = "linux", arch = "x86_64"), - whl_abi_tags = ["none", "cp311"], - whl_platform_tags = ["any", "manylinux_*_x86_64"], - ), - "osx_aarch64": struct( - env = pep508_env(python_version = "3.11.0", os = "osx", arch = "aarch64"), - whl_abi_tags = ["none", "cp311"], - whl_platform_tags = ["any", "macosx_*_arm64"], - ), - }, + platforms = platforms, )} +def _src_by_platform(pkg): + return { + p: src + for src in pkg.srcs + for p in src.target_platforms + } + def _test_uv_lock_dependency_markers(env): for wheels in [ [{"url": "https://example.org/uvloop-0.0.1-cp311-cp311-manylinux_2_17_x86_64.whl"}], @@ -1940,6 +1949,233 @@ def _test_uv_lock_partial_extra_markers(env): _tests.append(_test_uv_lock_partial_extra_markers) +def _test_uv_lock_same_version_split_records(env): + """One version split into per-platform records by `tool.uv.environments`.""" + + def _whl(platform_tag): + return {"url": "https://example.org/markupsafe-3.0.3-cp311-cp311-{}.whl".format(platform_tag)} + + got = _parse_uv_graph( + [ + _uv_graph_package("root", source = {"virtual": "."}, dependencies = [{"name": "markupsafe"}]), + _uv_graph_package( + "markupsafe", + version = "3.0.3", + wheels = [_whl("win_amd64")], + **{"resolution-markers": ["sys_platform == 'win32'"]} + ), + _uv_graph_package( + "markupsafe", + version = "3.0.3", + wheels = [_whl("macosx_10_9_x86_64"), _whl("macosx_11_0_arm64")], + **{"resolution-markers": ["sys_platform == 'darwin'"]} + ), + _uv_graph_package( + "markupsafe", + version = "3.0.3", + wheels = [_whl("manylinux_2_17_aarch64"), _whl("manylinux_2_17_x86_64")], + **{"resolution-markers": ["sys_platform != 'darwin' and sys_platform != 'win32'"]} + ), + ], + platforms = _UV_GRAPH_PLATFORMS | { + "windows_x86_64": _uv_platform("windows", "x86_64", ["win_amd64"]), + }, + ) + markupsafe = got["markupsafe"] + env.expect.that_bool(markupsafe.is_exposed).equals(True) + env.expect.that_bool(markupsafe.is_multiple_versions).equals(False) + env.expect.that_dict({ + p: src.filename + for p, src in _src_by_platform(markupsafe).items() + }).contains_exactly({ + "linux_x86_64": "markupsafe-3.0.3-cp311-cp311-manylinux_2_17_x86_64.whl", + "osx_aarch64": "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", + "windows_x86_64": "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", + }) + for src in markupsafe.srcs: + env.expect.that_str(src.requirement_line).equals("markupsafe==3.0.3") + +_tests.append(_test_uv_lock_same_version_split_records) + +def _test_uv_lock_conflicting_dependency_groups(env): + """Conflicting dev groups pin different versions of the same package. + + Without group selection, both versions (and the dependencies of either) + are reachable on every platform. + """ + registry = {"registry": "https://pypi.org/simple"} + got = _parse_uv_graph([ + _uv_graph_package("root", source = {"virtual": "."}, **{"dev-dependencies": { + "ambig-a": [{"name": "packaging", "source": registry, "version": "24.0"}], + "ambig-b": [{"name": "packaging", "source": registry, "version": "21.3"}], + }}), + _uv_graph_package("packaging", version = "21.3", dependencies = [{"name": "pyparsing"}]), + _uv_graph_package("packaging", version = "24.0"), + _uv_graph_package("pyparsing", version = "3.3.2"), + ]) + packaging = got["packaging"] + env.expect.that_bool(packaging.is_multiple_versions).equals(True) + env.expect.that_collection([ + (src.requirement_line, src.target_platforms) + for src in packaging.srcs + ]).contains_exactly([ + ("packaging==21.3", ["linux_x86_64", "osx_aarch64"]), + ("packaging==24.0", ["linux_x86_64", "osx_aarch64"]), + ]) + env.expect.that_bool(got["pyparsing"].is_exposed).equals(True) + +_tests.append(_test_uv_lock_conflicting_dependency_groups) + +def _test_uv_lock_hashless_wheel(env): + """uv omits `hash` for some wheels, e.g. those from `find-links` registries.""" + got = _parse_uv_graph([ + _uv_graph_package("root", source = {"virtual": "."}, dependencies = [{"name": "colorama"}]), + _uv_graph_package("colorama", version = "0.4.6", wheels = [ + {"url": "https://example.org/colorama-0.4.6-py2.py3-none-any.whl"}, + ]), + ]) + colorama = got["colorama"] + env.expect.that_bool(colorama.is_exposed).equals(True) + env.expect.that_collection(colorama.srcs).has_size(1) + env.expect.that_str(colorama.srcs[0].filename).equals("colorama-0.4.6-py2.py3-none-any.whl") + env.expect.that_str(colorama.srcs[0].digest).equals("") + +_tests.append(_test_uv_lock_hashless_wheel) + +def _test_uv_lock_platform_specific_wheels_without_sdist(env): + """A package with only Windows wheels and no sdist resolves on Windows only.""" + got = _parse_uv_graph( + [ + _uv_graph_package("root", source = {"virtual": "."}, dependencies = [{"name": "pywin32"}]), + _uv_graph_package("pywin32", version = "311", wheels = [ + {"url": "https://example.org/pywin32-311-cp311-cp311-{}.whl".format(p)} + for p in ["win32", "win_amd64", "win_arm64"] + ]), + ], + platforms = _UV_GRAPH_PLATFORMS | { + "windows_x86_64": _uv_platform("windows", "x86_64", ["win_amd64"]), + }, + ) + pywin32 = got["pywin32"] + env.expect.that_bool(pywin32.is_exposed).equals(False) + env.expect.that_dict({ + p: src.filename + for p, src in _src_by_platform(pywin32).items() + }).contains_exactly({ + "windows_x86_64": "pywin32-311-cp311-cp311-win_amd64.whl", + }) + +_tests.append(_test_uv_lock_platform_specific_wheels_without_sdist) + +def _test_uv_lock_local_and_epoch_versions(env): + """PEP 440 local (`+`) and epoch (`!`) segments are kept verbatim.""" + got = _parse_uv_graph([ + _uv_graph_package("root", source = {"virtual": "."}, dependencies = [ + {"name": "jaxlib"}, + {"name": "metomi-isodatetime"}, + ]), + _uv_graph_package("jaxlib", version = "0.4.25+cuda11.cudnn86", wheels = [ + {"url": "https://example.org/jaxlib-0.4.25+cuda11.cudnn86-cp311-cp311-manylinux2014_x86_64.whl"}, + ]), + _uv_graph_package( + "metomi-isodatetime", + version = "1!3.1.0", + sdist = {"url": "https://example.org/metomi-isodatetime-1!3.1.0.tar.gz"}, + wheels = [{"url": "https://example.org/metomi_isodatetime-1!3.1.0-py3-none-any.whl"}], + ), + ]) + env.expect.that_collection([ + (src.requirement_line, src.filename, src.target_platforms) + for src in got["jaxlib"].srcs + ]).contains_exactly([ + ("jaxlib==0.4.25+cuda11.cudnn86", "jaxlib-0.4.25+cuda11.cudnn86-cp311-cp311-manylinux2014_x86_64.whl", ["linux_x86_64"]), + ]) + env.expect.that_collection([ + (src.requirement_line, src.filename, src.target_platforms) + for src in got["metomi_isodatetime"].srcs + ]).contains_exactly([ + ("metomi-isodatetime==1!3.1.0", "metomi_isodatetime-1!3.1.0-py3-none-any.whl", ["linux_x86_64", "osx_aarch64"]), + ]) + +_tests.append(_test_uv_lock_local_and_epoch_versions) + +def _test_uv_lock_root_extra_enabled_by_dev_group(env): + """A dev group that enables an extra of its own project keeps `extra == ...` deps.""" + got = _parse_uv_graph([ + _uv_graph_package( + "extra-marker", + source = {"virtual": "."}, + **{ + "dev-dependencies": {"ci": [{"extra": ["build"], "name": "extra-marker"}]}, + "optional-dependencies": {"build": [{"marker": "extra == 'build'", "name": "colorama"}]}, + } + ), + _uv_graph_package("colorama", version = "0.4.6"), + ]) + colorama = got["colorama"] + env.expect.that_bool(colorama.is_exposed).equals(True) + env.expect.that_collection(colorama.srcs[0].target_platforms).contains_exactly(["linux_x86_64", "osx_aarch64"]) + +_tests.append(_test_uv_lock_root_extra_enabled_by_dev_group) + +def _test_uv_lock_bare_linux_platform_wheels(env): + """Wheels with bare `linux_` platform tags are selected when compatible.""" + got = _parse_uv_graph( + [ + _uv_graph_package("root", source = {"virtual": "."}, dependencies = [{"name": "grpcio"}]), + _uv_graph_package( + "grpcio", + version = "1.80.0", + sdist = {"url": "https://example.org/grpcio-1.80.0.tar.gz"}, + wheels = [ + {"url": "https://example.org/grpcio-1.80.0-cp311-cp311-linux_armv7l.whl"}, + {"url": "https://example.org/grpcio-1.80.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl"}, + ], + ), + ], + platforms = { + "linux_armv7": _uv_platform("linux", "armv7", ["linux_armv7l"]), + "linux_x86_64": _UV_GRAPH_PLATFORMS["linux_x86_64"], + }, + ) + env.expect.that_dict({ + p: src.filename + for p, src in _src_by_platform(got["grpcio"]).items() + }).contains_exactly({ + "linux_armv7": "grpcio-1.80.0-cp311-cp311-linux_armv7l.whl", + "linux_x86_64": "grpcio-1.80.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", + }) + +_tests.append(_test_uv_lock_bare_linux_platform_wheels) + +def _test_uv_lock_abi3_prefers_newest_cp_version(env): + """The most specific compatible abi3 wheel wins on a newer interpreter.""" + got = _parse_uv_graph( + [ + _uv_graph_package("root", source = {"virtual": "."}, dependencies = [{"name": "cryptography"}]), + _uv_graph_package( + "cryptography", + version = "46.0.5", + sdist = {"url": "https://example.org/cryptography-46.0.5.tar.gz"}, + wheels = [ + {"url": "https://example.org/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl"}, + {"url": "https://example.org/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl"}, + ], + ), + ], + platforms = { + "cp312_linux_x86_64": _uv_platform("linux", "x86_64", ["manylinux_*_x86_64"], python_version = "3.12.0"), + }, + ) + env.expect.that_collection([ + src.filename + for src in got["cryptography"].srcs + ]).contains_exactly([ + "cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", + ]) + +_tests.append(_test_uv_lock_abi3_prefers_newest_cp_version) + def parse_requirements_test_suite(name): """Create the test suite. From 57a9bce4837dbd491446d91cd27044bcb6682f78 Mon Sep 17 00:00:00 2001 From: Ignas Anikevicius <240938+aignas@users.noreply.github.com> Date: Thu, 12 Nov 2026 23:14:38 +0900 Subject: [PATCH 2/2] test(pypi): limit uv dependency group cases to lock graph reachability Selecting dependency groups, including self references to a project's own extras, is handled by pip.configure, so the parse_requirements tests only check what the uv.lock graph makes reachable. --- .../parse_requirements_tests.bzl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/pypi/parse_requirements/parse_requirements_tests.bzl b/tests/pypi/parse_requirements/parse_requirements_tests.bzl index 95c0175ab7..abbb6ca4b5 100644 --- a/tests/pypi/parse_requirements/parse_requirements_tests.bzl +++ b/tests/pypi/parse_requirements/parse_requirements_tests.bzl @@ -2000,8 +2000,9 @@ _tests.append(_test_uv_lock_same_version_split_records) def _test_uv_lock_conflicting_dependency_groups(env): """Conflicting dev groups pin different versions of the same package. - Without group selection, both versions (and the dependencies of either) - are reachable on every platform. + The lock graph keeps both versions and the dependencies of either + reachable on every platform. Choosing one group is left to + `pip.configure`, so only reachability is checked here. """ registry = {"registry": "https://pypi.org/simple"} got = _parse_uv_graph([ @@ -2022,7 +2023,7 @@ def _test_uv_lock_conflicting_dependency_groups(env): ("packaging==21.3", ["linux_x86_64", "osx_aarch64"]), ("packaging==24.0", ["linux_x86_64", "osx_aarch64"]), ]) - env.expect.that_bool(got["pyparsing"].is_exposed).equals(True) + env.expect.that_collection(got["pyparsing"].srcs[0].target_platforms).contains_exactly(["linux_x86_64", "osx_aarch64"]) _tests.append(_test_uv_lock_conflicting_dependency_groups) @@ -2100,7 +2101,11 @@ def _test_uv_lock_local_and_epoch_versions(env): _tests.append(_test_uv_lock_local_and_epoch_versions) def _test_uv_lock_root_extra_enabled_by_dev_group(env): - """A dev group that enables an extra of its own project keeps `extra == ...` deps.""" + """A lock edge from a dev group to its own project's extra keeps `extra == ...` deps. + + Selecting the group itself is left to `pip.configure`, so only the lock + graph reachability is checked here. + """ got = _parse_uv_graph([ _uv_graph_package( "extra-marker", @@ -2112,9 +2117,7 @@ def _test_uv_lock_root_extra_enabled_by_dev_group(env): ), _uv_graph_package("colorama", version = "0.4.6"), ]) - colorama = got["colorama"] - env.expect.that_bool(colorama.is_exposed).equals(True) - env.expect.that_collection(colorama.srcs[0].target_platforms).contains_exactly(["linux_x86_64", "osx_aarch64"]) + env.expect.that_collection(got["colorama"].srcs[0].target_platforms).contains_exactly(["linux_x86_64", "osx_aarch64"]) _tests.append(_test_uv_lock_root_extra_enabled_by_dev_group)