2024-11-05 22:18:43 -05:00
use std ::collections ::{ BTreeMap , BTreeSet , Bound } ;
2023-11-08 06:57:26 -08:00
use std ::fmt ::Formatter ;
2024-04-18 21:04:59 -04:00
use std ::sync ::Arc ;
2023-11-08 06:57:26 -08:00
2024-09-18 16:16:22 +02:00
use indexmap ::IndexSet ;
2024-11-05 22:18:43 -05:00
use pubgrub ::{
DefaultStringReporter , DerivationTree , Derived , External , Range , Ranges , Reporter , Term ,
} ;
2024-07-15 17:43:35 +02:00
use rustc_hash ::FxHashMap ;
2024-11-05 22:18:43 -05:00
use tracing ::trace ;
use uv_distribution_types ::{
2024-11-14 15:48:26 -05:00
BuiltDist , DerivationChain , IndexCapabilities , IndexLocations , IndexUrl , InstalledDist ,
SourceDist ,
2024-11-05 22:18:43 -05:00
} ;
2024-11-13 09:52:28 -05:00
use uv_normalize ::{ ExtraName , PackageName } ;
2024-11-05 22:18:43 -05:00
use uv_pep440 ::{ LocalVersionSlice , Version } ;
use uv_static ::EnvVars ;
2023-10-09 23:29:09 -04:00
2023-12-28 22:44:35 -04:00
use crate ::candidate_selector ::CandidateSelector ;
2024-03-25 15:51:31 +01:00
use crate ::dependency_provider ::UvDependencyProvider ;
2024-06-26 13:58:23 +02:00
use crate ::fork_urls ::ForkUrls ;
2024-10-29 20:15:18 +01:00
use crate ::pubgrub ::{ PubGrubPackage , PubGrubPackageInner , PubGrubReportFormatter } ;
2024-01-03 11:20:45 -04:00
use crate ::python_requirement ::PythonRequirement ;
2024-09-20 12:53:34 -04:00
use crate ::resolution ::ConflictingDistributionError ;
2024-10-11 15:23:38 -04:00
use crate ::resolver ::{
IncompletePackage , ResolverEnvironment , UnavailablePackage , UnavailableReason ,
} ;
2024-10-14 21:04:30 -04:00
use crate ::Options ;
2023-10-15 22:05:44 -04:00
2024-01-25 11:49:11 -08:00
#[ derive(Debug, thiserror::Error) ]
2023-10-09 23:29:09 -04:00
pub enum ResolveError {
#[ error(transparent) ]
2024-02-15 11:19:46 -06:00
Client ( #[ from ] uv_client ::Error ) ,
2023-10-09 23:29:09 -04:00
2024-11-13 15:49:08 -05:00
#[ error(transparent) ]
Distribution ( #[ from ] uv_distribution ::Error ) ,
2024-03-12 07:13:57 -07:00
#[ error( " The channel closed unexpectedly " ) ]
2024-02-02 18:18:24 +01:00
ChannelClosed ,
2023-10-15 22:05:44 -04:00
2023-10-16 13:26:46 -04:00
#[ error(transparent) ]
Join ( #[ from ] tokio ::task ::JoinError ) ,
2024-06-24 16:42:55 +02:00
#[ error( " Attempted to wait on an unregistered task: `{_0}` " ) ]
UnregisteredTask ( String ) ,
2024-01-22 09:00:21 -05:00
2024-11-13 09:52:28 -05:00
#[ error( " Found conflicting extra `{extra}` unconditionally enabled in `{requirement}` " ) ]
ConflictingExtra {
// Boxed because `Requirement` is large.
requirement : Box < uv_pypi_types ::Requirement > ,
extra : ExtraName ,
} ,
2024-06-26 13:58:23 +02:00
#[ error( " Overrides contain conflicting URLs for package `{0}`: \n - {1} \n - {2} " ) ]
ConflictingOverrideUrls ( PackageName , String , String ) ,
2024-02-21 21:27:58 -05:00
2024-10-11 15:23:38 -04:00
#[ error(
" Requirements contain conflicting URLs for package `{package_name}`{}: \n - {} " ,
if env.marker_environment().is_some() {
String::new()
} else {
format!( " in {env} " )
},
urls.join( " \n - " ),
) ]
ConflictingUrls {
2024-06-26 13:58:23 +02:00
package_name : PackageName ,
urls : Vec < String > ,
2024-10-11 15:23:38 -04:00
env : ResolverEnvironment ,
2024-06-26 13:58:23 +02:00
} ,
2023-11-05 09:09:58 -08:00
2024-10-11 15:23:38 -04:00
#[ error(
" Requirements contain conflicting indexes for package `{package_name}`{}: \n - {} " ,
if env.marker_environment().is_some() {
String::new()
} else {
format!( " in {env} " )
},
indexes.join( " \n - " ),
) ]
ConflictingIndexesForEnvironment {
2024-10-15 15:58:15 -07:00
package_name : PackageName ,
indexes : Vec < String > ,
2024-10-11 15:23:38 -04:00
env : ResolverEnvironment ,
2024-10-15 15:58:15 -07:00
} ,
2024-10-15 15:24:23 -07:00
#[ error( " Requirements contain conflicting indexes for package `{0}`: `{1}` vs. `{2}` " ) ]
ConflictingIndexes ( PackageName , String , String ) ,
2023-11-05 09:09:58 -08:00
#[ error( " Package `{0}` attempted to resolve via URL: {1}. URL dependencies must be expressed as direct requirements or constraints. Consider adding `{0} @ {1}` to your dependencies or constraints file. " ) ]
2024-02-21 21:27:58 -05:00
DisallowedUrl ( PackageName , String ) ,
2023-11-20 23:08:54 +00:00
#[ error(transparent) ]
2024-10-01 20:15:32 -04:00
DistributionType ( #[ from ] uv_distribution_types ::Error ) ,
2023-11-20 23:08:54 +00:00
2024-08-08 21:39:47 -04:00
#[ error(transparent) ]
2024-10-01 20:15:32 -04:00
ParsedUrl ( #[ from ] uv_pypi_types ::ParsedUrlError ) ,
2024-08-08 21:39:47 -04:00
2024-04-22 19:58:36 -04:00
#[ error( " Failed to download `{0}` " ) ]
2024-11-14 15:48:26 -05:00
Download (
Box < BuiltDist > ,
DerivationChain ,
#[ source ] Arc < uv_distribution ::Error > ,
) ,
2023-11-01 06:21:44 -07:00
2024-04-22 19:58:36 -04:00
#[ error( " Failed to download and build `{0}` " ) ]
2024-11-14 15:48:26 -05:00
DownloadAndBuild (
Box < SourceDist > ,
DerivationChain ,
#[ source ] Arc < uv_distribution ::Error > ,
) ,
2023-12-04 17:01:51 -05:00
2024-04-22 19:58:36 -04:00
#[ error( " Failed to read `{0}` " ) ]
2024-11-14 15:48:26 -05:00
Read (
Box < BuiltDist > ,
DerivationChain ,
#[ source ] Arc < uv_distribution ::Error > ,
) ,
2023-12-04 17:01:51 -05:00
2024-03-28 13:49:17 -05:00
// TODO(zanieb): Use `thiserror` in `InstalledDist` so we can avoid chaining `anyhow`
2024-04-22 19:58:36 -04:00
#[ error( " Failed to read metadata from installed package `{0}` " ) ]
2024-11-14 15:48:26 -05:00
ReadInstalled ( Box < InstalledDist > , DerivationChain , #[ source ] anyhow ::Error ) ,
2024-03-28 13:49:17 -05:00
2024-04-22 19:58:36 -04:00
#[ error( " Failed to build `{0}` " ) ]
2024-11-14 15:48:26 -05:00
Build (
Box < SourceDist > ,
DerivationChain ,
#[ source ] Arc < uv_distribution ::Error > ,
) ,
2023-12-12 17:25:16 -06:00
#[ error(transparent) ]
NoSolution ( #[ from ] NoSolutionError ) ,
2024-03-16 07:24:50 -07:00
#[ error( " Attempted to construct an invalid version specifier " ) ]
2024-10-01 20:15:32 -04:00
InvalidVersion ( #[ from ] uv_pep440 ::VersionSpecifierBuildError ) ,
2024-03-16 07:24:50 -07:00
2024-04-22 19:58:36 -04:00
#[ error( " In `--require-hashes` mode, all requirements must be pinned upfront with `==`, but found: `{0}` " ) ]
2024-04-10 15:09:03 -04:00
UnhashedPackage ( PackageName ) ,
2024-09-20 12:53:34 -04:00
#[ error( " found conflicting distribution in resolution: {0} " ) ]
ConflictingDistribution ( ConflictingDistributionError ) ,
2024-09-26 12:36:52 -04:00
#[ error( " Package `{0}` is unavailable " ) ]
PackageUnavailable ( PackageName ) ,
2023-10-09 23:29:09 -04:00
}
2024-02-02 18:18:24 +01:00
impl < T > From < tokio ::sync ::mpsc ::error ::SendError < T > > for ResolveError {
/// Drop the value we want to send to not leak the private type we're sending.
/// The tokio error only says "channel closed", so we don't lose information.
fn from ( _value : tokio ::sync ::mpsc ::error ::SendError < T > ) -> Self {
Self ::ChannelClosed
2023-10-09 23:29:09 -04:00
}
}
2023-11-08 06:57:26 -08:00
2024-08-02 17:27:07 -04:00
pub ( crate ) type ErrorTree = DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ;
2024-07-15 17:43:35 +02:00
/// A wrapper around [`pubgrub::error::NoSolutionError`] that displays a resolution failure report.
#[ derive(Debug) ]
pub struct NoSolutionError {
2024-07-31 14:54:11 +02:00
error : pubgrub ::NoSolutionError < UvDependencyProvider > ,
2024-08-16 15:21:49 -05:00
available_versions : FxHashMap < PackageName , BTreeSet < Version > > ,
2024-09-23 15:02:19 -04:00
available_indexes : FxHashMap < PackageName , BTreeSet < IndexUrl > > ,
2024-07-15 17:43:35 +02:00
selector : CandidateSelector ,
python_requirement : PythonRequirement ,
index_locations : IndexLocations ,
2024-10-16 10:34:29 -07:00
index_capabilities : IndexCapabilities ,
2024-07-15 17:43:35 +02:00
unavailable_packages : FxHashMap < PackageName , UnavailablePackage > ,
incomplete_packages : FxHashMap < PackageName , BTreeMap < Version , IncompletePackage > > ,
fork_urls : ForkUrls ,
2024-10-11 15:23:38 -04:00
env : ResolverEnvironment ,
2024-08-14 21:41:31 -05:00
workspace_members : BTreeSet < PackageName > ,
2024-10-14 21:04:30 -04:00
options : Options ,
2024-04-18 21:04:59 -04:00
}
2024-07-15 17:43:35 +02:00
impl NoSolutionError {
2024-08-09 23:21:56 -04:00
/// Create a new [`NoSolutionError`] from a [`pubgrub::NoSolutionError`].
2024-07-15 17:43:35 +02:00
pub ( crate ) fn new (
2024-07-31 14:54:11 +02:00
error : pubgrub ::NoSolutionError < UvDependencyProvider > ,
2024-08-16 15:21:49 -05:00
available_versions : FxHashMap < PackageName , BTreeSet < Version > > ,
2024-09-23 15:02:19 -04:00
available_indexes : FxHashMap < PackageName , BTreeSet < IndexUrl > > ,
2024-07-15 17:43:35 +02:00
selector : CandidateSelector ,
python_requirement : PythonRequirement ,
index_locations : IndexLocations ,
2024-10-16 10:34:29 -07:00
index_capabilities : IndexCapabilities ,
2024-07-15 17:43:35 +02:00
unavailable_packages : FxHashMap < PackageName , UnavailablePackage > ,
incomplete_packages : FxHashMap < PackageName , BTreeMap < Version , IncompletePackage > > ,
2024-06-26 13:58:23 +02:00
fork_urls : ForkUrls ,
2024-10-11 15:23:38 -04:00
env : ResolverEnvironment ,
2024-08-14 21:41:31 -05:00
workspace_members : BTreeSet < PackageName > ,
2024-10-14 21:04:30 -04:00
options : Options ,
2024-06-26 13:58:23 +02:00
) -> Self {
2024-07-15 17:43:35 +02:00
Self {
error ,
available_versions ,
2024-09-23 15:02:19 -04:00
available_indexes ,
2024-07-15 17:43:35 +02:00
selector ,
python_requirement ,
index_locations ,
2024-10-16 10:34:29 -07:00
index_capabilities ,
2024-07-15 17:43:35 +02:00
unavailable_packages ,
incomplete_packages ,
fork_urls ,
2024-10-11 15:23:38 -04:00
env ,
2024-08-14 21:41:31 -05:00
workspace_members ,
2024-10-14 21:04:30 -04:00
options ,
2024-07-15 17:43:35 +02:00
}
}
/// Given a [`DerivationTree`], collapse any [`External::FromDependencyOf`] incompatibilities
/// wrap an [`PubGrubPackageInner::Extra`] package.
2024-08-02 17:27:07 -04:00
pub ( crate ) fn collapse_proxies ( derivation_tree : ErrorTree ) -> ErrorTree {
fn collapse ( derivation_tree : ErrorTree ) -> Option < ErrorTree > {
match derivation_tree {
DerivationTree ::Derived ( derived ) = > {
match ( & * derived . cause1 , & * derived . cause2 ) {
(
DerivationTree ::External ( External ::FromDependencyOf ( package1 , .. ) ) ,
DerivationTree ::External ( External ::FromDependencyOf ( package2 , .. ) ) ,
) if package1 . is_proxy ( ) & & package2 . is_proxy ( ) = > None ,
(
DerivationTree ::External ( External ::FromDependencyOf ( package , .. ) ) ,
cause ,
) if package . is_proxy ( ) = > collapse ( cause . clone ( ) ) ,
(
cause ,
DerivationTree ::External ( External ::FromDependencyOf ( package , .. ) ) ,
) if package . is_proxy ( ) = > collapse ( cause . clone ( ) ) ,
( cause1 , cause2 ) = > {
let cause1 = collapse ( cause1 . clone ( ) ) ;
let cause2 = collapse ( cause2 . clone ( ) ) ;
match ( cause1 , cause2 ) {
( Some ( cause1 ) , Some ( cause2 ) ) = > {
Some ( DerivationTree ::Derived ( Derived {
cause1 : Arc ::new ( cause1 ) ,
cause2 : Arc ::new ( cause2 ) ,
.. derived
} ) )
}
( Some ( cause ) , None ) | ( None , Some ( cause ) ) = > Some ( cause ) ,
_ = > None ,
}
}
2024-07-15 17:43:35 +02:00
}
2023-12-12 17:25:16 -06:00
}
2024-08-02 17:27:07 -04:00
DerivationTree ::External ( _ ) = > Some ( derivation_tree ) ,
2023-12-12 17:25:16 -06:00
}
}
2024-08-02 17:27:07 -04:00
collapse ( derivation_tree )
. expect ( " derivation tree should contain at least one external term " )
2023-12-12 17:25:16 -06:00
}
2024-08-09 23:21:56 -04:00
2024-11-05 22:18:43 -05:00
/// Simplifies the version ranges on any incompatibilities to remove the `[max]` sentinel.
///
/// The `[max]` sentinel is used to represent the maximum local version of a package, to
/// implement PEP 440 semantics for local version equality. For example, `1.0.0+foo` needs to
/// satisfy `==1.0.0`.
pub ( crate ) fn collapse_local_version_segments ( derivation_tree : ErrorTree ) -> ErrorTree {
fn strip ( derivation_tree : ErrorTree ) -> Option < ErrorTree > {
match derivation_tree {
DerivationTree ::External ( External ::NotRoot ( _ , _ ) ) = > Some ( derivation_tree ) ,
DerivationTree ::External ( External ::NoVersions ( package , versions ) ) = > {
2024-11-15 15:06:24 -05:00
if SentinelRange ::from ( & versions ) . is_sentinel ( ) {
2024-11-05 22:18:43 -05:00
return None ;
}
2024-11-15 15:06:24 -05:00
let versions = SentinelRange ::from ( & versions ) . strip ( ) ;
2024-11-05 22:18:43 -05:00
Some ( DerivationTree ::External ( External ::NoVersions (
package , versions ,
) ) )
}
DerivationTree ::External ( External ::FromDependencyOf (
package1 ,
versions1 ,
package2 ,
versions2 ,
) ) = > {
2024-11-15 15:06:24 -05:00
let versions1 = SentinelRange ::from ( & versions1 ) . strip ( ) ;
let versions2 = SentinelRange ::from ( & versions2 ) . strip ( ) ;
2024-11-05 22:18:43 -05:00
Some ( DerivationTree ::External ( External ::FromDependencyOf (
package1 , versions1 , package2 , versions2 ,
) ) )
}
DerivationTree ::External ( External ::Custom ( package , versions , reason ) ) = > {
2024-11-15 15:06:24 -05:00
let versions = SentinelRange ::from ( & versions ) . strip ( ) ;
2024-11-05 22:18:43 -05:00
Some ( DerivationTree ::External ( External ::Custom (
package , versions , reason ,
) ) )
}
DerivationTree ::Derived ( mut derived ) = > {
let cause1 = strip ( ( * derived . cause1 ) . clone ( ) ) ;
let cause2 = strip ( ( * derived . cause2 ) . clone ( ) ) ;
match ( cause1 , cause2 ) {
( Some ( cause1 ) , Some ( cause2 ) ) = > Some ( DerivationTree ::Derived ( Derived {
cause1 : Arc ::new ( cause1 ) ,
cause2 : Arc ::new ( cause2 ) ,
terms : std ::mem ::take ( & mut derived . terms )
. into_iter ( )
. map ( | ( pkg , term ) | {
let term = match term {
Term ::Positive ( versions ) = > {
2024-11-15 15:06:24 -05:00
Term ::Positive ( SentinelRange ::from ( & versions ) . strip ( ) )
2024-11-05 22:18:43 -05:00
}
Term ::Negative ( versions ) = > {
2024-11-15 15:06:24 -05:00
Term ::Negative ( SentinelRange ::from ( & versions ) . strip ( ) )
2024-11-05 22:18:43 -05:00
}
} ;
( pkg , term )
} )
. collect ( ) ,
shared_id : derived . shared_id ,
} ) ) ,
( Some ( cause ) , None ) | ( None , Some ( cause ) ) = > Some ( cause ) ,
_ = > None ,
}
}
}
}
strip ( derivation_tree ) . expect ( " derivation tree should contain at least one term " )
}
2024-08-09 23:21:56 -04:00
/// Initialize a [`NoSolutionHeader`] for this error.
pub fn header ( & self ) -> NoSolutionHeader {
2024-10-11 15:23:38 -04:00
NoSolutionHeader ::new ( self . env . clone ( ) )
2024-08-09 23:21:56 -04:00
}
2023-12-12 17:25:16 -06:00
}
impl std ::error ::Error for NoSolutionError { }
2023-11-08 06:57:26 -08:00
2023-12-12 17:25:16 -06:00
impl std ::fmt ::Display for NoSolutionError {
2023-11-08 06:57:26 -08:00
fn fmt ( & self , f : & mut Formatter < '_ > ) -> std ::fmt ::Result {
2023-12-28 22:44:35 -04:00
// Write the derivation report.
2023-12-12 17:25:16 -06:00
let formatter = PubGrubReportFormatter {
available_versions : & self . available_versions ,
2024-07-15 17:43:35 +02:00
python_requirement : & self . python_requirement ,
2024-08-14 21:41:31 -05:00
workspace_members : & self . workspace_members ,
2023-12-12 17:25:16 -06:00
} ;
2024-08-14 21:41:31 -05:00
// Transform the error tree for reporting
let mut tree = self . error . clone ( ) ;
2024-08-19 18:05:01 -04:00
simplify_derivation_tree_markers ( & self . python_requirement , & mut tree ) ;
2024-10-14 21:48:13 +00:00
let should_display_tree = std ::env ::var_os ( EnvVars ::UV_INTERNAL__SHOW_DERIVATION_TREE )
. is_some ( )
2024-08-16 15:19:59 -05:00
| | tracing ::enabled! ( tracing ::Level ::TRACE ) ;
if should_display_tree {
display_tree ( & tree , " Resolver derivation tree before reduction " ) ;
}
2024-08-15 20:50:43 -05:00
collapse_no_versions_of_workspace_members ( & mut tree , & self . workspace_members ) ;
2024-08-14 21:41:31 -05:00
2024-08-14 22:08:56 -05:00
if self . workspace_members . len ( ) = = 1 {
let project = self . workspace_members . iter ( ) . next ( ) . unwrap ( ) ;
drop_root_dependency_on_project ( & mut tree , project ) ;
}
2024-08-16 15:19:59 -05:00
collapse_unavailable_versions ( & mut tree ) ;
2024-08-19 13:02:02 -05:00
collapse_redundant_depends_on_no_versions ( & mut tree ) ;
2024-08-16 15:19:59 -05:00
if should_display_tree {
display_tree ( & tree , " Resolver derivation tree after reduction " ) ;
2024-08-16 09:25:26 -05:00
}
2024-08-14 21:41:31 -05:00
let report = DefaultStringReporter ::report_with_formatter ( & tree , & formatter ) ;
2023-12-28 22:44:35 -04:00
write! ( f , " {report} " ) ? ;
// Include any additional hints.
2024-09-18 16:16:22 +02:00
let mut additional_hints = IndexSet ::default ( ) ;
formatter . generate_hints (
2024-09-19 16:06:55 +02:00
& tree ,
2024-02-06 11:04:14 -06:00
& self . selector ,
& self . index_locations ,
2024-10-16 10:34:29 -07:00
& self . index_capabilities ,
2024-09-23 15:02:19 -04:00
& self . available_indexes ,
2024-02-06 11:04:14 -06:00
& self . unavailable_packages ,
2024-04-09 23:12:10 -04:00
& self . incomplete_packages ,
2024-06-26 13:58:23 +02:00
& self . fork_urls ,
2024-10-11 15:23:38 -04:00
& self . env ,
2024-09-20 20:07:34 +02:00
& self . workspace_members ,
2024-10-14 21:04:30 -04:00
self . options ,
2024-09-18 16:16:22 +02:00
& mut additional_hints ,
) ;
for hint in additional_hints {
2024-02-06 11:04:14 -06:00
write! ( f , " \n \n {hint} " ) ? ;
2023-12-28 22:44:35 -04:00
}
Ok ( ( ) )
2023-11-08 06:57:26 -08:00
}
}
2024-08-09 23:21:56 -04:00
2024-08-16 09:25:26 -05:00
#[ allow(clippy::print_stderr) ]
2024-08-16 15:19:59 -05:00
fn display_tree (
error : & DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
name : & str ,
) {
2024-08-16 09:25:26 -05:00
let mut lines = Vec ::new ( ) ;
display_tree_inner ( error , & mut lines , 0 ) ;
lines . reverse ( ) ;
2024-10-14 21:48:13 +00:00
if std ::env ::var_os ( EnvVars ::UV_INTERNAL__SHOW_DERIVATION_TREE ) . is_some ( ) {
2024-08-16 15:19:59 -05:00
eprintln! ( " {name} \n {} " , lines . join ( " \n " ) ) ;
2024-08-16 09:25:26 -05:00
} else {
2024-08-16 15:19:59 -05:00
trace! ( " {name} \n {} " , lines . join ( " \n " ) ) ;
2024-08-16 09:25:26 -05:00
}
}
fn display_tree_inner (
error : & DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
lines : & mut Vec < String > ,
depth : usize ,
) {
match error {
DerivationTree ::Derived ( derived ) = > {
display_tree_inner ( & derived . cause1 , lines , depth + 1 ) ;
display_tree_inner ( & derived . cause2 , lines , depth + 1 ) ;
}
DerivationTree ::External ( external ) = > {
let prefix = " " . repeat ( depth ) . to_string ( ) ;
match external {
External ::FromDependencyOf ( package , version , dependency , dependency_version ) = > {
lines . push ( format! (
" {prefix} {package} {version} depends on {dependency} {dependency_version} "
) ) ;
}
External ::Custom ( package , versions , reason ) = > match reason {
UnavailableReason ::Package ( _ ) = > {
lines . push ( format! ( " {prefix} {package} {reason} " ) ) ;
}
UnavailableReason ::Version ( _ ) = > {
lines . push ( format! ( " {prefix} {package} {versions} {reason} " ) ) ;
}
} ,
External ::NoVersions ( package , versions ) = > {
lines . push ( format! ( " {prefix} no versions of {package} {versions} " ) ) ;
}
External ::NotRoot ( package , versions ) = > {
lines . push ( format! ( " {prefix} not root {package} {versions} " ) ) ;
}
}
}
}
}
2024-08-15 20:50:43 -05:00
/// Given a [`DerivationTree`], collapse any `NoVersion` incompatibilities for workspace members
2024-08-14 21:41:31 -05:00
/// to avoid saying things like "only <workspace-member>==0.1.0 is available".
2024-08-15 20:50:43 -05:00
fn collapse_no_versions_of_workspace_members (
2024-08-14 21:41:31 -05:00
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
2024-08-15 20:50:43 -05:00
workspace_members : & BTreeSet < PackageName > ,
2024-08-14 21:41:31 -05:00
) {
match tree {
DerivationTree ::External ( _ ) = > { }
DerivationTree ::Derived ( derived ) = > {
match (
Arc ::make_mut ( & mut derived . cause1 ) ,
Arc ::make_mut ( & mut derived . cause2 ) ,
) {
2024-08-15 20:50:43 -05:00
// If we have a node for a package with no versions...
( DerivationTree ::External ( External ::NoVersions ( package , _ ) ) , ref mut other )
| ( ref mut other , DerivationTree ::External ( External ::NoVersions ( package , _ ) ) ) = > {
// First, always recursively visit the other side of the tree
collapse_no_versions_of_workspace_members ( other , workspace_members ) ;
// Then, if the package is a workspace member...
let ( PubGrubPackageInner ::Package { name , .. }
| PubGrubPackageInner ::Extra { name , .. }
| PubGrubPackageInner ::Dev { name , .. } ) = & * * package
else {
return ;
} ;
if ! workspace_members . contains ( name ) {
return ;
}
// Replace this node with the other tree
2024-08-14 21:41:31 -05:00
* tree = other . clone ( ) ;
}
// If not, just recurse
_ = > {
2024-08-15 20:50:43 -05:00
collapse_no_versions_of_workspace_members (
Arc ::make_mut ( & mut derived . cause1 ) ,
workspace_members ,
) ;
collapse_no_versions_of_workspace_members (
Arc ::make_mut ( & mut derived . cause2 ) ,
workspace_members ,
) ;
2024-08-14 21:41:31 -05:00
}
}
}
}
}
2024-08-19 13:02:02 -05:00
/// Given a [`DerivationTree`], collapse `NoVersions` incompatibilities that are redundant children
/// of a dependency. For example, if we have a tree like:
2024-08-19 17:24:16 -04:00
///
/// ```text
/// A>=1,<2 depends on B
/// A has no versions >1,<2
/// C depends on A>=1,<2
/// ```
2024-08-19 13:02:02 -05:00
///
/// We can simplify this to `C depends A>=1 and A>=1 depends on B so C depends on B` without
/// explaining that there are no other versions of A. This is dependent on range of A in "A depends
/// on" being a subset of range of A in "depends on A". For example, in a tree like:
///
2024-08-19 17:24:16 -04:00
/// ```text
/// A>=1,<3 depends on B
/// A has no versions >2,<3
/// C depends on A>=2,<3
/// ```
2024-08-19 13:02:02 -05:00
///
/// We cannot say `C depends on A>=2 and A>=1 depends on B so C depends on B` because there is a
/// hole in the range — `A>=1,<3` is not a subset of `A>=2,<3`.
fn collapse_redundant_depends_on_no_versions (
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
) {
match tree {
DerivationTree ::External ( _ ) = > { }
DerivationTree ::Derived ( derived ) = > {
// If one node is a dependency incompatibility...
match (
Arc ::make_mut ( & mut derived . cause1 ) ,
Arc ::make_mut ( & mut derived . cause2 ) ,
) {
(
DerivationTree ::External ( External ::FromDependencyOf ( package , versions , _ , _ ) ) ,
ref mut other ,
)
| (
ref mut other ,
DerivationTree ::External ( External ::FromDependencyOf ( package , versions , _ , _ ) ) ,
) = > {
// Check if the other node is the relevant form of subtree...
collapse_redundant_depends_on_no_versions_inner ( other , package , versions ) ;
}
// If not, just recurse
_ = > {
collapse_redundant_depends_on_no_versions ( Arc ::make_mut ( & mut derived . cause1 ) ) ;
collapse_redundant_depends_on_no_versions ( Arc ::make_mut ( & mut derived . cause2 ) ) ;
}
}
}
}
}
/// Helper for [`collapse_redundant_depends_on_no_versions`].
fn collapse_redundant_depends_on_no_versions_inner (
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
package : & PubGrubPackage ,
versions : & Range < Version > ,
) {
match tree {
DerivationTree ::External ( _ ) = > { }
DerivationTree ::Derived ( derived ) = > {
// If we're a subtree with dependency and no versions incompatibilities...
match ( & * derived . cause1 , & * derived . cause2 ) {
(
DerivationTree ::External ( External ::NoVersions ( no_versions_package , _ ) ) ,
dependency_clause @ DerivationTree ::External ( External ::FromDependencyOf (
_ ,
_ ,
dependency_package ,
dependency_versions ,
) ) ,
)
| (
dependency_clause @ DerivationTree ::External ( External ::FromDependencyOf (
_ ,
_ ,
dependency_package ,
dependency_versions ,
) ) ,
DerivationTree ::External ( External ::NoVersions ( no_versions_package , _ ) ) ,
)
// And these incompatibilities (and the parent incompatibility) all are referring to
2024-08-19 17:24:16 -04:00
// the same package...
2024-08-19 13:02:02 -05:00
if no_versions_package = = dependency_package
& & package = = no_versions_package
// And parent dependency versions are a subset of the versions in this tree...
& & versions . subset_of ( dependency_versions ) = >
{
// Enumerating the available versions will be redundant and we can drop the no
// versions clause entirely in favor of the dependency clause.
* tree = dependency_clause . clone ( ) ;
// Note we are at a leaf of the tree so there's no further recursion to do
}
// If not, just recurse
_ = > {
collapse_redundant_depends_on_no_versions ( Arc ::make_mut ( & mut derived . cause1 ) ) ;
collapse_redundant_depends_on_no_versions ( Arc ::make_mut ( & mut derived . cause2 ) ) ;
}
}
}
}
}
2024-08-19 18:05:01 -04:00
/// Simplifies the markers on pubgrub packages in the given derivation tree
/// according to the given Python requirement.
///
/// For example, when there's a dependency like `foo ; python_version >=
/// '3.11'` and `requires-python = '>=3.11'`, this simplification will remove
/// the `python_version >= '3.11'` marker since it's implied to be true by
/// the `requires-python` setting. This simplifies error messages by reducing
/// noise.
fn simplify_derivation_tree_markers (
python_requirement : & PythonRequirement ,
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
) {
match tree {
DerivationTree ::External ( External ::NotRoot ( ref mut pkg , _ ) ) = > {
pkg . simplify_markers ( python_requirement ) ;
}
DerivationTree ::External ( External ::NoVersions ( ref mut pkg , _ ) ) = > {
pkg . simplify_markers ( python_requirement ) ;
}
DerivationTree ::External ( External ::FromDependencyOf ( ref mut pkg1 , _ , ref mut pkg2 , _ ) ) = > {
pkg1 . simplify_markers ( python_requirement ) ;
pkg2 . simplify_markers ( python_requirement ) ;
}
DerivationTree ::External ( External ::Custom ( ref mut pkg , _ , _ ) ) = > {
pkg . simplify_markers ( python_requirement ) ;
}
DerivationTree ::Derived ( derived ) = > {
derived . terms = std ::mem ::take ( & mut derived . terms )
. into_iter ( )
. map ( | ( mut pkg , term ) | {
pkg . simplify_markers ( python_requirement ) ;
( pkg , term )
} )
. collect ( ) ;
simplify_derivation_tree_markers (
python_requirement ,
Arc ::make_mut ( & mut derived . cause1 ) ,
) ;
simplify_derivation_tree_markers (
python_requirement ,
Arc ::make_mut ( & mut derived . cause2 ) ,
) ;
}
}
}
2024-08-16 15:19:59 -05:00
/// Given a [`DerivationTree`], collapse incompatibilities for versions of a package that are
/// unavailable for the same reason to avoid repeating the same message for every unavailable
/// version.
fn collapse_unavailable_versions (
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 we have a node for unavailable package versions
(
DerivationTree ::External ( External ::Custom ( package , versions , reason ) ) ,
ref mut other ,
)
| (
ref mut other ,
DerivationTree ::External ( External ::Custom ( package , versions , reason ) ) ,
) = > {
// First, recursively collapse the other side of the tree
collapse_unavailable_versions ( other ) ;
// If it's not a derived tree, nothing to do.
let DerivationTree ::Derived ( Derived {
terms ,
shared_id ,
cause1 ,
cause2 ,
} ) = other
else {
return ;
} ;
// If the other tree has an unavailable package...
match ( & * * cause1 , & * * cause2 ) {
// Note the following cases are the same, but we need two matches to retain
// the ordering of the causes
(
_ ,
DerivationTree ::External ( External ::Custom (
other_package ,
other_versions ,
other_reason ,
) ) ,
) = > {
// And the package and reason are the same...
if package = = other_package & & reason = = other_reason {
// Collapse both into a new node, with a union of their ranges
* tree = DerivationTree ::Derived ( Derived {
terms : terms . clone ( ) ,
shared_id : * shared_id ,
cause1 : cause1 . clone ( ) ,
cause2 : Arc ::new ( DerivationTree ::External ( External ::Custom (
package . clone ( ) ,
versions . union ( other_versions ) ,
reason . clone ( ) ,
) ) ) ,
} ) ;
}
}
(
DerivationTree ::External ( External ::Custom (
other_package ,
other_versions ,
other_reason ,
) ) ,
_ ,
) = > {
// And the package and reason are the same...
if package = = other_package & & reason = = other_reason {
// Collapse both into a new node, with a union of their ranges
* tree = DerivationTree ::Derived ( Derived {
terms : terms . clone ( ) ,
shared_id : * shared_id ,
cause1 : Arc ::new ( DerivationTree ::External ( External ::Custom (
package . clone ( ) ,
versions . union ( other_versions ) ,
reason . clone ( ) ,
) ) ) ,
cause2 : cause2 . clone ( ) ,
} ) ;
}
}
_ = > { }
}
}
// If not, just recurse
_ = > {
collapse_unavailable_versions ( Arc ::make_mut ( & mut derived . cause1 ) ) ;
collapse_unavailable_versions ( Arc ::make_mut ( & mut derived . cause2 ) ) ;
}
}
}
}
}
2024-08-14 22:08:56 -05:00
/// Given a [`DerivationTree`], drop dependency incompatibilities from the root
/// to the project.
///
/// Intended to effectively change the root to a workspace member in single project
/// workspaces, avoiding a level of indirection like "And because your project
/// requires your project, we can conclude that your projects's requirements are
/// unsatisfiable."
fn drop_root_dependency_on_project (
tree : & mut DerivationTree < PubGrubPackage , Range < Version > , UnavailableReason > ,
project : & PackageName ,
) {
match tree {
DerivationTree ::External ( _ ) = > { }
DerivationTree ::Derived ( derived ) = > {
match (
Arc ::make_mut ( & mut derived . cause1 ) ,
Arc ::make_mut ( & mut derived . cause2 ) ,
) {
// If one node is a dependency incompatibility...
(
DerivationTree ::External ( External ::FromDependencyOf ( package , _ , dependency , _ ) ) ,
ref mut other ,
)
| (
ref mut other ,
DerivationTree ::External ( External ::FromDependencyOf ( package , _ , dependency , _ ) ) ,
) = > {
// And the parent is the root package...
if ! matches! ( & * * package , PubGrubPackageInner ::Root ( _ ) ) {
return ;
}
// And the dependency is the project...
let PubGrubPackageInner ::Package { name , .. } = & * * dependency else {
return ;
} ;
if name ! = project {
return ;
}
// Recursively collapse the other side of the tree
drop_root_dependency_on_project ( other , project ) ;
// Then, replace this node with the other tree
* tree = other . clone ( ) ;
}
// If not, just recurse
_ = > {
drop_root_dependency_on_project ( Arc ::make_mut ( & mut derived . cause1 ) , project ) ;
drop_root_dependency_on_project ( Arc ::make_mut ( & mut derived . cause2 ) , project ) ;
}
}
}
}
}
2024-11-15 15:06:24 -05:00
/// A version range that may include local version sentinels (`+[max]`).
#[ derive(Debug) ]
pub struct SentinelRange < ' range > ( & ' range Range < Version > ) ;
impl < ' range > From < & ' range Range < Version > > for SentinelRange < ' range > {
fn from ( range : & ' range Range < Version > ) -> Self {
Self ( range )
}
}
impl < ' range > SentinelRange < ' range > {
/// Returns `true` if the range appears to be, e.g., `>1.0.0, <1.0.0+[max]`.
pub fn is_sentinel ( & self ) -> bool {
self . 0. iter ( ) . all ( | ( lower , upper ) | {
let ( Bound ::Excluded ( lower ) , Bound ::Excluded ( upper ) ) = ( lower , upper ) else {
return false ;
} ;
if lower . local ( ) = = LocalVersionSlice ::Max {
return false ;
}
if upper . local ( ) ! = LocalVersionSlice ::Max {
return false ;
}
* lower = = upper . clone ( ) . without_local ( )
} )
}
/// Remove local versions sentinels (`+[max]`) from the version ranges.
pub fn strip ( & self ) -> Ranges < Version > {
2024-11-18 13:28:17 +01:00
self . 0
. iter ( )
. map ( | ( lower , upper ) | Self ::strip_sentinel ( lower . clone ( ) , upper . clone ( ) ) )
. collect ( )
2024-11-15 15:06:24 -05:00
}
/// Remove local versions sentinels (`+[max]`) from the interval.
fn strip_sentinel (
mut lower : Bound < Version > ,
mut upper : Bound < Version > ,
) -> ( Bound < Version > , Bound < Version > ) {
match ( & lower , & upper ) {
( Bound ::Unbounded , Bound ::Unbounded ) = > { }
( Bound ::Unbounded , Bound ::Included ( v ) ) = > {
// `<=1.0.0+[max]` is equivalent to `<=1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
upper = Bound ::Included ( v . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Unbounded , Bound ::Excluded ( v ) ) = > {
// `<1.0.0+[max]` is equivalent to `<1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
upper = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Included ( v ) , Bound ::Unbounded ) = > {
// `>=1.0.0+[max]` is equivalent to `>1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
lower = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Included ( v ) , Bound ::Included ( b ) ) = > {
// `>=1.0.0+[max]` is equivalent to `>1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
lower = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
// `<=1.0.0+[max]` is equivalent to `<=1.0.0`
if b . local ( ) = = LocalVersionSlice ::Max {
upper = Bound ::Included ( b . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Included ( v ) , Bound ::Excluded ( b ) ) = > {
// `>=1.0.0+[max]` is equivalent to `>1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
lower = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
// `<1.0.0+[max]` is equivalent to `<1.0.0`
if b . local ( ) = = LocalVersionSlice ::Max {
upper = Bound ::Included ( b . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Excluded ( v ) , Bound ::Unbounded ) = > {
// `>1.0.0+[max]` is equivalent to `>1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
lower = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Excluded ( v ) , Bound ::Included ( b ) ) = > {
// `>1.0.0+[max]` is equivalent to `>1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
lower = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
// `<=1.0.0+[max]` is equivalent to `<=1.0.0`
if b . local ( ) = = LocalVersionSlice ::Max {
upper = Bound ::Included ( b . clone ( ) . without_local ( ) ) ;
}
}
( Bound ::Excluded ( v ) , Bound ::Excluded ( b ) ) = > {
// `>1.0.0+[max]` is equivalent to `>1.0.0`
if v . local ( ) = = LocalVersionSlice ::Max {
lower = Bound ::Excluded ( v . clone ( ) . without_local ( ) ) ;
}
// `<1.0.0+[max]` is equivalent to `<1.0.0`
if b . local ( ) = = LocalVersionSlice ::Max {
upper = Bound ::Excluded ( b . clone ( ) . without_local ( ) ) ;
}
}
}
( lower , upper )
}
}
2024-08-09 23:21:56 -04:00
#[ derive(Debug) ]
pub struct NoSolutionHeader {
2024-10-11 15:23:38 -04:00
/// The [`ResolverEnvironment`] that caused the failure.
env : ResolverEnvironment ,
2024-08-09 23:21:56 -04:00
/// The additional context for the resolution failure.
context : Option < & 'static str > ,
}
impl NoSolutionHeader {
2024-10-11 15:23:38 -04:00
/// Create a new [`NoSolutionHeader`] with the given [`ResolverEnvironment`].
pub fn new ( env : ResolverEnvironment ) -> Self {
Self { env , context : None }
2024-08-09 23:21:56 -04:00
}
/// Set the context for the resolution failure.
#[ must_use ]
pub fn with_context ( mut self , context : & 'static str ) -> Self {
self . context = Some ( context ) ;
self
}
}
impl std ::fmt ::Display for NoSolutionHeader {
fn fmt ( & self , f : & mut Formatter < '_ > ) -> std ::fmt ::Result {
2024-10-11 15:23:38 -04:00
match ( self . context , self . env . end_user_fork_display ( ) ) {
( None , None ) = > write! ( f , " No solution found when resolving dependencies: " ) ,
( Some ( context ) , None ) = > write! (
f ,
" No solution found when resolving {context} dependencies: "
) ,
( None , Some ( split ) ) = > write! (
f ,
" No solution found when resolving dependencies for {split}: "
) ,
( Some ( context ) , Some ( split ) ) = > write! (
f ,
" No solution found when resolving {context} dependencies for {split}: "
) ,
2024-08-09 23:21:56 -04:00
}
}
}