mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-29 03:02:55 +00:00
Use Dist in VersionMap (#851)
Refactoring split out from find links support: Find links files can be represented as `Dist`, but not really as `File`, they don't have url nor hashes. `DistRequiresPython` is somewhat odd as an in between type.
This commit is contained in:
parent
1203f8f9e8
commit
858d5584cc
10 changed files with 158 additions and 200 deletions
|
|
@ -1,18 +1,16 @@
|
||||||
use pubgrub::range::Range;
|
use pubgrub::range::Range;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use distribution_types::{Dist, DistributionMetadata, IndexUrl, Name};
|
use distribution_types::{Dist, DistributionMetadata, Name};
|
||||||
use pep440_rs::VersionSpecifiers;
|
use pep440_rs::VersionSpecifiers;
|
||||||
use pep508_rs::{Requirement, VersionOrUrl};
|
use pep508_rs::{Requirement, VersionOrUrl};
|
||||||
use puffin_normalize::PackageName;
|
use puffin_normalize::PackageName;
|
||||||
use pypi_types::BaseUrl;
|
|
||||||
|
|
||||||
use crate::file::DistFile;
|
|
||||||
use crate::prerelease_mode::PreReleaseStrategy;
|
use crate::prerelease_mode::PreReleaseStrategy;
|
||||||
use crate::pubgrub::PubGrubVersion;
|
use crate::pubgrub::PubGrubVersion;
|
||||||
use crate::python_requirement::PythonRequirement;
|
use crate::python_requirement::PythonRequirement;
|
||||||
use crate::resolution_mode::ResolutionStrategy;
|
use crate::resolution_mode::ResolutionStrategy;
|
||||||
use crate::version_map::{ResolvableFile, VersionMap};
|
use crate::version_map::{DistRequiresPython, ResolvableFile, VersionMap};
|
||||||
use crate::{Manifest, ResolutionOptions};
|
use crate::{Manifest, ResolutionOptions};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -247,12 +245,12 @@ impl<'a> Candidate<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the [`DistFile`] to use when resolving the package.
|
/// Return the [`DistFile`] to use when resolving the package.
|
||||||
pub(crate) fn resolve(&self) -> &DistFile {
|
pub(crate) fn resolve(&self) -> &DistRequiresPython {
|
||||||
self.file.resolve()
|
self.file.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the [`DistFile`] to use when installing the package.
|
/// Return the [`DistFile`] to use when installing the package.
|
||||||
pub(crate) fn install(&self) -> &DistFile {
|
pub(crate) fn install(&self) -> &DistRequiresPython {
|
||||||
self.file.install()
|
self.file.install()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,7 +269,7 @@ impl<'a> Candidate<'a> {
|
||||||
|
|
||||||
// If the candidate is a source distribution, and doesn't support the installed Python
|
// If the candidate is a source distribution, and doesn't support the installed Python
|
||||||
// version, return the failing version specifiers, since we won't be able to build it.
|
// version, return the failing version specifiers, since we won't be able to build it.
|
||||||
if self.install().is_sdist() {
|
if matches!(self.install().dist, Dist::Source(_)) {
|
||||||
if !requires_python.contains(requirement.installed()) {
|
if !requires_python.contains(requirement.installed()) {
|
||||||
return Some(requires_python);
|
return Some(requires_python);
|
||||||
}
|
}
|
||||||
|
|
@ -279,17 +277,6 @@ impl<'a> Candidate<'a> {
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the [`Dist`] to use when resolving the candidate.
|
|
||||||
pub(crate) fn into_distribution(self, index: IndexUrl, base: BaseUrl) -> Dist {
|
|
||||||
Dist::from_registry(
|
|
||||||
self.name().clone(),
|
|
||||||
self.version().clone().into(),
|
|
||||||
self.resolve().clone().into(),
|
|
||||||
index,
|
|
||||||
base,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Name for Candidate<'_> {
|
impl Name for Candidate<'_> {
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,11 @@ use rustc_hash::FxHashMap;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use distribution_types::{BuiltDist, IndexUrl, PathBuiltDist, PathSourceDist, SourceDist};
|
use distribution_types::{BuiltDist, PathBuiltDist, PathSourceDist, SourceDist};
|
||||||
use pep508_rs::Requirement;
|
use pep508_rs::Requirement;
|
||||||
use puffin_distribution::DistributionDatabaseError;
|
use puffin_distribution::DistributionDatabaseError;
|
||||||
use puffin_normalize::PackageName;
|
use puffin_normalize::PackageName;
|
||||||
use puffin_traits::OnceMap;
|
use puffin_traits::OnceMap;
|
||||||
use pypi_types::BaseUrl;
|
|
||||||
|
|
||||||
use crate::candidate_selector::CandidateSelector;
|
use crate::candidate_selector::CandidateSelector;
|
||||||
use crate::pubgrub::{PubGrubPackage, PubGrubPython, PubGrubReportFormatter, PubGrubVersion};
|
use crate::pubgrub::{PubGrubPackage, PubGrubPython, PubGrubReportFormatter, PubGrubVersion};
|
||||||
|
|
@ -161,7 +160,7 @@ impl NoSolutionError {
|
||||||
pub(crate) fn with_available_versions(
|
pub(crate) fn with_available_versions(
|
||||||
mut self,
|
mut self,
|
||||||
python_requirement: &PythonRequirement,
|
python_requirement: &PythonRequirement,
|
||||||
package_versions: &OnceMap<PackageName, (IndexUrl, BaseUrl, VersionMap)>,
|
package_versions: &OnceMap<PackageName, VersionMap>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut available_versions = FxHashMap::default();
|
let mut available_versions = FxHashMap::default();
|
||||||
for package in self.derivation_tree.packages() {
|
for package in self.derivation_tree.packages() {
|
||||||
|
|
@ -181,7 +180,7 @@ impl NoSolutionError {
|
||||||
}
|
}
|
||||||
PubGrubPackage::Package(name, ..) => {
|
PubGrubPackage::Package(name, ..) => {
|
||||||
if let Some(entry) = package_versions.get(name) {
|
if let Some(entry) = package_versions.get(name) {
|
||||||
let (_, _, version_map) = entry.value();
|
let version_map = entry.value();
|
||||||
available_versions.insert(
|
available_versions.insert(
|
||||||
package.clone(),
|
package.clone(),
|
||||||
version_map
|
version_map
|
||||||
|
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use distribution_types::File;
|
|
||||||
|
|
||||||
/// A distribution can either be a wheel or a source distribution.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct WheelFile(pub(crate) File);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct SdistFile(pub(crate) File);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) enum DistFile {
|
|
||||||
Wheel(WheelFile),
|
|
||||||
Sdist(SdistFile),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for WheelFile {
|
|
||||||
type Target = File;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for SdistFile {
|
|
||||||
type Target = File;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<WheelFile> for File {
|
|
||||||
fn from(wheel: WheelFile) -> Self {
|
|
||||||
wheel.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SdistFile> for File {
|
|
||||||
fn from(sdist: SdistFile) -> Self {
|
|
||||||
sdist.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<WheelFile> for DistFile {
|
|
||||||
fn from(wheel: WheelFile) -> Self {
|
|
||||||
Self::Wheel(wheel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SdistFile> for DistFile {
|
|
||||||
fn from(sdist: SdistFile) -> Self {
|
|
||||||
Self::Sdist(sdist)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DistFile {
|
|
||||||
pub(crate) fn filename(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
Self::Wheel(wheel) => wheel.filename.as_str(),
|
|
||||||
Self::Sdist(sdist) => sdist.filename.as_str(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn is_sdist(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Wheel(_) => false,
|
|
||||||
Self::Sdist(_) => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DistFile> for File {
|
|
||||||
fn from(file: DistFile) -> Self {
|
|
||||||
match file {
|
|
||||||
DistFile::Wheel(wheel) => wheel.into(),
|
|
||||||
DistFile::Sdist(sdist) => sdist.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for DistFile {
|
|
||||||
type Target = File;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
match self {
|
|
||||||
DistFile::Wheel(file) => &file.0,
|
|
||||||
DistFile::Sdist(file) => &file.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -9,7 +9,6 @@ pub use resolver::{BuildId, Reporter as ResolverReporter, Resolver, ResolverProv
|
||||||
|
|
||||||
mod candidate_selector;
|
mod candidate_selector;
|
||||||
mod error;
|
mod error;
|
||||||
mod file;
|
|
||||||
mod finder;
|
mod finder;
|
||||||
mod manifest;
|
mod manifest;
|
||||||
mod overrides;
|
mod overrides;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use distribution_types::{File, IndexUrl};
|
use distribution_types::Dist;
|
||||||
use puffin_normalize::PackageName;
|
use puffin_normalize::PackageName;
|
||||||
use pypi_types::BaseUrl;
|
|
||||||
|
|
||||||
use crate::candidate_selector::Candidate;
|
use crate::candidate_selector::Candidate;
|
||||||
|
|
||||||
|
|
@ -11,29 +10,19 @@ use crate::candidate_selector::Candidate;
|
||||||
/// For example, given `Flask==3.0.0`, the [`FilePins`] would contain a mapping from `Flask` to
|
/// For example, given `Flask==3.0.0`, the [`FilePins`] would contain a mapping from `Flask` to
|
||||||
/// `3.0.0` to the specific wheel or source distribution archive that was pinned for that version.
|
/// `3.0.0` to the specific wheel or source distribution archive that was pinned for that version.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct FilePins(
|
pub(crate) struct FilePins(FxHashMap<PackageName, FxHashMap<pep440_rs::Version, Dist>>);
|
||||||
FxHashMap<PackageName, FxHashMap<pep440_rs::Version, (IndexUrl, BaseUrl, File)>>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl FilePins {
|
impl FilePins {
|
||||||
/// Pin a candidate package.
|
/// Pin a candidate package.
|
||||||
pub(crate) fn insert(&mut self, candidate: &Candidate, index: &IndexUrl, base: &BaseUrl) {
|
pub(crate) fn insert(&mut self, candidate: &Candidate) {
|
||||||
self.0.entry(candidate.name().clone()).or_default().insert(
|
self.0.entry(candidate.name().clone()).or_default().insert(
|
||||||
candidate.version().clone().into(),
|
candidate.version().clone().into(),
|
||||||
(
|
candidate.install().dist.clone(),
|
||||||
index.clone(),
|
|
||||||
base.clone(),
|
|
||||||
candidate.install().clone().into(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the pinned file for the given package name and version, if it exists.
|
/// Return the pinned file for the given package name and version, if it exists.
|
||||||
pub(crate) fn get(
|
pub(crate) fn get(&self, name: &PackageName, version: &pep440_rs::Version) -> Option<&Dist> {
|
||||||
&self,
|
|
||||||
name: &PackageName,
|
|
||||||
version: &pep440_rs::Version,
|
|
||||||
) -> Option<&(IndexUrl, BaseUrl, File)> {
|
|
||||||
self.0.get(name)?.get(version)
|
self.0.get(name)?.get(version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,12 +55,10 @@ impl ResolutionGraph {
|
||||||
match package {
|
match package {
|
||||||
PubGrubPackage::Package(package_name, None, None) => {
|
PubGrubPackage::Package(package_name, None, None) => {
|
||||||
let version = Version::from(version.clone());
|
let version = Version::from(version.clone());
|
||||||
let (index, base, file) = pins
|
let pinned_package = pins
|
||||||
.get(package_name, &version)
|
.get(package_name, &version)
|
||||||
.expect("Every package should be pinned")
|
.expect("Every package should be pinned")
|
||||||
.clone();
|
.clone();
|
||||||
let pinned_package =
|
|
||||||
Dist::from_registry(package_name.clone(), version, file, index, base);
|
|
||||||
|
|
||||||
let index = petgraph.add_node(pinned_package);
|
let index = petgraph.add_node(pinned_package);
|
||||||
inverse.insert(package_name, index);
|
inverse.insert(package_name, index);
|
||||||
|
|
@ -89,12 +87,10 @@ impl ResolutionGraph {
|
||||||
|
|
||||||
if !metadata.provides_extras.contains(extra) {
|
if !metadata.provides_extras.contains(extra) {
|
||||||
let version = Version::from(version.clone());
|
let version = Version::from(version.clone());
|
||||||
let (index, base, file) = pins
|
let pinned_package = pins
|
||||||
.get(package_name, &version)
|
.get(package_name, &version)
|
||||||
.expect("Every package should be pinned")
|
.expect("Every package should be pinned")
|
||||||
.clone();
|
.clone();
|
||||||
let pinned_package =
|
|
||||||
Dist::from_registry(package_name.clone(), version, file, index, base);
|
|
||||||
|
|
||||||
diagnostics.push(Diagnostic::MissingExtra {
|
diagnostics.push(Diagnostic::MissingExtra {
|
||||||
dist: pinned_package,
|
dist: pinned_package,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use distribution_types::{IndexUrl, PackageId};
|
use distribution_types::PackageId;
|
||||||
use pep440_rs::VersionSpecifiers;
|
use pep440_rs::VersionSpecifiers;
|
||||||
use puffin_normalize::PackageName;
|
use puffin_normalize::PackageName;
|
||||||
use puffin_traits::OnceMap;
|
use puffin_traits::OnceMap;
|
||||||
use pypi_types::{BaseUrl, Metadata21};
|
use pypi_types::Metadata21;
|
||||||
|
|
||||||
use crate::version_map::VersionMap;
|
use crate::version_map::VersionMap;
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@ use crate::version_map::VersionMap;
|
||||||
pub(crate) struct Index {
|
pub(crate) struct Index {
|
||||||
/// A map from package name to the metadata for that package and the index where the metadata
|
/// A map from package name to the metadata for that package and the index where the metadata
|
||||||
/// came from.
|
/// came from.
|
||||||
pub(crate) packages: OnceMap<PackageName, (IndexUrl, BaseUrl, VersionMap)>,
|
pub(crate) packages: OnceMap<PackageName, VersionMap>,
|
||||||
|
|
||||||
/// A map from package ID to metadata for that distribution.
|
/// A map from package ID to metadata for that distribution.
|
||||||
pub(crate) distributions: OnceMap<PackageId, Metadata21>,
|
pub(crate) distributions: OnceMap<PackageId, Metadata21>,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@ use url::Url;
|
||||||
|
|
||||||
use distribution_filename::WheelFilename;
|
use distribution_filename::WheelFilename;
|
||||||
use distribution_types::{
|
use distribution_types::{
|
||||||
BuiltDist, Dist, DistributionMetadata, IndexUrl, LocalEditable, Name, SourceDist, VersionOrUrl,
|
BuiltDist, Dist, DistributionMetadata, LocalEditable, Name, RemoteSource, SourceDist,
|
||||||
|
VersionOrUrl,
|
||||||
};
|
};
|
||||||
use pep508_rs::{MarkerEnvironment, Requirement};
|
use pep508_rs::{MarkerEnvironment, Requirement};
|
||||||
use platform_tags::Tags;
|
use platform_tags::Tags;
|
||||||
|
|
@ -26,7 +27,7 @@ use puffin_distribution::DistributionDatabase;
|
||||||
use puffin_interpreter::Interpreter;
|
use puffin_interpreter::Interpreter;
|
||||||
use puffin_normalize::PackageName;
|
use puffin_normalize::PackageName;
|
||||||
use puffin_traits::BuildContext;
|
use puffin_traits::BuildContext;
|
||||||
use pypi_types::{BaseUrl, Metadata21};
|
use pypi_types::Metadata21;
|
||||||
|
|
||||||
use crate::candidate_selector::CandidateSelector;
|
use crate::candidate_selector::CandidateSelector;
|
||||||
use crate::error::ResolveError;
|
use crate::error::ResolveError;
|
||||||
|
|
@ -472,7 +473,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
|
||||||
PubGrubPackage::Package(package_name, extra, None) => {
|
PubGrubPackage::Package(package_name, extra, None) => {
|
||||||
// Wait for the metadata to be available.
|
// Wait for the metadata to be available.
|
||||||
let entry = self.index.packages.wait(package_name).await;
|
let entry = self.index.packages.wait(package_name).await;
|
||||||
let (index, base, version_map) = entry.value();
|
let version_map = entry.value();
|
||||||
|
|
||||||
if let Some(extra) = extra {
|
if let Some(extra) = extra {
|
||||||
debug!(
|
debug!(
|
||||||
|
|
@ -502,20 +503,28 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
|
||||||
candidate.name(),
|
candidate.name(),
|
||||||
extra,
|
extra,
|
||||||
candidate.version(),
|
candidate.version(),
|
||||||
candidate.resolve().filename()
|
candidate
|
||||||
|
.resolve()
|
||||||
|
.dist
|
||||||
|
.filename()
|
||||||
|
.unwrap_or("unknown filename")
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!(
|
||||||
"Selecting: {}=={} ({})",
|
"Selecting: {}=={} ({})",
|
||||||
candidate.name(),
|
candidate.name(),
|
||||||
candidate.version(),
|
candidate.version(),
|
||||||
candidate.resolve().filename()
|
candidate
|
||||||
|
.resolve()
|
||||||
|
.dist
|
||||||
|
.filename()
|
||||||
|
.unwrap_or("unknown filename")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want to return a package pinned to a specific version; but we _also_ want to
|
// We want to return a package pinned to a specific version; but we _also_ want to
|
||||||
// store the exact file that we selected to satisfy that version.
|
// store the exact file that we selected to satisfy that version.
|
||||||
pins.insert(&candidate, index, base);
|
pins.insert(&candidate);
|
||||||
|
|
||||||
let version = candidate.version().clone();
|
let version = candidate.version().clone();
|
||||||
|
|
||||||
|
|
@ -525,7 +534,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
|
||||||
.distributions
|
.distributions
|
||||||
.register_owned(candidate.package_id())
|
.register_owned(candidate.package_id())
|
||||||
{
|
{
|
||||||
let distribution = candidate.into_distribution(index.clone(), base.clone());
|
let distribution = candidate.resolve().dist.clone();
|
||||||
request_sink.unbounded_send(Request::Dist(distribution))?;
|
request_sink.unbounded_send(Request::Dist(distribution))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -670,11 +679,9 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
|
||||||
|
|
||||||
while let Some(response) = response_stream.next().await {
|
while let Some(response) = response_stream.next().await {
|
||||||
match response? {
|
match response? {
|
||||||
Some(Response::Package(package_name, index, base, version_map)) => {
|
Some(Response::Package(package_name, version_map)) => {
|
||||||
trace!("Received package metadata for: {package_name}");
|
trace!("Received package metadata for: {package_name}");
|
||||||
self.index
|
self.index.packages.done(package_name, version_map);
|
||||||
.packages
|
|
||||||
.done(package_name, (index, base, version_map));
|
|
||||||
}
|
}
|
||||||
Some(Response::Dist(Dist::Built(distribution), metadata, ..)) => {
|
Some(Response::Dist(Dist::Built(distribution), metadata, ..)) => {
|
||||||
trace!("Received built distribution metadata for: {distribution}");
|
trace!("Received built distribution metadata for: {distribution}");
|
||||||
|
|
@ -713,12 +720,12 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
|
||||||
match request {
|
match request {
|
||||||
// Fetch package metadata from the registry.
|
// Fetch package metadata from the registry.
|
||||||
Request::Package(package_name) => {
|
Request::Package(package_name) => {
|
||||||
let (index, base, metadata) = self
|
let version_map = self
|
||||||
.provider
|
.provider
|
||||||
.get_version_map(&package_name)
|
.get_version_map(&package_name)
|
||||||
.await
|
.await
|
||||||
.map_err(ResolveError::Client)?;
|
.map_err(ResolveError::Client)?;
|
||||||
Ok(Some(Response::Package(package_name, index, base, metadata)))
|
Ok(Some(Response::Package(package_name, version_map)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch distribution metadata from the distribution database.
|
// Fetch distribution metadata from the distribution database.
|
||||||
|
|
@ -746,7 +753,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
|
||||||
Request::Prefetch(package_name, range) => {
|
Request::Prefetch(package_name, range) => {
|
||||||
// Wait for the package metadata to become available.
|
// Wait for the package metadata to become available.
|
||||||
let entry = self.index.packages.wait(&package_name).await;
|
let entry = self.index.packages.wait(&package_name).await;
|
||||||
let (index, base, version_map) = entry.value();
|
let version_map = entry.value();
|
||||||
|
|
||||||
// Try to find a compatible version. If there aren't any compatible versions,
|
// Try to find a compatible version. If there aren't any compatible versions,
|
||||||
// short-circuit and return `None`.
|
// short-circuit and return `None`.
|
||||||
|
|
@ -769,7 +776,7 @@ impl<'a, Provider: ResolverProvider> Resolver<'a, Provider> {
|
||||||
.distributions
|
.distributions
|
||||||
.register_owned(candidate.package_id())
|
.register_owned(candidate.package_id())
|
||||||
{
|
{
|
||||||
let dist = candidate.into_distribution(index.clone(), base.clone());
|
let dist = candidate.resolve().dist.clone();
|
||||||
drop(entry);
|
drop(entry);
|
||||||
|
|
||||||
let (metadata, precise) = self
|
let (metadata, precise) = self
|
||||||
|
|
@ -837,7 +844,7 @@ enum Request {
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
enum Response {
|
enum Response {
|
||||||
/// The returned metadata for a package hosted on a registry.
|
/// The returned metadata for a package hosted on a registry.
|
||||||
Package(PackageName, IndexUrl, BaseUrl, VersionMap),
|
Package(PackageName, VersionMap),
|
||||||
/// The returned metadata for a distribution.
|
/// The returned metadata for a distribution.
|
||||||
Dist(Dist, Metadata21, Option<Url>),
|
Dist(Dist, Metadata21, Option<Url>),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,19 @@ use chrono::{DateTime, Utc};
|
||||||
use futures::TryFutureExt;
|
use futures::TryFutureExt;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use distribution_types::{Dist, IndexUrl};
|
use distribution_types::Dist;
|
||||||
use platform_tags::Tags;
|
use platform_tags::Tags;
|
||||||
use puffin_client::RegistryClient;
|
use puffin_client::RegistryClient;
|
||||||
use puffin_distribution::{DistributionDatabase, DistributionDatabaseError};
|
use puffin_distribution::{DistributionDatabase, DistributionDatabaseError};
|
||||||
use puffin_normalize::PackageName;
|
use puffin_normalize::PackageName;
|
||||||
use puffin_traits::BuildContext;
|
use puffin_traits::BuildContext;
|
||||||
use pypi_types::{BaseUrl, Metadata21};
|
use pypi_types::Metadata21;
|
||||||
|
|
||||||
use crate::python_requirement::PythonRequirement;
|
use crate::python_requirement::PythonRequirement;
|
||||||
use crate::version_map::VersionMap;
|
use crate::version_map::VersionMap;
|
||||||
use crate::yanks::AllowedYanks;
|
use crate::yanks::AllowedYanks;
|
||||||
|
|
||||||
type VersionMapResponse = Result<(IndexUrl, BaseUrl, VersionMap), puffin_client::Error>;
|
type VersionMapResponse = Result<VersionMap, puffin_client::Error>;
|
||||||
type WheelMetadataResponse = Result<(Metadata21, Option<Url>), DistributionDatabaseError>;
|
type WheelMetadataResponse = Result<(Metadata21, Option<Url>), DistributionDatabaseError>;
|
||||||
|
|
||||||
pub trait ResolverProvider: Send + Sync {
|
pub trait ResolverProvider: Send + Sync {
|
||||||
|
|
@ -83,17 +83,15 @@ impl<'a, Context: BuildContext + Send + Sync> ResolverProvider
|
||||||
self.client
|
self.client
|
||||||
.simple(package_name)
|
.simple(package_name)
|
||||||
.map_ok(move |(index, base, metadata)| {
|
.map_ok(move |(index, base, metadata)| {
|
||||||
(
|
VersionMap::from_metadata(
|
||||||
index,
|
metadata,
|
||||||
base,
|
package_name,
|
||||||
VersionMap::from_metadata(
|
&index,
|
||||||
metadata,
|
&base,
|
||||||
package_name,
|
self.tags,
|
||||||
self.tags,
|
&self.python_requirement,
|
||||||
&self.python_requirement,
|
&self.allowed_yanks,
|
||||||
&self.allowed_yanks,
|
self.exclude_newer.as_ref(),
|
||||||
self.exclude_newer.as_ref(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@ use chrono::{DateTime, Utc};
|
||||||
use tracing::{instrument, warn};
|
use tracing::{instrument, warn};
|
||||||
|
|
||||||
use distribution_filename::DistFilename;
|
use distribution_filename::DistFilename;
|
||||||
|
use distribution_types::{Dist, IndexUrl};
|
||||||
|
use pep440_rs::VersionSpecifiers;
|
||||||
use platform_tags::{TagPriority, Tags};
|
use platform_tags::{TagPriority, Tags};
|
||||||
use puffin_client::SimpleMetadata;
|
use puffin_client::SimpleMetadata;
|
||||||
use puffin_normalize::PackageName;
|
use puffin_normalize::PackageName;
|
||||||
use puffin_warnings::warn_user_once;
|
use puffin_warnings::warn_user_once;
|
||||||
use pypi_types::Yanked;
|
use pypi_types::{BaseUrl, Yanked};
|
||||||
|
|
||||||
use crate::file::{DistFile, SdistFile, WheelFile};
|
|
||||||
use crate::pubgrub::PubGrubVersion;
|
use crate::pubgrub::PubGrubVersion;
|
||||||
use crate::python_requirement::PythonRequirement;
|
use crate::python_requirement::PythonRequirement;
|
||||||
use crate::yanks::AllowedYanks;
|
use crate::yanks::AllowedYanks;
|
||||||
|
|
@ -23,9 +24,12 @@ pub struct VersionMap(BTreeMap<PubGrubVersion, PrioritizedDistribution>);
|
||||||
impl VersionMap {
|
impl VersionMap {
|
||||||
/// Initialize a [`VersionMap`] from the given metadata.
|
/// Initialize a [`VersionMap`] from the given metadata.
|
||||||
#[instrument(skip_all, fields(package_name = % package_name))]
|
#[instrument(skip_all, fields(package_name = % package_name))]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) fn from_metadata(
|
pub(crate) fn from_metadata(
|
||||||
metadata: SimpleMetadata,
|
metadata: SimpleMetadata,
|
||||||
package_name: &PackageName,
|
package_name: &PackageName,
|
||||||
|
index: &IndexUrl,
|
||||||
|
base: &BaseUrl,
|
||||||
tags: &Tags,
|
tags: &Tags,
|
||||||
python_requirement: &PythonRequirement,
|
python_requirement: &PythonRequirement,
|
||||||
allowed_yanks: &AllowedYanks,
|
allowed_yanks: &AllowedYanks,
|
||||||
|
|
@ -65,6 +69,7 @@ impl VersionMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let requires_python = file.requires_python.clone();
|
||||||
match filename {
|
match filename {
|
||||||
DistFilename::WheelFilename(filename) => {
|
DistFilename::WheelFilename(filename) => {
|
||||||
// To be compatible, the wheel must both have compatible tags _and_ have a
|
// To be compatible, the wheel must both have compatible tags _and_ have a
|
||||||
|
|
@ -78,25 +83,45 @@ impl VersionMap {
|
||||||
.all(|version| requires_python.contains(version))
|
.all(|version| requires_python.contains(version))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
let dist = Dist::from_registry(
|
||||||
|
filename.name.clone(),
|
||||||
|
filename.version.clone(),
|
||||||
|
file,
|
||||||
|
index.clone(),
|
||||||
|
base.clone(),
|
||||||
|
);
|
||||||
match version_map.entry(version.clone().into()) {
|
match version_map.entry(version.clone().into()) {
|
||||||
Entry::Occupied(mut entry) => {
|
Entry::Occupied(mut entry) => {
|
||||||
entry.get_mut().insert_built(WheelFile(file), priority);
|
entry
|
||||||
|
.get_mut()
|
||||||
|
.insert_built(dist, requires_python, priority);
|
||||||
}
|
}
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
entry.insert(PrioritizedDistribution::from_built(
|
entry.insert(PrioritizedDistribution::from_built(
|
||||||
WheelFile(file),
|
dist,
|
||||||
|
requires_python,
|
||||||
priority,
|
priority,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DistFilename::SourceDistFilename(_) => {
|
DistFilename::SourceDistFilename(filename) => {
|
||||||
|
let dist = Dist::from_registry(
|
||||||
|
filename.name.clone(),
|
||||||
|
filename.version.clone(),
|
||||||
|
file,
|
||||||
|
index.clone(),
|
||||||
|
base.clone(),
|
||||||
|
);
|
||||||
match version_map.entry(version.clone().into()) {
|
match version_map.entry(version.clone().into()) {
|
||||||
Entry::Occupied(mut entry) => {
|
Entry::Occupied(mut entry) => {
|
||||||
entry.get_mut().insert_source(SdistFile(file));
|
entry.get_mut().insert_source(dist, requires_python);
|
||||||
}
|
}
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
entry.insert(PrioritizedDistribution::from_source(SdistFile(file)));
|
entry.insert(PrioritizedDistribution::from_source(
|
||||||
|
dist,
|
||||||
|
requires_python,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -122,63 +147,111 @@ impl VersionMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attach its requires-python to a [`Dist`], since downstream needs this information to filter
|
||||||
|
/// [`PrioritizedDistribution`].
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct DistRequiresPython {
|
||||||
|
pub(crate) dist: Dist,
|
||||||
|
pub(crate) requires_python: Option<VersionSpecifiers>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PrioritizedDistribution {
|
struct PrioritizedDistribution {
|
||||||
/// An arbitrary source distribution for the package version.
|
/// An arbitrary source distribution for the package version.
|
||||||
source: Option<DistFile>,
|
source: Option<DistRequiresPython>,
|
||||||
/// The highest-priority, platform-compatible wheel for the package version.
|
/// The highest-priority, platform-compatible wheel for the package version.
|
||||||
compatible_wheel: Option<(DistFile, TagPriority)>,
|
compatible_wheel: Option<(DistRequiresPython, TagPriority)>,
|
||||||
/// An arbitrary, platform-incompatible wheel for the package version.
|
/// An arbitrary, platform-incompatible wheel for the package version.
|
||||||
incompatible_wheel: Option<DistFile>,
|
incompatible_wheel: Option<DistRequiresPython>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrioritizedDistribution {
|
impl PrioritizedDistribution {
|
||||||
/// Create a new [`PrioritizedDistribution`] from the given wheel distribution.
|
/// Create a new [`PrioritizedDistribution`] from the given wheel distribution.
|
||||||
fn from_built(dist: WheelFile, priority: Option<TagPriority>) -> Self {
|
fn from_built(
|
||||||
|
dist: Dist,
|
||||||
|
requires_python: Option<VersionSpecifiers>,
|
||||||
|
priority: Option<TagPriority>,
|
||||||
|
) -> Self {
|
||||||
if let Some(priority) = priority {
|
if let Some(priority) = priority {
|
||||||
Self {
|
Self {
|
||||||
source: None,
|
source: None,
|
||||||
compatible_wheel: Some((dist.into(), priority)),
|
compatible_wheel: Some((
|
||||||
|
DistRequiresPython {
|
||||||
|
dist,
|
||||||
|
|
||||||
|
requires_python,
|
||||||
|
},
|
||||||
|
priority,
|
||||||
|
)),
|
||||||
incompatible_wheel: None,
|
incompatible_wheel: None,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Self {
|
Self {
|
||||||
source: None,
|
source: None,
|
||||||
compatible_wheel: None,
|
compatible_wheel: None,
|
||||||
incompatible_wheel: Some(dist.into()),
|
incompatible_wheel: Some(DistRequiresPython {
|
||||||
|
dist,
|
||||||
|
requires_python,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new [`PrioritizedDistribution`] from the given source distribution.
|
/// Create a new [`PrioritizedDistribution`] from the given source distribution.
|
||||||
fn from_source(dist: SdistFile) -> Self {
|
fn from_source(dist: Dist, requires_python: Option<VersionSpecifiers>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
source: Some(dist.into()),
|
source: Some(DistRequiresPython {
|
||||||
|
dist,
|
||||||
|
requires_python,
|
||||||
|
}),
|
||||||
compatible_wheel: None,
|
compatible_wheel: None,
|
||||||
incompatible_wheel: None,
|
incompatible_wheel: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert the given built distribution into the [`PrioritizedDistribution`].
|
/// Insert the given built distribution into the [`PrioritizedDistribution`].
|
||||||
fn insert_built(&mut self, file: WheelFile, priority: Option<TagPriority>) {
|
fn insert_built(
|
||||||
|
&mut self,
|
||||||
|
dist: Dist,
|
||||||
|
requires_python: Option<VersionSpecifiers>,
|
||||||
|
priority: Option<TagPriority>,
|
||||||
|
) {
|
||||||
// Prefer the highest-priority, platform-compatible wheel.
|
// Prefer the highest-priority, platform-compatible wheel.
|
||||||
if let Some(priority) = priority {
|
if let Some(priority) = priority {
|
||||||
if let Some((.., existing_priority)) = &self.compatible_wheel {
|
if let Some((.., existing_priority)) = &self.compatible_wheel {
|
||||||
if priority > *existing_priority {
|
if priority > *existing_priority {
|
||||||
self.compatible_wheel = Some((file.into(), priority));
|
self.compatible_wheel = Some((
|
||||||
|
DistRequiresPython {
|
||||||
|
dist,
|
||||||
|
requires_python,
|
||||||
|
},
|
||||||
|
priority,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.compatible_wheel = Some((file.into(), priority));
|
self.compatible_wheel = Some((
|
||||||
|
DistRequiresPython {
|
||||||
|
dist,
|
||||||
|
requires_python,
|
||||||
|
},
|
||||||
|
priority,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} else if self.incompatible_wheel.is_none() {
|
} else if self.incompatible_wheel.is_none() {
|
||||||
self.incompatible_wheel = Some(file.into());
|
self.incompatible_wheel = Some(DistRequiresPython {
|
||||||
|
dist,
|
||||||
|
requires_python,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert the given source distribution into the [`PrioritizedDistribution`].
|
/// Insert the given source distribution into the [`PrioritizedDistribution`].
|
||||||
fn insert_source(&mut self, file: SdistFile) {
|
fn insert_source(&mut self, dist: Dist, requires_python: Option<VersionSpecifiers>) {
|
||||||
if self.source.is_none() {
|
if self.source.is_none() {
|
||||||
self.source = Some(file.into());
|
self.source = Some(DistRequiresPython {
|
||||||
|
dist,
|
||||||
|
requires_python,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,9 +268,11 @@ impl PrioritizedDistribution {
|
||||||
// wheel. We assume that all distributions have the same metadata for a given package
|
// wheel. We assume that all distributions have the same metadata for a given package
|
||||||
// version. If a compatible source distribution exists, we assume we can build it, but
|
// version. If a compatible source distribution exists, we assume we can build it, but
|
||||||
// using the wheel is faster.
|
// using the wheel is faster.
|
||||||
(_, Some(sdist), Some(wheel)) => Some(ResolvableFile::IncompatibleWheel(sdist, wheel)),
|
(_, Some(source_dist), Some(wheel)) => {
|
||||||
|
Some(ResolvableFile::IncompatibleWheel(source_dist, wheel))
|
||||||
|
}
|
||||||
// Otherwise, if we have a source distribution, return it.
|
// Otherwise, if we have a source distribution, return it.
|
||||||
(_, Some(sdist), _) => Some(ResolvableFile::SourceDist(sdist)),
|
(_, Some(source_dist), _) => Some(ResolvableFile::SourceDist(source_dist)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -206,18 +281,18 @@ impl PrioritizedDistribution {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) enum ResolvableFile<'a> {
|
pub(crate) enum ResolvableFile<'a> {
|
||||||
/// The distribution should be resolved and installed using a source distribution.
|
/// The distribution should be resolved and installed using a source distribution.
|
||||||
SourceDist(&'a DistFile),
|
SourceDist(&'a DistRequiresPython),
|
||||||
/// The distribution should be resolved and installed using a wheel distribution.
|
/// The distribution should be resolved and installed using a wheel distribution.
|
||||||
CompatibleWheel(&'a DistFile),
|
CompatibleWheel(&'a DistRequiresPython),
|
||||||
/// The distribution should be resolved using an incompatible wheel distribution, but
|
/// The distribution should be resolved using an incompatible wheel distribution, but
|
||||||
/// installed using a source distribution.
|
/// installed using a source distribution.
|
||||||
IncompatibleWheel(&'a DistFile, &'a DistFile),
|
IncompatibleWheel(&'a DistRequiresPython, &'a DistRequiresPython),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ResolvableFile<'a> {
|
impl<'a> ResolvableFile<'a> {
|
||||||
/// Return the [`DistFile`] to use during resolution.
|
/// Return the [`DistFile`] to use during resolution.
|
||||||
pub(crate) fn resolve(&self) -> &DistFile {
|
pub(crate) fn resolve(&self) -> &DistRequiresPython {
|
||||||
match self {
|
match *self {
|
||||||
ResolvableFile::SourceDist(sdist) => sdist,
|
ResolvableFile::SourceDist(sdist) => sdist,
|
||||||
ResolvableFile::CompatibleWheel(wheel) => wheel,
|
ResolvableFile::CompatibleWheel(wheel) => wheel,
|
||||||
ResolvableFile::IncompatibleWheel(_, wheel) => wheel,
|
ResolvableFile::IncompatibleWheel(_, wheel) => wheel,
|
||||||
|
|
@ -225,8 +300,8 @@ impl<'a> ResolvableFile<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the [`DistFile`] to use during installation.
|
/// Return the [`DistFile`] to use during installation.
|
||||||
pub(crate) fn install(&self) -> &DistFile {
|
pub(crate) fn install(&self) -> &DistRequiresPython {
|
||||||
match self {
|
match *self {
|
||||||
ResolvableFile::SourceDist(sdist) => sdist,
|
ResolvableFile::SourceDist(sdist) => sdist,
|
||||||
ResolvableFile::CompatibleWheel(wheel) => wheel,
|
ResolvableFile::CompatibleWheel(wheel) => wheel,
|
||||||
ResolvableFile::IncompatibleWheel(sdist, _) => sdist,
|
ResolvableFile::IncompatibleWheel(sdist, _) => sdist,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue