Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement --annotation-style parameter for uv pip compile #1679

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/uv-resolver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
93 changes: 69 additions & 24 deletions crates/uv-resolver/src/resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -23,9 +22,20 @@ 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, 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,
}

/// A complete resolution graph in which every node represents a pinned package and every edge
/// represents a dependency between two pinned packages.
#[derive(Debug)]
Expand Down Expand Up @@ -256,11 +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,
/// 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)
Self::new(resolution, false, true, AnnotationStyle::default())
}
}

Expand All @@ -270,11 +283,13 @@ 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,
}
}
}
Expand Down Expand Up @@ -339,16 +354,13 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
// Print out the dependency graph.
for (index, node) in nodes {
// Display the node itself.
match node {
Node::Distribution(_, dist) => {
write!(f, "{}", dist.verbatim())?;
}
Node::Editable(_, editable) => {
write!(f, "-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
Expand All @@ -358,13 +370,17 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
{
for hash in hashes {
if let Some(hash) = hash.to_string() {
writeln!(f, " \\")?;
write!(f, " --hash={hash}")?;
has_hashes = true;
line.push_str(" \\\n");
line.push_str(" --hash=");
line.push_str(&hash);
}
}
}
}
writeln!(f)?;

// Determine the annotation comment and separator (between comment and requirement).
let mut annotation = None;

if self.include_annotations {
// Display all dependencies.
Expand All @@ -376,20 +392,49 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
.collect::<Vec<_>>();
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() {
let separator = if has_hashes { "\n " } else { " " };
let deps = edges
.into_iter()
.map(|dependency| dependency.name().to_string())
.collect::<Vec<_>>()
.join(", ");
let comment = format!("# via {deps}").green().to_string();
annotation = Some((separator, comment));
}
}
_ => {
writeln!(f, "{}", " # via".green())?;
for dependency in edges {
writeln!(f, "{}", format!(" # {}", dependency.name()).green())?;
AnnotationStyle::Split => match edges.as_slice() {
[] => {}
[edge] => {
let separator = "\n";
let comment = format!(" # via {}", edge.name()).green().to_string();
annotation = Some((separator, comment));
}
}
edges => {
let separator = "\n";
let deps = edges
.iter()
.map(|dependency| format!(" # {}", dependency.name()))
.collect::<Vec<_>>()
.join("\n");
let comment = format!(" # via\n{deps}").green().to_string();
annotation = Some((separator, comment));
}
},
}
}

if let Some((separator, comment)) = annotation {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DrJackilD -- I just tweaked this to couple the presence of the comment and separator.

// 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}")?;
}
}

Expand Down
12 changes: 9 additions & 3 deletions crates/uv/src/commands/pip_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -62,6 +62,7 @@ pub(crate) async fn pip_compile(
no_build: &NoBuild,
python_version: Option<PythonVersion>,
exclude_newer: Option<DateTime<Utc>>,
annotation_style: AnnotationStyle,
cache: Cache,
mut printer: Printer,
) -> Result<ExitStatus> {
Expand Down Expand Up @@ -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)
Expand Down
24 changes: 0 additions & 24 deletions crates/uv/src/compat/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ pub(crate) struct PipCompileCompatArgs {
#[clap(long, hide = true)]
resolver: Option<Resolver>,

#[clap(long, hide = true)]
annotation_style: Option<AnnotationStyle>,

#[clap(long, hide = true)]
max_rounds: Option<usize>,

Expand Down Expand Up @@ -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)."
Expand Down Expand Up @@ -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.
Expand Down
7 changes: 6 additions & 1 deletion crates/uv/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -354,6 +354,10 @@ struct PipCompileArgs {
#[clap(long, hide = true)]
emit_find_links: bool,

/// 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,

#[command(flatten)]
compat_args: compat::PipCompileCompatArgs,
}
Expand Down Expand Up @@ -898,6 +902,7 @@ async fn run() -> Result<ExitStatus> {
&no_build,
args.python_version,
args.exclude_newer,
args.annotation_style,
cache,
printer,
)
Expand Down
2 changes: 2 additions & 0 deletions crates/uv/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,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 {
Expand Down
Loading