Skip to content

Commit

Permalink
Fix uv tree --invert for platform dependencies (#7808)
Browse files Browse the repository at this point in the history
## Summary

`click` has one dependency of `colorama` only on Windows, `uv tree
--invert` should not include `colorama` on non-Windows platforms, but
currently:

```console
$ uv init
$ uv add click
$ uv tree --invert --python-platform macos
colorama v0.4.6
```

it should:
```console
$ uv tree --invert --python-platform macos
click v8.1.7
    └── project v0.1.0
```
  • Loading branch information
j178 authored Sep 30, 2024
1 parent 7435ee3 commit da9e85c
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 45 deletions.
93 changes: 48 additions & 45 deletions crates/uv-resolver/src/lock/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub struct TreeDisplay<'env> {
/// Prune the given packages from the display of the dependency tree.
prune: Vec<PackageName>,
/// Display only the specified packages.
package: Vec<PackageName>,
packages: Vec<PackageName>,
/// Whether to de-duplicate the displayed dependencies.
no_dedupe: bool,
}
Expand All @@ -39,7 +39,7 @@ impl<'env> TreeDisplay<'env> {
markers: Option<&'env ResolverMarkerEnvironment>,
depth: usize,
prune: Vec<PackageName>,
package: Vec<PackageName>,
packages: Vec<PackageName>,
no_dedupe: bool,
invert: bool,
) -> Self {
Expand All @@ -51,16 +51,27 @@ impl<'env> TreeDisplay<'env> {
let mut optional_dependencies: FxHashMap<_, FxHashMap<_, Vec<_>>> = FxHashMap::default();
let mut dev_dependencies: FxHashMap<_, FxHashMap<_, Vec<_>>> = FxHashMap::default();

for packages in &lock.packages {
for dependency in &packages.dependencies {
for package in &lock.packages {
for dependency in &package.dependencies {
// Skip dependencies that don't apply to the current environment.
if let Some(environment_markers) = markers {
if !dependency
.complexified_marker
.evaluate(environment_markers, &[])
{
non_roots.insert(dependency.package_id.clone());
continue;
}
}

let parent = if invert {
&dependency.package_id
} else {
&packages.id
&package.id
};
let child = if invert {
Cow::Owned(Dependency {
package_id: packages.id.clone(),
package_id: package.id.clone(),
extra: dependency.extra.clone(),
simplified_marker: dependency.simplified_marker.clone(),
complexified_marker: dependency.complexified_marker.clone(),
Expand All @@ -71,29 +82,30 @@ impl<'env> TreeDisplay<'env> {

non_roots.insert(child.package_id.clone());

// Skip dependencies that don't apply to the current environment.
if let Some(environment_markers) = markers {
if !dependency
.complexified_marker
.evaluate(environment_markers, &[])
{
continue;
}
}

dependencies.entry(parent).or_default().push(child);
}

for (extra, dependencies) in &packages.optional_dependencies {
for (extra, dependencies) in &package.optional_dependencies {
for dependency in dependencies {
// Skip dependencies that don't apply to the current environment.
if let Some(environment_markers) = markers {
if !dependency
.complexified_marker
.evaluate(environment_markers, &[])
{
non_roots.insert(dependency.package_id.clone());
continue;
}
}

let parent = if invert {
&dependency.package_id
} else {
&packages.id
&package.id
};
let child = if invert {
Cow::Owned(Dependency {
package_id: packages.id.clone(),
package_id: package.id.clone(),
extra: dependency.extra.clone(),
simplified_marker: dependency.simplified_marker.clone(),
complexified_marker: dependency.complexified_marker.clone(),
Expand All @@ -104,16 +116,6 @@ impl<'env> TreeDisplay<'env> {

non_roots.insert(child.package_id.clone());

// Skip dependencies that don't apply to the current environment.
if let Some(environment_markers) = markers {
if !dependency
.complexified_marker
.evaluate(environment_markers, &[])
{
continue;
}
}

optional_dependencies
.entry(parent)
.or_default()
Expand All @@ -123,16 +125,27 @@ impl<'env> TreeDisplay<'env> {
}
}

for (group, dependencies) in &packages.dev_dependencies {
for (group, dependencies) in &package.dev_dependencies {
for dependency in dependencies {
// Skip dependencies that don't apply to the current environment.
if let Some(environment_markers) = markers {
if !dependency
.complexified_marker
.evaluate(environment_markers, &[])
{
non_roots.insert(dependency.package_id.clone());
continue;
}
}

let parent = if invert {
&dependency.package_id
} else {
&packages.id
&package.id
};
let child = if invert {
Cow::Owned(Dependency {
package_id: packages.id.clone(),
package_id: package.id.clone(),
extra: dependency.extra.clone(),
simplified_marker: dependency.simplified_marker.clone(),
complexified_marker: dependency.complexified_marker.clone(),
Expand All @@ -143,16 +156,6 @@ impl<'env> TreeDisplay<'env> {

non_roots.insert(child.package_id.clone());

// Skip dependencies that don't apply to the current environment.
if let Some(environment_markers) = markers {
if !dependency
.complexified_marker
.evaluate(environment_markers, &[])
{
continue;
}
}

dev_dependencies
.entry(parent)
.or_default()
Expand All @@ -178,7 +181,7 @@ impl<'env> TreeDisplay<'env> {
dev_dependencies,
depth,
prune,
package,
packages,
no_dedupe,
}
}
Expand Down Expand Up @@ -309,15 +312,15 @@ impl<'env> TreeDisplay<'env> {
let mut path = Vec::new();
let mut lines = Vec::new();

if self.package.is_empty() {
if self.packages.is_empty() {
for id in &self.roots {
path.clear();
lines.extend(self.visit(Node::Root(id), &mut visited, &mut path));
}
} else {
let by_package: FxHashMap<_, _> = self.roots.iter().map(|id| (&id.name, id)).collect();
let mut first = true;
for package in &self.package {
for package in &self.packages {
if std::mem::take(&mut first) {
lines.push(String::new());
}
Expand Down
46 changes: 46 additions & 0 deletions crates/uv/tests/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,52 @@ fn platform_dependencies() -> Result<()> {
Ok(())
}

#[test]
fn platform_dependencies_inverted() -> Result<()> {
let context = TestContext::new("3.12");

let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"click"
]
"#,
)?;

// When `--universal` is _not_ provided, `colorama` should _not_ be included.
#[cfg(not(windows))]
uv_snapshot!(context.filters(), context.tree().arg("--invert"), @r#"
success: true
exit_code: 0
----- stdout -----
click v8.1.7
└── project v0.1.0
----- stderr -----
Resolved 3 packages in [TIME]
"#);

// Unless `--python-platform` is set to `windows`, in which case it should be included.
uv_snapshot!(context.filters(), context.tree().arg("--invert").arg("--python-platform").arg("windows"), @r#"
success: true
exit_code: 0
----- stdout -----
colorama v0.4.6
└── click v8.1.7
└── project v0.1.0
----- stderr -----
Resolved 3 packages in [TIME]
"#);

Ok(())
}

#[test]
fn repeated_dependencies() -> Result<()> {
let context = TestContext::new("3.12");
Expand Down

0 comments on commit da9e85c

Please sign in to comment.