diff --git a/crates/uv-requirements/src/lookahead.rs b/crates/uv-requirements/src/lookahead.rs index e6a6f18b8..197430711 100644 --- a/crates/uv-requirements/src/lookahead.rs +++ b/crates/uv-requirements/src/lookahead.rs @@ -5,11 +5,12 @@ use anyhow::Result; use futures::stream::FuturesUnordered; use futures::StreamExt; -use distribution_types::{BuildableSource, Dist}; +use distribution_types::{BuildableSource, Dist, LocalEditable}; use pep508_rs::{MarkerEnvironment, Requirement, VersionOrUrl}; +use pypi_types::Metadata23; use uv_client::RegistryClient; use uv_distribution::{Reporter, SourceDistCachedBuilder}; -use uv_types::{BuildContext, RequestedRequirements}; +use uv_types::{BuildContext, Constraints, Overrides, RequestedRequirements}; /// A resolver for resolving lookahead requirements from local dependencies. /// @@ -22,18 +23,32 @@ use uv_types::{BuildContext, RequestedRequirements}; /// /// The lookahead resolver resolves requirements for local dependencies, so that the resolver can /// treat them as first-party dependencies for the purpose of analyzing their specifiers. -pub struct LookaheadResolver { - /// The requirements for the project. - requirements: Vec, +pub struct LookaheadResolver<'a> { + /// The direct requirements for the project. + requirements: &'a [Requirement], + /// The constraints for the project. + constraints: &'a Constraints, + /// The overrides for the project. + overrides: &'a Overrides, + /// The editable requirements for the project. + editables: &'a [(LocalEditable, Metadata23)], /// The reporter to use when building source distributions. reporter: Option>, } -impl LookaheadResolver { +impl<'a> LookaheadResolver<'a> { /// Instantiate a new [`LookaheadResolver`] for a given set of requirements. - pub fn new(requirements: Vec) -> Self { + pub fn new( + requirements: &'a [Requirement], + constraints: &'a Constraints, + overrides: &'a Overrides, + editables: &'a [(LocalEditable, Metadata23)], + ) -> Self { Self { requirements, + constraints, + overrides, + editables, reporter: None, } } @@ -55,10 +70,22 @@ impl LookaheadResolver { markers: &MarkerEnvironment, client: &RegistryClient, ) -> Result> { - let mut queue = VecDeque::from(self.requirements.clone()); let mut results = Vec::new(); let mut futures = FuturesUnordered::new(); + // Queue up the initial requirements. + let mut queue: VecDeque = self + .constraints + .apply(self.overrides.apply(self.requirements)) + .filter(|requirement| requirement.evaluate_markers(markers, &[])) + .chain(self.editables.iter().flat_map(|(editable, metadata)| { + self.constraints + .apply(self.overrides.apply(&metadata.requires_dist)) + .filter(|requirement| requirement.evaluate_markers(markers, &editable.extras)) + })) + .cloned() + .collect(); + while !queue.is_empty() || !futures.is_empty() { while let Some(requirement) = queue.pop_front() { futures.push(self.lookahead(requirement, context, client)); @@ -66,7 +93,10 @@ impl LookaheadResolver { while let Some(result) = futures.next().await { if let Some(lookahead) = result? { - for requirement in lookahead.requirements() { + for requirement in self + .constraints + .apply(self.overrides.apply(lookahead.requirements())) + { if requirement.evaluate_markers(markers, lookahead.extras()) { queue.push_back(requirement.clone()); } diff --git a/crates/uv-resolver/src/manifest.rs b/crates/uv-resolver/src/manifest.rs index b319682c7..7252e4f83 100644 --- a/crates/uv-resolver/src/manifest.rs +++ b/crates/uv-resolver/src/manifest.rs @@ -100,20 +100,18 @@ impl Manifest { self.lookaheads .iter() .flat_map(|lookahead| { - lookahead - .requirements() - .iter() + self.overrides + .apply(lookahead.requirements()) .filter(|requirement| requirement.evaluate_markers(markers, lookahead.extras())) }) .chain(self.editables.iter().flat_map(|(editable, metadata)| { - metadata - .requires_dist - .iter() + self.overrides + .apply(&metadata.requires_dist) .filter(|requirement| requirement.evaluate_markers(markers, &editable.extras)) })) .chain( - self.requirements - .iter() + self.overrides + .apply(&self.requirements) .filter(|requirement| requirement.evaluate_markers(markers, &[])), ) .chain( @@ -140,22 +138,31 @@ impl Manifest { self.lookaheads .iter() .flat_map(|lookahead| { - lookahead - .requirements() - .iter() + self.overrides + .apply(lookahead.requirements()) .filter(|requirement| requirement.evaluate_markers(markers, lookahead.extras())) }) .chain(self.editables.iter().flat_map(|(editable, metadata)| { - metadata - .requires_dist - .iter() + self.overrides + .apply(&metadata.requires_dist) .filter(|requirement| requirement.evaluate_markers(markers, &editable.extras)) })) .chain( - self.requirements - .iter() + self.overrides + .apply(&self.requirements) .filter(|requirement| requirement.evaluate_markers(markers, &[])), ) .map(|requirement| &requirement.name) } + + /// Apply the overrides and constraints to a set of requirements. + /// + /// Constraints are always applied _on top_ of overrides, such that constraints are applied + /// even if a requirement is overridden. + pub fn apply<'a>( + &'a self, + requirements: impl IntoIterator, + ) -> impl Iterator { + self.constraints.apply(self.overrides.apply(requirements)) + } } diff --git a/crates/uv-resolver/src/resolution.rs b/crates/uv-resolver/src/resolution.rs index 9c4e93f00..7edf9c4ef 100644 --- a/crates/uv-resolver/src/resolution.rs +++ b/crates/uv-resolver/src/resolution.rs @@ -421,19 +421,22 @@ impl ResolutionGraph { .distributions .get(&package_id) .expect("every package in resolution graph has metadata"); - for req in &md.requires_dist { + for req in manifest.apply(&md.requires_dist) { let Some(ref marker_tree) = req.marker else { continue; }; add_marker_params_from_tree(marker_tree, &mut seen_marker_values); } } - let direct_reqs = manifest - .requirements - .iter() - .chain(manifest.constraints.requirements()) - .chain(manifest.overrides.requirements()); - for direct_req in direct_reqs { + + // Ensure that we consider markers from direct dependencies. + let direct_reqs = manifest.requirements.iter().chain( + manifest + .editables + .iter() + .flat_map(|(_, metadata)| &metadata.requires_dist), + ); + for direct_req in manifest.apply(direct_reqs) { let Some(ref marker_tree) = direct_req.marker else { continue; }; diff --git a/crates/uv-resolver/src/resolver/urls.rs b/crates/uv-resolver/src/resolver/urls.rs index a046dc558..fd6877db8 100644 --- a/crates/uv-resolver/src/resolver/urls.rs +++ b/crates/uv-resolver/src/resolver/urls.rs @@ -23,61 +23,7 @@ impl Urls { let mut required: FxHashMap = FxHashMap::default(); let mut allowed: FxHashMap = FxHashMap::default(); - // Add any lookahead requirements. If there are any conflicts, return an error. - for lookahead in &manifest.lookaheads { - for requirement in lookahead - .requirements() - .iter() - .filter(|requirement| requirement.evaluate_markers(markers, lookahead.extras())) - { - if let Some(pep508_rs::VersionOrUrl::Url(url)) = &requirement.version_or_url { - if let Some(previous) = required.insert(requirement.name.clone(), url.clone()) { - if !is_equal(&previous, url) { - if is_precise(&previous, url) { - debug!("Assuming {url} is a precise variant of {previous}"); - allowed.insert(url.clone(), previous); - } else { - return Err(ResolveError::ConflictingUrlsDirect( - requirement.name.clone(), - previous.verbatim().to_string(), - url.verbatim().to_string(), - )); - } - } - } - } - } - } - - // Add all direct requirements and constraints. If there are any conflicts, return an error. - for requirement in manifest - .requirements - .iter() - .chain(manifest.constraints.requirements()) - .filter(|requirement| requirement.evaluate_markers(markers, &[])) - { - if let Some(pep508_rs::VersionOrUrl::Url(url)) = &requirement.version_or_url { - if let Some(previous) = required.insert(requirement.name.clone(), url.clone()) { - if is_equal(&previous, url) { - continue; - } - - if is_precise(&previous, url) { - debug!("Assuming {url} is a precise variant of {previous}"); - allowed.insert(url.clone(), previous); - continue; - } - - return Err(ResolveError::ConflictingUrlsDirect( - requirement.name.clone(), - previous.verbatim().to_string(), - url.verbatim().to_string(), - )); - } - } - } - - // Add any editable requirements. If there are any conflicts, return an error. + // Add the themselves to the list of required URLs. for (editable, metadata) in &manifest.editables { if let Some(previous) = required.insert(metadata.name.clone(), editable.url.clone()) { if !is_equal(&previous, &editable.url) { @@ -96,40 +42,28 @@ impl Urls { } } } - - for requirement in metadata - .requires_dist - .iter() - .filter(|requirement| requirement.evaluate_markers(markers, &editable.extras)) - { - if let Some(pep508_rs::VersionOrUrl::Url(url)) = &requirement.version_or_url { - if let Some(previous) = required.insert(requirement.name.clone(), url.clone()) { - if !is_equal(&previous, url) { - if is_precise(&previous, url) { - debug!("Assuming {url} is a precise variant of {previous}"); - allowed.insert(url.clone(), previous); - } else { - return Err(ResolveError::ConflictingUrlsDirect( - requirement.name.clone(), - previous.verbatim().to_string(), - url.verbatim().to_string(), - )); - } - } - } - } - } } - // Add any overrides. Conflicts here are fine, as the overrides are meant to be - // authoritative. - for requirement in manifest - .overrides - .requirements() - .filter(|requirement| requirement.evaluate_markers(markers, &[])) - { + // Add all direct requirements and constraints. If there are any conflicts, return an error. + for requirement in manifest.requirements(markers) { if let Some(pep508_rs::VersionOrUrl::Url(url)) = &requirement.version_or_url { - required.insert(requirement.name.clone(), url.clone()); + if let Some(previous) = required.insert(requirement.name.clone(), url.clone()) { + if is_equal(&previous, url) { + continue; + } + + if is_precise(&previous, url) { + debug!("Assuming {url} is a precise variant of {previous}"); + allowed.insert(url.clone(), previous); + continue; + } + + return Err(ResolveError::ConflictingUrlsDirect( + requirement.name.clone(), + previous.verbatim().to_string(), + url.verbatim().to_string(), + )); + } } } diff --git a/crates/uv-types/src/constraints.rs b/crates/uv-types/src/constraints.rs index 76e89816e..49e4a0f5d 100644 --- a/crates/uv-types/src/constraints.rs +++ b/crates/uv-types/src/constraints.rs @@ -32,4 +32,18 @@ impl Constraints { pub fn get(&self, name: &PackageName) -> Option<&Vec> { self.0.get(name) } + + /// Apply the constraints to a set of requirements. + pub fn apply<'a>( + &'a self, + requirements: impl IntoIterator, + ) -> impl Iterator { + requirements.into_iter().flat_map(|requirement| { + std::iter::once(requirement).chain( + self.get(&requirement.name) + .into_iter() + .flat_map(|constraints| constraints.iter()), + ) + }) + } } diff --git a/crates/uv-types/src/overrides.rs b/crates/uv-types/src/overrides.rs index 07d93b133..8f34617bf 100644 --- a/crates/uv-types/src/overrides.rs +++ b/crates/uv-types/src/overrides.rs @@ -37,9 +37,9 @@ impl Overrides { /// Apply the overrides to a set of requirements. pub fn apply<'a>( &'a self, - requirements: &'a [Requirement], + requirements: impl IntoIterator, ) -> impl Iterator { - requirements.iter().flat_map(|requirement| { + requirements.into_iter().flat_map(|requirement| { if let Some(overrides) = self.get(&requirement.name) { Either::Left(overrides.iter()) } else { diff --git a/crates/uv/src/commands/pip_compile.rs b/crates/uv/src/commands/pip_compile.rs index 0b2a57db2..95b79cedf 100644 --- a/crates/uv/src/commands/pip_compile.rs +++ b/crates/uv/src/commands/pip_compile.rs @@ -338,22 +338,10 @@ pub(crate) async fn pip_compile( }; // Determine any lookahead requirements. - let lookaheads = LookaheadResolver::new( - requirements - .iter() - .filter(|requirement| requirement.evaluate_markers(&markers, &[])) - .chain(editables.iter().flat_map(|(editable, metadata)| { - metadata - .requires_dist - .iter() - .filter(|requirement| requirement.evaluate_markers(&markers, &editable.extras)) - })) - .cloned() - .collect(), - ) - .with_reporter(ResolverReporter::from(printer)) - .resolve(&build_dispatch, &markers, &client) - .await?; + let lookaheads = LookaheadResolver::new(&requirements, &constraints, &overrides, &editables) + .with_reporter(ResolverReporter::from(printer)) + .resolve(&build_dispatch, &markers, &client) + .await?; // Create a manifest of the requirements. let manifest = Manifest::new( diff --git a/crates/uv/src/commands/pip_install.rs b/crates/uv/src/commands/pip_install.rs index 0eb81d1cd..6b26b1c26 100644 --- a/crates/uv/src/commands/pip_install.rs +++ b/crates/uv/src/commands/pip_install.rs @@ -539,22 +539,10 @@ async fn resolve( .collect(); // Determine any lookahead requirements. - let lookaheads = LookaheadResolver::new( - requirements - .iter() - .filter(|requirement| requirement.evaluate_markers(markers, &[])) - .chain(editables.iter().flat_map(|(editable, metadata)| { - metadata - .requires_dist - .iter() - .filter(|requirement| requirement.evaluate_markers(markers, &editable.extras)) - })) - .cloned() - .collect(), - ) - .with_reporter(ResolverReporter::from(printer)) - .resolve(build_dispatch, markers, client) - .await?; + let lookaheads = LookaheadResolver::new(&requirements, &constraints, &overrides, &editables) + .with_reporter(ResolverReporter::from(printer)) + .resolve(build_dispatch, markers, client) + .await?; // Create a manifest of the requirements. let manifest = Manifest::new( diff --git a/crates/uv/tests/pip_compile.rs b/crates/uv/tests/pip_compile.rs index 9116ca015..c1b02e1da 100644 --- a/crates/uv/tests/pip_compile.rs +++ b/crates/uv/tests/pip_compile.rs @@ -1841,8 +1841,8 @@ fn allowed_transitive_url_path_dependency() -> Result<()> { Ok(()) } -/// A dependency with conflicting URLs in `requirements.in` and `constraints.txt` should arguably -/// be ignored if the dependency has an override. However, we currently error in this case. +/// A dependency with conflicting URLs in `requirements.in` and `constraints.txt` should be ignored +/// if the dependency has an override. #[test] fn requirement_constraint_override_url() -> Result<()> { let context = TestContext::new("3.12"); @@ -1863,13 +1863,13 @@ fn requirement_constraint_override_url() -> Result<()> { .arg("--override") .arg("overrides.txt"), @r###" success: false - exit_code: 2 + exit_code: 1 ----- stdout ----- ----- stderr ----- - error: Requirements contain conflicting URLs for package `anyio`: - - https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz - - https://files.pythonhosted.org/packages/14/fd/2f20c40b45e4fb4324834aea24bd4afdf1143390242c0b33774da0e2e34f/anyio-4.3.0-py3-none-any.whl + × No solution found when resolving dependencies: + ╰─▶ Because there is no version of anyio==3.7.0 and you require + anyio==3.7.0, we can conclude that the requirements are unsatisfiable. "### ); @@ -1877,8 +1877,8 @@ fn requirement_constraint_override_url() -> Result<()> { } /// A dependency that uses a pre-release marker in `requirements.in` should be overridden by a -/// non-pre-release version in `overrides.txt`. We currently allow Flask to use a pre-release below, -/// but probably shouldn't. +/// non-pre-release version in `overrides.txt`. We should _not_ allow Flask to be resolved to +/// a pre-release version. #[test] fn requirement_override_prerelease() -> Result<()> { let context = TestContext::new("3.12"); @@ -1898,18 +1898,16 @@ fn requirement_override_prerelease() -> Result<()> { ----- 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 --override overrides.txt - click==8.1.7 + click==7.1.2 # via flask - flask==2.0.0rc2 - itsdangerous==2.1.2 + flask==1.1.4 + itsdangerous==1.1.0 # via flask - jinja2==3.1.3 + jinja2==2.11.3 # via flask markupsafe==2.1.5 - # via - # jinja2 - # werkzeug - werkzeug==3.0.1 + # via jinja2 + werkzeug==1.0.1 # via flask ----- stderr ----- @@ -6573,13 +6571,13 @@ fn pendulum_no_tzdata_on_windows() -> Result<()> { fn allow_recursive_url_local_path() -> Result<()> { let context = TestContext::new("3.12"); - // Create a standalone library. - let lib2 = context.temp_dir.child("lib2"); - lib2.create_dir_all()?; - let pyproject_toml = lib2.child("pyproject.toml"); + // Create a standalone library named "anyio". + let anyio = context.temp_dir.child("anyio"); + anyio.create_dir_all()?; + let pyproject_toml = anyio.child("pyproject.toml"); pyproject_toml.write_str( r#"[project] -name = "lib2" +name = "anyio" version = "0.0.0" dependencies = [ "idna" @@ -6589,19 +6587,19 @@ requires-python = ">3.8" )?; // Create a library that depends on the standalone library. - let lib1 = context.temp_dir.child("lib1"); - lib1.create_dir_all()?; - let pyproject_toml = lib1.child("pyproject.toml"); + let lib = context.temp_dir.child("lib"); + lib.create_dir_all()?; + let pyproject_toml = lib.child("pyproject.toml"); pyproject_toml.write_str(&format!( r#"[project] -name = "lib1" +name = "lib" version = "0.0.0" dependencies = [ - "lib2 @ {}" + "anyio @ {}" ] requires-python = ">3.8" "#, - Url::from_directory_path(lib2.path()).unwrap().as_str(), + Url::from_directory_path(anyio.path()).unwrap().as_str(), ))?; // Create an application that depends on the library. @@ -6613,12 +6611,11 @@ requires-python = ">3.8" name = "example" version = "0.0.0" dependencies = [ - "anyio", - "lib1 @ {}" + "lib @ {}" ] requires-python = ">3.8" "#, - Url::from_directory_path(lib1.path()).unwrap().as_str(), + Url::from_directory_path(lib.path()).unwrap().as_str(), ))?; // Write to a requirements file. @@ -6632,22 +6629,215 @@ requires-python = ">3.8" ----- 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 - anyio==4.3.0 - # via example + anyio @ file://[TEMP_DIR]/anyio/ + # via lib example @ ./app idna==3.6 - # via - # anyio - # lib2 - lib1 @ file://[TEMP_DIR]/lib1/ + # via anyio + lib @ file://[TEMP_DIR]/lib/ + # via example + + ----- stderr ----- + Resolved 4 packages in [TIME] + "### + ); + + Ok(()) +} + +/// Allow URL dependencies recursively for local source trees, but respect overrides. +#[test] +fn allow_recursive_url_local_path_override() -> Result<()> { + let context = TestContext::new("3.12"); + + // Create a standalone library named "anyio". + let anyio = context.temp_dir.child("anyio"); + anyio.create_dir_all()?; + let pyproject_toml = anyio.child("pyproject.toml"); + pyproject_toml.write_str( + r#"[project] +name = "anyio" +version = "0.0.0" +dependencies = [ + "idna" +] +requires-python = ">3.8" +"#, + )?; + + // Create a library that depends on the standalone library. + let lib = context.temp_dir.child("lib"); + lib.create_dir_all()?; + let pyproject_toml = lib.child("pyproject.toml"); + pyproject_toml.write_str(&format!( + r#"[project] +name = "lib" +version = "0.0.0" +dependencies = [ + "anyio @ {}" +] +requires-python = ">3.8" +"#, + Url::from_directory_path(anyio.path()).unwrap().as_str(), + ))?; + + // Create an application that depends on the library. + let app = context.temp_dir.child("app"); + app.create_dir_all()?; + let pyproject_toml = app.child("pyproject.toml"); + pyproject_toml.write_str(&format!( + r#"[project] +name = "example" +version = "0.0.0" +dependencies = [ + "lib @ {}" +] +requires-python = ">3.8" +"#, + Url::from_directory_path(lib.path()).unwrap().as_str(), + ))?; + + // Write to a requirements file. + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("./app")?; + + // Create an override that pulls `anyio` from PyPI. + let overrides_txt = context.temp_dir.child("overrides.txt"); + overrides_txt.write_str("anyio==3.7.0")?; + + uv_snapshot!(context.filters(), context.compile() + .arg("requirements.in") + .arg("--override") + .arg("overrides.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 --override overrides.txt + anyio==3.7.0 + # via lib + example @ ./app + idna==3.6 + # via anyio + lib @ file://[TEMP_DIR]/lib/ # via example - lib2 @ file://[TEMP_DIR]/lib2/ - # via lib1 sniffio==1.3.1 # via anyio ----- stderr ----- - Resolved 6 packages in [TIME] + Resolved 5 packages in [TIME] + "### + ); + + Ok(()) +} + +/// Allow URL dependencies recursively for local source trees, but respect both overrides _and_ +/// constraints. +#[test] +fn allow_recursive_url_local_path_override_constraint() -> Result<()> { + let context = TestContext::new("3.12"); + + // Create a standalone library named "anyio". + let anyio = context.temp_dir.child("anyio"); + anyio.create_dir_all()?; + let pyproject_toml = anyio.child("pyproject.toml"); + pyproject_toml.write_str( + r#"[project] +name = "anyio" +version = "0.0.0" +dependencies = [ + "idna" +] +requires-python = ">3.8" +"#, + )?; + + // Create a library that depends on the standalone library. + let lib = context.temp_dir.child("lib"); + lib.create_dir_all()?; + let pyproject_toml = lib.child("pyproject.toml"); + pyproject_toml.write_str(&format!( + r#"[project] +name = "lib" +version = "0.0.0" +dependencies = [ + "anyio @ {}" +] +requires-python = ">3.8" +"#, + Url::from_directory_path(anyio.path()).unwrap().as_str(), + ))?; + + // Create an application that depends on the library. + let app = context.temp_dir.child("app"); + app.create_dir_all()?; + let pyproject_toml = app.child("pyproject.toml"); + pyproject_toml.write_str(&format!( + r#"[project] +name = "example" +version = "0.0.0" +dependencies = [ + "lib @ {}" +] +requires-python = ">3.8" +"#, + Url::from_directory_path(lib.path()).unwrap().as_str(), + ))?; + + // Write to a requirements file. + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("./app")?; + + // Create an override that pulls `anyio` from PyPI. + let overrides_txt = context.temp_dir.child("overrides.txt"); + overrides_txt.write_str("anyio==0.0.0")?; + + // Ensure that resolution fails, since `0.0.0` does not exist on PyPI. + uv_snapshot!(context.filters(), context.compile() + .arg("requirements.in") + .arg("--override") + .arg("overrides.txt"), @r###" + success: false + exit_code: 1 + ----- stdout ----- + + ----- stderr ----- + × No solution found when resolving dependencies: + ╰─▶ Because there is no version of anyio==0.0.0 and lib==0.0.0 depends on + anyio==0.0.0, we can conclude that lib==0.0.0 cannot be used. + And because only lib==0.0.0 is available and example==0.0.0 depends on + lib, we can conclude that example==0.0.0 cannot be used. + And because only example==0.0.0 is available and you require example, we + can conclude that the requirements are unsatisfiable. + "### + ); + + // Now constrain `anyio` to the local version. + let constraints_txt = context.temp_dir.child("constraints.txt"); + constraints_txt.write_str("anyio @ ./anyio")?; + + uv_snapshot!(context.filters(), context.compile() + .arg("requirements.in") + .arg("--override") + .arg("overrides.txt") + .arg("--constraint") + .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 --override overrides.txt --constraint constraints.txt + anyio @ ./anyio + # via lib + example @ ./app + idna==3.6 + # via anyio + lib @ file://[TEMP_DIR]/lib + # via example + + ----- stderr ----- + Resolved 4 packages in [TIME] "### );