mirror of https://github.com/astral-sh/uv
Improve resolver error messages referencing workspace members (#6092)
An extension of #6090 that replaces #6066. In brief, 1. Workspace member names are passed to the resolver for no solution errors 2. There is a new derivation tree pre-processing step that trims `NoVersion` incompatibilities for workspace members from the derivation tree. This avoids showing redundant clauses like `Because only bird==0.1.0 is available and bird==0.1.0 depends on anyio==4.3.0, we can conclude that all versions of bird depend on anyio==4.3.0.`. As a minor note, we use a custom incompatibility kind to mark these incompatibilities at resolution-time instead of afterwards. 3. Root dependencies on workspace members say `your workspace requires bird` rather than `you require bird` 4. Workspace member package display omits the version, e.g., `bird` instead of `bird==0.1.0` 5. Instead of reporting a workspace member as unusable we note that its requirements cannot be solved, e.g., `bird's requirements are unsatisfiable` instead of `bird cannot be used`. 6. Instead of saying `your requirements are unsatisfiable` we say `your workspace's requirements are unsatisfiable` when in a workspace, since we're not in a "provide direct requirements" paradigm. As an annoying but minor implementation detail, `PackageRange` now requires access to the `PubGrubReportFormatter` so it can determine if it is formatting a workspace member or not. We could probably improve the abstractions in the future. As a follow-up, we should additional special casing for "single project" workspaces to avoid mention of the workspace concept in simple projects. However, it looks like this will require additional tree manipulations so I'm going to keep it separate.
This commit is contained in:
parent
f28ce546cf
commit
2e3e6a01aa
|
|
@ -125,6 +125,7 @@ pub struct NoSolutionError {
|
||||||
incomplete_packages: FxHashMap<PackageName, BTreeMap<Version, IncompletePackage>>,
|
incomplete_packages: FxHashMap<PackageName, BTreeMap<Version, IncompletePackage>>,
|
||||||
fork_urls: ForkUrls,
|
fork_urls: ForkUrls,
|
||||||
markers: ResolverMarkers,
|
markers: ResolverMarkers,
|
||||||
|
workspace_members: BTreeSet<PackageName>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NoSolutionError {
|
impl NoSolutionError {
|
||||||
|
|
@ -139,6 +140,7 @@ impl NoSolutionError {
|
||||||
incomplete_packages: FxHashMap<PackageName, BTreeMap<Version, IncompletePackage>>,
|
incomplete_packages: FxHashMap<PackageName, BTreeMap<Version, IncompletePackage>>,
|
||||||
fork_urls: ForkUrls,
|
fork_urls: ForkUrls,
|
||||||
markers: ResolverMarkers,
|
markers: ResolverMarkers,
|
||||||
|
workspace_members: BTreeSet<PackageName>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
error,
|
error,
|
||||||
|
|
@ -150,6 +152,7 @@ impl NoSolutionError {
|
||||||
incomplete_packages,
|
incomplete_packages,
|
||||||
fork_urls,
|
fork_urls,
|
||||||
markers,
|
markers,
|
||||||
|
workspace_members,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,8 +214,14 @@ impl std::fmt::Display for NoSolutionError {
|
||||||
let formatter = PubGrubReportFormatter {
|
let formatter = PubGrubReportFormatter {
|
||||||
available_versions: &self.available_versions,
|
available_versions: &self.available_versions,
|
||||||
python_requirement: &self.python_requirement,
|
python_requirement: &self.python_requirement,
|
||||||
|
workspace_members: &self.workspace_members,
|
||||||
};
|
};
|
||||||
let report = DefaultStringReporter::report_with_formatter(&self.error, &formatter);
|
|
||||||
|
// Transform the error tree for reporting
|
||||||
|
let mut tree = self.error.clone();
|
||||||
|
collapse_unavailable_workspace_members(&mut tree);
|
||||||
|
|
||||||
|
let report = DefaultStringReporter::report_with_formatter(&tree, &formatter);
|
||||||
write!(f, "{report}")?;
|
write!(f, "{report}")?;
|
||||||
|
|
||||||
// Include any additional hints.
|
// Include any additional hints.
|
||||||
|
|
@ -232,6 +241,51 @@ impl std::fmt::Display for NoSolutionError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given a [`DerivationTree`], collapse any [`UnavailablePackage::WorkspaceMember`] incompatibilities
|
||||||
|
/// to avoid saying things like "only <workspace-member>==0.1.0 is available".
|
||||||
|
fn collapse_unavailable_workspace_members(
|
||||||
|
tree: &mut DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>,
|
||||||
|
) {
|
||||||
|
match tree {
|
||||||
|
DerivationTree::External(_) => {}
|
||||||
|
DerivationTree::Derived(derived) => {
|
||||||
|
match (
|
||||||
|
Arc::make_mut(&mut derived.cause1),
|
||||||
|
Arc::make_mut(&mut derived.cause2),
|
||||||
|
) {
|
||||||
|
// If one node is an unavailable workspace member...
|
||||||
|
(
|
||||||
|
DerivationTree::External(External::Custom(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
UnavailableReason::Package(UnavailablePackage::WorkspaceMember),
|
||||||
|
)),
|
||||||
|
ref mut other,
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
ref mut other,
|
||||||
|
DerivationTree::External(External::Custom(
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
UnavailableReason::Package(UnavailablePackage::WorkspaceMember),
|
||||||
|
)),
|
||||||
|
) => {
|
||||||
|
// First, recursively collapse the other side of the tree
|
||||||
|
collapse_unavailable_workspace_members(other);
|
||||||
|
|
||||||
|
// Then, replace this node with the other tree
|
||||||
|
*tree = other.clone();
|
||||||
|
}
|
||||||
|
// If not, just recurse
|
||||||
|
_ => {
|
||||||
|
collapse_unavailable_workspace_members(Arc::make_mut(&mut derived.cause1));
|
||||||
|
collapse_unavailable_workspace_members(Arc::make_mut(&mut derived.cause2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NoSolutionHeader {
|
pub struct NoSolutionHeader {
|
||||||
/// The [`ResolverMarkers`] that caused the failure.
|
/// The [`ResolverMarkers`] that caused the failure.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use pep508_rs::MarkerEnvironment;
|
use pep508_rs::MarkerEnvironment;
|
||||||
use pypi_types::Requirement;
|
use pypi_types::Requirement;
|
||||||
|
|
@ -36,6 +37,9 @@ pub struct Manifest {
|
||||||
/// The name of the project.
|
/// The name of the project.
|
||||||
pub(crate) project: Option<PackageName>,
|
pub(crate) project: Option<PackageName>,
|
||||||
|
|
||||||
|
/// Members of the project's workspace.
|
||||||
|
pub(crate) workspace_members: BTreeSet<PackageName>,
|
||||||
|
|
||||||
/// The installed packages to exclude from consideration during resolution.
|
/// The installed packages to exclude from consideration during resolution.
|
||||||
///
|
///
|
||||||
/// These typically represent packages that are being upgraded or reinstalled
|
/// These typically represent packages that are being upgraded or reinstalled
|
||||||
|
|
@ -58,6 +62,7 @@ impl Manifest {
|
||||||
dev: Vec<GroupName>,
|
dev: Vec<GroupName>,
|
||||||
preferences: Preferences,
|
preferences: Preferences,
|
||||||
project: Option<PackageName>,
|
project: Option<PackageName>,
|
||||||
|
workspace_members: Option<BTreeSet<PackageName>>,
|
||||||
exclusions: Exclusions,
|
exclusions: Exclusions,
|
||||||
lookaheads: Vec<RequestedRequirements>,
|
lookaheads: Vec<RequestedRequirements>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
@ -68,6 +73,7 @@ impl Manifest {
|
||||||
dev,
|
dev,
|
||||||
preferences,
|
preferences,
|
||||||
project,
|
project,
|
||||||
|
workspace_members: workspace_members.unwrap_or_default(),
|
||||||
exclusions,
|
exclusions,
|
||||||
lookaheads,
|
lookaheads,
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +88,7 @@ impl Manifest {
|
||||||
preferences: Preferences::default(),
|
preferences: Preferences::default(),
|
||||||
project: None,
|
project: None,
|
||||||
exclusions: Exclusions::default(),
|
exclusions: Exclusions::default(),
|
||||||
|
workspace_members: BTreeSet::new(),
|
||||||
lookaheads: Vec::new(),
|
lookaheads: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ pub(crate) struct PubGrubReportFormatter<'a> {
|
||||||
|
|
||||||
/// The versions that were available for each package
|
/// The versions that were available for each package
|
||||||
pub(crate) python_requirement: &'a PythonRequirement,
|
pub(crate) python_requirement: &'a PythonRequirement,
|
||||||
|
|
||||||
|
pub(crate) workspace_members: &'a BTreeSet<PackageName>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
|
impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
|
||||||
|
|
@ -53,12 +55,12 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
|
||||||
return if let Some(target) = self.python_requirement.target() {
|
return if let Some(target) = self.python_requirement.target() {
|
||||||
format!(
|
format!(
|
||||||
"the requested {package} version ({target}) does not satisfy {}",
|
"the requested {package} version ({target}) does not satisfy {}",
|
||||||
PackageRange::compatibility(package, set)
|
self.compatible_range(package, set)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"the requested {package} version does not satisfy {}",
|
"the requested {package} version does not satisfy {}",
|
||||||
PackageRange::compatibility(package, set)
|
self.compatible_range(package, set)
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +71,7 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
|
||||||
return format!(
|
return format!(
|
||||||
"the current {package} version ({}) does not satisfy {}",
|
"the current {package} version ({}) does not satisfy {}",
|
||||||
self.python_requirement.installed(),
|
self.python_requirement.installed(),
|
||||||
PackageRange::compatibility(package, set)
|
self.compatible_range(package, set)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,57 +88,51 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
|
||||||
if segments == 1 {
|
if segments == 1 {
|
||||||
format!(
|
format!(
|
||||||
"only {} is available",
|
"only {} is available",
|
||||||
PackageRange::compatibility(package, &complement)
|
self.compatible_range(package, &complement)
|
||||||
)
|
)
|
||||||
// Complex case, there are multiple ranges
|
// Complex case, there are multiple ranges
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"only the following versions of {} {}",
|
"only the following versions of {} {}",
|
||||||
package,
|
package,
|
||||||
PackageRange::available(package, &complement)
|
self.availability_range(package, &complement)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
External::Custom(package, set, reason) => match &**package {
|
External::Custom(package, set, reason) => {
|
||||||
PubGrubPackageInner::Root(Some(name)) => {
|
if let Some(root) = self.format_root(package) {
|
||||||
format!("{name} cannot be used because {reason}")
|
format!("{root} cannot be used because {reason}")
|
||||||
}
|
} else {
|
||||||
PubGrubPackageInner::Root(None) => {
|
match reason {
|
||||||
format!("your requirements cannot be used because {reason}")
|
UnavailableReason::Package(reason) => {
|
||||||
}
|
// While there may be a term attached, this error applies to the entire
|
||||||
_ => match reason {
|
// package, so we show it for the entire package
|
||||||
UnavailableReason::Package(reason) => {
|
format!("{}{reason}", Padded::new("", &package, " "))
|
||||||
// While there may be a term attached, this error applies to the entire
|
}
|
||||||
// package, so we show it for the entire package
|
UnavailableReason::Version(reason) => {
|
||||||
format!("{}{reason}", Padded::new("", &package, " "))
|
format!(
|
||||||
|
"{}{reason}",
|
||||||
|
Padded::new("", &self.compatible_range(package, set), " ")
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
UnavailableReason::Version(reason) => {
|
}
|
||||||
format!(
|
}
|
||||||
"{}{reason}",
|
|
||||||
Padded::new("", &PackageRange::compatibility(package, set), " ")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
External::FromDependencyOf(package, package_set, dependency, dependency_set) => {
|
External::FromDependencyOf(package, package_set, dependency, dependency_set) => {
|
||||||
let package_set = self.simplify_set(package_set, package);
|
let package_set = self.simplify_set(package_set, package);
|
||||||
let dependency_set = self.simplify_set(dependency_set, dependency);
|
let dependency_set = self.simplify_set(dependency_set, dependency);
|
||||||
match &**package {
|
if let Some(root) = self.format_root_requires(package) {
|
||||||
PubGrubPackageInner::Root(Some(name)) => format!(
|
return format!(
|
||||||
"{name} depends on {}",
|
"{root} {}",
|
||||||
PackageRange::dependency(dependency, &dependency_set)
|
self.dependency_range(dependency, &dependency_set)
|
||||||
),
|
);
|
||||||
PubGrubPackageInner::Root(None) => format!(
|
|
||||||
"you require {}",
|
|
||||||
PackageRange::dependency(dependency, &dependency_set)
|
|
||||||
),
|
|
||||||
_ => format!(
|
|
||||||
"{}",
|
|
||||||
PackageRange::compatibility(package, &package_set)
|
|
||||||
.depends_on(dependency, &dependency_set),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
format!(
|
||||||
|
"{}",
|
||||||
|
self.compatible_range(package, &package_set)
|
||||||
|
.depends_on(dependency, &dependency_set),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -150,25 +146,24 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
|
||||||
match terms_vec.as_slice() {
|
match terms_vec.as_slice() {
|
||||||
[] => "the requirements are unsatisfiable".into(),
|
[] => "the requirements are unsatisfiable".into(),
|
||||||
[(root, _)] if matches!(&**(*root), PubGrubPackageInner::Root(_)) => {
|
[(root, _)] if matches!(&**(*root), PubGrubPackageInner::Root(_)) => {
|
||||||
"the requirements are unsatisfiable".into()
|
let root = self.format_root(root).unwrap();
|
||||||
|
format!("{root} are unsatisfiable")
|
||||||
}
|
}
|
||||||
[(package, Term::Positive(range))]
|
[(package, Term::Positive(range))]
|
||||||
if matches!(&**(*package), PubGrubPackageInner::Package { .. }) =>
|
if matches!(&**(*package), PubGrubPackageInner::Package { .. }) =>
|
||||||
{
|
{
|
||||||
let range = self.simplify_set(range, package);
|
let range = self.simplify_set(range, package);
|
||||||
format!(
|
if let Some(member) = self.format_workspace_member(package) {
|
||||||
"{} cannot be used",
|
format!("{member}'s requirements are unsatisfiable")
|
||||||
PackageRange::compatibility(package, &range)
|
} else {
|
||||||
)
|
format!("{} cannot be used", self.compatible_range(package, &range))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
[(package, Term::Negative(range))]
|
[(package, Term::Negative(range))]
|
||||||
if matches!(&**(*package), PubGrubPackageInner::Package { .. }) =>
|
if matches!(&**(*package), PubGrubPackageInner::Package { .. }) =>
|
||||||
{
|
{
|
||||||
let range = self.simplify_set(range, package);
|
let range = self.simplify_set(range, package);
|
||||||
format!(
|
format!("{} must be used", self.compatible_range(package, &range))
|
||||||
"{} must be used",
|
|
||||||
PackageRange::compatibility(package, &range)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
[(p1, Term::Positive(r1)), (p2, Term::Negative(r2))] => self.format_external(
|
[(p1, Term::Positive(r1)), (p2, Term::Negative(r2))] => self.format_external(
|
||||||
&External::FromDependencyOf((*p1).clone(), r1.clone(), (*p2).clone(), r2.clone()),
|
&External::FromDependencyOf((*p1).clone(), r1.clone(), (*p2).clone(), r2.clone()),
|
||||||
|
|
@ -180,7 +175,7 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
let str_terms: Vec<_> = slice
|
let str_terms: Vec<_> = slice
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(p, t)| format!("{}", PackageTerm::new(p, t)))
|
.map(|(p, t)| format!("{}", PackageTerm::new(p, t, self)))
|
||||||
.collect();
|
.collect();
|
||||||
for (index, term) in str_terms.iter().enumerate() {
|
for (index, term) in str_terms.iter().enumerate() {
|
||||||
result.push_str(term);
|
result.push_str(term);
|
||||||
|
|
@ -195,7 +190,7 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let [(p, t)] = slice {
|
if let [(p, t)] = slice {
|
||||||
if PackageTerm::new(p, t).plural() {
|
if PackageTerm::new(p, t, self).plural() {
|
||||||
result.push_str(" are incompatible");
|
result.push_str(" are incompatible");
|
||||||
} else {
|
} else {
|
||||||
result.push_str(" is incompatible");
|
result.push_str(" is incompatible");
|
||||||
|
|
@ -328,6 +323,88 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PubGrubReportFormatter<'_> {
|
impl PubGrubReportFormatter<'_> {
|
||||||
|
/// Return the formatting for "the root package requires", if the given
|
||||||
|
/// package is the root package.
|
||||||
|
///
|
||||||
|
/// If not given the root package, returns `None`.
|
||||||
|
fn format_root_requires(&self, package: &PubGrubPackage) -> Option<String> {
|
||||||
|
if self.is_workspace() {
|
||||||
|
if matches!(&**package, PubGrubPackageInner::Root(_)) {
|
||||||
|
return Some("your workspace requires".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match &**package {
|
||||||
|
PubGrubPackageInner::Root(Some(name)) => Some(format!("{name} depends on")),
|
||||||
|
PubGrubPackageInner::Root(None) => Some("you require".to_string()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the formatting for "the root package", if the given
|
||||||
|
/// package is the root package.
|
||||||
|
///
|
||||||
|
/// If not given the root package, returns `None`.
|
||||||
|
fn format_root(&self, package: &PubGrubPackage) -> Option<String> {
|
||||||
|
if self.is_workspace() {
|
||||||
|
if matches!(&**package, PubGrubPackageInner::Root(_)) {
|
||||||
|
return Some("your workspace's requirements".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match &**package {
|
||||||
|
PubGrubPackageInner::Root(Some(_)) => Some("the requirements".to_string()),
|
||||||
|
PubGrubPackageInner::Root(None) => Some("the requirements".to_string()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the resolution error is for a workspace.
|
||||||
|
fn is_workspace(&self) -> bool {
|
||||||
|
!self.workspace_members.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a display name for the package if it is a workspace member.
|
||||||
|
fn format_workspace_member(&self, package: &PubGrubPackage) -> Option<String> {
|
||||||
|
match &**package {
|
||||||
|
PubGrubPackageInner::Package { name, .. }
|
||||||
|
| PubGrubPackageInner::Extra { name, .. }
|
||||||
|
| PubGrubPackageInner::Dev { name, .. } => {
|
||||||
|
if self.workspace_members.contains(name) {
|
||||||
|
Some(format!("{name}"))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [`PackageRange::compatibility`] display with this formatter attached.
|
||||||
|
fn compatible_range<'a>(
|
||||||
|
&'a self,
|
||||||
|
package: &'a PubGrubPackage,
|
||||||
|
range: &'a Range<Version>,
|
||||||
|
) -> PackageRange<'a> {
|
||||||
|
PackageRange::compatibility(package, range, Some(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [`PackageRange::dependency`] display with this formatter attached.
|
||||||
|
fn dependency_range<'a>(
|
||||||
|
&'a self,
|
||||||
|
package: &'a PubGrubPackage,
|
||||||
|
range: &'a Range<Version>,
|
||||||
|
) -> PackageRange<'a> {
|
||||||
|
PackageRange::dependency(package, range, Some(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [`PackageRange::availability`] display with this formatter attached.
|
||||||
|
fn availability_range<'a>(
|
||||||
|
&'a self,
|
||||||
|
package: &'a PubGrubPackage,
|
||||||
|
range: &'a Range<Version>,
|
||||||
|
) -> PackageRange<'a> {
|
||||||
|
PackageRange::availability(package, range, Some(self))
|
||||||
|
}
|
||||||
|
|
||||||
/// Format two external incompatibilities, combining them if possible.
|
/// Format two external incompatibilities, combining them if possible.
|
||||||
fn format_both_external(
|
fn format_both_external(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -340,33 +417,26 @@ impl PubGrubReportFormatter<'_> {
|
||||||
External::FromDependencyOf(package2, _, dependency2, dependency_set2),
|
External::FromDependencyOf(package2, _, dependency2, dependency_set2),
|
||||||
) if package1 == package2 => {
|
) if package1 == package2 => {
|
||||||
let dependency_set1 = self.simplify_set(dependency_set1, dependency1);
|
let dependency_set1 = self.simplify_set(dependency_set1, dependency1);
|
||||||
let dependency1 = PackageRange::dependency(dependency1, &dependency_set1);
|
let dependency1 = self.dependency_range(dependency1, &dependency_set1);
|
||||||
|
|
||||||
let dependency_set2 = self.simplify_set(dependency_set2, dependency2);
|
let dependency_set2 = self.simplify_set(dependency_set2, dependency2);
|
||||||
let dependency2 = PackageRange::dependency(dependency2, &dependency_set2);
|
let dependency2 = self.dependency_range(dependency2, &dependency_set2);
|
||||||
|
|
||||||
match &**package1 {
|
if let Some(root) = self.format_root_requires(package1) {
|
||||||
PubGrubPackageInner::Root(Some(name)) => format!(
|
return format!(
|
||||||
"{name} depends on {}and {}",
|
"{root} {}and {}",
|
||||||
Padded::new("", &dependency1, " "),
|
Padded::new("", &dependency1, " "),
|
||||||
dependency2,
|
dependency2,
|
||||||
),
|
);
|
||||||
PubGrubPackageInner::Root(None) => format!(
|
|
||||||
"you require {}and {}",
|
|
||||||
Padded::new("", &dependency1, " "),
|
|
||||||
dependency2,
|
|
||||||
),
|
|
||||||
_ => {
|
|
||||||
let package_set = self.simplify_set(package_set1, package1);
|
|
||||||
|
|
||||||
format!(
|
|
||||||
"{}",
|
|
||||||
PackageRange::compatibility(package1, &package_set)
|
|
||||||
.depends_on(dependency1.package, &dependency_set1)
|
|
||||||
.and(dependency2.package, &dependency_set2),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let package_set = self.simplify_set(package_set1, package1);
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"{}",
|
||||||
|
self.compatible_range(package1, &package_set)
|
||||||
|
.depends_on(dependency1.package, &dependency_set1)
|
||||||
|
.and(dependency2.package, &dependency_set2),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let external1 = self.format_external(external1);
|
let external1 = self.format_external(external1);
|
||||||
|
|
@ -521,7 +591,7 @@ impl PubGrubReportFormatter<'_> {
|
||||||
reason: reason.clone(),
|
reason: reason.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Some(UnavailablePackage::NotFound) => {}
|
Some(UnavailablePackage::NotFound | UnavailablePackage::WorkspaceMember) => {}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -716,7 +786,7 @@ impl std::fmt::Display for PubGrubHint {
|
||||||
"hint".bold().cyan(),
|
"hint".bold().cyan(),
|
||||||
":".bold(),
|
":".bold(),
|
||||||
package.bold(),
|
package.bold(),
|
||||||
PackageRange::compatibility(package, range).bold()
|
PackageRange::compatibility(package, range, None).bold()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Self::NoIndex => {
|
Self::NoIndex => {
|
||||||
|
|
@ -831,7 +901,7 @@ impl std::fmt::Display for PubGrubHint {
|
||||||
"hint".bold().cyan(),
|
"hint".bold().cyan(),
|
||||||
":".bold(),
|
":".bold(),
|
||||||
requires_python.bold(),
|
requires_python.bold(),
|
||||||
PackageRange::compatibility(package, package_set).bold(),
|
PackageRange::compatibility(package, package_set, None).bold(),
|
||||||
package_requires_python.bold(),
|
package_requires_python.bold(),
|
||||||
package_requires_python.bold(),
|
package_requires_python.bold(),
|
||||||
)
|
)
|
||||||
|
|
@ -844,12 +914,15 @@ impl std::fmt::Display for PubGrubHint {
|
||||||
struct PackageTerm<'a> {
|
struct PackageTerm<'a> {
|
||||||
package: &'a PubGrubPackage,
|
package: &'a PubGrubPackage,
|
||||||
term: &'a Term<Range<Version>>,
|
term: &'a Term<Range<Version>>,
|
||||||
|
formatter: &'a PubGrubReportFormatter<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for PackageTerm<'_> {
|
impl std::fmt::Display for PackageTerm<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match &self.term {
|
match &self.term {
|
||||||
Term::Positive(set) => write!(f, "{}", PackageRange::compatibility(self.package, set)),
|
Term::Positive(set) => {
|
||||||
|
write!(f, "{}", self.formatter.compatible_range(self.package, set))
|
||||||
|
}
|
||||||
Term::Negative(set) => {
|
Term::Negative(set) => {
|
||||||
if let Some(version) = set.as_singleton() {
|
if let Some(version) = set.as_singleton() {
|
||||||
// Note we do not handle the "root" package here but we should never
|
// Note we do not handle the "root" package here but we should never
|
||||||
|
|
@ -860,7 +933,8 @@ impl std::fmt::Display for PackageTerm<'_> {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{}",
|
"{}",
|
||||||
PackageRange::compatibility(self.package, &set.complement())
|
self.formatter
|
||||||
|
.compatible_range(self.package, &set.complement())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -870,19 +944,29 @@ impl std::fmt::Display for PackageTerm<'_> {
|
||||||
|
|
||||||
impl PackageTerm<'_> {
|
impl PackageTerm<'_> {
|
||||||
/// Create a new [`PackageTerm`] from a [`PubGrubPackage`] and a [`Term`].
|
/// Create a new [`PackageTerm`] from a [`PubGrubPackage`] and a [`Term`].
|
||||||
fn new<'a>(package: &'a PubGrubPackage, term: &'a Term<Range<Version>>) -> PackageTerm<'a> {
|
fn new<'a>(
|
||||||
PackageTerm { package, term }
|
package: &'a PubGrubPackage,
|
||||||
|
term: &'a Term<Range<Version>>,
|
||||||
|
formatter: &'a PubGrubReportFormatter<'a>,
|
||||||
|
) -> PackageTerm<'a> {
|
||||||
|
PackageTerm {
|
||||||
|
package,
|
||||||
|
term,
|
||||||
|
formatter,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the predicate following this package term should be singular or plural.
|
/// Returns `true` if the predicate following this package term should be singular or plural.
|
||||||
fn plural(&self) -> bool {
|
fn plural(&self) -> bool {
|
||||||
match self.term {
|
match self.term {
|
||||||
Term::Positive(set) => PackageRange::compatibility(self.package, set).plural(),
|
Term::Positive(set) => self.formatter.compatible_range(self.package, set).plural(),
|
||||||
Term::Negative(set) => {
|
Term::Negative(set) => {
|
||||||
if set.as_singleton().is_some() {
|
if set.as_singleton().is_some() {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
PackageRange::compatibility(self.package, &set.complement()).plural()
|
self.formatter
|
||||||
|
.compatible_range(self.package, &set.complement())
|
||||||
|
.plural()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -903,9 +987,49 @@ struct PackageRange<'a> {
|
||||||
package: &'a PubGrubPackage,
|
package: &'a PubGrubPackage,
|
||||||
range: &'a Range<Version>,
|
range: &'a Range<Version>,
|
||||||
kind: PackageRangeKind,
|
kind: PackageRangeKind,
|
||||||
|
formatter: Option<&'a PubGrubReportFormatter<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackageRange<'_> {
|
impl PackageRange<'_> {
|
||||||
|
fn compatibility<'a>(
|
||||||
|
package: &'a PubGrubPackage,
|
||||||
|
range: &'a Range<Version>,
|
||||||
|
formatter: Option<&'a PubGrubReportFormatter<'a>>,
|
||||||
|
) -> PackageRange<'a> {
|
||||||
|
PackageRange {
|
||||||
|
package,
|
||||||
|
range,
|
||||||
|
kind: PackageRangeKind::Compatibility,
|
||||||
|
formatter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dependency<'a>(
|
||||||
|
package: &'a PubGrubPackage,
|
||||||
|
range: &'a Range<Version>,
|
||||||
|
formatter: Option<&'a PubGrubReportFormatter<'a>>,
|
||||||
|
) -> PackageRange<'a> {
|
||||||
|
PackageRange {
|
||||||
|
package,
|
||||||
|
range,
|
||||||
|
kind: PackageRangeKind::Dependency,
|
||||||
|
formatter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn availability<'a>(
|
||||||
|
package: &'a PubGrubPackage,
|
||||||
|
range: &'a Range<Version>,
|
||||||
|
formatter: Option<&'a PubGrubReportFormatter<'a>>,
|
||||||
|
) -> PackageRange<'a> {
|
||||||
|
PackageRange {
|
||||||
|
package,
|
||||||
|
range,
|
||||||
|
kind: PackageRangeKind::Available,
|
||||||
|
formatter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a boolean indicating if the predicate following this package range should
|
/// Returns a boolean indicating if the predicate following this package range should
|
||||||
/// be singular or plural e.g. if false use "<range> depends on <...>" and
|
/// be singular or plural e.g. if false use "<range> depends on <...>" and
|
||||||
/// if true use "<range> depend on <...>"
|
/// if true use "<range> depend on <...>"
|
||||||
|
|
@ -930,11 +1054,20 @@ impl PackageRange<'_> {
|
||||||
impl std::fmt::Display for PackageRange<'_> {
|
impl std::fmt::Display for PackageRange<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
// Exit early for the root package — the range is not meaningful
|
// Exit early for the root package — the range is not meaningful
|
||||||
let package = match &**self.package {
|
if let Some(root) = self
|
||||||
PubGrubPackageInner::Root(Some(name)) => return write!(f, "{name}"),
|
.formatter
|
||||||
PubGrubPackageInner::Root(None) => return write!(f, "your requirements"),
|
.and_then(|formatter| formatter.format_root(self.package))
|
||||||
_ => self.package,
|
{
|
||||||
};
|
return write!(f, "{root}");
|
||||||
|
}
|
||||||
|
// Exit early for workspace members, only a single version is available
|
||||||
|
if let Some(member) = self
|
||||||
|
.formatter
|
||||||
|
.and_then(|formatter| formatter.format_workspace_member(self.package))
|
||||||
|
{
|
||||||
|
return write!(f, "{member}");
|
||||||
|
}
|
||||||
|
let package = self.package;
|
||||||
|
|
||||||
if self.range.is_empty() {
|
if self.range.is_empty() {
|
||||||
return write!(f, "{package} ∅");
|
return write!(f, "{package} ∅");
|
||||||
|
|
@ -982,33 +1115,6 @@ impl std::fmt::Display for PackageRange<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PackageRange<'_> {
|
impl PackageRange<'_> {
|
||||||
fn compatibility<'a>(
|
|
||||||
package: &'a PubGrubPackage,
|
|
||||||
range: &'a Range<Version>,
|
|
||||||
) -> PackageRange<'a> {
|
|
||||||
PackageRange {
|
|
||||||
package,
|
|
||||||
range,
|
|
||||||
kind: PackageRangeKind::Compatibility,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dependency<'a>(package: &'a PubGrubPackage, range: &'a Range<Version>) -> PackageRange<'a> {
|
|
||||||
PackageRange {
|
|
||||||
package,
|
|
||||||
range,
|
|
||||||
kind: PackageRangeKind::Dependency,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn available<'a>(package: &'a PubGrubPackage, range: &'a Range<Version>) -> PackageRange<'a> {
|
|
||||||
PackageRange {
|
|
||||||
package,
|
|
||||||
range,
|
|
||||||
kind: PackageRangeKind::Available,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn depends_on<'a>(
|
fn depends_on<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
package: &'a PubGrubPackage,
|
package: &'a PubGrubPackage,
|
||||||
|
|
@ -1016,7 +1122,12 @@ impl PackageRange<'_> {
|
||||||
) -> DependsOn<'a> {
|
) -> DependsOn<'a> {
|
||||||
DependsOn {
|
DependsOn {
|
||||||
package: self,
|
package: self,
|
||||||
dependency1: PackageRange::dependency(package, range),
|
dependency1: PackageRange {
|
||||||
|
package,
|
||||||
|
range,
|
||||||
|
kind: PackageRangeKind::Dependency,
|
||||||
|
formatter: self.formatter,
|
||||||
|
},
|
||||||
dependency2: None,
|
dependency2: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1035,7 +1146,12 @@ impl<'a> DependsOn<'a> {
|
||||||
///
|
///
|
||||||
/// Note this overwrites previous calls to `DependsOn::and`.
|
/// Note this overwrites previous calls to `DependsOn::and`.
|
||||||
fn and(mut self, package: &'a PubGrubPackage, range: &'a Range<Version>) -> DependsOn<'a> {
|
fn and(mut self, package: &'a PubGrubPackage, range: &'a Range<Version>) -> DependsOn<'a> {
|
||||||
self.dependency2 = Some(PackageRange::dependency(package, range));
|
self.dependency2 = Some(PackageRange {
|
||||||
|
package,
|
||||||
|
range,
|
||||||
|
kind: PackageRangeKind::Dependency,
|
||||||
|
formatter: self.package.formatter,
|
||||||
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,8 @@ pub(crate) enum UnavailablePackage {
|
||||||
InvalidMetadata(String),
|
InvalidMetadata(String),
|
||||||
/// The package has an invalid structure.
|
/// The package has an invalid structure.
|
||||||
InvalidStructure(String),
|
InvalidStructure(String),
|
||||||
|
/// No other versions of the package can be used because it is a workspace member
|
||||||
|
WorkspaceMember,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnavailablePackage {
|
impl UnavailablePackage {
|
||||||
|
|
@ -85,6 +87,7 @@ impl UnavailablePackage {
|
||||||
UnavailablePackage::MissingMetadata => "does not include a `METADATA` file",
|
UnavailablePackage::MissingMetadata => "does not include a `METADATA` file",
|
||||||
UnavailablePackage::InvalidMetadata(_) => "has invalid metadata",
|
UnavailablePackage::InvalidMetadata(_) => "has invalid metadata",
|
||||||
UnavailablePackage::InvalidStructure(_) => "has an invalid package format",
|
UnavailablePackage::InvalidStructure(_) => "has an invalid package format",
|
||||||
|
UnavailablePackage::WorkspaceMember => "is a workspace member",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@ struct ResolverState<InstalledPackages: InstalledPackagesProvider> {
|
||||||
hasher: HashStrategy,
|
hasher: HashStrategy,
|
||||||
markers: ResolverMarkers,
|
markers: ResolverMarkers,
|
||||||
python_requirement: PythonRequirement,
|
python_requirement: PythonRequirement,
|
||||||
|
workspace_members: BTreeSet<PackageName>,
|
||||||
/// This is derived from `PythonRequirement` once at initialization
|
/// This is derived from `PythonRequirement` once at initialization
|
||||||
/// time. It's used in universal mode to filter our dependencies with
|
/// time. It's used in universal mode to filter our dependencies with
|
||||||
/// a `python_version` marker expression that has no overlap with the
|
/// a `python_version` marker expression that has no overlap with the
|
||||||
|
|
@ -225,6 +226,7 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
|
||||||
),
|
),
|
||||||
groups: Groups::from_manifest(&manifest, markers.marker_environment()),
|
groups: Groups::from_manifest(&manifest, markers.marker_environment()),
|
||||||
project: manifest.project,
|
project: manifest.project,
|
||||||
|
workspace_members: manifest.workspace_members,
|
||||||
requirements: manifest.requirements,
|
requirements: manifest.requirements,
|
||||||
constraints: manifest.constraints,
|
constraints: manifest.constraints,
|
||||||
overrides: manifest.overrides,
|
overrides: manifest.overrides,
|
||||||
|
|
@ -449,8 +451,23 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
||||||
.term_intersection_for_package(&state.next)
|
.term_intersection_for_package(&state.next)
|
||||||
.expect("a package was chosen but we don't have a term");
|
.expect("a package was chosen but we don't have a term");
|
||||||
|
|
||||||
// Check if the decision was due to the package being unavailable
|
|
||||||
if let PubGrubPackageInner::Package { ref name, .. } = &*state.next {
|
if let PubGrubPackageInner::Package { ref name, .. } = &*state.next {
|
||||||
|
// Check if the decision was due to the package being a
|
||||||
|
// workspace member
|
||||||
|
if self.workspace_members.contains(name) {
|
||||||
|
state
|
||||||
|
.pubgrub
|
||||||
|
.add_incompatibility(Incompatibility::custom_term(
|
||||||
|
state.next.clone(),
|
||||||
|
term_intersection.clone(),
|
||||||
|
UnavailableReason::Package(
|
||||||
|
UnavailablePackage::WorkspaceMember,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the decision was due to the package being unavailable
|
||||||
if let Some(entry) = self.unavailable_packages.get(name) {
|
if let Some(entry) = self.unavailable_packages.get(name) {
|
||||||
state
|
state
|
||||||
.pubgrub
|
.pubgrub
|
||||||
|
|
@ -1988,6 +2005,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
||||||
incomplete_packages,
|
incomplete_packages,
|
||||||
fork_urls,
|
fork_urls,
|
||||||
markers,
|
markers,
|
||||||
|
self.workspace_members.clone(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -350,6 +350,7 @@ pub(crate) async fn pip_compile(
|
||||||
dev,
|
dev,
|
||||||
source_trees,
|
source_trees,
|
||||||
project,
|
project,
|
||||||
|
None,
|
||||||
&extras,
|
&extras,
|
||||||
preferences,
|
preferences,
|
||||||
EmptyInstalledPackages,
|
EmptyInstalledPackages,
|
||||||
|
|
|
||||||
|
|
@ -340,6 +340,7 @@ pub(crate) async fn pip_install(
|
||||||
dev,
|
dev,
|
||||||
source_trees,
|
source_trees,
|
||||||
project,
|
project,
|
||||||
|
None,
|
||||||
extras,
|
extras,
|
||||||
preferences,
|
preferences,
|
||||||
site_packages.clone(),
|
site_packages.clone(),
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
@ -91,6 +92,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
|
||||||
dev: Vec<GroupName>,
|
dev: Vec<GroupName>,
|
||||||
source_trees: Vec<PathBuf>,
|
source_trees: Vec<PathBuf>,
|
||||||
mut project: Option<PackageName>,
|
mut project: Option<PackageName>,
|
||||||
|
workspace_members: Option<BTreeSet<PackageName>>,
|
||||||
extras: &ExtrasSpecification,
|
extras: &ExtrasSpecification,
|
||||||
preferences: Vec<Preference>,
|
preferences: Vec<Preference>,
|
||||||
installed_packages: InstalledPackages,
|
installed_packages: InstalledPackages,
|
||||||
|
|
@ -230,6 +232,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
|
||||||
dev,
|
dev,
|
||||||
preferences,
|
preferences,
|
||||||
project,
|
project,
|
||||||
|
workspace_members,
|
||||||
exclusions,
|
exclusions,
|
||||||
lookaheads,
|
lookaheads,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -288,6 +288,7 @@ pub(crate) async fn pip_sync(
|
||||||
dev,
|
dev,
|
||||||
source_trees,
|
source_trees,
|
||||||
project,
|
project,
|
||||||
|
None,
|
||||||
&extras,
|
&extras,
|
||||||
preferences,
|
preferences,
|
||||||
site_packages.clone(),
|
site_packages.clone(),
|
||||||
|
|
|
||||||
|
|
@ -596,6 +596,7 @@ async fn do_lock(
|
||||||
dev,
|
dev,
|
||||||
source_trees,
|
source_trees,
|
||||||
None,
|
None,
|
||||||
|
Some(workspace.packages().keys().cloned().collect()),
|
||||||
&extras,
|
&extras,
|
||||||
preferences,
|
preferences,
|
||||||
EmptyInstalledPackages,
|
EmptyInstalledPackages,
|
||||||
|
|
|
||||||
|
|
@ -630,6 +630,7 @@ pub(crate) async fn resolve_environment<'a>(
|
||||||
dev,
|
dev,
|
||||||
source_trees,
|
source_trees,
|
||||||
project,
|
project,
|
||||||
|
None,
|
||||||
&extras,
|
&extras,
|
||||||
preferences,
|
preferences,
|
||||||
EmptyInstalledPackages,
|
EmptyInstalledPackages,
|
||||||
|
|
@ -952,6 +953,7 @@ pub(crate) async fn update_environment(
|
||||||
dev,
|
dev,
|
||||||
source_trees,
|
source_trees,
|
||||||
project,
|
project,
|
||||||
|
None,
|
||||||
&extras,
|
&extras,
|
||||||
preferences,
|
preferences,
|
||||||
site_packages.clone(),
|
site_packages.clone(),
|
||||||
|
|
|
||||||
|
|
@ -2578,8 +2578,8 @@ fn add_error() -> Result<()> {
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
warning: `uv add` is experimental and may change without warning
|
warning: `uv add` is experimental and may change without warning
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because there are no versions of xyz and project==0.1.0 depends on xyz, we can conclude that project==0.1.0 cannot be used.
|
╰─▶ Because there are no versions of xyz and project depends on xyz, we can conclude that project's requirements are unsatisfiable.
|
||||||
And because only project==0.1.0 is available and you require project, we can conclude that the requirements are unsatisfiable.
|
And because your workspace requires project, we can conclude that your workspace's requirements are unsatisfiable.
|
||||||
help: If this is intentional, run `uv add --frozen` to skip the lock and sync steps.
|
help: If this is intentional, run `uv add --frozen` to skip the lock and sync steps.
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2748,8 +2748,7 @@ fn lock_requires_python() -> Result<()> {
|
||||||
pygls>=1.1.0,<1.3.0
|
pygls>=1.1.0,<1.3.0
|
||||||
pygls>1.3.0
|
pygls>1.3.0
|
||||||
cannot be used, we can conclude that pygls>=1.1.0 cannot be used.
|
cannot be used, we can conclude that pygls>=1.1.0 cannot be used.
|
||||||
And because project==0.1.0 depends on pygls>=1.1.0, we can conclude that project==0.1.0 cannot be used.
|
And because project depends on pygls>=1.1.0 and your workspace requires project, we can conclude that your workspace's requirements are unsatisfiable.
|
||||||
And because only project==0.1.0 is available and you require project, we can conclude that the requirements are unsatisfiable.
|
|
||||||
|
|
||||||
hint: The `requires-python` value (>=3.7) includes Python versions that are not supported by your dependencies (e.g., pygls>=1.1.0,<=1.2.1 only supports >=3.7.9, <4). Consider using a more restrictive `requires-python` value (like >=3.7.9, <4).
|
hint: The `requires-python` value (>=3.7) includes Python versions that are not supported by your dependencies (e.g., pygls>=1.1.0,<=1.2.1 only supports >=3.7.9, <4). Consider using a more restrictive `requires-python` value (like >=3.7.9, <4).
|
||||||
"###);
|
"###);
|
||||||
|
|
@ -5016,8 +5015,8 @@ fn lock_requires_python_no_wheels() -> Result<()> {
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
warning: `uv lock` is experimental and may change without warning
|
warning: `uv lock` is experimental and may change without warning
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because dearpygui==1.9.1 has no wheels with a matching Python ABI tag and project==0.1.0 depends on dearpygui==1.9.1, we can conclude that project==0.1.0 cannot be used.
|
╰─▶ Because dearpygui==1.9.1 has no wheels with a matching Python ABI tag and project depends on dearpygui==1.9.1, we can conclude that project's requirements are unsatisfiable.
|
||||||
And because only project==0.1.0 is available and you require project, we can conclude that the requirements are unsatisfiable.
|
And because your workspace requires project, we can conclude that your workspace's requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -8211,8 +8210,8 @@ fn unconditional_overlapping_marker_disjoint_version_constraints() -> Result<()>
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
warning: `uv lock` is experimental and may change without warning
|
warning: `uv lock` is experimental and may change without warning
|
||||||
× No solution found when resolving dependencies for split (python_version > '3.10'):
|
× No solution found when resolving dependencies for split (python_version > '3.10'):
|
||||||
╰─▶ Because only datasets{python_version > '3.10'}<2.19 is available and project==0.1.0 depends on datasets{python_version > '3.10'}>=2.19, we can conclude that project==0.1.0 cannot be used.
|
╰─▶ Because only datasets{python_version > '3.10'}<2.19 is available and project depends on datasets{python_version > '3.10'}>=2.19, we can conclude that project's requirements are unsatisfiable.
|
||||||
And because only project==0.1.0 is available and you require project, we can conclude that the requirements are unsatisfiable.
|
And because your workspace requires project, we can conclude that your workspace's requirements are unsatisfiable.
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -468,13 +468,13 @@ fn conflict_in_fork() -> Result<()> {
|
||||||
warning: `uv lock` is experimental and may change without warning
|
warning: `uv lock` is experimental and may change without warning
|
||||||
× No solution found when resolving dependencies for split (sys_platform == 'darwin'):
|
× No solution found when resolving dependencies for split (sys_platform == 'darwin'):
|
||||||
╰─▶ Because only package-b==1.0.0 is available and package-b==1.0.0 depends on package-d==1, we can conclude that all versions of package-b depend on package-d==1.
|
╰─▶ Because only package-b==1.0.0 is available and package-b==1.0.0 depends on package-d==1, we can conclude that all versions of package-b depend on package-d==1.
|
||||||
And because package-c==1.0.0 depends on package-d==2 and only package-c==1.0.0 is available, we can conclude that all versions of package-b and all versions of package-c are incompatible.
|
And because package-c==1.0.0 depends on package-d==2, we can conclude that all versions of package-b and package-c==1.0.0 are incompatible.
|
||||||
And because package-a{sys_platform == 'darwin'}==1.0.0 depends on package-b and package-c, we can conclude that package-a{sys_platform == 'darwin'}==1.0.0 cannot be used.
|
And because only package-c==1.0.0 is available and package-a{sys_platform == 'darwin'}==1.0.0 depends on package-b, we can conclude that package-a{sys_platform == 'darwin'}==1.0.0 and all versions of package-c are incompatible.
|
||||||
And because only the following versions of package-a{sys_platform == 'darwin'} are available:
|
And because package-a{sys_platform == 'darwin'}==1.0.0 depends on package-c and only the following versions of package-a{sys_platform == 'darwin'} are available:
|
||||||
package-a{sys_platform == 'darwin'}==1.0.0
|
package-a{sys_platform == 'darwin'}==1.0.0
|
||||||
package-a{sys_platform == 'darwin'}>=2
|
package-a{sys_platform == 'darwin'}>=2
|
||||||
and project==0.1.0 depends on package-a{sys_platform == 'darwin'}<2, we can conclude that project==0.1.0 cannot be used.
|
we can conclude that package-a{sys_platform == 'darwin'}<2 is incompatible.
|
||||||
And because only project==0.1.0 is available and you require project, we can conclude that the requirements are unsatisfiable.
|
And because project depends on package-a{sys_platform == 'darwin'}<2 and your workspace requires project, we can conclude that your workspace's requirements are unsatisfiable.
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -537,8 +537,8 @@ fn fork_conflict_unsatisfiable() -> Result<()> {
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
warning: `uv lock` is experimental and may change without warning
|
warning: `uv lock` is experimental and may change without warning
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because project==0.1.0 depends on package-a>=2 and package-a<2, we can conclude that project==0.1.0 cannot be used.
|
╰─▶ Because project depends on package-a>=2 and package-a<2, we can conclude that project's requirements are unsatisfiable.
|
||||||
And because only project==0.1.0 is available and you require project, we can conclude that the requirements are unsatisfiable.
|
And because your workspace requires project, we can conclude that your workspace's requirements are unsatisfiable.
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -1262,8 +1262,8 @@ fn fork_marker_disjoint() -> Result<()> {
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
warning: `uv lock` is experimental and may change without warning
|
warning: `uv lock` is experimental and may change without warning
|
||||||
× No solution found when resolving dependencies for split (sys_platform == 'linux'):
|
× No solution found when resolving dependencies for split (sys_platform == 'linux'):
|
||||||
╰─▶ Because project==0.1.0 depends on package-a{sys_platform == 'linux'}>=2 and package-a{sys_platform == 'linux'}<2, we can conclude that project==0.1.0 cannot be used.
|
╰─▶ Because project depends on package-a{sys_platform == 'linux'}>=2 and package-a{sys_platform == 'linux'}<2, we can conclude that project's requirements are unsatisfiable.
|
||||||
And because only project==0.1.0 is available and you require project, we can conclude that the requirements are unsatisfiable.
|
And because your workspace requires project, we can conclude that your workspace's requirements are unsatisfiable.
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -3024,8 +3024,8 @@ fn fork_non_local_fork_marker_direct() -> Result<()> {
|
||||||
warning: `uv lock` is experimental and may change without warning
|
warning: `uv lock` is experimental and may change without warning
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because package-b{sys_platform == 'darwin'}==1.0.0 depends on package-c>=2.0.0 and package-a{sys_platform == 'linux'}==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-b{sys_platform == 'darwin'}==1.0.0 depends on package-c>=2.0.0 and package-a{sys_platform == 'linux'}==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.
|
||||||
And because project==0.1.0 depends on package-a{sys_platform == 'linux'}==1.0.0 and package-b{sys_platform == 'darwin'}==1.0.0, we can conclude that project==0.1.0 cannot be used.
|
And because project depends on package-a{sys_platform == 'linux'}==1.0.0, we can conclude that project and package-b{sys_platform == 'darwin'}==1.0.0 are incompatible.
|
||||||
And because only project==0.1.0 is available and you require project, we can conclude that the requirements are unsatisfiable.
|
And because project depends on package-b{sys_platform == 'darwin'}==1.0.0 and your workspace requires project, we can conclude that your workspace's requirements are unsatisfiable.
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -3101,9 +3101,9 @@ fn fork_non_local_fork_marker_transitive() -> Result<()> {
|
||||||
And because only the following versions of package-c{sys_platform == 'linux'} are available:
|
And because 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'}==1.0.0
|
||||||
package-c{sys_platform == 'linux'}>=2.0.0
|
package-c{sys_platform == 'linux'}>=2.0.0
|
||||||
and package-a==1.0.0 depends on package-c{sys_platform == 'linux'}<2.0.0, we can conclude that package-a==1.0.0 and package-b==1.0.0 are incompatible.
|
we can conclude that package-b==1.0.0 and package-c{sys_platform == 'linux'}<2.0.0 are incompatible.
|
||||||
And because project==0.1.0 depends on package-a==1.0.0 and package-b==1.0.0, we can conclude that project==0.1.0 cannot be used.
|
And because package-a==1.0.0 depends on package-c{sys_platform == 'linux'}<2.0.0 and project depends on package-a==1.0.0, we can conclude that package-b==1.0.0 and project are incompatible.
|
||||||
And because only project==0.1.0 is available and you require project, we can conclude that the requirements are unsatisfiable.
|
And because project depends on package-b==1.0.0 and your workspace requires project, we can conclude that your workspace's requirements are unsatisfiable.
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -387,7 +387,7 @@ werkzeug==3.0.1
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because flask==3.0.2 depends on click>=8.1.3 and you require click==7.0.0, we can conclude that your requirements and flask==3.0.2 are incompatible.
|
╰─▶ Because flask==3.0.2 depends on click>=8.1.3 and you require click==7.0.0, we can conclude that the requirements and flask==3.0.2 are incompatible.
|
||||||
And because you require flask==3.0.2, we can conclude that the requirements are unsatisfiable.
|
And because you require flask==3.0.2, we can conclude that the requirements are unsatisfiable.
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1008,8 +1008,8 @@ fn workspace_inherit_sources() -> Result<()> {
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
|
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because library was not found in the cache and leaf==0.1.0 depends on library, we can conclude that leaf==0.1.0 cannot be used.
|
╰─▶ Because library was not found in the cache and leaf depends on library, we can conclude that leaf's requirements are unsatisfiable.
|
||||||
And because only leaf==0.1.0 is available and you require leaf, we can conclude that the requirements are unsatisfiable.
|
And because your workspace requires leaf, we can conclude that your workspace's requirements are unsatisfiable.
|
||||||
|
|
||||||
hint: Packages were unavailable because the network was disabled
|
hint: Packages were unavailable because the network was disabled
|
||||||
"###
|
"###
|
||||||
|
|
@ -1200,8 +1200,8 @@ fn workspace_unsatisfiable_member_dependencies() -> Result<()> {
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
|
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because only httpx<=9999 is available and leaf==0.1.0 depends on httpx>9999, we can conclude that leaf==0.1.0 cannot be used.
|
╰─▶ Because only httpx<=9999 is available and leaf depends on httpx>9999, we can conclude that leaf's requirements are unsatisfiable.
|
||||||
And because only leaf==0.1.0 is available and you require leaf, we can conclude that the requirements are unsatisfiable.
|
And because your workspace requires leaf, we can conclude that your workspace's requirements are unsatisfiable.
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -1256,9 +1256,8 @@ fn workspace_unsatisfiable_member_dependencies_conflicting() -> Result<()> {
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
|
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because only bar==0.1.0 is available and bar==0.1.0 depends on anyio==4.2.0, we can conclude that all versions of bar depend on anyio==4.2.0.
|
╰─▶ Because bar depends on anyio==4.2.0 and foo depends on anyio==4.1.0, we can conclude that bar and foo are incompatible.
|
||||||
And because foo==0.1.0 depends on anyio==4.1.0 and only foo==0.1.0 is available, we can conclude that all versions of bar and all versions of foo are incompatible.
|
And because your workspace requires bar and foo, we can conclude that your workspace's requirements are unsatisfiable.
|
||||||
And because you require bar and foo, we can conclude that the requirements are unsatisfiable.
|
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -1324,9 +1323,8 @@ fn workspace_unsatisfiable_member_dependencies_conflicting_threeway() -> Result<
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
|
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||||
× No solution found when resolving dependencies:
|
× No solution found when resolving dependencies:
|
||||||
╰─▶ Because only bird==0.1.0 is available and bird==0.1.0 depends on anyio==4.3.0, we can conclude that all versions of bird depend on anyio==4.3.0.
|
╰─▶ Because bird depends on anyio==4.3.0 and knot depends on anyio==4.2.0, we can conclude that bird and knot are incompatible.
|
||||||
And because knot==0.1.0 depends on anyio==4.2.0 and only knot==0.1.0 is available, we can conclude that all versions of bird and all versions of knot are incompatible.
|
And because your workspace requires bird and knot, we can conclude that your workspace's requirements are unsatisfiable.
|
||||||
And because you require bird and knot, we can conclude that the requirements are unsatisfiable.
|
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue