diff --git a/crates/uv-resolver/src/manifest.rs b/crates/uv-resolver/src/manifest.rs index 9b194609e73e..2f16aae0dc2c 100644 --- a/crates/uv-resolver/src/manifest.rs +++ b/crates/uv-resolver/src/manifest.rs @@ -102,46 +102,52 @@ impl Manifest { ) -> impl Iterator { match mode { // Include all direct and transitive requirements, with constraints and overrides applied. - DependencyMode::Transitive => Either::Left( self - .lookaheads - .iter() - .flat_map(|lookahead| { - self.overrides - .apply(lookahead.requirements()) - .filter(|requirement| { - requirement.evaluate_markers(markers, lookahead.extras()) + DependencyMode::Transitive => { + Either::Left( + self.lookaheads + .iter() + .flat_map(|lookahead| { + self.overrides + .apply(lookahead.requirements()) + .filter(|requirement| { + requirement.evaluate_markers(markers, lookahead.extras()) + }) }) - }) - .chain(self.editables.iter().flat_map(|(editable, _metadata, requirements)| { - self.overrides - .apply(&requirements.dependencies) - .filter(|requirement| { - requirement.evaluate_markers(markers, &editable.extras) - }) - })) - .chain( - self.overrides - .apply(&self.requirements) - .filter(|requirement| requirement.evaluate_markers(markers, &[])), - ) - .chain( - self.constraints - .requirements() - .filter(|requirement| requirement.evaluate_markers(markers, &[])), + .chain(self.editables.iter().flat_map( + |(editable, _metadata, requirements)| { + self.overrides.apply(&requirements.dependencies).filter( + |requirement| { + requirement.evaluate_markers(markers, &editable.extras) + }, + ) + }, + )) + .chain( + self.overrides + .apply(&self.requirements) + .filter(|requirement| requirement.evaluate_markers(markers, &[])), + ) + .chain( + self.constraints + .requirements() + .filter(|requirement| requirement.evaluate_markers(markers, &[])), + ) + .chain( + self.overrides + .requirements() + .filter(|requirement| requirement.evaluate_markers(markers, &[])), + ), ) - .chain( - self.overrides - .requirements() - .filter(|requirement| requirement.evaluate_markers(markers, &[])), - )) - , + } // Include direct requirements, with constraints and overrides applied. DependencyMode::Direct => Either::Right( - self.overrides.apply(& self.requirements) - .chain(self.constraints.requirements()) - .chain(self.overrides.requirements()) - .filter(|requirement| requirement.evaluate_markers(markers, &[]))), + self.overrides + .apply(&self.requirements) + .chain(self.constraints.requirements()) + .chain(self.overrides.requirements()) + .filter(|requirement| requirement.evaluate_markers(markers, &[])), + ), } } diff --git a/crates/uv/src/commands/pip_compile.rs b/crates/uv/src/commands/pip_compile.rs index f1561244faeb..00bc2a49fca7 100644 --- a/crates/uv/src/commands/pip_compile.rs +++ b/crates/uv/src/commands/pip_compile.rs @@ -357,7 +357,10 @@ pub(crate) async fn pip_compile( // Generate a map from requirement to originating source file. let mut sources = SourceAnnotations::default(); - for requirement in &requirements { + for requirement in requirements + .iter() + .filter(|requirement| requirement.evaluate_markers(&markers, &[])) + { if let Some(path) = &requirement.path { if path.ends_with("pyproject.toml") { sources.add( @@ -376,7 +379,10 @@ pub(crate) async fn pip_compile( } } - for requirement in &constraints { + for requirement in constraints + .iter() + .filter(|requirement| requirement.evaluate_markers(&markers, &[])) + { if let Some(path) = &requirement.path { sources.add( &requirement.name, @@ -385,7 +391,10 @@ pub(crate) async fn pip_compile( } } - for requirement in &overrides { + for requirement in overrides + .iter() + .filter(|requirement| requirement.evaluate_markers(&markers, &[])) + { if let Some(path) = &requirement.path { sources.add(&requirement.name, SourceAnnotation::Override(path.clone())); } diff --git a/crates/uv/tests/pip_compile.rs b/crates/uv/tests/pip_compile.rs index 94067fd70661..bf1b3f129d7a 100644 --- a/crates/uv/tests/pip_compile.rs +++ b/crates/uv/tests/pip_compile.rs @@ -1034,6 +1034,41 @@ fn compile_python_dev_version() -> Result<()> { Ok(()) } +/// Omit the constraint annotation (e.g., `# from -c constraints.txt`) when the constraint is not +/// applicable due to a marker expression. +#[test] +fn omit_non_matching_annotation() -> Result<()> { + let context = TestContext::new("3.12"); + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("anyio")?; + + let constraints_txt = context.temp_dir.child("constraints.txt"); + constraints_txt.write_str("idna <3.7; python_version < '3.7'")?; + + uv_snapshot!(context.compile() + .arg("requirements.in") + .arg("-c") + .arg("constraints.txt"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in -c constraints.txt + anyio==4.3.0 + # via -r requirements.in + idna==3.6 + # via anyio + sniffio==1.3.1 + # via anyio + + ----- stderr ----- + Resolved 3 packages in [TIME] + "### + ); + + Ok(()) +} + /// Test that we select the last 3.8 compatible numpy version instead of trying to compile an /// incompatible sdist #[test]