Use generic pubgrub incompatibility reason (#3335)

Pubgrub got a new feature where all unavailability is a custom, instead
of the reasonless `UnavailableDependencies` and our custom `String` type
previously (https://github.com/pubgrub-rs/pubgrub/pull/208). This PR
introduces a `UnavailableReason` that tracks either an entire version
being unusable, or a specific version. The error messages now also track
this difference properly.

The pubgrub commit is our main rebased onto the merged
https://github.com/pubgrub-rs/pubgrub/pull/208, i'll push
`konsti/main-rebase-generic-reason` to `main` after checking for rebase
problems.
This commit is contained in:
konsti 2024-05-08 10:40:15 +02:00 committed by GitHub
parent bd7860de17
commit 1ad6aa8a23
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 215 additions and 165 deletions

View file

@ -53,59 +53,59 @@ impl Display for IncompatibleDist {
match self {
Self::Wheel(incompatibility) => match incompatibility {
IncompatibleWheel::NoBinary => {
f.write_str("no source distribution is available and using wheels is disabled")
f.write_str("has no available source distribution and using wheels is disabled")
}
IncompatibleWheel::Tag(tag) => match tag {
IncompatibleTag::Invalid => {
f.write_str("no wheels are available with valid tags")
}
IncompatibleTag::Python => {
f.write_str("no wheels are available with a matching Python implementation")
f.write_str("has no wheels are available with valid tags")
}
IncompatibleTag::Python => f.write_str(
"has no wheels are available with a matching Python implementation",
),
IncompatibleTag::Abi => {
f.write_str("no wheels are available with a matching Python ABI")
f.write_str("has no wheels are available with a matching Python ABI")
}
IncompatibleTag::Platform => {
f.write_str("no wheels are available with a matching platform")
f.write_str("has no wheels are available with a matching platform")
}
},
IncompatibleWheel::Yanked(yanked) => match yanked {
Yanked::Bool(_) => f.write_str("it was yanked"),
Yanked::Bool(_) => f.write_str("was yanked"),
Yanked::Reason(reason) => write!(
f,
"it was yanked (reason: {})",
"was yanked (reason: {})",
reason.trim().trim_end_matches('.')
),
},
IncompatibleWheel::ExcludeNewer(ts) => match ts {
Some(_) => f.write_str("it was published after the exclude newer time"),
None => f.write_str("it has no publish time"),
Some(_) => f.write_str("was published after the exclude newer time"),
None => f.write_str("has no publish time"),
},
IncompatibleWheel::RequiresPython(python) => {
write!(f, "it requires at python {python}")
write!(f, "requires at python {python}")
}
},
Self::Source(incompatibility) => match incompatibility {
IncompatibleSource::NoBuild => {
f.write_str("no wheels are usable and building from source is disabled")
f.write_str("has no usable wheels and building from source is disabled")
}
IncompatibleSource::Yanked(yanked) => match yanked {
Yanked::Bool(_) => f.write_str("it was yanked"),
Yanked::Bool(_) => f.write_str("was yanked"),
Yanked::Reason(reason) => write!(
f,
"it was yanked (reason: {})",
"was yanked (reason: {})",
reason.trim().trim_end_matches('.')
),
},
IncompatibleSource::ExcludeNewer(ts) => match ts {
Some(_) => f.write_str("it was published after the exclude newer time"),
None => f.write_str("it has no publish time"),
Some(_) => f.write_str("was published after the exclude newer time"),
None => f.write_str("has no publish time"),
},
IncompatibleSource::RequiresPython(python) => {
write!(f, "it requires python {python}")
write!(f, "requires python {python}")
}
},
Self::Unavailable => f.write_str("no distributions are available"),
Self::Unavailable => f.write_str("has no available distributions"),
}
}
}

View file

@ -6,6 +6,7 @@ use pubgrub::solver::{Dependencies, DependencyProvider};
use pep440_rs::Version;
use crate::pubgrub::{PubGrubPackage, PubGrubPriority};
use crate::resolver::UnavailableReason;
/// We don't use a dependency provider, we interact with state directly, but we still need this one
/// for type
@ -15,6 +16,8 @@ impl DependencyProvider for UvDependencyProvider {
type P = PubGrubPackage;
type V = Version;
type VS = Range<Version>;
type M = UnavailableReason;
fn prioritize(&self, _package: &Self::P, _range: &Self::VS) -> Self::Priority {
unimplemented!()
}
@ -34,7 +37,7 @@ impl DependencyProvider for UvDependencyProvider {
&self,
_package: &Self::P,
_version: &Self::V,
) -> Result<Dependencies<Vec<(Self::P, Self::VS)>>, Self::Err> {
) -> Result<Dependencies<Vec<(Self::P, Self::VS)>, Self::M>, Self::Err> {
unimplemented!()
}
}

View file

@ -23,7 +23,8 @@ use crate::dependency_provider::UvDependencyProvider;
use crate::pubgrub::{PubGrubPackage, PubGrubPython, PubGrubReportFormatter};
use crate::python_requirement::PythonRequirement;
use crate::resolver::{
IncompletePackage, SharedMap, SharedSet, UnavailablePackage, VersionsResponse,
IncompletePackage, SharedMap, SharedSet, UnavailablePackage, UnavailableReason,
VersionsResponse,
};
#[derive(Debug, thiserror::Error)]
@ -119,7 +120,9 @@ impl<T> From<tokio::sync::mpsc::error::SendError<T>> for ResolveError {
/// Given a [`DerivationTree`], collapse any [`External::FromDependencyOf`] incompatibilities
/// wrap an [`PubGrubPackage::Extra`] package.
fn collapse_extra_proxies(derivation_tree: &mut DerivationTree<PubGrubPackage, Range<Version>>) {
fn collapse_extra_proxies(
derivation_tree: &mut DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>,
) {
match derivation_tree {
DerivationTree::External(_) => {}
DerivationTree::Derived(derived) => {
@ -193,7 +196,7 @@ impl From<pubgrub::error::PubGrubError<UvDependencyProvider>> for ResolveError {
/// A wrapper around [`pubgrub::error::PubGrubError::NoSolution`] that displays a resolution failure report.
#[derive(Debug)]
pub struct NoSolutionError {
derivation_tree: DerivationTree<PubGrubPackage, Range<Version>>,
derivation_tree: DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>,
available_versions: IndexMap<PubGrubPackage, BTreeSet<Version>>,
selector: Option<CandidateSelector>,
python_requirement: Option<PythonRequirement>,

View file

@ -17,7 +17,7 @@ use uv_normalize::PackageName;
use crate::candidate_selector::CandidateSelector;
use crate::python_requirement::PythonRequirement;
use crate::resolver::{IncompletePackage, UnavailablePackage};
use crate::resolver::{IncompletePackage, UnavailablePackage, UnavailableReason};
use super::PubGrubPackage;
@ -30,15 +30,20 @@ pub(crate) struct PubGrubReportFormatter<'a> {
pub(crate) python_requirement: Option<&'a PythonRequirement>,
}
impl ReportFormatter<PubGrubPackage, Range<Version>> for PubGrubReportFormatter<'_> {
impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason>
for PubGrubReportFormatter<'_>
{
type Output = String;
fn format_external(&self, external: &External<PubGrubPackage, Range<Version>>) -> Self::Output {
fn format_external(
&self,
external: &External<PubGrubPackage, Range<Version>, UnavailableReason>,
) -> Self::Output {
match external {
External::NotRoot(package, version) => {
format!("we are solving dependencies of {package} {version}")
}
External::NoVersions(package, set, reason) => {
External::NoVersions(package, set) => {
if matches!(package, PubGrubPackage::Python(_)) {
if let Some(python) = self.python_requirement {
if python.target() == python.installed() {
@ -79,16 +84,6 @@ impl ReportFormatter<PubGrubPackage, Range<Version>> for PubGrubReportFormatter<
}
let set = self.simplify_set(set, package);
// Check for a reason
if let Some(reason) = reason {
let formatted = if set.as_ref() == &Range::full() {
format!("{package} {reason}")
} else {
format!("{package}{set} {reason}")
};
return formatted;
}
if set.as_ref() == &Range::full() {
format!("there are no versions of {package}")
} else if set.as_singleton().is_some() {
@ -112,17 +107,26 @@ impl ReportFormatter<PubGrubPackage, Range<Version>> for PubGrubReportFormatter<
}
}
}
External::Unavailable(package, set, reason) => match package {
External::Custom(package, set, reason) => match package {
PubGrubPackage::Root(Some(name)) => {
format!("{name} cannot be used because {reason}")
}
PubGrubPackage::Root(None) => {
format!("your requirements cannot be used because {reason}")
}
_ => format!(
"{}is unusable because {reason}",
Padded::new("", &PackageRange::compatibility(package, set), " ")
),
_ => match reason {
UnavailableReason::Package(reason) => {
// While there may be a term attached, this error applies to the entire
// package, so we show it for the entire package
format!("{}{reason}", Padded::new("", &package, " "))
}
UnavailableReason::Version(reason) => {
format!(
"{}{reason}",
Padded::new("", &PackageRange::compatibility(package, set), " ")
)
}
},
},
External::FromDependencyOf(package, package_set, dependency, dependency_set) => {
let package_set = self.simplify_set(package_set, package);
@ -198,8 +202,8 @@ impl ReportFormatter<PubGrubPackage, Range<Version>> for PubGrubReportFormatter<
/// Simplest case, we just combine two external incompatibilities.
fn explain_both_external(
&self,
external1: &External<PubGrubPackage, Range<Version>>,
external2: &External<PubGrubPackage, Range<Version>>,
external1: &External<PubGrubPackage, Range<Version>, UnavailableReason>,
external2: &External<PubGrubPackage, Range<Version>, UnavailableReason>,
current_terms: &Map<PubGrubPackage, Term<Range<Version>>>,
) -> String {
let external = self.format_both_external(external1, external2);
@ -216,9 +220,9 @@ impl ReportFormatter<PubGrubPackage, Range<Version>> for PubGrubReportFormatter<
fn explain_both_ref(
&self,
ref_id1: usize,
derived1: &Derived<PubGrubPackage, Range<Version>>,
derived1: &Derived<PubGrubPackage, Range<Version>, UnavailableReason>,
ref_id2: usize,
derived2: &Derived<PubGrubPackage, Range<Version>>,
derived2: &Derived<PubGrubPackage, Range<Version>, UnavailableReason>,
current_terms: &Map<PubGrubPackage, Term<Range<Version>>>,
) -> String {
// TODO: order should be chosen to make it more logical.
@ -243,8 +247,8 @@ impl ReportFormatter<PubGrubPackage, Range<Version>> for PubGrubReportFormatter<
fn explain_ref_and_external(
&self,
ref_id: usize,
derived: &Derived<PubGrubPackage, Range<Version>>,
external: &External<PubGrubPackage, Range<Version>>,
derived: &Derived<PubGrubPackage, Range<Version>, UnavailableReason>,
external: &External<PubGrubPackage, Range<Version>, UnavailableReason>,
current_terms: &Map<PubGrubPackage, Term<Range<Version>>>,
) -> String {
// TODO: order should be chosen to make it more logical.
@ -265,7 +269,7 @@ impl ReportFormatter<PubGrubPackage, Range<Version>> for PubGrubReportFormatter<
/// Add an external cause to the chain of explanations.
fn and_explain_external(
&self,
external: &External<PubGrubPackage, Range<Version>>,
external: &External<PubGrubPackage, Range<Version>, UnavailableReason>,
current_terms: &Map<PubGrubPackage, Term<Range<Version>>>,
) -> String {
let external = self.format_external(external);
@ -282,7 +286,7 @@ impl ReportFormatter<PubGrubPackage, Range<Version>> for PubGrubReportFormatter<
fn and_explain_ref(
&self,
ref_id: usize,
derived: &Derived<PubGrubPackage, Range<Version>>,
derived: &Derived<PubGrubPackage, Range<Version>, UnavailableReason>,
current_terms: &Map<PubGrubPackage, Term<Range<Version>>>,
) -> String {
let derived = self.format_terms(&derived.terms);
@ -299,8 +303,8 @@ impl ReportFormatter<PubGrubPackage, Range<Version>> for PubGrubReportFormatter<
/// Add an already explained incompat to the chain of explanations.
fn and_explain_prior_and_external(
&self,
prior_external: &External<PubGrubPackage, Range<Version>>,
external: &External<PubGrubPackage, Range<Version>>,
prior_external: &External<PubGrubPackage, Range<Version>, UnavailableReason>,
external: &External<PubGrubPackage, Range<Version>, UnavailableReason>,
current_terms: &Map<PubGrubPackage, Term<Range<Version>>>,
) -> String {
let external = self.format_both_external(prior_external, external);
@ -318,8 +322,8 @@ impl PubGrubReportFormatter<'_> {
/// Format two external incompatibilities, combining them if possible.
fn format_both_external(
&self,
external1: &External<PubGrubPackage, Range<Version>>,
external2: &External<PubGrubPackage, Range<Version>>,
external1: &External<PubGrubPackage, Range<Version>, UnavailableReason>,
external2: &External<PubGrubPackage, Range<Version>, UnavailableReason>,
) -> String {
match (external1, external2) {
(
@ -387,7 +391,7 @@ impl PubGrubReportFormatter<'_> {
/// their requirements.
pub(crate) fn hints(
&self,
derivation_tree: &DerivationTree<PubGrubPackage, Range<Version>>,
derivation_tree: &DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>,
selector: &Option<CandidateSelector>,
index_locations: &Option<IndexLocations>,
unavailable_packages: &FxHashMap<PackageName, UnavailablePackage>,
@ -404,7 +408,7 @@ impl PubGrubReportFormatter<'_> {
let mut hints = IndexSet::default();
match derivation_tree {
DerivationTree::External(external) => match external {
External::Unavailable(package, set, _) | External::NoVersions(package, set, _) => {
External::Custom(package, set, _) | External::NoVersions(package, set) => {
// Check for no versions due to pre-release options
if let Some(selector) = selector {
let any_prerelease = set.iter().any(|(start, end)| {

View file

@ -66,16 +66,62 @@ mod provider;
mod reporter;
mod urls;
/// The package version is unavailable and cannot be used
/// Unlike [`PackageUnavailable`] this applies to a single version of the package
#[derive(Debug, Clone)]
/// The reason why a package or a version cannot be used.
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum UnavailableReason {
/// The entire package cannot be used.
Package(UnavailablePackage),
/// A single version cannot be used.
Version(UnavailableVersion),
}
impl Display for UnavailableReason {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Version(version) => Display::fmt(version, f),
Self::Package(package) => Display::fmt(package, f),
}
}
}
/// The package version is unavailable and cannot be used. Unlike [`PackageUnavailable`], this
/// applies to a single version of the package.
///
/// Most variant are from [`MetadataResponse`] without the error source (since we don't format
/// the source).
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum UnavailableVersion {
/// Version is incompatible because it has no usable distributions
IncompatibleDist(IncompatibleDist),
/// The wheel metadata was found, but could not be parsed.
InvalidMetadata,
/// The wheel metadata was found, but the metadata was inconsistent.
InconsistentMetadata,
/// The wheel has an invalid structure.
InvalidStructure,
/// The wheel metadata was not found in the cache and the network is not available.
Offline,
/// Forward any kind of resolver error.
ResolverError(String),
}
impl Display for UnavailableVersion {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
UnavailableVersion::IncompatibleDist(invalid_dist) => Display::fmt(invalid_dist, f),
UnavailableVersion::InvalidMetadata => f.write_str("has invalid metadata"),
UnavailableVersion::InconsistentMetadata => f.write_str("has inconsistent metadata"),
UnavailableVersion::InvalidStructure => f.write_str("has an invalid package format"),
UnavailableVersion::Offline => f.write_str(
"network connectivity is disabled, but the metadata wasn't found in the cache",
),
UnavailableVersion::ResolverError(err) => f.write_str(err),
}
}
}
/// The package is unavailable and cannot be used.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum UnavailablePackage {
/// Index lookups were disabled (i.e., `--no-index`) and the package was not found in a flat index (i.e. from `--find-links`).
NoIndex,
@ -89,6 +135,24 @@ pub(crate) enum UnavailablePackage {
InvalidStructure(String),
}
impl UnavailablePackage {
pub(crate) fn as_str(&self) -> &'static str {
match self {
UnavailablePackage::NoIndex => "was not found in the provided package locations",
UnavailablePackage::Offline => "was not found in the cache",
UnavailablePackage::NotFound => "was not found in the package registry",
UnavailablePackage::InvalidMetadata(_) => "has invalid metadata",
UnavailablePackage::InvalidStructure(_) => "has an invalid package format",
}
}
}
impl Display for UnavailablePackage {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
/// The package is unavailable at specific versions.
#[derive(Debug, Clone)]
pub(crate) enum IncompletePackage {
@ -359,84 +423,61 @@ impl<'a, Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvide
.term_intersection_for_package(&next)
.expect("a package was chosen but we don't have a term.");
let reason = {
if let PubGrubPackage::Package(ref package_name, _, _) = next {
// Check if the decision was due to the package being unavailable
self.unavailable_packages
.borrow()
.get(package_name)
.map(|entry| match *entry {
UnavailablePackage::NoIndex => {
"was not found in the provided package locations"
}
UnavailablePackage::Offline => "was not found in the cache",
UnavailablePackage::NotFound => {
"was not found in the package registry"
}
UnavailablePackage::InvalidMetadata(_) => {
"was found, but the metadata could not be parsed"
}
UnavailablePackage::InvalidStructure(_) => {
"was found, but has an invalid format"
}
})
} else {
None
// Check if the decision was due to the package being unavailable
if let PubGrubPackage::Package(ref package_name, _, _) = next {
if let Some(entry) = self.unavailable_packages.borrow().get(package_name) {
state.add_incompatibility(Incompatibility::custom_term(
next.clone(),
term_intersection.clone(),
UnavailableReason::Package(entry.clone()),
));
continue;
}
};
}
let inc = Incompatibility::no_versions(
state.add_incompatibility(Incompatibility::no_versions(
next.clone(),
term_intersection.clone(),
reason.map(ToString::to_string),
);
state.add_incompatibility(inc);
));
continue;
}
Some(version) => version,
};
let version = match version {
ResolverVersion::Available(version) => version,
ResolverVersion::Unavailable(version, unavailable) => {
let reason = match unavailable {
// Incompatible requires-python versions are special in that we track
// them as incompatible dependencies instead of marking the package version
// as unavailable directly
UnavailableVersion::IncompatibleDist(
IncompatibleDist::Source(IncompatibleSource::RequiresPython(
requires_python,
))
| IncompatibleDist::Wheel(IncompatibleWheel::RequiresPython(
requires_python,
)),
) => {
let python_version = requires_python
.iter()
.map(PubGrubSpecifier::try_from)
.fold_ok(Range::full(), |range, specifier| {
range.intersection(&specifier.into())
})?;
ResolverVersion::Unavailable(version, reason) => {
// Incompatible requires-python versions are special in that we track
// them as incompatible dependencies instead of marking the package version
// as unavailable directly
if let UnavailableVersion::IncompatibleDist(
IncompatibleDist::Source(IncompatibleSource::RequiresPython(
requires_python,
))
| IncompatibleDist::Wheel(IncompatibleWheel::RequiresPython(requires_python)),
) = reason
{
let python_version = requires_python
.iter()
.map(PubGrubSpecifier::try_from)
.fold_ok(Range::full(), |range, specifier| {
range.intersection(&specifier.into())
})?;
let package = &next;
for kind in [PubGrubPython::Installed, PubGrubPython::Target] {
state.add_incompatibility(Incompatibility::from_dependency(
package.clone(),
Range::singleton(version.clone()),
(PubGrubPackage::Python(kind), python_version.clone()),
));
}
state.partial_solution.add_decision(next.clone(), version);
continue;
}
UnavailableVersion::IncompatibleDist(incompatibility) => {
incompatibility.to_string()
let package = &next;
for kind in [PubGrubPython::Installed, PubGrubPython::Target] {
state.add_incompatibility(Incompatibility::from_dependency(
package.clone(),
Range::singleton(version.clone()),
(PubGrubPackage::Python(kind), python_version.clone()),
));
}
state.partial_solution.add_decision(next.clone(), version);
continue;
};
state.add_incompatibility(Incompatibility::unavailable(
state.add_incompatibility(Incompatibility::custom_version(
next.clone(),
version.clone(),
reason,
UnavailableReason::Version(reason),
));
continue;
}
@ -467,10 +508,10 @@ impl<'a, Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvide
.await?
{
Dependencies::Unavailable(reason) => {
state.add_incompatibility(Incompatibility::unavailable(
state.add_incompatibility(Incompatibility::custom_version(
package.clone(),
version.clone(),
reason.clone(),
UnavailableReason::Version(reason),
));
continue;
}
@ -816,7 +857,9 @@ impl<'a, Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvide
let mut constraints = match constraints {
Ok(constraints) => constraints,
Err(err) => {
return Ok(Dependencies::Unavailable(uncapitalize(err.to_string())));
return Ok(Dependencies::Unavailable(
UnavailableVersion::ResolverError(uncapitalize(err.to_string())),
));
}
};
@ -939,9 +982,9 @@ impl<'a, Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvide
false,
"Dependencies were requested for a package that is not available"
);
return Ok(Dependencies::Unavailable(
"The package is unavailable".to_string(),
));
return Err(ResolveError::Failure(format!(
"The package is unavailable: {package_name}"
)));
}
// Wait for the metadata to be available.
@ -962,10 +1005,7 @@ impl<'a, Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvide
.or_default()
.borrow_mut()
.insert(version.clone(), IncompletePackage::Offline);
return Ok(Dependencies::Unavailable(
"network connectivity is disabled, but the metadata wasn't found in the cache"
.to_string(),
));
return Ok(Dependencies::Unavailable(UnavailableVersion::Offline));
}
MetadataResponse::InvalidMetadata(err) => {
warn!("Unable to extract metadata for {package_name}: {err}");
@ -979,7 +1019,7 @@ impl<'a, Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvide
IncompletePackage::InvalidMetadata(err.to_string()),
);
return Ok(Dependencies::Unavailable(
"the package metadata could not be parsed".to_string(),
UnavailableVersion::InvalidMetadata,
));
}
MetadataResponse::InconsistentMetadata(err) => {
@ -994,7 +1034,7 @@ impl<'a, Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvide
IncompletePackage::InconsistentMetadata(err.to_string()),
);
return Ok(Dependencies::Unavailable(
"the package metadata was inconsistent".to_string(),
UnavailableVersion::InconsistentMetadata,
));
}
MetadataResponse::InvalidStructure(err) => {
@ -1009,7 +1049,7 @@ impl<'a, Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvide
IncompletePackage::InvalidStructure(err.to_string()),
);
return Ok(Dependencies::Unavailable(
"the package has an invalid format".to_string(),
UnavailableVersion::InvalidStructure,
));
}
};
@ -1342,7 +1382,7 @@ enum Response {
#[derive(Clone)]
enum Dependencies {
/// Package dependencies are not available.
Unavailable(String),
Unavailable(UnavailableVersion),
/// Container for all available package versions.
Available(Vec<(PubGrubPackage, Range<Version>)>),
}

View file

@ -2668,7 +2668,7 @@ fn compile_yanked_version_indirect() -> Result<()> {
attrs<=20.3.0
attrs==21.1.0
attrs>=21.2.0
and attrs==21.1.0 is unusable because it was yanked (reason: Installable but not importable on Python 3.4), we can conclude that attrs>20.3.0,<21.2.0 cannot be used.
and attrs==21.1.0 was yanked (reason: Installable but not importable on Python 3.4), we can conclude that attrs>20.3.0,<21.2.0 cannot be used.
And because you require attrs>20.3.0,<21.2.0, we can conclude that the requirements are unsatisfiable.
"###
);
@ -4668,7 +4668,7 @@ fn offline_registry() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
Because black==23.10.1 was not found in the cache and you require black==23.10.1, we can conclude that the requirements are unsatisfiable.
Because black was not found in the cache and you require black==23.10.1, we can conclude that the requirements are unsatisfiable.
hint: Packages were unavailable because the network was disabled
"###
@ -4898,7 +4898,7 @@ fn invalid_metadata_requires_python() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
Because validation==2.0.0 is unusable because the package metadata could not be parsed and you require validation==2.0.0, we can conclude that the requirements are unsatisfiable.
Because validation==2.0.0 has invalid metadata and you require validation==2.0.0, we can conclude that the requirements are unsatisfiable.
hint: Metadata for validation==2.0.0 could not be parsed:
Failed to parse version: Unexpected end of version specifier, expected operator:
@ -4930,7 +4930,7 @@ fn invalid_metadata_multiple_dist_info() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
Because validation==3.0.0 is unusable because the package has an invalid format and you require validation==3.0.0, we can conclude that the requirements are unsatisfiable.
Because validation==3.0.0 has an invalid package format and you require validation==3.0.0, we can conclude that the requirements are unsatisfiable.
hint: The structure of validation==3.0.0 was invalid:
Multiple .dist-info directories found: validation-2.0.0, validation-3.0.0
@ -5171,7 +5171,7 @@ fn index_url_in_requirements() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
Because anyio<4 was not found in the package registry and you require anyio<4, we can conclude that the requirements are unsatisfiable.
Because anyio was not found in the package registry and you require anyio<4, we can conclude that the requirements are unsatisfiable.
"###
);

View file

@ -1018,7 +1018,7 @@ fn install_no_index_version() {
----- stderr -----
× No solution found when resolving dependencies:
Because flask==3.0.0 was not found in the provided package locations and you require flask==3.0.0, we can conclude that the requirements are unsatisfiable.
Because flask was not found in the provided package locations and you require flask==3.0.0, we can conclude that the requirements are unsatisfiable.
hint: Packages were unavailable because index lookups were disabled and no additional package locations were provided (try: `--find-links <uri>`)
"###
@ -1558,7 +1558,7 @@ fn only_binary_requirements_txt() {
----- stderr -----
× No solution found when resolving dependencies:
Because django-allauth==0.51.0 is unusable because no wheels are usable and building from source is disabled and you require django-allauth==0.51.0, we can conclude that the requirements are unsatisfiable.
Because django-allauth==0.51.0 has no usable wheels and building from source is disabled and you require django-allauth==0.51.0, we can conclude that the requirements are unsatisfiable.
"###
);
}
@ -3890,7 +3890,7 @@ fn already_installed_local_version_of_remote_package() {
----- stderr -----
× No solution found when resolving dependencies:
Because anyio==4.2.0 was not found in the provided package locations and you require anyio==4.2.0, we can conclude that the requirements are unsatisfiable.
Because anyio was not found in the provided package locations and you require anyio==4.2.0, we can conclude that the requirements are unsatisfiable.
hint: Packages were unavailable because index lookups were disabled and no additional package locations were provided (try: `--find-links <uri>`)
"###
@ -4188,7 +4188,7 @@ fn already_installed_remote_url() {
----- stderr -----
× No solution found when resolving dependencies:
Because uv-public-pypackage==0.2.0 was not found in the provided package locations and you require uv-public-pypackage==0.2.0, we can conclude that the requirements are unsatisfiable.
Because uv-public-pypackage was not found in the provided package locations and you require uv-public-pypackage==0.2.0, we can conclude that the requirements are unsatisfiable.
hint: Packages were unavailable because index lookups were disabled and no additional package locations were provided (try: `--find-links <uri>`)
"###);

View file

@ -1419,7 +1419,7 @@ fn local_used_without_sdist() {
----- stderr -----
× No solution found when resolving dependencies:
Because package-a==1.2.3 is unusable because no wheels are available with a matching Python ABI and you require package-a==1.2.3, we can conclude that the requirements are unsatisfiable.
Because package-a==1.2.3 has no wheels are available with a matching Python ABI and you require package-a==1.2.3, we can conclude that the requirements are unsatisfiable.
"###);
// The version '1.2.3+foo' satisfies the constraint '==1.2.3'.
@ -1793,7 +1793,7 @@ fn local_transitive_confounding() {
----- stderr -----
× No solution found when resolving dependencies:
Because package-b==2.0.0 is unusable because no wheels are available with a matching Python ABI and package-a==1.0.0 depends on package-b==2.0.0, we can conclude that package-a==1.0.0 cannot be used.
Because package-b==2.0.0 has no wheels are available with a matching Python ABI and package-a==1.0.0 depends on package-b==2.0.0, we can conclude that package-a==1.0.0 cannot be used.
And because only package-a==1.0.0 is available and you require package-a, we can conclude that the requirements are unsatisfiable.
"###);
@ -4204,7 +4204,7 @@ fn no_sdist_no_wheels_with_matching_platform() {
----- stderr -----
× No solution found when resolving dependencies:
Because only package-a==1.0.0 is available and package-a==1.0.0 is unusable because no wheels are available with a matching platform, we can conclude that all versions of package-a cannot be used.
Because only package-a==1.0.0 is available and package-a==1.0.0 has no wheels are available with a matching platform, we can conclude that all versions of package-a cannot be used.
And because you require package-a, we can conclude that the requirements are unsatisfiable.
"###);
@ -4245,7 +4245,7 @@ fn no_sdist_no_wheels_with_matching_python() {
----- stderr -----
× No solution found when resolving dependencies:
Because only package-a==1.0.0 is available and package-a==1.0.0 is unusable because no wheels are available with a matching Python implementation, we can conclude that all versions of package-a cannot be used.
Because only package-a==1.0.0 is available and package-a==1.0.0 has no wheels are available with a matching Python implementation, we can conclude that all versions of package-a cannot be used.
And because you require package-a, we can conclude that the requirements are unsatisfiable.
"###);
@ -4286,7 +4286,7 @@ fn no_sdist_no_wheels_with_matching_abi() {
----- stderr -----
× No solution found when resolving dependencies:
Because only package-a==1.0.0 is available and package-a==1.0.0 is unusable because no wheels are available with a matching Python ABI, we can conclude that all versions of package-a cannot be used.
Because only package-a==1.0.0 is available and package-a==1.0.0 has no wheels are available with a matching Python ABI, we can conclude that all versions of package-a cannot be used.
And because you require package-a, we can conclude that the requirements are unsatisfiable.
"###);
@ -4329,7 +4329,7 @@ fn no_wheels_no_build() {
----- stderr -----
× No solution found when resolving dependencies:
Because only package-a==1.0.0 is available and package-a==1.0.0 is unusable because no wheels are usable and building from source is disabled, we can conclude that all versions of package-a cannot be used.
Because only package-a==1.0.0 is available and package-a==1.0.0 has no usable wheels and building from source is disabled, we can conclude that all versions of package-a cannot be used.
And because you require package-a, we can conclude that the requirements are unsatisfiable.
"###);
@ -4368,7 +4368,7 @@ fn only_wheels_no_binary() {
----- stderr -----
× No solution found when resolving dependencies:
Because only package-a==1.0.0 is available and package-a==1.0.0 is unusable because no source distribution is available and using wheels is disabled, we can conclude that all versions of package-a cannot be used.
Because only package-a==1.0.0 is available and package-a==1.0.0 has no available source distribution and using wheels is disabled, we can conclude that all versions of package-a cannot be used.
And because you require package-a, we can conclude that the requirements are unsatisfiable.
"###);
@ -4485,7 +4485,7 @@ fn package_only_yanked() {
----- stderr -----
× No solution found when resolving dependencies:
Because only package-a==1.0.0 is available and package-a==1.0.0 is unusable because it was yanked (reason: Yanked for testing), we can conclude that all versions of package-a cannot be used.
Because only package-a==1.0.0 is available and package-a==1.0.0 was yanked (reason: Yanked for testing), we can conclude that all versions of package-a cannot be used.
And because you require package-a, we can conclude that the requirements are unsatisfiable.
"###);
@ -4527,7 +4527,7 @@ fn package_only_yanked_in_range() {
Because only the following versions of package-a are available:
package-a<=0.1.0
package-a==1.0.0
and package-a==1.0.0 is unusable because it was yanked (reason: Yanked for testing), we can conclude that package-a>0.1.0 cannot be used.
and package-a==1.0.0 was yanked (reason: Yanked for testing), we can conclude that package-a>0.1.0 cannot be used.
And because you require package-a>0.1.0, we can conclude that the requirements are unsatisfiable.
"###);
@ -4667,7 +4667,7 @@ fn transitive_package_only_yanked() {
----- stderr -----
× No solution found when resolving dependencies:
Because only package-b==1.0.0 is available and package-b==1.0.0 is unusable because it was yanked (reason: Yanked for testing), we can conclude that all versions of package-b cannot be used.
Because only package-b==1.0.0 is available and package-b==1.0.0 was yanked (reason: Yanked for testing), we can conclude that all versions of package-b cannot be used.
And because package-a==0.1.0 depends on package-b, we can conclude that package-a==0.1.0 cannot be used.
And because only package-a==0.1.0 is available and you require package-a, we can conclude that the requirements are unsatisfiable.
"###);
@ -4719,7 +4719,7 @@ fn transitive_package_only_yanked_in_range() {
Because only the following versions of package-b are available:
package-b<=0.1
package-b==1.0.0
and package-b==1.0.0 is unusable because it was yanked (reason: Yanked for testing), we can conclude that package-b>0.1 cannot be used.
and package-b==1.0.0 was yanked (reason: Yanked for testing), we can conclude that package-b>0.1 cannot be used.
And because package-a==0.1.0 depends on package-b>0.1, we can conclude that package-a==0.1.0 cannot be used.
And because only package-a==0.1.0 is available and you require package-a, we can conclude that the requirements are unsatisfiable.
"###);
@ -4840,7 +4840,7 @@ fn transitive_yanked_and_unyanked_dependency() {
----- stderr -----
× No solution found when resolving dependencies:
Because package-c==2.0.0 is unusable because it was yanked (reason: Yanked for testing) and package-a==1.0.0 depends on package-c==2.0.0, we can conclude that package-a==1.0.0 cannot be used.
Because package-c==2.0.0 was yanked (reason: Yanked for testing) and package-a==1.0.0 depends on package-c==2.0.0, we can conclude that package-a==1.0.0 cannot be used.
And because only package-a==1.0.0 is available and you require package-a, we can conclude that the requirements are unsatisfiable.
"###);

View file

@ -850,7 +850,7 @@ fn install_no_index() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
Because markupsafe==2.1.3 was not found in the provided package locations and you require markupsafe==2.1.3, we can conclude that the requirements are unsatisfiable.
Because markupsafe was not found in the provided package locations and you require markupsafe==2.1.3, we can conclude that the requirements are unsatisfiable.
hint: Packages were unavailable because index lookups were disabled and no additional package locations were provided (try: `--find-links <uri>`)
"###
@ -902,7 +902,7 @@ fn install_no_index_cached() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
Because markupsafe==2.1.3 was not found in the provided package locations and you require markupsafe==2.1.3, we can conclude that the requirements are unsatisfiable.
Because markupsafe was not found in the provided package locations and you require markupsafe==2.1.3, we can conclude that the requirements are unsatisfiable.
hint: Packages were unavailable because index lookups were disabled and no additional package locations were provided (try: `--find-links <uri>`)
"###
@ -1107,7 +1107,7 @@ fn mismatched_name() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
Because foo was found, but has an invalid format and you require foo, we can conclude that the requirements are unsatisfiable.
Because foo has an invalid package format and you require foo, we can conclude that the requirements are unsatisfiable.
hint: The structure of foo was invalid:
The .dist-info directory tomli-2.0.1 does not start with the normalized package name: foo
@ -2766,7 +2766,7 @@ fn offline() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
Because black==23.10.1 was not found in the cache and you require black==23.10.1, we can conclude that the requirements are unsatisfiable.
Because black was not found in the cache and you require black==23.10.1, we can conclude that the requirements are unsatisfiable.
hint: Packages were unavailable because the network was disabled
"###
@ -4658,7 +4658,7 @@ fn require_hashes_registry_valid_hash() -> Result<()> {
----- stderr -----
× No solution found when resolving dependencies:
Because example-a-961b4c22==1.0.0 was not found in the package registry and you require example-a-961b4c22==1.0.0, we can conclude that the requirements are unsatisfiable.
Because example-a-961b4c22 was not found in the package registry and you require example-a-961b4c22==1.0.0, we can conclude that the requirements are unsatisfiable.
"###
);