From 5493deff655f26f2f30a29e68f465e103c5f07a8 Mon Sep 17 00:00:00 2001 From: konsti Date: Thu, 6 Feb 2025 22:31:53 +0100 Subject: [PATCH] Fix marker merging for requirements.txt for psycopg (#11298) Given an input in the shape: ``` foo[bar]==1.0.0; sys_platform == 'linux' foo==1.0.0; sys_platform != 'linux' ``` We would write either ``` foo==1.0.0; sys_platform == 'linux' ``` or ``` foo==1.0.0 ``` depending on the iteration order, as the first one is from the marker proxy package and the second one from the package without marker. The fix correctly merges graph entries when there are two nodes with different extras and different markers. I tried to write a packse test but it failed due to a different iteration order showing the correct case directly instead of the failing one we'd need. Only `strip_extras` is affected, since `combine_extras` uses `version_marker`. --- crates/uv-resolver/src/resolution/display.rs | 7 ++++ crates/uv/tests/it/pip_compile.rs | 37 +++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/crates/uv-resolver/src/resolution/display.rs b/crates/uv-resolver/src/resolution/display.rs index 2e8af1380..2968074b7 100644 --- a/crates/uv-resolver/src/resolution/display.rs +++ b/crates/uv-resolver/src/resolution/display.rs @@ -404,6 +404,13 @@ fn strip_extras<'dist>(graph: &IntermediatePetGraph<'dist>) -> RequirementsTxtGr let index = *entry.get(); let node: &mut RequirementsTxtDist = &mut next[index]; node.extras.clear(); + // Consider: + // ``` + // foo[bar]==1.0.0; sys_platform == 'linux' + // foo==1.0.0; sys_platform != 'linux' + // ``` + // In this case, we want to write `foo==1.0.0; sys_platform == 'linux' or sys_platform == 'windows'` + node.markers.or(dist.markers); } std::collections::hash_map::Entry::Vacant(entry) => { let index = next.add_node(dist.clone()); diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index 4fcb2c6a3..f7d62c222 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -9130,7 +9130,7 @@ fn universal_disjoint_extra() -> Result<()> { # via flask click==8.1.7 ; sys_platform == 'darwin' or sys_platform == 'linux' # via flask - flask==3.0.2 ; sys_platform == 'linux' + flask==3.0.2 ; sys_platform == 'darwin' or sys_platform == 'linux' # via -r requirements.in itsdangerous==2.1.2 ; sys_platform == 'darwin' or sys_platform == 'linux' # via flask @@ -14554,3 +14554,38 @@ fn compile_preserve_requires_python_split() -> Result<()> { Ok(()) } + +/// Regression test for +#[test] +fn markers_on_extra_packages() -> Result<()> { + let context = TestContext::new("3.12"); + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str(indoc! {" + psycopg[binary]; platform_python_implementation != 'PyPy' + psycopg; platform_python_implementation == 'PyPy' + "})?; + + uv_snapshot!(context + .pip_compile() + .arg("--universal") + .arg("requirements.in"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv pip compile --cache-dir [CACHE_DIR] --universal requirements.in + psycopg==3.1.18 + # via -r requirements.in + psycopg-binary==3.1.18 ; implementation_name != 'pypy' and platform_python_implementation != 'PyPy' + # via psycopg + typing-extensions==4.10.0 + # via psycopg + tzdata==2024.1 ; sys_platform == 'win32' + # via psycopg + + ----- stderr ----- + Resolved 4 packages in [TIME] + "###); + + Ok(()) +}