mirror of
				https://github.com/astral-sh/uv.git
				synced 2025-10-31 12:06:13 +00:00 
			
		
		
		
	Simplify available package version ranges when the name includes markers or extras (#6162)
There were different `PubGrubPackage` types so they never matched the available versions set! Luckily, the available versions are agnostic to the markers and optional dependencies so we can just broaden to using `PackageName` as a lookup key. Addresses yet another complaint in https://github.com/astral-sh/uv/issues/5046
This commit is contained in:
		
							parent
							
								
									05cceee523
								
							
						
					
					
						commit
						ea636bbe61
					
				
					 5 changed files with 24 additions and 46 deletions
				
			
		|  | @ -120,7 +120,7 @@ pub(crate) type ErrorTree = DerivationTree<PubGrubPackage, Range<Version>, Unava | |||
| #[derive(Debug)] | ||||
| pub struct NoSolutionError { | ||||
|     error: pubgrub::NoSolutionError<UvDependencyProvider>, | ||||
|     available_versions: FxHashMap<PubGrubPackage, BTreeSet<Version>>, | ||||
|     available_versions: FxHashMap<PackageName, BTreeSet<Version>>, | ||||
|     selector: CandidateSelector, | ||||
|     python_requirement: PythonRequirement, | ||||
|     index_locations: IndexLocations, | ||||
|  | @ -135,7 +135,7 @@ impl NoSolutionError { | |||
|     /// Create a new [`NoSolutionError`] from a [`pubgrub::NoSolutionError`].
 | ||||
|     pub(crate) fn new( | ||||
|         error: pubgrub::NoSolutionError<UvDependencyProvider>, | ||||
|         available_versions: FxHashMap<PubGrubPackage, BTreeSet<Version>>, | ||||
|         available_versions: FxHashMap<PackageName, BTreeSet<Version>>, | ||||
|         selector: CandidateSelector, | ||||
|         python_requirement: PythonRequirement, | ||||
|         index_locations: IndexLocations, | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ use super::{PubGrubPackage, PubGrubPackageInner, PubGrubPython}; | |||
| #[derive(Debug)] | ||||
| pub(crate) struct PubGrubReportFormatter<'a> { | ||||
|     /// The versions that were available for each package
 | ||||
|     pub(crate) available_versions: &'a FxHashMap<PubGrubPackage, BTreeSet<Version>>, | ||||
|     pub(crate) available_versions: &'a FxHashMap<PackageName, BTreeSet<Version>>, | ||||
| 
 | ||||
|     /// The versions that were available for each package
 | ||||
|     pub(crate) python_requirement: &'a PythonRequirement, | ||||
|  | @ -87,7 +87,7 @@ impl ReportFormatter<PubGrubPackage, Range<Version>, UnavailableReason> | |||
|                         // Note that sometimes we do not have a range of available versions, e.g.,
 | ||||
|                         // when a package is from a non-registry source. In that case, we cannot
 | ||||
|                         // perform further simplicifaction of the range.
 | ||||
|                         if let Some(available_versions) = self.available_versions.get(package) { | ||||
|                         if let Some(available_versions) = package.name().and_then(|name| self.available_versions.get(name)) { | ||||
|                             update_availability_range(&complement, available_versions) | ||||
|                         } else { | ||||
|                             complement | ||||
|  | @ -484,10 +484,13 @@ impl PubGrubReportFormatter<'_> { | |||
|         set: &'a Range<Version>, | ||||
|         package: &PubGrubPackage, | ||||
|     ) -> Cow<'a, Range<Version>> { | ||||
|         let Some(name) = package.name() else { | ||||
|             return Cow::Borrowed(set); | ||||
|         }; | ||||
|         if set == &Range::full() { | ||||
|             Cow::Borrowed(set) | ||||
|         } else { | ||||
|             Cow::Owned(set.simplify(self.available_versions.get(package).into_iter().flatten())) | ||||
|             Cow::Owned(set.simplify(self.available_versions.get(name).into_iter().flatten())) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -695,13 +698,17 @@ impl PubGrubReportFormatter<'_> { | |||
|                     range: self.simplify_set(set, package).into_owned(), | ||||
|                 }); | ||||
|             } | ||||
|         } else if let Some(version) = self.available_versions.get(package).and_then(|versions| { | ||||
|             versions | ||||
|                 .iter() | ||||
|                 .rev() | ||||
|                 .filter(|version| version.any_prerelease()) | ||||
|                 .find(|version| set.contains(version)) | ||||
|         }) { | ||||
|         } else if let Some(version) = package | ||||
|             .name() | ||||
|             .and_then(|name| self.available_versions.get(name)) | ||||
|             .and_then(|versions| { | ||||
|                 versions | ||||
|                     .iter() | ||||
|                     .rev() | ||||
|                     .filter(|version| version.any_prerelease()) | ||||
|                     .find(|version| set.contains(version)) | ||||
|             }) | ||||
|         { | ||||
|             // There are pre-release versions available for the package.
 | ||||
|             if selector.prerelease_strategy().allows(name, markers) != AllowPrerelease::Yes { | ||||
|                 hints.insert(PubGrubHint::PrereleaseAvailable { | ||||
|  |  | |||
|  | @ -1963,9 +1963,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag | |||
| 
 | ||||
|         let mut available_versions = FxHashMap::default(); | ||||
|         for package in err.packages() { | ||||
|             let PubGrubPackageInner::Package { name, .. } = &**package else { | ||||
|                 continue; | ||||
|             }; | ||||
|             let Some(name) = package.name() else { continue }; | ||||
|             if !visited.contains(name) { | ||||
|                 // Avoid including available versions for packages that exist in the derivation
 | ||||
|                 // tree, but were never visited during resolution. We _may_ have metadata for
 | ||||
|  | @ -1977,7 +1975,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag | |||
|                 if let VersionsResponse::Found(ref version_maps) = *response { | ||||
|                     for version_map in version_maps { | ||||
|                         available_versions | ||||
|                             .entry(package.clone()) | ||||
|                             .entry(name.clone()) | ||||
|                             .or_insert_with(BTreeSet::new) | ||||
|                             .extend(version_map.iter().map(|(version, _)| version.clone())); | ||||
|                     } | ||||
|  |  | |||
|  | @ -433,7 +433,7 @@ fn conflict_in_fork() -> Result<()> { | |||
|           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 the following versions of package-a{sys_platform == 'darwin'} are available: | ||||
|               package-a{sys_platform == 'darwin'}==1.0.0 | ||||
|               package-a{sys_platform == 'darwin'}>=2 | ||||
|               package-a{sys_platform == 'darwin'}>2 | ||||
|           and your project depends on package-a{sys_platform == 'darwin'}<2, we can conclude that your project's requirements are unsatisfiable. | ||||
|     "###
 | ||||
|     ); | ||||
|  | @ -2875,7 +2875,7 @@ fn fork_non_local_fork_marker_transitive() -> Result<()> { | |||
|       ╰─▶ Because package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}>=2.0.0 and only package-c{sys_platform == 'darwin'}<=2.0.0 is available, we can conclude that package-b==1.0.0 depends on package-c{sys_platform == 'darwin'}==2.0.0. | ||||
|           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'}>=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. | ||||
|           And because your project depends on package-a==1.0.0 and package-b==1.0.0, we can conclude that your project's requirements are unsatisfiable. | ||||
|     "###
 | ||||
|  |  | |||
|  | @ -7993,34 +7993,7 @@ fn universal_requires_python_incomplete() -> Result<()> { | |||
|     ----- stderr ----- | ||||
|     warning: The requested Python version 3.7 is not available; 3.12.[X] will be used to build dependencies instead. | ||||
|       × No solution found when resolving dependencies: | ||||
|       ╰─▶ Because only the following versions of uv{python_full_version >= '3.8'} are available: | ||||
|               uv{python_full_version >= '3.8'}==0.0.5 | ||||
|               uv{python_full_version >= '3.8'}==0.1.0 | ||||
|               uv{python_full_version >= '3.8'}==0.1.1 | ||||
|               uv{python_full_version >= '3.8'}==0.1.2 | ||||
|               uv{python_full_version >= '3.8'}==0.1.3 | ||||
|               uv{python_full_version >= '3.8'}==0.1.4 | ||||
|               uv{python_full_version >= '3.8'}==0.1.5 | ||||
|               uv{python_full_version >= '3.8'}==0.1.6 | ||||
|               uv{python_full_version >= '3.8'}==0.1.7 | ||||
|               uv{python_full_version >= '3.8'}==0.1.8 | ||||
|               uv{python_full_version >= '3.8'}==0.1.9 | ||||
|               uv{python_full_version >= '3.8'}==0.1.10 | ||||
|               uv{python_full_version >= '3.8'}==0.1.11 | ||||
|               uv{python_full_version >= '3.8'}==0.1.12 | ||||
|               uv{python_full_version >= '3.8'}==0.1.13 | ||||
|               uv{python_full_version >= '3.8'}==0.1.14 | ||||
|               uv{python_full_version >= '3.8'}==0.1.15 | ||||
|               uv{python_full_version >= '3.8'}==0.1.16 | ||||
|               uv{python_full_version >= '3.8'}==0.1.17 | ||||
|               uv{python_full_version >= '3.8'}==0.1.18 | ||||
|               uv{python_full_version >= '3.8'}==0.1.19 | ||||
|               uv{python_full_version >= '3.8'}==0.1.20 | ||||
|               uv{python_full_version >= '3.8'}==0.1.21 | ||||
|               uv{python_full_version >= '3.8'}==0.1.22 | ||||
|               uv{python_full_version >= '3.8'}==0.1.23 | ||||
|               uv{python_full_version >= '3.8'}==0.1.24 | ||||
|           and the requested Python version (>=3.7) does not satisfy Python>=3.8, we can conclude that all versions of uv{python_full_version >= '3.8'} are incompatible. | ||||
|       ╰─▶ Because only uv{python_full_version >= '3.8'}<=0.1.24 is available and the requested Python version (>=3.7) does not satisfy Python>=3.8, we can conclude that all versions of uv{python_full_version >= '3.8'} are incompatible. | ||||
|           And because you require uv{python_full_version >= '3.8'}, we can conclude that your requirements are unsatisfiable. | ||||
|     "###
 | ||||
|     ); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Zanie Blue
						Zanie Blue