mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-26 12:09:12 +00:00
Only respect preferences across the same indexes (#9302)
Some checks are pending
CI / build binary | linux (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on linux (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on opensuse (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86_64 (push) Blocked by required conditions
CI / check system | python3.10 on windows (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on linux (push) Blocked by required conditions
CI / check system | conda3.8 on linux (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / check system | conda3.11 on macos (push) Blocked by required conditions
CI / check system | conda3.8 on macos (push) Blocked by required conditions
CI / check system | conda3.11 on windows (push) Blocked by required conditions
CI / check system | conda3.8 on windows (push) Blocked by required conditions
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / check system | embedded python3.10 on windows (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
Some checks are pending
CI / build binary | linux (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on linux (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on opensuse (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86_64 (push) Blocked by required conditions
CI / check system | python3.10 on windows (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on linux (push) Blocked by required conditions
CI / check system | conda3.8 on linux (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / check system | conda3.11 on macos (push) Blocked by required conditions
CI / check system | conda3.8 on macos (push) Blocked by required conditions
CI / check system | conda3.11 on windows (push) Blocked by required conditions
CI / check system | conda3.8 on windows (push) Blocked by required conditions
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / check system | embedded python3.10 on windows (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
## Summary The issue here is fairly complex. Consider the following: ```toml [project] name = "project" version = "0.1.0" requires-python = ">=3.12.0" dependencies = [] [project.optional-dependencies] cpu = [ "torch>=2.5.1", "torchvision>=0.20.1", ] cu124 = [ "torch>=2.5.1", "torchvision>=0.20.1", ] [tool.uv] conflicts = [ [ { extra = "cpu" }, { extra = "cu124" }, ], ] [tool.uv.sources] torch = [ { index = "pytorch-cpu", extra = "cpu", marker = "platform_system != 'Darwin'" }, ] torchvision = [ { index = "pytorch-cpu", extra = "cpu", marker = "platform_system != 'Darwin'" }, ] [[tool.uv.index]] name = "pytorch-cpu" url = "https://download.pytorch.org/whl/cpu" explicit = true ``` When solving this project, we first pick a PyTorch version from PyPI, to solve the `cu124` extra, selecting `2.5.1`. Later, we try to solve the `cpu` extra. In solving that extra, we look at the PyTorch CPU index. Ideally, we'd select `2.5.1+cpu`... But `2.5.1` is already a preference. So we choose that. Now, we only respect preferences for explicit indexes if they came from the same index. Closes https://github.com/astral-sh/uv/issues/9295.
This commit is contained in:
parent
c6482dd038
commit
5e48819dbb
9 changed files with 129 additions and 64 deletions
|
@ -7,7 +7,7 @@ use uv_configuration::Upgrade;
|
||||||
use uv_fs::CWD;
|
use uv_fs::CWD;
|
||||||
use uv_git::ResolvedRepositoryReference;
|
use uv_git::ResolvedRepositoryReference;
|
||||||
use uv_requirements_txt::RequirementsTxt;
|
use uv_requirements_txt::RequirementsTxt;
|
||||||
use uv_resolver::{Lock, Preference, PreferenceError};
|
use uv_resolver::{Lock, LockError, Preference, PreferenceError};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct LockedRequirements {
|
pub struct LockedRequirements {
|
||||||
|
@ -63,7 +63,11 @@ pub async fn read_requirements_txt(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load the preferred requirements from an existing lockfile, applying the upgrade strategy.
|
/// Load the preferred requirements from an existing lockfile, applying the upgrade strategy.
|
||||||
pub fn read_lock_requirements(lock: &Lock, upgrade: &Upgrade) -> LockedRequirements {
|
pub fn read_lock_requirements(
|
||||||
|
lock: &Lock,
|
||||||
|
install_path: &Path,
|
||||||
|
upgrade: &Upgrade,
|
||||||
|
) -> Result<LockedRequirements, LockError> {
|
||||||
let mut preferences = Vec::new();
|
let mut preferences = Vec::new();
|
||||||
let mut git = Vec::new();
|
let mut git = Vec::new();
|
||||||
|
|
||||||
|
@ -74,7 +78,7 @@ pub fn read_lock_requirements(lock: &Lock, upgrade: &Upgrade) -> LockedRequireme
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map each entry in the lockfile to a preference.
|
// Map each entry in the lockfile to a preference.
|
||||||
preferences.push(Preference::from_lock(package));
|
preferences.push(Preference::from_lock(package, install_path)?);
|
||||||
|
|
||||||
// Map each entry in the lockfile to a Git SHA.
|
// Map each entry in the lockfile to a Git SHA.
|
||||||
if let Some(git_ref) = package.as_git_ref() {
|
if let Some(git_ref) = package.as_git_ref() {
|
||||||
|
@ -82,5 +86,5 @@ pub fn read_lock_requirements(lock: &Lock, upgrade: &Upgrade) -> LockedRequireme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LockedRequirements { preferences, git }
|
Ok(LockedRequirements { preferences, git })
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::fmt::{Display, Formatter};
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
use uv_configuration::IndexStrategy;
|
use uv_configuration::IndexStrategy;
|
||||||
use uv_distribution_types::{CompatibleDist, IncompatibleDist, IncompatibleSource};
|
use uv_distribution_types::{CompatibleDist, IncompatibleDist, IncompatibleSource, IndexUrl};
|
||||||
use uv_distribution_types::{DistributionMetadata, IncompatibleWheel, Name, PrioritizedDist};
|
use uv_distribution_types::{DistributionMetadata, IncompatibleWheel, Name, PrioritizedDist};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_pep440::Version;
|
use uv_pep440::Version;
|
||||||
|
@ -80,11 +80,12 @@ impl CandidateSelector {
|
||||||
preferences: &'a Preferences,
|
preferences: &'a Preferences,
|
||||||
installed_packages: &'a InstalledPackages,
|
installed_packages: &'a InstalledPackages,
|
||||||
exclusions: &'a Exclusions,
|
exclusions: &'a Exclusions,
|
||||||
|
index: Option<&'a IndexUrl>,
|
||||||
env: &ResolverEnvironment,
|
env: &ResolverEnvironment,
|
||||||
) -> Option<Candidate<'a>> {
|
) -> Option<Candidate<'a>> {
|
||||||
let is_excluded = exclusions.contains(package_name);
|
let is_excluded = exclusions.contains(package_name);
|
||||||
|
|
||||||
// Check for a preference from a lockfile or a previous fork that satisfies the range and
|
// Check for a preference from a lockfile or a previous fork that satisfies the range and
|
||||||
// is allowed.
|
// is allowed.
|
||||||
if let Some(preferred) = self.get_preferred(
|
if let Some(preferred) = self.get_preferred(
|
||||||
package_name,
|
package_name,
|
||||||
|
@ -93,6 +94,7 @@ impl CandidateSelector {
|
||||||
preferences,
|
preferences,
|
||||||
installed_packages,
|
installed_packages,
|
||||||
is_excluded,
|
is_excluded,
|
||||||
|
index,
|
||||||
env,
|
env,
|
||||||
) {
|
) {
|
||||||
trace!("Using preference {} {}", preferred.name, preferred.version);
|
trace!("Using preference {} {}", preferred.name, preferred.version);
|
||||||
|
@ -131,23 +133,39 @@ impl CandidateSelector {
|
||||||
preferences: &'a Preferences,
|
preferences: &'a Preferences,
|
||||||
installed_packages: &'a InstalledPackages,
|
installed_packages: &'a InstalledPackages,
|
||||||
is_excluded: bool,
|
is_excluded: bool,
|
||||||
|
index: Option<&'a IndexUrl>,
|
||||||
env: &ResolverEnvironment,
|
env: &ResolverEnvironment,
|
||||||
) -> Option<Candidate> {
|
) -> Option<Candidate> {
|
||||||
// In the branches, we "sort" the preferences by marker-matching through an iterator that
|
// In the branches, we "sort" the preferences by marker-matching through an iterator that
|
||||||
// first has the matching half and then the mismatching half.
|
// first has the matching half and then the mismatching half.
|
||||||
let preferences_match = preferences.get(package_name).filter(|(marker, _version)| {
|
let preferences_match =
|
||||||
// `.unwrap_or(true)` because the universal marker is considered matching.
|
preferences
|
||||||
marker
|
.get(package_name)
|
||||||
.map(|marker| env.included_by_marker(marker))
|
.filter(|(marker, _index, _version)| {
|
||||||
.unwrap_or(true)
|
// `.unwrap_or(true)` because the universal marker is considered matching.
|
||||||
});
|
marker
|
||||||
let preferences_mismatch = preferences.get(package_name).filter(|(marker, _version)| {
|
.map(|marker| env.included_by_marker(marker))
|
||||||
marker
|
.unwrap_or(true)
|
||||||
.map(|marker| !env.included_by_marker(marker))
|
});
|
||||||
.unwrap_or(false)
|
let preferences_mismatch =
|
||||||
});
|
preferences
|
||||||
|
.get(package_name)
|
||||||
|
.filter(|(marker, _index, _version)| {
|
||||||
|
marker
|
||||||
|
.map(|marker| !env.included_by_marker(marker))
|
||||||
|
.unwrap_or(false)
|
||||||
|
});
|
||||||
|
let preferences = preferences_match.chain(preferences_mismatch).filter_map(
|
||||||
|
|(marker, source, version)| {
|
||||||
|
// If the package is mapped to an explicit index, only consider preferences that
|
||||||
|
// match the index.
|
||||||
|
index
|
||||||
|
.map_or(true, |index| source == Some(index))
|
||||||
|
.then_some((marker, version))
|
||||||
|
},
|
||||||
|
);
|
||||||
self.get_preferred_from_iter(
|
self.get_preferred_from_iter(
|
||||||
preferences_match.chain(preferences_mismatch),
|
preferences,
|
||||||
package_name,
|
package_name,
|
||||||
range,
|
range,
|
||||||
version_maps,
|
version_maps,
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
use uv_distribution_types::{InstalledDist, InstalledMetadata, InstalledVersion, Name};
|
use uv_distribution_types::{IndexUrl, InstalledDist, InstalledMetadata, InstalledVersion, Name};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_pep440::{Operator, Version};
|
use uv_pep440::{Operator, Version};
|
||||||
use uv_pep508::{MarkerTree, VersionOrUrl};
|
use uv_pep508::{MarkerTree, VersionOrUrl};
|
||||||
use uv_pypi_types::{HashDigest, HashError};
|
use uv_pypi_types::{HashDigest, HashError};
|
||||||
use uv_requirements_txt::{RequirementEntry, RequirementsTxtRequirement};
|
use uv_requirements_txt::{RequirementEntry, RequirementsTxtRequirement};
|
||||||
|
|
||||||
use crate::ResolverEnvironment;
|
use crate::{LockError, ResolverEnvironment};
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum PreferenceError {
|
pub enum PreferenceError {
|
||||||
|
@ -25,6 +26,8 @@ pub struct Preference {
|
||||||
version: Version,
|
version: Version,
|
||||||
/// The markers on the requirement itself (those after the semicolon).
|
/// The markers on the requirement itself (those after the semicolon).
|
||||||
marker: MarkerTree,
|
marker: MarkerTree,
|
||||||
|
/// The index URL of the package, if any.
|
||||||
|
index: Option<IndexUrl>,
|
||||||
/// If coming from a package with diverging versions, the markers of the forks this preference
|
/// If coming from a package with diverging versions, the markers of the forks this preference
|
||||||
/// is part of, otherwise `None`.
|
/// is part of, otherwise `None`.
|
||||||
fork_markers: Vec<MarkerTree>,
|
fork_markers: Vec<MarkerTree>,
|
||||||
|
@ -60,6 +63,7 @@ impl Preference {
|
||||||
marker: requirement.marker,
|
marker: requirement.marker,
|
||||||
// requirements.txt doesn't have fork annotations.
|
// requirements.txt doesn't have fork annotations.
|
||||||
fork_markers: vec![],
|
fork_markers: vec![],
|
||||||
|
index: None,
|
||||||
hashes: entry
|
hashes: entry
|
||||||
.hashes
|
.hashes
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -79,6 +83,7 @@ impl Preference {
|
||||||
name: dist.name().clone(),
|
name: dist.name().clone(),
|
||||||
version: version.clone(),
|
version: version.clone(),
|
||||||
marker: MarkerTree::TRUE,
|
marker: MarkerTree::TRUE,
|
||||||
|
index: None,
|
||||||
// Installed distributions don't have fork annotations.
|
// Installed distributions don't have fork annotations.
|
||||||
fork_markers: vec![],
|
fork_markers: vec![],
|
||||||
hashes: Vec::new(),
|
hashes: Vec::new(),
|
||||||
|
@ -86,14 +91,18 @@ impl Preference {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a [`Preference`] from a locked distribution.
|
/// Create a [`Preference`] from a locked distribution.
|
||||||
pub fn from_lock(package: &crate::lock::Package) -> Self {
|
pub fn from_lock(
|
||||||
Self {
|
package: &crate::lock::Package,
|
||||||
|
install_path: &Path,
|
||||||
|
) -> Result<Self, LockError> {
|
||||||
|
Ok(Self {
|
||||||
name: package.id.name.clone(),
|
name: package.id.name.clone(),
|
||||||
version: package.id.version.clone(),
|
version: package.id.version.clone(),
|
||||||
marker: MarkerTree::TRUE,
|
marker: MarkerTree::TRUE,
|
||||||
|
index: package.index(install_path)?,
|
||||||
fork_markers: package.fork_markers().to_vec(),
|
fork_markers: package.fork_markers().to_vec(),
|
||||||
hashes: Vec::new(),
|
hashes: Vec::new(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the [`PackageName`] of the package for this [`Preference`].
|
/// Return the [`PackageName`] of the package for this [`Preference`].
|
||||||
|
@ -107,6 +116,13 @@ impl Preference {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Entry {
|
||||||
|
marker: Option<MarkerTree>,
|
||||||
|
index: Option<IndexUrl>,
|
||||||
|
pin: Pin,
|
||||||
|
}
|
||||||
|
|
||||||
/// A set of pinned packages that should be preserved during resolution, if possible.
|
/// A set of pinned packages that should be preserved during resolution, if possible.
|
||||||
///
|
///
|
||||||
/// The marker is the marker of the fork that resolved to the pin, if any.
|
/// The marker is the marker of the fork that resolved to the pin, if any.
|
||||||
|
@ -114,15 +130,15 @@ impl Preference {
|
||||||
/// Preferences should be prioritized first by whether their marker matches and then by the order
|
/// Preferences should be prioritized first by whether their marker matches and then by the order
|
||||||
/// they are stored, so that a lockfile has higher precedence than sibling forks.
|
/// they are stored, so that a lockfile has higher precedence than sibling forks.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct Preferences(FxHashMap<PackageName, Vec<(Option<MarkerTree>, Pin)>>);
|
pub struct Preferences(FxHashMap<PackageName, Vec<Entry>>);
|
||||||
|
|
||||||
impl Preferences {
|
impl Preferences {
|
||||||
/// Create a map of pinned packages from an iterator of [`Preference`] entries.
|
/// Create a map of pinned packages from an iterator of [`Preference`] entries.
|
||||||
///
|
///
|
||||||
/// The provided [`ResolverEnvironment`] will be used to filter the preferences
|
/// The provided [`ResolverEnvironment`] will be used to filter the preferences
|
||||||
/// to an applicable subset.
|
/// to an applicable subset.
|
||||||
pub fn from_iter<PreferenceIterator: IntoIterator<Item = Preference>>(
|
pub fn from_iter(
|
||||||
preferences: PreferenceIterator,
|
preferences: impl IntoIterator<Item = Preference>,
|
||||||
env: &ResolverEnvironment,
|
env: &ResolverEnvironment,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut slf = Self::default();
|
let mut slf = Self::default();
|
||||||
|
@ -152,6 +168,7 @@ impl Preferences {
|
||||||
if preference.fork_markers.is_empty() {
|
if preference.fork_markers.is_empty() {
|
||||||
slf.insert(
|
slf.insert(
|
||||||
preference.name,
|
preference.name,
|
||||||
|
preference.index,
|
||||||
None,
|
None,
|
||||||
Pin {
|
Pin {
|
||||||
version: preference.version,
|
version: preference.version,
|
||||||
|
@ -162,6 +179,7 @@ impl Preferences {
|
||||||
for fork_marker in preference.fork_markers {
|
for fork_marker in preference.fork_markers {
|
||||||
slf.insert(
|
slf.insert(
|
||||||
preference.name.clone(),
|
preference.name.clone(),
|
||||||
|
preference.index.clone(),
|
||||||
Some(fork_marker),
|
Some(fork_marker),
|
||||||
Pin {
|
Pin {
|
||||||
version: preference.version.clone(),
|
version: preference.version.clone(),
|
||||||
|
@ -179,13 +197,15 @@ impl Preferences {
|
||||||
pub(crate) fn insert(
|
pub(crate) fn insert(
|
||||||
&mut self,
|
&mut self,
|
||||||
package_name: PackageName,
|
package_name: PackageName,
|
||||||
|
index: Option<IndexUrl>,
|
||||||
markers: Option<MarkerTree>,
|
markers: Option<MarkerTree>,
|
||||||
pin: impl Into<Pin>,
|
pin: impl Into<Pin>,
|
||||||
) {
|
) {
|
||||||
self.0
|
self.0.entry(package_name).or_default().push(Entry {
|
||||||
.entry(package_name)
|
marker: markers,
|
||||||
.or_default()
|
index,
|
||||||
.push((markers, pin.into()));
|
pin: pin.into(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over the preferences.
|
/// Returns an iterator over the preferences.
|
||||||
|
@ -194,15 +214,19 @@ impl Preferences {
|
||||||
) -> impl Iterator<
|
) -> impl Iterator<
|
||||||
Item = (
|
Item = (
|
||||||
&PackageName,
|
&PackageName,
|
||||||
impl Iterator<Item = (Option<&MarkerTree>, &Version)>,
|
impl Iterator<Item = (Option<&MarkerTree>, Option<&IndexUrl>, &Version)>,
|
||||||
),
|
),
|
||||||
> {
|
> {
|
||||||
self.0.iter().map(|(name, preferences)| {
|
self.0.iter().map(|(name, preferences)| {
|
||||||
(
|
(
|
||||||
name,
|
name,
|
||||||
preferences
|
preferences.iter().map(|entry| {
|
||||||
.iter()
|
(
|
||||||
.map(|(markers, pin)| (markers.as_ref(), pin.version())),
|
entry.marker.as_ref(),
|
||||||
|
entry.index.as_ref(),
|
||||||
|
entry.pin.version(),
|
||||||
|
)
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -211,12 +235,14 @@ impl Preferences {
|
||||||
pub(crate) fn get(
|
pub(crate) fn get(
|
||||||
&self,
|
&self,
|
||||||
package_name: &PackageName,
|
package_name: &PackageName,
|
||||||
) -> impl Iterator<Item = (Option<&MarkerTree>, &Version)> {
|
) -> impl Iterator<Item = (Option<&MarkerTree>, Option<&IndexUrl>, &Version)> {
|
||||||
self.0
|
self.0.get(package_name).into_iter().flatten().map(|entry| {
|
||||||
.get(package_name)
|
(
|
||||||
.into_iter()
|
entry.marker.as_ref(),
|
||||||
.flatten()
|
entry.index.as_ref(),
|
||||||
.map(|(markers, pin)| (markers.as_ref(), pin.version()))
|
entry.pin.version(),
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the hashes for a package, if the version matches that of the pin.
|
/// Return the hashes for a package, if the version matches that of the pin.
|
||||||
|
@ -229,8 +255,8 @@ impl Preferences {
|
||||||
.get(package_name)
|
.get(package_name)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.find(|(_markers, pin)| pin.version() == version)
|
.find(|entry| entry.pin.version() == version)
|
||||||
.map(|(_markers, pin)| pin.hashes())
|
.map(|entry| entry.pin.hashes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -381,6 +381,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
||||||
for (package, version) in &resolution.nodes {
|
for (package, version) in &resolution.nodes {
|
||||||
preferences.insert(
|
preferences.insert(
|
||||||
package.name.clone(),
|
package.name.clone(),
|
||||||
|
package.index.clone(),
|
||||||
resolution.env.try_markers().cloned(),
|
resolution.env.try_markers().cloned(),
|
||||||
version.clone(),
|
version.clone(),
|
||||||
);
|
);
|
||||||
|
@ -669,14 +670,15 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
||||||
diverging_packages: &'a [PackageName],
|
diverging_packages: &'a [PackageName],
|
||||||
) -> impl Iterator<Item = Result<ForkState, ResolveError>> + 'a {
|
) -> impl Iterator<Item = Result<ForkState, ResolveError>> + 'a {
|
||||||
debug!(
|
debug!(
|
||||||
"Splitting resolution on {}=={} over {} into {} resolution with separate markers",
|
"Splitting resolution on {}=={} over {} into {} resolution{} with separate markers",
|
||||||
current_state.next,
|
current_state.next,
|
||||||
version,
|
version,
|
||||||
diverging_packages
|
diverging_packages
|
||||||
.iter()
|
.iter()
|
||||||
.map(ToString::to_string)
|
.map(ToString::to_string)
|
||||||
.join(", "),
|
.join(", "),
|
||||||
forks.len()
|
forks.len(),
|
||||||
|
if forks.len() == 1 { "" } else { "s" }
|
||||||
);
|
);
|
||||||
assert!(forks.len() >= 2);
|
assert!(forks.len() >= 2);
|
||||||
// This is a somewhat tortured technique to ensure
|
// This is a somewhat tortured technique to ensure
|
||||||
|
@ -1075,6 +1077,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
||||||
preferences,
|
preferences,
|
||||||
&self.installed_packages,
|
&self.installed_packages,
|
||||||
&self.exclusions,
|
&self.exclusions,
|
||||||
|
index,
|
||||||
env,
|
env,
|
||||||
) else {
|
) else {
|
||||||
// Short circuit: we couldn't find _any_ versions for a package.
|
// Short circuit: we couldn't find _any_ versions for a package.
|
||||||
|
@ -1934,6 +1937,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
||||||
&self.preferences,
|
&self.preferences,
|
||||||
&self.installed_packages,
|
&self.installed_packages,
|
||||||
&self.exclusions,
|
&self.exclusions,
|
||||||
|
None,
|
||||||
&env,
|
&env,
|
||||||
) else {
|
) else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl AllowedYanks {
|
||||||
allowed_yanks
|
allowed_yanks
|
||||||
.entry(name.clone())
|
.entry(name.clone())
|
||||||
.or_default()
|
.or_default()
|
||||||
.extend(preferences.map(|(_markers, version)| version.clone()));
|
.extend(preferences.map(|(.., version)| version.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Self(Arc::new(allowed_yanks))
|
Self(Arc::new(allowed_yanks))
|
||||||
|
|
|
@ -563,7 +563,8 @@ async fn do_lock(
|
||||||
|
|
||||||
// If an existing lockfile exists, build up a set of preferences.
|
// If an existing lockfile exists, build up a set of preferences.
|
||||||
let LockedRequirements { preferences, git } = versions_lock
|
let LockedRequirements { preferences, git } = versions_lock
|
||||||
.map(|lock| read_lock_requirements(lock, upgrade))
|
.map(|lock| read_lock_requirements(lock, workspace.install_path(), upgrade))
|
||||||
|
.transpose()?
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Populate the Git resolver.
|
// Populate the Git resolver.
|
||||||
|
|
|
@ -935,8 +935,8 @@ pub(crate) async fn resolve_names(
|
||||||
pub(crate) struct EnvironmentSpecification<'lock> {
|
pub(crate) struct EnvironmentSpecification<'lock> {
|
||||||
/// The requirements to include in the environment.
|
/// The requirements to include in the environment.
|
||||||
requirements: RequirementsSpecification,
|
requirements: RequirementsSpecification,
|
||||||
/// The lockfile from which to extract preferences.
|
/// The lockfile from which to extract preferences, along with the install path.
|
||||||
lock: Option<&'lock Lock>,
|
lock: Option<(&'lock Lock, &'lock Path)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RequirementsSpecification> for EnvironmentSpecification<'_> {
|
impl From<RequirementsSpecification> for EnvironmentSpecification<'_> {
|
||||||
|
@ -950,7 +950,7 @@ impl From<RequirementsSpecification> for EnvironmentSpecification<'_> {
|
||||||
|
|
||||||
impl<'lock> EnvironmentSpecification<'lock> {
|
impl<'lock> EnvironmentSpecification<'lock> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn with_lock(self, lock: Option<&'lock Lock>) -> Self {
|
pub(crate) fn with_lock(self, lock: Option<(&'lock Lock, &'lock Path)>) -> Self {
|
||||||
Self { lock, ..self }
|
Self { lock, ..self }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1057,7 +1057,8 @@ pub(crate) async fn resolve_environment<'a>(
|
||||||
// If an existing lockfile exists, build up a set of preferences.
|
// If an existing lockfile exists, build up a set of preferences.
|
||||||
let LockedRequirements { preferences, git } = spec
|
let LockedRequirements { preferences, git } = spec
|
||||||
.lock
|
.lock
|
||||||
.map(|lock| read_lock_requirements(lock, &upgrade))
|
.map(|(lock, install_path)| read_lock_requirements(lock, install_path, &upgrade))
|
||||||
|
.transpose()?
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Populate the Git resolver.
|
// Populate the Git resolver.
|
||||||
|
|
|
@ -372,7 +372,7 @@ pub(crate) async fn run(
|
||||||
};
|
};
|
||||||
|
|
||||||
// The lockfile used for the base environment.
|
// The lockfile used for the base environment.
|
||||||
let mut lock: Option<Lock> = None;
|
let mut lock: Option<(Lock, PathBuf)> = None;
|
||||||
|
|
||||||
// Discover and sync the base environment.
|
// Discover and sync the base environment.
|
||||||
let temp_dir;
|
let temp_dir;
|
||||||
|
@ -609,7 +609,8 @@ pub(crate) async fn run(
|
||||||
lock = project::lock::read(project.workspace())
|
lock = project::lock::read(project.workspace())
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
.flatten();
|
.flatten()
|
||||||
|
.map(|lock| (lock, project.workspace().install_path().to_owned()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Validate that any referenced dependency groups are defined in the workspace.
|
// Validate that any referenced dependency groups are defined in the workspace.
|
||||||
|
@ -749,7 +750,10 @@ pub(crate) async fn run(
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
lock = Some(result.into_lock());
|
lock = Some((
|
||||||
|
result.into_lock(),
|
||||||
|
project.workspace().install_path().to_owned(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
venv.into_interpreter()
|
venv.into_interpreter()
|
||||||
|
@ -861,7 +865,10 @@ pub(crate) async fn run(
|
||||||
debug!("Syncing ephemeral requirements");
|
debug!("Syncing ephemeral requirements");
|
||||||
|
|
||||||
let result = CachedEnvironment::get_or_create(
|
let result = CachedEnvironment::get_or_create(
|
||||||
EnvironmentSpecification::from(spec).with_lock(lock.as_ref()),
|
EnvironmentSpecification::from(spec).with_lock(
|
||||||
|
lock.as_ref()
|
||||||
|
.map(|(lock, install_path)| (lock, install_path.as_ref())),
|
||||||
|
),
|
||||||
base_interpreter.clone(),
|
base_interpreter.clone(),
|
||||||
&settings,
|
&settings,
|
||||||
&state,
|
&state,
|
||||||
|
|
|
@ -6458,7 +6458,11 @@ fn add_index() -> Result<()> {
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
Resolved 4 packages in [TIME]
|
Resolved 4 packages in [TIME]
|
||||||
Audited 3 packages in [TIME]
|
Prepared 1 package in [TIME]
|
||||||
|
Uninstalled 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
- jinja2==3.1.3
|
||||||
|
+ jinja2==3.1.4
|
||||||
"###);
|
"###);
|
||||||
|
|
||||||
let pyproject_toml = fs_err::read_to_string(context.temp_dir.join("pyproject.toml"))?;
|
let pyproject_toml = fs_err::read_to_string(context.temp_dir.join("pyproject.toml"))?;
|
||||||
|
@ -6517,14 +6521,14 @@ fn add_index() -> Result<()> {
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jinja2"
|
name = "jinja2"
|
||||||
version = "3.1.3"
|
version = "3.1.4"
|
||||||
source = { registry = "https://test.pypi.org/simple" }
|
source = { registry = "https://test.pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "markupsafe" },
|
{ name = "markupsafe" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://test-files.pythonhosted.org/packages/3e/f0/69ae37cced6b277dc0419dbb1c6e4fb259e5e319a1a971061a2776316bec/Jinja2-3.1.3.tar.gz", hash = "sha256:27fb536952e578492fa66d8681d8967d8bdf1eb36368b1f842b53251c9f0bfe1", size = 268254 }
|
sdist = { url = "https://test-files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://test-files.pythonhosted.org/packages/47/dc/9d1c0f1ddbedb1e67f7d00e91819b5a9157056ad83bfa64c12ecef8a4f4e/Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:ddd11470e8a1dc4c30e3146400f0130fed7d85886c5f8082f309355b4b0c1128", size = 133236 },
|
{ url = "https://test-files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -6633,14 +6637,14 @@ fn add_index() -> Result<()> {
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jinja2"
|
name = "jinja2"
|
||||||
version = "3.1.3"
|
version = "3.1.4"
|
||||||
source = { registry = "https://test.pypi.org/simple" }
|
source = { registry = "https://test.pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "markupsafe" },
|
{ name = "markupsafe" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://test-files.pythonhosted.org/packages/3e/f0/69ae37cced6b277dc0419dbb1c6e4fb259e5e319a1a971061a2776316bec/Jinja2-3.1.3.tar.gz", hash = "sha256:27fb536952e578492fa66d8681d8967d8bdf1eb36368b1f842b53251c9f0bfe1", size = 268254 }
|
sdist = { url = "https://test-files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://test-files.pythonhosted.org/packages/47/dc/9d1c0f1ddbedb1e67f7d00e91819b5a9157056ad83bfa64c12ecef8a4f4e/Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:ddd11470e8a1dc4c30e3146400f0130fed7d85886c5f8082f309355b4b0c1128", size = 133236 },
|
{ url = "https://test-files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -6758,14 +6762,14 @@ fn add_index() -> Result<()> {
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jinja2"
|
name = "jinja2"
|
||||||
version = "3.1.3"
|
version = "3.1.4"
|
||||||
source = { registry = "https://test.pypi.org/simple" }
|
source = { registry = "https://test.pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "markupsafe" },
|
{ name = "markupsafe" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://test-files.pythonhosted.org/packages/3e/f0/69ae37cced6b277dc0419dbb1c6e4fb259e5e319a1a971061a2776316bec/Jinja2-3.1.3.tar.gz", hash = "sha256:27fb536952e578492fa66d8681d8967d8bdf1eb36368b1f842b53251c9f0bfe1", size = 268254 }
|
sdist = { url = "https://test-files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://test-files.pythonhosted.org/packages/47/dc/9d1c0f1ddbedb1e67f7d00e91819b5a9157056ad83bfa64c12ecef8a4f4e/Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:ddd11470e8a1dc4c30e3146400f0130fed7d85886c5f8082f309355b4b0c1128", size = 133236 },
|
{ url = "https://test-files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue