From a2dd81a65faf88ac79864a103bf07283da85b007 Mon Sep 17 00:00:00 2001 From: Evgeniy Dubovskoy Date: Mon, 19 Feb 2024 04:33:21 +0200 Subject: [PATCH 1/5] Implement `--annotation-style` parameter --- crates/uv-resolver/src/lib.rs | 2 +- crates/uv-resolver/src/resolution.rs | 81 ++++++++++++++++---- crates/uv/src/commands/pip_compile.rs | 12 ++- crates/uv/src/compat/mod.rs | 24 ------ crates/uv/src/main.rs | 7 +- crates/uv/tests/pip_compile.rs | 106 ++++++++++++++++++++++++++ 6 files changed, 188 insertions(+), 44 deletions(-) diff --git a/crates/uv-resolver/src/lib.rs b/crates/uv-resolver/src/lib.rs index 1075b832a5e4..97ed74d87c2e 100644 --- a/crates/uv-resolver/src/lib.rs +++ b/crates/uv-resolver/src/lib.rs @@ -4,7 +4,7 @@ pub use finder::{DistFinder, Reporter as FinderReporter}; pub use manifest::Manifest; pub use options::{Options, OptionsBuilder}; pub use prerelease_mode::PreReleaseMode; -pub use resolution::{Diagnostic, DisplayResolutionGraph, ResolutionGraph}; +pub use resolution::{AnnotationStyle, Diagnostic, DisplayResolutionGraph, ResolutionGraph}; pub use resolution_mode::ResolutionMode; pub use resolver::{ BuildId, DefaultResolverProvider, InMemoryIndex, Reporter as ResolverReporter, Resolver, diff --git a/crates/uv-resolver/src/resolution.rs b/crates/uv-resolver/src/resolution.rs index 8781274dc656..a3500c7715b7 100644 --- a/crates/uv-resolver/src/resolution.rs +++ b/crates/uv-resolver/src/resolution.rs @@ -26,6 +26,14 @@ use crate::resolver::VersionsResponse; use crate::ResolveError; +/// Indicate the style of annotation comments +#[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "clap", derive(clap::ValueEnum))] +pub enum AnnotationStyle { + Line, + Split, +} + /// A complete resolution graph in which every node represents a pinned package and every edge /// represents a dependency between two pinned packages. #[derive(Debug)] @@ -256,11 +264,13 @@ pub struct DisplayResolutionGraph<'a> { /// Whether to include annotations in the output, to indicate which dependency or dependencies /// requested each package. include_annotations: bool, + /// Format of annotation comments + annotation_style: AnnotationStyle, } impl<'a> From<&'a ResolutionGraph> for DisplayResolutionGraph<'a> { fn from(resolution: &'a ResolutionGraph) -> Self { - Self::new(resolution, false, true) + Self::new(resolution, false, true, AnnotationStyle::Split) } } @@ -270,12 +280,42 @@ impl<'a> DisplayResolutionGraph<'a> { underlying: &'a ResolutionGraph, show_hashes: bool, include_annotations: bool, + annotation_style: AnnotationStyle, ) -> DisplayResolutionGraph<'a> { Self { resolution: underlying, show_hashes, include_annotations, + annotation_style, + } + } + + fn annotation_line(edges: Vec<&Dist>) -> String { + let deps = edges + .into_iter() + .map(|dependency| format!("{}", dependency.name())) + .collect::>() + .join(", "); + format!("{}", format!("# via {deps}").green()) + } + + fn annotation_split(edges: Vec<&Dist>) -> String { + let mut result: String = String::new(); + match edges.len() { + 0 => {} + 1 => { + result = format!("{}", format!(" # via {}", edges[0].name()).green()); + } + _ => { + let deps = edges + .into_iter() + .map(|dependency| format!(" # {}", dependency.name())) + .collect::>() + .join("\n"); + result = format!("{}", format!(" # via\n{deps}").green()); + } } + result } } @@ -339,12 +379,13 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> { // Print out the dependency graph. for (index, node) in nodes { // Display the node itself. + let mut line: String; match node { Node::Distribution(_, dist) => { - write!(f, "{}", dist.verbatim())?; + line = format!("{}", dist.verbatim()); } Node::Editable(_, editable) => { - write!(f, "-e {}", editable.verbatim())?; + line = format!("-e {}", editable.verbatim()); } } @@ -358,14 +399,15 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> { { for hash in hashes { if let Some(hash) = hash.to_string() { - writeln!(f, " \\")?; - write!(f, " --hash={hash}")?; + line.push_str(" \\\n"); + line.push_str(&format!(" --hash={hash}")); } } } } - writeln!(f)?; + let mut sep = ""; + let mut annotation = String::new(); if self.include_annotations { // Display all dependencies. let mut edges = self @@ -376,21 +418,30 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> { .collect::>(); edges.sort_unstable_by_key(|package| package.name()); - match edges.len() { - 0 => {} - 1 => { - for dependency in edges { - writeln!(f, "{}", format!(" # via {}", dependency.name()).green())?; + match self.annotation_style { + AnnotationStyle::Line => { + if !edges.is_empty() { + sep = if self.show_hashes { "\n " } else { " " }; + annotation = DisplayResolutionGraph::annotation_line(edges); } } - _ => { - writeln!(f, "{}", " # via".green())?; - for dependency in edges { - writeln!(f, "{}", format!(" # {}", dependency.name()).green())?; + AnnotationStyle::Split => { + if !edges.is_empty() { + sep = "\n"; } + annotation = DisplayResolutionGraph::annotation_split(edges); } } } + // Assemble the line with the annotations and remove trailing whitespaces. + line = format!("{line:24}{sep}{annotation}") + .split('\n') + .map(str::trim_end) + .collect::>() + .join("\n"); + + // Write requirement line to the formatter. + writeln!(f, "{line}")?; } Ok(()) diff --git a/crates/uv/src/commands/pip_compile.rs b/crates/uv/src/commands/pip_compile.rs index 9d372a1b8dba..f1732a914da2 100644 --- a/crates/uv/src/commands/pip_compile.rs +++ b/crates/uv/src/commands/pip_compile.rs @@ -28,8 +28,8 @@ use uv_installer::{Downloader, NoBinary}; use uv_interpreter::{Interpreter, PythonVersion}; use uv_normalize::{ExtraName, PackageName}; use uv_resolver::{ - DependencyMode, DisplayResolutionGraph, InMemoryIndex, Manifest, OptionsBuilder, - PreReleaseMode, ResolutionMode, Resolver, + AnnotationStyle, DependencyMode, DisplayResolutionGraph, InMemoryIndex, Manifest, + OptionsBuilder, PreReleaseMode, ResolutionMode, Resolver, }; use uv_traits::{InFlight, NoBuild, SetupPyStrategy}; use uv_warnings::warn_user; @@ -62,6 +62,7 @@ pub(crate) async fn pip_compile( no_build: &NoBuild, python_version: Option, exclude_newer: Option>, + annotation_style: AnnotationStyle, cache: Cache, mut printer: Printer, ) -> Result { @@ -391,7 +392,12 @@ pub(crate) async fn pip_compile( write!( writer, "{}", - DisplayResolutionGraph::new(&resolution, generate_hashes, include_annotations) + DisplayResolutionGraph::new( + &resolution, + generate_hashes, + include_annotations, + annotation_style + ) )?; Ok(ExitStatus::Success) diff --git a/crates/uv/src/compat/mod.rs b/crates/uv/src/compat/mod.rs index e91086be1ac1..b779f7deba04 100644 --- a/crates/uv/src/compat/mod.rs +++ b/crates/uv/src/compat/mod.rs @@ -36,9 +36,6 @@ pub(crate) struct PipCompileCompatArgs { #[clap(long, hide = true)] resolver: Option, - #[clap(long, hide = true)] - annotation_style: Option, - #[clap(long, hide = true)] max_rounds: Option, @@ -144,21 +141,6 @@ impl CompatArgs for PipCompileCompatArgs { } } - if let Some(annotation_style) = self.annotation_style { - match annotation_style { - AnnotationStyle::Split => { - warn_user!( - "pip-compile's `--annotation-style=split` has no effect (uv always emits split annotations)." - ); - } - AnnotationStyle::Line => { - return Err(anyhow!( - "pip-compile's `--annotation-style=line` is unsupported (uv always emits split annotations)." - )); - } - } - } - if self.max_rounds.is_some() { return Err(anyhow!( "pip-compile's `--max-rounds` is unsupported (uv always resolves until convergence)." @@ -346,12 +328,6 @@ enum Resolver { Legacy, } -#[derive(Debug, Copy, Clone, ValueEnum)] -enum AnnotationStyle { - Line, - Split, -} - /// Arguments for `venv` compatibility. /// /// These represent a subset of the `virtualenv` interface that uv supports by default. diff --git a/crates/uv/src/main.rs b/crates/uv/src/main.rs index e6c262cd2ab6..de6cbf3b593a 100644 --- a/crates/uv/src/main.rs +++ b/crates/uv/src/main.rs @@ -19,7 +19,7 @@ use uv_client::Connectivity; use uv_installer::{NoBinary, Reinstall}; use uv_interpreter::PythonVersion; use uv_normalize::{ExtraName, PackageName}; -use uv_resolver::{DependencyMode, PreReleaseMode, ResolutionMode}; +use uv_resolver::{AnnotationStyle, DependencyMode, PreReleaseMode, ResolutionMode}; use uv_traits::{NoBuild, PackageNameSpecifier, SetupPyStrategy}; use crate::commands::{extra_name_with_clap_error, ExitStatus, Upgrade}; @@ -354,6 +354,10 @@ struct PipCompileArgs { #[clap(long, hide = true)] emit_find_links: bool, + /// Choose the format of annotation comments + #[clap(long, default_value_t=AnnotationStyle::Split, value_enum)] + annotation_style: AnnotationStyle, + #[command(flatten)] compat_args: compat::PipCompileCompatArgs, } @@ -898,6 +902,7 @@ async fn run() -> Result { &no_build, args.python_version, args.exclude_newer, + args.annotation_style, cache, printer, ) diff --git a/crates/uv/tests/pip_compile.rs b/crates/uv/tests/pip_compile.rs index 5cb52e0b59f2..118d71591577 100644 --- a/crates/uv/tests/pip_compile.rs +++ b/crates/uv/tests/pip_compile.rs @@ -47,6 +47,33 @@ fn compile_requirements_in() -> Result<()> { Ok(()) } +/// Resolve a specific version of Django from a `requirements.in` file with a `--annotation-style=line` flag. +#[test] +fn compile_requirements_in_annotation_line() -> Result<()> { + let context = TestContext::new("3.12"); + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("django==5.0b1")?; + + uv_snapshot!(context + .compile() + .arg("--annotation-style=line") + .arg("requirements.in"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv v[VERSION] via the following command: + # uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z --annotation-style=line requirements.in + asgiref==3.7.2 # via django + django==5.0b1 + sqlparse==0.4.4 # via django + + ----- stderr ----- + Resolved 3 packages in [TIME] + "###); + + Ok(()) +} + /// Resolve a specific version of Django from a `requirements.in` file on stdin /// when passed a path of `-`. #[test] @@ -550,6 +577,38 @@ fn compile_python_312() -> Result<()> { Ok(()) } +/// Resolve a specific version of Black at Python 3.12 with `--annotation-style=line`. +#[test] +fn compile_python_312_annotation_line() -> Result<()> { + let context = TestContext::new("3.12"); + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("black==23.10.1")?; + + uv_snapshot!(context.compile() + .arg("--annotation-style=line") + .arg("requirements.in") + .arg("--python-version") + .arg("3.12"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv v[VERSION] via the following command: + # uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z --annotation-style=line requirements.in --python-version 3.12 + black==23.10.1 + click==8.1.7 # via black + mypy-extensions==1.0.0 # via black + packaging==23.2 # via black + pathspec==0.11.2 # via black + platformdirs==4.0.0 # via black + + ----- stderr ----- + Resolved 6 packages in [TIME] + "### + ); + + Ok(()) +} + /// Resolve a specific version of Black at Python 3.12 without deps. #[test] fn compile_python_312_no_deps() -> Result<()> { @@ -1426,6 +1485,53 @@ optional-dependencies.bar = [ Ok(()) } +#[test] +fn compile_pyproject_toml_all_extras_annotation_line() -> Result<()> { + let context = TestContext::new("3.12"); + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#"[build-system] +requires = ["setuptools", "wheel"] + +[project] +name = "project" +dependencies = ["django==5.0b1"] +optional-dependencies.foo = [ + "anyio==4.0.0", +] +optional-dependencies.bar = [ + "httpcore==0.18.0", +] +"#, + )?; + + uv_snapshot!(context.compile() + .arg("--annotation-style=line") + .arg("pyproject.toml") + .arg("--all-extras"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv v[VERSION] via the following command: + # uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z --annotation-style=line pyproject.toml --all-extras + anyio==4.0.0 # via httpcore + asgiref==3.7.2 # via django + certifi==2023.11.17 # via httpcore + django==5.0b1 + h11==0.14.0 # via httpcore + httpcore==0.18.0 + idna==3.4 # via anyio + sniffio==1.3.0 # via anyio, httpcore + sqlparse==0.4.4 # via django + + ----- stderr ----- + Resolved 9 packages in [TIME] + "### + ); + + Ok(()) +} + /// Resolve packages from all optional dependency groups in a `pyproject.toml` file. #[test] fn compile_does_not_allow_both_extra_and_all_extras() -> Result<()> { From 70b322d6a2adf0be3d8c8b61d9e3c3f0d55e1095 Mon Sep 17 00:00:00 2001 From: Evgeniy Dubovskoy Date: Mon, 19 Feb 2024 12:43:14 +0200 Subject: [PATCH 2/5] Fix replacement regex for dependencies in Windows tests --- crates/uv/tests/common/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/uv/tests/common/mod.rs b/crates/uv/tests/common/mod.rs index 22cd714bb226..89a273ca2d2c 100644 --- a/crates/uv/tests/common/mod.rs +++ b/crates/uv/tests/common/mod.rs @@ -257,7 +257,9 @@ pub fn run_and_format<'a>( // The optional leading +/- is for install logs, the optional next line is for lock files let windows_only_deps = [ ("( [+-] )?colorama==\\d+(\\.[\\d+])+\n( # via .*\n)?"), + ("( [+-] )?colorama==\\d+(\\.[\\d+])+\\s+(# via .*\n)?"), ("( [+-] )?tzdata==\\d+(\\.[\\d+])+\n( # via .*\n)?"), + ("( [+-] )?tzdata==\\d+(\\.[\\d+])+\\s+(# via .*\n)?"), ]; let mut removed_packages = 0; for windows_only_dep in windows_only_deps { From 304f5642ce12eaec94175fddd04711a059fb1424 Mon Sep 17 00:00:00 2001 From: Evgeniy Dubovskoy Date: Wed, 21 Feb 2024 00:50:25 +0200 Subject: [PATCH 3/5] Extract annotation format functions onto match statement --- crates/uv-resolver/src/resolution.rs | 51 ++++++++++++---------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/crates/uv-resolver/src/resolution.rs b/crates/uv-resolver/src/resolution.rs index a3500c7715b7..02b088ee3e9f 100644 --- a/crates/uv-resolver/src/resolution.rs +++ b/crates/uv-resolver/src/resolution.rs @@ -289,34 +289,6 @@ impl<'a> DisplayResolutionGraph<'a> { annotation_style, } } - - fn annotation_line(edges: Vec<&Dist>) -> String { - let deps = edges - .into_iter() - .map(|dependency| format!("{}", dependency.name())) - .collect::>() - .join(", "); - format!("{}", format!("# via {deps}").green()) - } - - fn annotation_split(edges: Vec<&Dist>) -> String { - let mut result: String = String::new(); - match edges.len() { - 0 => {} - 1 => { - result = format!("{}", format!(" # via {}", edges[0].name()).green()); - } - _ => { - let deps = edges - .into_iter() - .map(|dependency| format!(" # {}", dependency.name())) - .collect::>() - .join("\n"); - result = format!("{}", format!(" # via\n{deps}").green()); - } - } - result - } } /// Write the graph in the `{name}=={version}` format of requirements.txt that pip uses. @@ -422,14 +394,33 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> { AnnotationStyle::Line => { if !edges.is_empty() { sep = if self.show_hashes { "\n " } else { " " }; - annotation = DisplayResolutionGraph::annotation_line(edges); + let deps = edges + .into_iter() + .map(|dependency| format!("{}", dependency.name())) + .collect::>() + .join(", "); + annotation = format!("{}", format!("# via {deps}").green()); } } AnnotationStyle::Split => { if !edges.is_empty() { sep = "\n"; } - annotation = DisplayResolutionGraph::annotation_split(edges); + match edges.len() { + 0 => {} + 1 => { + annotation = + format!("{}", format!(" # via {}", edges[0].name()).green()); + } + _ => { + let deps = edges + .into_iter() + .map(|dependency| format!(" # {}", dependency.name())) + .collect::>() + .join("\n"); + annotation = format!("{}", format!(" # via\n{deps}").green()); + } + } } } } From b2a12b4b98f0085a8ac9023054df5c1e9dad7fd7 Mon Sep 17 00:00:00 2001 From: Evgeniy Dubovskoy Date: Wed, 21 Feb 2024 01:44:44 +0200 Subject: [PATCH 4/5] Fix snapshots for tests --- crates/uv/tests/pip_compile.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/uv/tests/pip_compile.rs b/crates/uv/tests/pip_compile.rs index 118d71591577..72f68c39f64b 100644 --- a/crates/uv/tests/pip_compile.rs +++ b/crates/uv/tests/pip_compile.rs @@ -61,7 +61,7 @@ fn compile_requirements_in_annotation_line() -> Result<()> { success: true exit_code: 0 ----- stdout ----- - # This file was autogenerated by uv v[VERSION] via the following command: + # This file was autogenerated by uv via the following command: # uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z --annotation-style=line requirements.in asgiref==3.7.2 # via django django==5.0b1 @@ -592,7 +592,7 @@ fn compile_python_312_annotation_line() -> Result<()> { success: true exit_code: 0 ----- stdout ----- - # This file was autogenerated by uv v[VERSION] via the following command: + # This file was autogenerated by uv via the following command: # uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z --annotation-style=line requirements.in --python-version 3.12 black==23.10.1 click==8.1.7 # via black @@ -1512,7 +1512,7 @@ optional-dependencies.bar = [ success: true exit_code: 0 ----- stdout ----- - # This file was autogenerated by uv v[VERSION] via the following command: + # This file was autogenerated by uv via the following command: # uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z --annotation-style=line pyproject.toml --all-extras anyio==4.0.0 # via httpcore asgiref==3.7.2 # via django From d22aa3e9067fbddafa19f8bdef028ec0a34d22ff Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 20 Feb 2024 20:49:49 -0500 Subject: [PATCH 5/5] Nits --- crates/uv-resolver/src/resolution.rs | 99 ++++++++++++++-------------- crates/uv/src/main.rs | 2 +- 2 files changed, 52 insertions(+), 49 deletions(-) diff --git a/crates/uv-resolver/src/resolution.rs b/crates/uv-resolver/src/resolution.rs index 02b088ee3e9f..966b1eac2be2 100644 --- a/crates/uv-resolver/src/resolution.rs +++ b/crates/uv-resolver/src/resolution.rs @@ -9,7 +9,6 @@ use petgraph::Direction; use pubgrub::range::Range; use pubgrub::solver::{Kind, State}; use pubgrub::type_aliases::SelectedDependencies; - use rustc_hash::FxHashMap; use url::Url; @@ -23,14 +22,17 @@ use uv_normalize::{ExtraName, PackageName}; use crate::pins::FilePins; use crate::pubgrub::{PubGrubDistribution, PubGrubPackage, PubGrubPriority}; use crate::resolver::VersionsResponse; - use crate::ResolveError; -/// Indicate the style of annotation comments -#[derive(Debug, Copy, Clone, PartialEq)] +/// Indicate the style of annotation comments, used to indicate the dependencies that requested each +/// package. +#[derive(Debug, Default, Copy, Clone, PartialEq)] #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] pub enum AnnotationStyle { + /// Render the annotations on a single, comma-separated line. Line, + /// Render each annotation on its own line. + #[default] Split, } @@ -264,13 +266,14 @@ pub struct DisplayResolutionGraph<'a> { /// Whether to include annotations in the output, to indicate which dependency or dependencies /// requested each package. include_annotations: bool, - /// Format of annotation comments + /// The style of annotation comments, used to indicate the dependencies that requested each + /// package. annotation_style: AnnotationStyle, } impl<'a> From<&'a ResolutionGraph> for DisplayResolutionGraph<'a> { fn from(resolution: &'a ResolutionGraph) -> Self { - Self::new(resolution, false, true, AnnotationStyle::Split) + Self::new(resolution, false, true, AnnotationStyle::default()) } } @@ -351,17 +354,13 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> { // Print out the dependency graph. for (index, node) in nodes { // Display the node itself. - let mut line: String; - match node { - Node::Distribution(_, dist) => { - line = format!("{}", dist.verbatim()); - } - Node::Editable(_, editable) => { - line = format!("-e {}", editable.verbatim()); - } - } + let mut line = match node { + Node::Distribution(_, dist) => format!("{}", dist.verbatim()), + Node::Editable(_, editable) => format!("-e {}", editable.verbatim()), + }; // Display the distribution hashes, if any. + let mut has_hashes = false; if self.show_hashes { if let Some(hashes) = self .resolution @@ -371,15 +370,18 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> { { for hash in hashes { if let Some(hash) = hash.to_string() { + has_hashes = true; line.push_str(" \\\n"); - line.push_str(&format!(" --hash={hash}")); + line.push_str(" --hash="); + line.push_str(&hash); } } } } - let mut sep = ""; - let mut annotation = String::new(); + // Determine the annotation comment and separator (between comment and requirement). + let mut annotation = None; + if self.include_annotations { // Display all dependencies. let mut edges = self @@ -393,46 +395,47 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> { match self.annotation_style { AnnotationStyle::Line => { if !edges.is_empty() { - sep = if self.show_hashes { "\n " } else { " " }; + let separator = if has_hashes { "\n " } else { " " }; let deps = edges .into_iter() - .map(|dependency| format!("{}", dependency.name())) + .map(|dependency| dependency.name().to_string()) .collect::>() .join(", "); - annotation = format!("{}", format!("# via {deps}").green()); + let comment = format!("# via {deps}").green().to_string(); + annotation = Some((separator, comment)); } } - AnnotationStyle::Split => { - if !edges.is_empty() { - sep = "\n"; + AnnotationStyle::Split => match edges.as_slice() { + [] => {} + [edge] => { + let separator = "\n"; + let comment = format!(" # via {}", edge.name()).green().to_string(); + annotation = Some((separator, comment)); } - match edges.len() { - 0 => {} - 1 => { - annotation = - format!("{}", format!(" # via {}", edges[0].name()).green()); - } - _ => { - let deps = edges - .into_iter() - .map(|dependency| format!(" # {}", dependency.name())) - .collect::>() - .join("\n"); - annotation = format!("{}", format!(" # via\n{deps}").green()); - } + edges => { + let separator = "\n"; + let deps = edges + .iter() + .map(|dependency| format!(" # {}", dependency.name())) + .collect::>() + .join("\n"); + let comment = format!(" # via\n{deps}").green().to_string(); + annotation = Some((separator, comment)); } - } + }, + } + } + + if let Some((separator, comment)) = annotation { + // Assemble the line with the annotations and remove trailing whitespaces. + for line in format!("{line:24}{separator}{comment}").lines() { + let line = line.trim_end(); + writeln!(f, "{line}")?; } + } else { + // Write the line as is. + writeln!(f, "{line}")?; } - // Assemble the line with the annotations and remove trailing whitespaces. - line = format!("{line:24}{sep}{annotation}") - .split('\n') - .map(str::trim_end) - .collect::>() - .join("\n"); - - // Write requirement line to the formatter. - writeln!(f, "{line}")?; } Ok(()) diff --git a/crates/uv/src/main.rs b/crates/uv/src/main.rs index de6cbf3b593a..218cdc4213ba 100644 --- a/crates/uv/src/main.rs +++ b/crates/uv/src/main.rs @@ -354,7 +354,7 @@ struct PipCompileArgs { #[clap(long, hide = true)] emit_find_links: bool, - /// Choose the format of annotation comments + /// Choose the style of the annotation comments, which indicate the source of each package. #[clap(long, default_value_t=AnnotationStyle::Split, value_enum)] annotation_style: AnnotationStyle,