Skip to content
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
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ percent-encoding = { version = "2.3.1" }
petgraph = { version = "0.8.0" }
proc-macro2 = { version = "1.0.86" }
procfs = { version = "0.17.0", default-features = false, features = ["flate2"] }
pubgrub = { git = "https://github.com/astral-sh/pubgrub", rev = "06ec5a5f59ffaeb6cf5079c6cb184467da06c9db" }
pubgrub = { git = "https://github.com/astral-sh/pubgrub", rev = "d8efd77673c9a90792da9da31b6c0da7ea8a324b" }
quote = { version = "1.0.37" }
rayon = { version = "1.10.0" }
ref-cast = { version = "1.0.24" }
Expand Down Expand Up @@ -193,7 +193,7 @@ unicode-width = { version = "0.2.0" }
unscanny = { version = "0.1.0" }
url = { version = "2.5.2", features = ["serde"] }
uuid = { version = "1.16.0" }
version-ranges = { git = "https://github.com/astral-sh/pubgrub", rev = "06ec5a5f59ffaeb6cf5079c6cb184467da06c9db" }
version-ranges = { git = "https://github.com/astral-sh/pubgrub", rev = "d8efd77673c9a90792da9da31b6c0da7ea8a324b" }
walkdir = { version = "2.5.0" }
which = { version = "8.0.0", features = ["regex"] }
windows = { version = "0.59.0", features = ["Win32_Globalization", "Win32_Security", "Win32_System_Console", "Win32_System_Kernel", "Win32_System_Diagnostics_Debug", "Win32_Storage_FileSystem", "Win32_System_Registry", "Win32_System_IO", "Win32_System_Ioctl"] }
Expand Down
26 changes: 26 additions & 0 deletions crates/uv-resolver/src/pubgrub/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,32 @@ impl PubGrubPackage {
}
}

/// If this package is a proxy package, return the base package it depends on.
///
/// While dependency groups may be attached to a package, we don't consider them here as
/// there is no (mandatory) dependency from a dependency group to the package.
pub(crate) fn base_package(&self) -> Option<Self> {
match &**self {
PubGrubPackageInner::Root(_)
| PubGrubPackageInner::Python(_)
| PubGrubPackageInner::System(_)
| PubGrubPackageInner::Package { .. } => None,
PubGrubPackageInner::Group { .. } => {
// The dependency groups of a package do not by themselves require the package
// itself.
None
}
PubGrubPackageInner::Extra { name, .. } | PubGrubPackageInner::Marker { name, .. } => {
Some(Self::from_package(
name.clone(),
None,
None,
MarkerTree::TRUE,
))
}
}
}

/// Returns the name of this PubGrub package, if it has one.
pub(crate) fn name(&self) -> Option<&PackageName> {
match &**self {
Expand Down
2 changes: 1 addition & 1 deletion crates/uv-resolver/src/pubgrub/priority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ impl PubGrubPriorities {
Some(tiebreaker) => *tiebreaker,
None => {
if cfg!(debug_assertions) {
panic!("Virtual package not known: `{package}`")
panic!("Package not registered in prioritization: `{package:?}`")
} else {
PubGrubTiebreaker(Reverse(u32::MAX))
}
Expand Down
24 changes: 24 additions & 0 deletions crates/uv-resolver/src/resolver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2952,6 +2952,12 @@ impl ForkState {

// Update the package priorities.
self.priorities.insert(package, version, &self.fork_urls);
// As we're adding an incompatibility from the proxy package to the base package,
// we need to register the base package.
if let Some(base_package) = package.base_package() {
self.priorities
.insert(&base_package, version, &self.fork_urls);
}
}

Ok(())
Expand All @@ -2964,6 +2970,24 @@ impl ForkState {
for_version: &Version,
dependencies: Vec<PubGrubDependency>,
) {
for dependency in &dependencies {
let PubGrubDependency {
package,
version,
parent: _,
url: _,
} = dependency;

let Some(base_package) = package.base_package() else {
continue;
};

let proxy_package = self.pubgrub.package_store.alloc(package.clone());
let base_package_id = self.pubgrub.package_store.alloc(base_package.clone());
self.pubgrub
.add_proxy_package(proxy_package, base_package_id, version.clone());
}

let conflict = self.pubgrub.add_package_version_dependencies(
self.next,
for_version.clone(),
Expand Down
92 changes: 42 additions & 50 deletions crates/uv/tests/it/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13103,7 +13103,7 @@ fn unconditional_overlapping_marker_disjoint_version_constraints() -> Result<()>

----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because only datasets<=2.18.0 is available and your project depends on datasets>=2.19, we can conclude that your project's requirements are unsatisfiable.
╰─▶ Because your project depends on datasets<2.19 and datasets>=2.19, we can conclude that your project's requirements are unsatisfiable.
");

Ok(())
Expand Down Expand Up @@ -26786,17 +26786,17 @@ fn lock_self_marker_incompatible() -> Result<()> {
"#,
)?;

uv_snapshot!(context.filters(), context.lock(), @r###"
uv_snapshot!(context.filters(), context.lock(), @r"
success: false
exit_code: 1
----- stdout -----

----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because only project{sys_platform == 'win32'}<=0.1 is available and your project depends on itself at an incompatible version (project{sys_platform == 'win32'}>0.1), we can conclude that your project's requirements are unsatisfiable.
╰─▶ Because your project depends on itself at an incompatible version (project{sys_platform == 'win32'}>0.1), we can conclude that your project's requirements are unsatisfiable.

hint: The project `project` depends on itself at an incompatible version. This is likely a mistake. If you intended to depend on a third-party package named `project`, consider renaming the project `project` to avoid creating a conflict.
"###);
");

Ok(())
}
Expand Down Expand Up @@ -29965,40 +29965,8 @@ fn lock_conflict_for_disjoint_python_version() -> Result<()> {

----- stderr -----
× No solution found when resolving dependencies for split (markers: python_full_version >= '3.11'):
╰─▶ Because only the following versions of numpy{python_full_version >= '3.10'} are available:
numpy{python_full_version >= '3.10'}<=1.21.0
numpy{python_full_version >= '3.10'}==1.21.1
numpy{python_full_version >= '3.10'}==1.21.2
numpy{python_full_version >= '3.10'}==1.21.3
numpy{python_full_version >= '3.10'}==1.21.4
numpy{python_full_version >= '3.10'}==1.21.5
numpy{python_full_version >= '3.10'}==1.21.6
numpy{python_full_version >= '3.10'}==1.22.0
numpy{python_full_version >= '3.10'}==1.22.1
numpy{python_full_version >= '3.10'}==1.22.2
numpy{python_full_version >= '3.10'}==1.22.3
numpy{python_full_version >= '3.10'}==1.22.4
numpy{python_full_version >= '3.10'}==1.23.0
numpy{python_full_version >= '3.10'}==1.23.1
numpy{python_full_version >= '3.10'}==1.23.2
numpy{python_full_version >= '3.10'}==1.23.3
numpy{python_full_version >= '3.10'}==1.23.4
numpy{python_full_version >= '3.10'}==1.23.5
numpy{python_full_version >= '3.10'}==1.24.0
numpy{python_full_version >= '3.10'}==1.24.1
numpy{python_full_version >= '3.10'}==1.24.2
numpy{python_full_version >= '3.10'}==1.24.3
numpy{python_full_version >= '3.10'}==1.24.4
numpy{python_full_version >= '3.10'}==1.25.0
numpy{python_full_version >= '3.10'}==1.25.1
numpy{python_full_version >= '3.10'}==1.25.2
numpy{python_full_version >= '3.10'}==1.26.0
numpy{python_full_version >= '3.10'}==1.26.1
numpy{python_full_version >= '3.10'}==1.26.2
numpy{python_full_version >= '3.10'}==1.26.3
numpy{python_full_version >= '3.10'}==1.26.4
and pandas==1.5.3 depends on numpy{python_full_version >= '3.10'}>=1.21.0, we can conclude that pandas==1.5.3 depends on numpy>=1.21.0.
And because your project depends on numpy==1.20.3 and pandas==1.5.3, we can conclude that your project's requirements are unsatisfiable.
╰─▶ Because pandas==1.5.3 depends on numpy{python_full_version >= '3.10'}>=1.21.0 and your project depends on numpy==1.20.3, we can conclude that your project and pandas==1.5.3 are incompatible.
And because your project depends on pandas==1.5.3, we can conclude that your project's requirements are unsatisfiable.

hint: While the active Python version is 3.9, the resolution failed for other Python versions supported by your project. Consider limiting your project's supported Python versions using `requires-python`.
");
Expand Down Expand Up @@ -30219,18 +30187,7 @@ fn lock_conflict_for_disjoint_platform() -> Result<()> {

----- stderr -----
× No solution found when resolving dependencies for split (markers: sys_platform == 'exotic'):
╰─▶ Because only the following versions of numpy{sys_platform == 'exotic'} are available:
numpy{sys_platform == 'exotic'}<=1.24.0
numpy{sys_platform == 'exotic'}==1.24.1
numpy{sys_platform == 'exotic'}==1.24.2
numpy{sys_platform == 'exotic'}==1.24.3
numpy{sys_platform == 'exotic'}==1.24.4
numpy{sys_platform == 'exotic'}==1.25.0
numpy{sys_platform == 'exotic'}==1.25.1
numpy{sys_platform == 'exotic'}==1.25.2
numpy{sys_platform == 'exotic'}>1.26
and your project depends on numpy{sys_platform == 'exotic'}>=1.24,<1.26, we can conclude that your project depends on numpy>=1.24.0,<=1.25.2.
And because your project depends on numpy>=1.26, we can conclude that your project's requirements are unsatisfiable.
╰─▶ Because your project depends on numpy{sys_platform == 'exotic'}>=1.24,<1.26 and numpy>=1.26, we can conclude that your project's requirements are unsatisfiable.

hint: The resolution failed for an environment that is not the current one, consider limiting the environments with `tool.uv.environments`.
");
Expand Down Expand Up @@ -31700,3 +31657,38 @@ fn lock_required_intersection() -> Result<()> {

Ok(())
}

/// Ensure conflicts on virtual packages (such as markers) give good error messages.
#[test]
fn collapsed_error_with_marker_packages() -> Result<()> {
let context = TestContext::new("3.12");

let pyproject_toml = indoc! {r#"
[project]
name = "test-project"
version = "1.0.0"
requires-python = ">=3.12"
dependencies = [
"anyio<=4.3.0; sys_platform == 'other'",
"anyio>=4.4.0; python_version < '3.14'",
]
"#};
context
.temp_dir
.child("pyproject.toml")
.write_str(pyproject_toml)?;

uv_snapshot!(context.filters(), context.lock(), @r"
success: false
exit_code: 1
----- stdout -----

----- stderr -----
× No solution found when resolving dependencies for split (markers: python_full_version < '3.14' and sys_platform == 'other'):
╰─▶ Because your project depends on anyio{sys_platform == 'other'} and anyio{python_full_version < '3.14'}>=4.4.0, we can conclude that your project's requirements are unsatisfiable.

hint: The resolution failed for an environment that is not the current one, consider limiting the environments with `tool.uv.environments`.
");

Ok(())
}
8 changes: 2 additions & 6 deletions crates/uv/tests/it/lock_scenarios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3148,7 +3148,7 @@ fn fork_non_local_fork_marker_direct() -> Result<()> {

----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because package-a==1.0.0 depends on package-c<2.0.0 and package-b==1.0.0 depends on package-c>=2.0.0, we can conclude that package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0 are incompatible.
╰─▶ Because package-a==1.0.0 depends on package-c<2.0.0 and package-b==1.0.0 depends on package-c>=2.0.0, we can conclude that package-b==1.0.0 and package-a{sys_platform == 'linux'}==1.0.0 are incompatible.
And because your project depends on package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0, we can conclude that your project's requirements are unsatisfiable.
"
);
Expand Down Expand Up @@ -3220,11 +3220,7 @@ fn fork_non_local_fork_marker_transitive() -> Result<()> {

----- stderr -----
× No solution found when resolving dependencies:
╰─▶ Because package-a==1.0.0 depends on package-c{sys_platform == 'linux'}<2.0.0 and only the following versions of package-c{sys_platform == 'linux'} are available:
package-c{sys_platform == 'linux'}==1.0.0
package-c{sys_platform == 'linux'}>2.0.0
we can conclude that package-a==1.0.0 depends on package-c{sys_platform == 'linux'}==1.0.0.
And because only package-c{sys_platform == 'darwin'}<=2.0.0 is available and package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}>=2.0.0, we can conclude that package-a==1.0.0 and package-b==1.0.0 are incompatible.
╰─▶ Because package-a==1.0.0 depends on package-c{sys_platform == 'linux'}<2.0.0 and package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}>=2.0.0, we can conclude that package-a==1.0.0 and package-b==1.0.0 are incompatible.
And because your project depends on package-a==1.0.0 and package-b==1.0.0, we can conclude that your project's requirements are unsatisfiable.
"
);
Expand Down
Loading