mirror of https://github.com/astral-sh/uv
Use separate types to represent raw vs. resolver markers (#6646)
## Summary This is similar to https://github.com/astral-sh/uv/pull/6171 but more expansive... _Anywhere_ that we test requirements for platform compatibility, we _need_ to respect the resolver-friendly markers. In fixing the motivating issue (#6621), I also realized that we had a bunch of bugs here around `pip install` with `--python-platform` and `--python-version`, because we always performed our `satisfy` and `Plan` operations on the interpreter's markers, not the adjusted markers! Closes https://github.com/astral-sh/uv/issues/6621.
This commit is contained in:
parent
6220532373
commit
a7850d2a1c
|
|
@ -88,6 +88,7 @@ mod resolver {
|
||||||
use pep440_rs::Version;
|
use pep440_rs::Version;
|
||||||
use pep508_rs::{MarkerEnvironment, MarkerEnvironmentBuilder};
|
use pep508_rs::{MarkerEnvironment, MarkerEnvironmentBuilder};
|
||||||
use platform_tags::{Arch, Os, Platform, Tags};
|
use platform_tags::{Arch, Os, Platform, Tags};
|
||||||
|
use pypi_types::ResolverMarkerEnvironment;
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::RegistryClient;
|
use uv_client::RegistryClient;
|
||||||
use uv_configuration::{
|
use uv_configuration::{
|
||||||
|
|
@ -192,7 +193,7 @@ mod resolver {
|
||||||
let markers = if universal {
|
let markers = if universal {
|
||||||
ResolverMarkers::universal(vec![])
|
ResolverMarkers::universal(vec![])
|
||||||
} else {
|
} else {
|
||||||
ResolverMarkers::specific_environment(MARKERS.clone())
|
ResolverMarkers::specific_environment(ResolverMarkerEnvironment::from(MARKERS.clone()))
|
||||||
};
|
};
|
||||||
|
|
||||||
let resolver = Resolver::new(
|
let resolver = Resolver::new(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
pub use base_url::*;
|
pub use base_url::*;
|
||||||
pub use direct_url::*;
|
pub use direct_url::*;
|
||||||
pub use lenient_requirement::*;
|
pub use lenient_requirement::*;
|
||||||
|
pub use marker_environment::*;
|
||||||
pub use metadata::*;
|
pub use metadata::*;
|
||||||
pub use parsed_url::*;
|
pub use parsed_url::*;
|
||||||
pub use requirement::*;
|
pub use requirement::*;
|
||||||
|
|
@ -10,6 +11,7 @@ pub use simple_json::*;
|
||||||
mod base_url;
|
mod base_url;
|
||||||
mod direct_url;
|
mod direct_url;
|
||||||
mod lenient_requirement;
|
mod lenient_requirement;
|
||||||
|
mod marker_environment;
|
||||||
mod metadata;
|
mod metadata;
|
||||||
mod parsed_url;
|
mod parsed_url;
|
||||||
mod requirement;
|
mod requirement;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
|
use pep508_rs::MarkerEnvironment;
|
||||||
|
|
||||||
|
/// A wrapper type around [`MarkerEnvironment`] that ensures the Python version markers are
|
||||||
|
/// release-only, to match the resolver's semantics.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ResolverMarkerEnvironment(MarkerEnvironment);
|
||||||
|
|
||||||
|
impl ResolverMarkerEnvironment {
|
||||||
|
/// Returns the underlying [`MarkerEnvironment`].
|
||||||
|
pub fn markers(&self) -> &MarkerEnvironment {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MarkerEnvironment> for ResolverMarkerEnvironment {
|
||||||
|
fn from(value: MarkerEnvironment) -> Self {
|
||||||
|
// Strip `python_version`.
|
||||||
|
let python_version = value.python_version().only_release();
|
||||||
|
let value = if python_version == **value.python_version() {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
debug!(
|
||||||
|
"Stripping pre-release from `python_version`: {}",
|
||||||
|
value.python_version()
|
||||||
|
);
|
||||||
|
value.with_python_version(python_version)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Strip `python_full_version`.
|
||||||
|
let python_full_version = value.python_full_version().only_release();
|
||||||
|
let value = if python_full_version == **value.python_full_version() {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
debug!(
|
||||||
|
"Stripping pre-release from `python_full_version`: {}",
|
||||||
|
value.python_full_version()
|
||||||
|
);
|
||||||
|
value.with_python_full_version(python_full_version)
|
||||||
|
};
|
||||||
|
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for ResolverMarkerEnvironment {
|
||||||
|
type Target = MarkerEnvironment;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -138,7 +138,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
||||||
|
|
||||||
async fn resolve<'data>(&'data self, requirements: &'data [Requirement]) -> Result<Resolution> {
|
async fn resolve<'data>(&'data self, requirements: &'data [Requirement]) -> Result<Resolution> {
|
||||||
let python_requirement = PythonRequirement::from_interpreter(self.interpreter);
|
let python_requirement = PythonRequirement::from_interpreter(self.interpreter);
|
||||||
let markers = self.interpreter.markers();
|
let markers = self.interpreter.resolver_markers();
|
||||||
let tags = self.interpreter.tags()?;
|
let tags = self.interpreter.tags()?;
|
||||||
|
|
||||||
let resolver = Resolver::new(
|
let resolver = Resolver::new(
|
||||||
|
|
@ -148,7 +148,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
||||||
.index_strategy(self.index_strategy)
|
.index_strategy(self.index_strategy)
|
||||||
.build(),
|
.build(),
|
||||||
&python_requirement,
|
&python_requirement,
|
||||||
ResolverMarkers::specific_environment(markers.clone()),
|
ResolverMarkers::specific_environment(markers),
|
||||||
Some(tags),
|
Some(tags),
|
||||||
self.flat_index,
|
self.flat_index,
|
||||||
self.index,
|
self.index,
|
||||||
|
|
@ -189,6 +189,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
||||||
|
|
||||||
// Determine the current environment markers.
|
// Determine the current environment markers.
|
||||||
let tags = self.interpreter.tags()?;
|
let tags = self.interpreter.tags()?;
|
||||||
|
let markers = self.interpreter.resolver_markers();
|
||||||
|
|
||||||
// Determine the set of installed packages.
|
// Determine the set of installed packages.
|
||||||
let site_packages = SitePackages::from_environment(venv)?;
|
let site_packages = SitePackages::from_environment(venv)?;
|
||||||
|
|
@ -208,6 +209,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
||||||
self.index_locations,
|
self.index_locations,
|
||||||
self.cache(),
|
self.cache(),
|
||||||
venv,
|
venv,
|
||||||
|
&markers,
|
||||||
tags,
|
tags,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use distribution_types::{
|
||||||
PathSourceDist, RemoteSource, Verbatim,
|
PathSourceDist, RemoteSource, Verbatim,
|
||||||
};
|
};
|
||||||
use platform_tags::Tags;
|
use platform_tags::Tags;
|
||||||
use pypi_types::{Requirement, RequirementSource};
|
use pypi_types::{Requirement, RequirementSource, ResolverMarkerEnvironment};
|
||||||
use uv_cache::{ArchiveTimestamp, Cache, CacheBucket, WheelCache};
|
use uv_cache::{ArchiveTimestamp, Cache, CacheBucket, WheelCache};
|
||||||
use uv_configuration::{BuildOptions, Reinstall};
|
use uv_configuration::{BuildOptions, Reinstall};
|
||||||
use uv_distribution::{
|
use uv_distribution::{
|
||||||
|
|
@ -58,6 +58,7 @@ impl<'a> Planner<'a> {
|
||||||
index_locations: &IndexLocations,
|
index_locations: &IndexLocations,
|
||||||
cache: &Cache,
|
cache: &Cache,
|
||||||
venv: &PythonEnvironment,
|
venv: &PythonEnvironment,
|
||||||
|
markers: &ResolverMarkerEnvironment,
|
||||||
tags: &Tags,
|
tags: &Tags,
|
||||||
) -> Result<Plan> {
|
) -> Result<Plan> {
|
||||||
// Index all the already-downloaded wheels in the cache.
|
// Index all the already-downloaded wheels in the cache.
|
||||||
|
|
@ -72,7 +73,7 @@ impl<'a> Planner<'a> {
|
||||||
|
|
||||||
for requirement in self.requirements {
|
for requirement in self.requirements {
|
||||||
// Filter out incompatible requirements.
|
// Filter out incompatible requirements.
|
||||||
if !requirement.evaluate_markers(Some(venv.interpreter().markers()), &[]) {
|
if !requirement.evaluate_markers(Some(markers), &[]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use distribution_types::{
|
||||||
Diagnostic, InstalledDist, Name, UnresolvedRequirement, UnresolvedRequirementSpecification,
|
Diagnostic, InstalledDist, Name, UnresolvedRequirement, UnresolvedRequirementSpecification,
|
||||||
};
|
};
|
||||||
use pep440_rs::{Version, VersionSpecifiers};
|
use pep440_rs::{Version, VersionSpecifiers};
|
||||||
use pypi_types::{Requirement, VerbatimParsedUrl};
|
use pypi_types::{Requirement, ResolverMarkerEnvironment, VerbatimParsedUrl};
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_python::{Interpreter, PythonEnvironment};
|
use uv_python::{Interpreter, PythonEnvironment};
|
||||||
|
|
@ -181,7 +181,10 @@ impl SitePackages {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validate the installed packages in the virtual environment.
|
/// Validate the installed packages in the virtual environment.
|
||||||
pub fn diagnostics(&self) -> Result<Vec<SitePackagesDiagnostic>> {
|
pub fn diagnostics(
|
||||||
|
&self,
|
||||||
|
markers: &ResolverMarkerEnvironment,
|
||||||
|
) -> Result<Vec<SitePackagesDiagnostic>> {
|
||||||
let mut diagnostics = Vec::new();
|
let mut diagnostics = Vec::new();
|
||||||
|
|
||||||
for (package, indexes) in &self.by_name {
|
for (package, indexes) in &self.by_name {
|
||||||
|
|
@ -220,7 +223,7 @@ impl SitePackages {
|
||||||
|
|
||||||
// Verify that the package is compatible with the current Python version.
|
// Verify that the package is compatible with the current Python version.
|
||||||
if let Some(requires_python) = metadata.requires_python.as_ref() {
|
if let Some(requires_python) = metadata.requires_python.as_ref() {
|
||||||
if !requires_python.contains(self.interpreter.python_version()) {
|
if !requires_python.contains(markers.python_full_version()) {
|
||||||
diagnostics.push(SitePackagesDiagnostic::IncompatiblePythonVersion {
|
diagnostics.push(SitePackagesDiagnostic::IncompatiblePythonVersion {
|
||||||
package: package.clone(),
|
package: package.clone(),
|
||||||
version: self.interpreter.python_version().clone(),
|
version: self.interpreter.python_version().clone(),
|
||||||
|
|
@ -231,7 +234,7 @@ impl SitePackages {
|
||||||
|
|
||||||
// Verify that the dependencies are installed.
|
// Verify that the dependencies are installed.
|
||||||
for dependency in &metadata.requires_dist {
|
for dependency in &metadata.requires_dist {
|
||||||
if !dependency.evaluate_markers(self.interpreter.markers(), &[]) {
|
if !dependency.evaluate_markers(markers, &[]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -281,6 +284,7 @@ impl SitePackages {
|
||||||
&self,
|
&self,
|
||||||
requirements: &[UnresolvedRequirementSpecification],
|
requirements: &[UnresolvedRequirementSpecification],
|
||||||
constraints: &[Requirement],
|
constraints: &[Requirement],
|
||||||
|
markers: &ResolverMarkerEnvironment,
|
||||||
) -> Result<SatisfiesResult> {
|
) -> Result<SatisfiesResult> {
|
||||||
// Collect the constraints.
|
// Collect the constraints.
|
||||||
let constraints: FxHashMap<&PackageName, Vec<&Requirement>> =
|
let constraints: FxHashMap<&PackageName, Vec<&Requirement>> =
|
||||||
|
|
@ -299,10 +303,7 @@ impl SitePackages {
|
||||||
|
|
||||||
// Add the direct requirements to the queue.
|
// Add the direct requirements to the queue.
|
||||||
for entry in requirements {
|
for entry in requirements {
|
||||||
if entry
|
if entry.requirement.evaluate_markers(Some(markers), &[]) {
|
||||||
.requirement
|
|
||||||
.evaluate_markers(Some(self.interpreter.markers()), &[])
|
|
||||||
{
|
|
||||||
if seen.insert(entry.clone()) {
|
if seen.insert(entry.clone()) {
|
||||||
stack.push(entry.clone());
|
stack.push(entry.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -353,10 +354,7 @@ impl SitePackages {
|
||||||
|
|
||||||
// Add the dependencies to the queue.
|
// Add the dependencies to the queue.
|
||||||
for dependency in metadata.requires_dist {
|
for dependency in metadata.requires_dist {
|
||||||
if dependency.evaluate_markers(
|
if dependency.evaluate_markers(markers, entry.requirement.extras()) {
|
||||||
self.interpreter.markers(),
|
|
||||||
entry.requirement.extras(),
|
|
||||||
) {
|
|
||||||
let dependency = UnresolvedRequirementSpecification {
|
let dependency = UnresolvedRequirementSpecification {
|
||||||
requirement: UnresolvedRequirement::Named(Requirement::from(
|
requirement: UnresolvedRequirement::Named(Requirement::from(
|
||||||
dependency,
|
dependency,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ use pep440_rs::Version;
|
||||||
use pep508_rs::{MarkerEnvironment, StringVersion};
|
use pep508_rs::{MarkerEnvironment, StringVersion};
|
||||||
use platform_tags::Platform;
|
use platform_tags::Platform;
|
||||||
use platform_tags::{Tags, TagsError};
|
use platform_tags::{Tags, TagsError};
|
||||||
use pypi_types::Scheme;
|
use pypi_types::{ResolverMarkerEnvironment, Scheme};
|
||||||
use uv_cache::{Cache, CacheBucket, CachedByTimestamp, Freshness, Timestamp};
|
use uv_cache::{Cache, CacheBucket, CachedByTimestamp, Freshness, Timestamp};
|
||||||
use uv_fs::{write_atomic_sync, PythonExt, Simplified};
|
use uv_fs::{write_atomic_sync, PythonExt, Simplified};
|
||||||
|
|
||||||
|
|
@ -142,6 +142,11 @@ impl Interpreter {
|
||||||
&self.markers
|
&self.markers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the [`ResolverMarkerEnvironment`] for this Python executable.
|
||||||
|
pub fn resolver_markers(&self) -> ResolverMarkerEnvironment {
|
||||||
|
ResolverMarkerEnvironment::from(self.markers().clone())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the [`PythonInstallationKey`] for this interpreter.
|
/// Returns the [`PythonInstallationKey`] for this interpreter.
|
||||||
pub fn key(&self) -> PythonInstallationKey {
|
pub fn key(&self) -> PythonInstallationKey {
|
||||||
PythonInstallationKey::new(
|
PythonInstallationKey::new(
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ impl PythonVersion {
|
||||||
///
|
///
|
||||||
/// The returned [`MarkerEnvironment`] will preserve the base environment's platform markers,
|
/// The returned [`MarkerEnvironment`] will preserve the base environment's platform markers,
|
||||||
/// but override its Python version markers.
|
/// but override its Python version markers.
|
||||||
pub fn markers(self, base: &MarkerEnvironment) -> MarkerEnvironment {
|
pub fn markers(&self, base: &MarkerEnvironment) -> MarkerEnvironment {
|
||||||
let mut markers = base.clone();
|
let mut markers = base.clone();
|
||||||
|
|
||||||
// Ex) `implementation_version == "3.12.0"`
|
// Ex) `implementation_version == "3.12.0"`
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use tracing::{debug, trace};
|
||||||
use distribution_types::{CompatibleDist, IncompatibleDist, IncompatibleSource};
|
use distribution_types::{CompatibleDist, IncompatibleDist, IncompatibleSource};
|
||||||
use distribution_types::{DistributionMetadata, IncompatibleWheel, Name, PrioritizedDist};
|
use distribution_types::{DistributionMetadata, IncompatibleWheel, Name, PrioritizedDist};
|
||||||
use pep440_rs::Version;
|
use pep440_rs::Version;
|
||||||
use pep508_rs::{MarkerEnvironment, MarkerTree};
|
use pep508_rs::MarkerTree;
|
||||||
use uv_configuration::IndexStrategy;
|
use uv_configuration::IndexStrategy;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_types::InstalledPackagesProvider;
|
use uv_types::InstalledPackagesProvider;
|
||||||
|
|
@ -30,7 +30,7 @@ impl CandidateSelector {
|
||||||
pub(crate) fn for_resolution(
|
pub(crate) fn for_resolution(
|
||||||
options: Options,
|
options: Options,
|
||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
markers: Option<&MarkerEnvironment>,
|
markers: &ResolverMarkers,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
resolution_strategy: ResolutionStrategy::from_mode(
|
resolution_strategy: ResolutionStrategy::from_mode(
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use pep508_rs::{split_scheme, MarkerEnvironment, MarkerTree, VerbatimUrl, Verbat
|
||||||
use platform_tags::{TagCompatibility, TagPriority, Tags};
|
use platform_tags::{TagCompatibility, TagPriority, Tags};
|
||||||
use pypi_types::{
|
use pypi_types::{
|
||||||
redact_git_credentials, HashDigest, ParsedArchiveUrl, ParsedGitUrl, Requirement,
|
redact_git_credentials, HashDigest, ParsedArchiveUrl, ParsedGitUrl, Requirement,
|
||||||
RequirementSource,
|
RequirementSource, ResolverMarkerEnvironment,
|
||||||
};
|
};
|
||||||
use uv_configuration::ExtrasSpecification;
|
use uv_configuration::ExtrasSpecification;
|
||||||
use uv_distribution::DistributionDatabase;
|
use uv_distribution::DistributionDatabase;
|
||||||
|
|
@ -419,7 +419,7 @@ impl Lock {
|
||||||
pub fn to_resolution(
|
pub fn to_resolution(
|
||||||
&self,
|
&self,
|
||||||
project: &VirtualProject,
|
project: &VirtualProject,
|
||||||
marker_env: &MarkerEnvironment,
|
marker_env: &ResolverMarkerEnvironment,
|
||||||
tags: &Tags,
|
tags: &Tags,
|
||||||
extras: &ExtrasSpecification,
|
extras: &ExtrasSpecification,
|
||||||
dev: &[GroupName],
|
dev: &[GroupName],
|
||||||
|
|
@ -3641,7 +3641,7 @@ impl<'env> TreeDisplay<'env> {
|
||||||
/// Create a new [`DisplayDependencyGraph`] for the set of installed packages.
|
/// Create a new [`DisplayDependencyGraph`] for the set of installed packages.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
lock: &'env Lock,
|
lock: &'env Lock,
|
||||||
markers: Option<&'env MarkerEnvironment>,
|
markers: Option<&'env ResolverMarkerEnvironment>,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
prune: Vec<PackageName>,
|
prune: Vec<PackageName>,
|
||||||
package: Vec<PackageName>,
|
package: Vec<PackageName>,
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
use either::Either;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use pep508_rs::MarkerEnvironment;
|
use either::Either;
|
||||||
|
|
||||||
use pypi_types::Requirement;
|
use pypi_types::Requirement;
|
||||||
use uv_configuration::{Constraints, Overrides};
|
use uv_configuration::{Constraints, Overrides};
|
||||||
use uv_normalize::{GroupName, PackageName};
|
use uv_normalize::{GroupName, PackageName};
|
||||||
use uv_types::RequestedRequirements;
|
use uv_types::RequestedRequirements;
|
||||||
|
|
||||||
use crate::preferences::Preferences;
|
use crate::preferences::Preferences;
|
||||||
use crate::{DependencyMode, Exclusions};
|
use crate::{DependencyMode, Exclusions, ResolverMarkers};
|
||||||
|
|
||||||
/// A manifest of requirements, constraints, and preferences.
|
/// A manifest of requirements, constraints, and preferences.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -109,7 +109,7 @@ impl Manifest {
|
||||||
/// - Determining which requirements should allow local version specifiers (e.g., `torch==2.2.0+cpu`).
|
/// - Determining which requirements should allow local version specifiers (e.g., `torch==2.2.0+cpu`).
|
||||||
pub fn requirements<'a>(
|
pub fn requirements<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
markers: Option<&'a MarkerEnvironment>,
|
markers: &'a ResolverMarkers,
|
||||||
mode: DependencyMode,
|
mode: DependencyMode,
|
||||||
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
||||||
self.requirements_no_overrides(markers, mode)
|
self.requirements_no_overrides(markers, mode)
|
||||||
|
|
@ -119,39 +119,48 @@ impl Manifest {
|
||||||
/// Like [`Self::requirements`], but without the overrides.
|
/// Like [`Self::requirements`], but without the overrides.
|
||||||
pub fn requirements_no_overrides<'a>(
|
pub fn requirements_no_overrides<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
markers: Option<&'a MarkerEnvironment>,
|
markers: &'a ResolverMarkers,
|
||||||
mode: DependencyMode,
|
mode: DependencyMode,
|
||||||
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
||||||
match mode {
|
match mode {
|
||||||
// Include all direct and transitive requirements, with constraints and overrides applied.
|
// Include all direct and transitive requirements, with constraints and overrides applied.
|
||||||
DependencyMode::Transitive => Either::Left(
|
DependencyMode::Transitive => {
|
||||||
self.lookaheads
|
Either::Left(
|
||||||
.iter()
|
self.lookaheads
|
||||||
.flat_map(move |lookahead| {
|
.iter()
|
||||||
self.overrides
|
.flat_map(move |lookahead| {
|
||||||
.apply(lookahead.requirements())
|
self.overrides.apply(lookahead.requirements()).filter(
|
||||||
.filter(move |requirement| {
|
move |requirement| {
|
||||||
requirement.evaluate_markers(markers, lookahead.extras())
|
requirement.evaluate_markers(
|
||||||
})
|
markers.marker_environment(),
|
||||||
})
|
lookahead.extras(),
|
||||||
.chain(
|
)
|
||||||
self.overrides
|
},
|
||||||
.apply(&self.requirements)
|
)
|
||||||
.filter(move |requirement| requirement.evaluate_markers(markers, &[])),
|
})
|
||||||
)
|
.chain(self.overrides.apply(&self.requirements).filter(
|
||||||
.chain(
|
move |requirement| {
|
||||||
self.constraints
|
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||||
.requirements()
|
},
|
||||||
.filter(move |requirement| requirement.evaluate_markers(markers, &[]))
|
))
|
||||||
.map(Cow::Borrowed),
|
.chain(
|
||||||
),
|
self.constraints
|
||||||
),
|
.requirements()
|
||||||
|
.filter(move |requirement| {
|
||||||
|
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||||
|
})
|
||||||
|
.map(Cow::Borrowed),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
// Include direct requirements, with constraints and overrides applied.
|
// Include direct requirements, with constraints and overrides applied.
|
||||||
DependencyMode::Direct => Either::Right(
|
DependencyMode::Direct => Either::Right(
|
||||||
self.overrides
|
self.overrides
|
||||||
.apply(&self.requirements)
|
.apply(&self.requirements)
|
||||||
.chain(self.constraints.requirements().map(Cow::Borrowed))
|
.chain(self.constraints.requirements().map(Cow::Borrowed))
|
||||||
.filter(move |requirement| requirement.evaluate_markers(markers, &[])),
|
.filter(move |requirement| {
|
||||||
|
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +168,7 @@ impl Manifest {
|
||||||
/// Only the overrides from [`Self::requirements`].
|
/// Only the overrides from [`Self::requirements`].
|
||||||
pub fn overrides<'a>(
|
pub fn overrides<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
markers: Option<&'a MarkerEnvironment>,
|
markers: &'a ResolverMarkers,
|
||||||
mode: DependencyMode,
|
mode: DependencyMode,
|
||||||
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
||||||
match mode {
|
match mode {
|
||||||
|
|
@ -167,14 +176,18 @@ impl Manifest {
|
||||||
DependencyMode::Transitive => Either::Left(
|
DependencyMode::Transitive => Either::Left(
|
||||||
self.overrides
|
self.overrides
|
||||||
.requirements()
|
.requirements()
|
||||||
.filter(move |requirement| requirement.evaluate_markers(markers, &[]))
|
.filter(move |requirement| {
|
||||||
|
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||||
|
})
|
||||||
.map(Cow::Borrowed),
|
.map(Cow::Borrowed),
|
||||||
),
|
),
|
||||||
// Include direct requirements, with constraints and overrides applied.
|
// Include direct requirements, with constraints and overrides applied.
|
||||||
DependencyMode::Direct => Either::Right(
|
DependencyMode::Direct => Either::Right(
|
||||||
self.overrides
|
self.overrides
|
||||||
.requirements()
|
.requirements()
|
||||||
.filter(move |requirement| requirement.evaluate_markers(markers, &[]))
|
.filter(move |requirement| {
|
||||||
|
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||||
|
})
|
||||||
.map(Cow::Borrowed),
|
.map(Cow::Borrowed),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
@ -192,36 +205,43 @@ impl Manifest {
|
||||||
/// the `lowest-direct` strategy is in use.
|
/// the `lowest-direct` strategy is in use.
|
||||||
pub fn user_requirements<'a>(
|
pub fn user_requirements<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
markers: Option<&'a MarkerEnvironment>,
|
markers: &'a ResolverMarkers,
|
||||||
mode: DependencyMode,
|
mode: DependencyMode,
|
||||||
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
||||||
match mode {
|
match mode {
|
||||||
// Include direct requirements, dependencies of editables, and transitive dependencies
|
// Include direct requirements, dependencies of editables, and transitive dependencies
|
||||||
// of local packages.
|
// of local packages.
|
||||||
DependencyMode::Transitive => Either::Left(
|
DependencyMode::Transitive => {
|
||||||
self.lookaheads
|
Either::Left(
|
||||||
.iter()
|
self.lookaheads
|
||||||
.filter(|lookahead| lookahead.direct())
|
.iter()
|
||||||
.flat_map(move |lookahead| {
|
.filter(|lookahead| lookahead.direct())
|
||||||
self.overrides
|
.flat_map(move |lookahead| {
|
||||||
.apply(lookahead.requirements())
|
self.overrides.apply(lookahead.requirements()).filter(
|
||||||
.filter(move |requirement| {
|
move |requirement| {
|
||||||
requirement.evaluate_markers(markers, lookahead.extras())
|
requirement.evaluate_markers(
|
||||||
})
|
markers.marker_environment(),
|
||||||
})
|
lookahead.extras(),
|
||||||
.chain(
|
)
|
||||||
self.overrides
|
},
|
||||||
.apply(&self.requirements)
|
)
|
||||||
.filter(move |requirement| requirement.evaluate_markers(markers, &[])),
|
})
|
||||||
),
|
.chain(self.overrides.apply(&self.requirements).filter(
|
||||||
),
|
move |requirement| {
|
||||||
|
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Restrict to the direct requirements.
|
// Restrict to the direct requirements.
|
||||||
DependencyMode::Direct => Either::Right(
|
DependencyMode::Direct => {
|
||||||
self.overrides
|
Either::Right(self.overrides.apply(self.requirements.iter()).filter(
|
||||||
.apply(self.requirements.iter())
|
move |requirement| {
|
||||||
.filter(move |requirement| requirement.evaluate_markers(markers, &[])),
|
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||||
),
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,11 +252,13 @@ impl Manifest {
|
||||||
/// resolution (assuming the user enabled development dependencies).
|
/// resolution (assuming the user enabled development dependencies).
|
||||||
pub fn direct_requirements<'a>(
|
pub fn direct_requirements<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
markers: Option<&'a MarkerEnvironment>,
|
markers: &'a ResolverMarkers,
|
||||||
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
) -> impl Iterator<Item = Cow<'a, Requirement>> + 'a {
|
||||||
self.overrides
|
self.overrides
|
||||||
.apply(self.requirements.iter())
|
.apply(self.requirements.iter())
|
||||||
.filter(move |requirement| requirement.evaluate_markers(markers, &[]))
|
.filter(move |requirement| {
|
||||||
|
requirement.evaluate_markers(markers.marker_environment(), &[])
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply the overrides and constraints to a set of requirements.
|
/// Apply the overrides and constraints to a set of requirements.
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,13 @@ use tracing::trace;
|
||||||
|
|
||||||
use distribution_types::{InstalledDist, InstalledMetadata, InstalledVersion, Name};
|
use distribution_types::{InstalledDist, InstalledMetadata, InstalledVersion, Name};
|
||||||
use pep440_rs::{Operator, Version};
|
use pep440_rs::{Operator, Version};
|
||||||
use pep508_rs::{MarkerEnvironment, MarkerTree, VersionOrUrl};
|
use pep508_rs::{MarkerTree, VersionOrUrl};
|
||||||
use pypi_types::{HashDigest, HashError};
|
use pypi_types::{HashDigest, HashError};
|
||||||
use requirements_txt::{RequirementEntry, RequirementsTxtRequirement};
|
use requirements_txt::{RequirementEntry, RequirementsTxtRequirement};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
|
|
||||||
|
use crate::ResolverMarkers;
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum PreferenceError {
|
pub enum PreferenceError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
|
@ -121,12 +123,12 @@ impl Preferences {
|
||||||
/// to an applicable subset.
|
/// to an applicable subset.
|
||||||
pub fn from_iter<PreferenceIterator: IntoIterator<Item = Preference>>(
|
pub fn from_iter<PreferenceIterator: IntoIterator<Item = Preference>>(
|
||||||
preferences: PreferenceIterator,
|
preferences: PreferenceIterator,
|
||||||
markers: Option<&MarkerEnvironment>,
|
markers: &ResolverMarkers,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut slf = Self::default();
|
let mut slf = Self::default();
|
||||||
for preference in preferences {
|
for preference in preferences {
|
||||||
// Filter non-matching preferences when resolving for an environment.
|
// Filter non-matching preferences when resolving for an environment.
|
||||||
if let Some(markers) = markers {
|
if let Some(markers) = markers.marker_environment() {
|
||||||
if !preference.marker.evaluate(markers, &[]) {
|
if !preference.marker.evaluate(markers, &[]) {
|
||||||
trace!("Excluding {preference} from preferences due to unmatched markers");
|
trace!("Excluding {preference} from preferences due to unmatched markers");
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use pypi_types::RequirementSource;
|
use pypi_types::RequirementSource;
|
||||||
|
|
||||||
use pep508_rs::MarkerEnvironment;
|
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
|
|
||||||
use crate::resolver::ForkSet;
|
use crate::resolver::ForkSet;
|
||||||
|
|
@ -68,7 +67,7 @@ impl PrereleaseStrategy {
|
||||||
pub(crate) fn from_mode(
|
pub(crate) fn from_mode(
|
||||||
mode: PrereleaseMode,
|
mode: PrereleaseMode,
|
||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
markers: Option<&MarkerEnvironment>,
|
markers: &ResolverMarkers,
|
||||||
dependencies: DependencyMode,
|
dependencies: DependencyMode,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut packages = ForkSet::default();
|
let mut packages = ForkSet::default();
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use pep508_rs::MarkerEnvironment;
|
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
|
|
||||||
use crate::{DependencyMode, Manifest};
|
use crate::{DependencyMode, Manifest, ResolverMarkers};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
|
|
@ -47,7 +46,7 @@ impl ResolutionStrategy {
|
||||||
pub(crate) fn from_mode(
|
pub(crate) fn from_mode(
|
||||||
mode: ResolutionMode,
|
mode: ResolutionMode,
|
||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
markers: Option<&MarkerEnvironment>,
|
markers: &ResolverMarkers,
|
||||||
dependencies: DependencyMode,
|
dependencies: DependencyMode,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match mode {
|
match mode {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use pep508_rs::MarkerEnvironment;
|
|
||||||
use uv_normalize::{GroupName, PackageName};
|
use uv_normalize::{GroupName, PackageName};
|
||||||
|
|
||||||
use crate::Manifest;
|
use crate::{Manifest, ResolverMarkers};
|
||||||
|
|
||||||
/// A map of package names to their activated dependency groups.
|
/// A map of package names to their activated dependency groups.
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
|
|
@ -11,7 +10,7 @@ pub(crate) struct Groups(FxHashMap<PackageName, Vec<GroupName>>);
|
||||||
|
|
||||||
impl Groups {
|
impl Groups {
|
||||||
/// Determine the set of enabled dependency groups in the [`Manifest`].
|
/// Determine the set of enabled dependency groups in the [`Manifest`].
|
||||||
pub(crate) fn from_manifest(manifest: &Manifest, markers: Option<&MarkerEnvironment>) -> Self {
|
pub(crate) fn from_manifest(manifest: &Manifest, markers: &ResolverMarkers) -> Self {
|
||||||
let mut groups = FxHashMap::default();
|
let mut groups = FxHashMap::default();
|
||||||
|
|
||||||
// Enable the groups for all direct dependencies. In practice, this tends to mean: when
|
// Enable the groups for all direct dependencies. In practice, this tends to mean: when
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::str::FromStr;
|
||||||
use distribution_filename::{SourceDistFilename, WheelFilename};
|
use distribution_filename::{SourceDistFilename, WheelFilename};
|
||||||
use distribution_types::RemoteSource;
|
use distribution_types::RemoteSource;
|
||||||
use pep440_rs::{Operator, Version, VersionSpecifier, VersionSpecifierBuildError};
|
use pep440_rs::{Operator, Version, VersionSpecifier, VersionSpecifierBuildError};
|
||||||
use pep508_rs::{MarkerEnvironment, PackageName};
|
use pep508_rs::PackageName;
|
||||||
use pypi_types::RequirementSource;
|
use pypi_types::RequirementSource;
|
||||||
|
|
||||||
use crate::resolver::ForkMap;
|
use crate::resolver::ForkMap;
|
||||||
|
|
@ -17,7 +17,7 @@ impl Locals {
|
||||||
/// Determine the set of permitted local versions in the [`Manifest`].
|
/// Determine the set of permitted local versions in the [`Manifest`].
|
||||||
pub(crate) fn from_manifest(
|
pub(crate) fn from_manifest(
|
||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
markers: Option<&MarkerEnvironment>,
|
markers: &ResolverMarkers,
|
||||||
dependencies: DependencyMode,
|
dependencies: DependencyMode,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut locals = ForkMap::default();
|
let mut locals = ForkMap::default();
|
||||||
|
|
|
||||||
|
|
@ -157,11 +157,7 @@ impl<'a, Context: BuildContext, InstalledPackages: InstalledPackagesProvider>
|
||||||
python_requirement
|
python_requirement
|
||||||
.target()
|
.target()
|
||||||
.and_then(|target| target.as_requires_python()),
|
.and_then(|target| target.as_requires_python()),
|
||||||
AllowedYanks::from_manifest(
|
AllowedYanks::from_manifest(&manifest, &markers, options.dependency_mode),
|
||||||
&manifest,
|
|
||||||
markers.marker_environment(),
|
|
||||||
options.dependency_mode,
|
|
||||||
),
|
|
||||||
hasher,
|
hasher,
|
||||||
options.exclude_newer,
|
options.exclude_newer,
|
||||||
build_context.build_options(),
|
build_context.build_options(),
|
||||||
|
|
@ -199,24 +195,11 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
|
||||||
let state = ResolverState {
|
let state = ResolverState {
|
||||||
index: index.clone(),
|
index: index.clone(),
|
||||||
git: git.clone(),
|
git: git.clone(),
|
||||||
selector: CandidateSelector::for_resolution(
|
selector: CandidateSelector::for_resolution(options, &manifest, &markers),
|
||||||
options,
|
|
||||||
&manifest,
|
|
||||||
markers.marker_environment(),
|
|
||||||
),
|
|
||||||
dependency_mode: options.dependency_mode,
|
dependency_mode: options.dependency_mode,
|
||||||
urls: Urls::from_manifest(
|
urls: Urls::from_manifest(&manifest, &markers, git, options.dependency_mode)?,
|
||||||
&manifest,
|
locals: Locals::from_manifest(&manifest, &markers, options.dependency_mode),
|
||||||
markers.marker_environment(),
|
groups: Groups::from_manifest(&manifest, &markers),
|
||||||
git,
|
|
||||||
options.dependency_mode,
|
|
||||||
)?,
|
|
||||||
locals: Locals::from_manifest(
|
|
||||||
&manifest,
|
|
||||||
markers.marker_environment(),
|
|
||||||
options.dependency_mode,
|
|
||||||
),
|
|
||||||
groups: Groups::from_manifest(&manifest, markers.marker_environment()),
|
|
||||||
project: manifest.project,
|
project: manifest.project,
|
||||||
workspace_members: manifest.workspace_members,
|
workspace_members: manifest.workspace_members,
|
||||||
requirements: manifest.requirements,
|
requirements: manifest.requirements,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
use pep508_rs::{MarkerEnvironment, MarkerTree};
|
use pep508_rs::{MarkerEnvironment, MarkerTree};
|
||||||
|
use pypi_types::ResolverMarkerEnvironment;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
/// Whether we're solving for a specific environment, universally or for a specific fork.
|
/// Whether we're solving for a specific environment, universally or for a specific fork.
|
||||||
|
|
@ -20,8 +20,8 @@ pub enum ResolverMarkers {
|
||||||
|
|
||||||
impl ResolverMarkers {
|
impl ResolverMarkers {
|
||||||
/// Set the resolver to perform a resolution for a specific environment.
|
/// Set the resolver to perform a resolution for a specific environment.
|
||||||
pub fn specific_environment(markers: MarkerEnvironment) -> Self {
|
pub fn specific_environment(markers: ResolverMarkerEnvironment) -> Self {
|
||||||
Self::SpecificEnvironment(ResolverMarkerEnvironment::from(markers))
|
Self::SpecificEnvironment(markers)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the resolver to perform a universal resolution.
|
/// Set the resolver to perform a universal resolution.
|
||||||
|
|
@ -71,46 +71,3 @@ impl Display for ResolverMarkers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper type around [`MarkerEnvironment`] that ensures the Python version markers are
|
|
||||||
/// release-only, to match the resolver's semantics.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ResolverMarkerEnvironment(MarkerEnvironment);
|
|
||||||
|
|
||||||
impl From<MarkerEnvironment> for ResolverMarkerEnvironment {
|
|
||||||
fn from(value: MarkerEnvironment) -> Self {
|
|
||||||
// Strip `python_version`.
|
|
||||||
let python_version = value.python_version().only_release();
|
|
||||||
let value = if python_version == **value.python_version() {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
debug!(
|
|
||||||
"Stripping pre-release from `python_version`: {}",
|
|
||||||
value.python_version()
|
|
||||||
);
|
|
||||||
value.with_python_version(python_version)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Strip `python_full_version`.
|
|
||||||
let python_full_version = value.python_full_version().only_release();
|
|
||||||
let value = if python_full_version == **value.python_full_version() {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
debug!(
|
|
||||||
"Stripping pre-release from `python_full_version`: {}",
|
|
||||||
value.python_full_version()
|
|
||||||
);
|
|
||||||
value.with_python_full_version(python_full_version)
|
|
||||||
};
|
|
||||||
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Deref for ResolverMarkerEnvironment {
|
|
||||||
type Target = MarkerEnvironment;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,12 @@ use tracing::debug;
|
||||||
|
|
||||||
use cache_key::CanonicalUrl;
|
use cache_key::CanonicalUrl;
|
||||||
use distribution_types::Verbatim;
|
use distribution_types::Verbatim;
|
||||||
use pep508_rs::{MarkerEnvironment, VerbatimUrl};
|
use pep508_rs::VerbatimUrl;
|
||||||
use pypi_types::{ParsedDirectoryUrl, ParsedUrl, VerbatimParsedUrl};
|
use pypi_types::{ParsedDirectoryUrl, ParsedUrl, VerbatimParsedUrl};
|
||||||
use uv_git::GitResolver;
|
use uv_git::GitResolver;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
|
|
||||||
use crate::{DependencyMode, Manifest, ResolveError};
|
use crate::{DependencyMode, Manifest, ResolveError, ResolverMarkers};
|
||||||
|
|
||||||
/// The URLs that are allowed for packages.
|
/// The URLs that are allowed for packages.
|
||||||
///
|
///
|
||||||
|
|
@ -36,7 +36,7 @@ pub(crate) struct Urls {
|
||||||
impl Urls {
|
impl Urls {
|
||||||
pub(crate) fn from_manifest(
|
pub(crate) fn from_manifest(
|
||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
markers: Option<&MarkerEnvironment>,
|
markers: &ResolverMarkers,
|
||||||
git: &GitResolver,
|
git: &GitResolver,
|
||||||
dependencies: DependencyMode,
|
dependencies: DependencyMode,
|
||||||
) -> Result<Self, ResolveError> {
|
) -> Result<Self, ResolveError> {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use pypi_types::RequirementSource;
|
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
|
||||||
use pep440_rs::Version;
|
use pep440_rs::Version;
|
||||||
use pep508_rs::MarkerEnvironment;
|
use pypi_types::RequirementSource;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
|
|
||||||
use crate::{DependencyMode, Manifest};
|
use crate::{DependencyMode, Manifest, ResolverMarkers};
|
||||||
|
|
||||||
/// A set of package versions that are permitted, even if they're marked as yanked by the
|
/// A set of package versions that are permitted, even if they're marked as yanked by the
|
||||||
/// relevant index.
|
/// relevant index.
|
||||||
|
|
@ -16,7 +16,7 @@ pub struct AllowedYanks(Arc<FxHashMap<PackageName, FxHashSet<Version>>>);
|
||||||
impl AllowedYanks {
|
impl AllowedYanks {
|
||||||
pub fn from_manifest(
|
pub fn from_manifest(
|
||||||
manifest: &Manifest,
|
manifest: &Manifest,
|
||||||
markers: Option<&MarkerEnvironment>,
|
markers: &ResolverMarkers,
|
||||||
dependencies: DependencyMode,
|
dependencies: DependencyMode,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut allowed_yanks = FxHashMap::<PackageName, FxHashSet<Version>>::default();
|
let mut allowed_yanks = FxHashMap::<PackageName, FxHashSet<Version>>::default();
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@ use distribution_types::{
|
||||||
DistributionMetadata, HashPolicy, Name, Resolution, UnresolvedRequirement, VersionId,
|
DistributionMetadata, HashPolicy, Name, Resolution, UnresolvedRequirement, VersionId,
|
||||||
};
|
};
|
||||||
use pep440_rs::Version;
|
use pep440_rs::Version;
|
||||||
use pep508_rs::MarkerEnvironment;
|
use pypi_types::{
|
||||||
use pypi_types::{HashDigest, HashError, Requirement, RequirementSource};
|
HashDigest, HashError, Requirement, RequirementSource, ResolverMarkerEnvironment,
|
||||||
|
};
|
||||||
use uv_configuration::HashCheckingMode;
|
use uv_configuration::HashCheckingMode;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
|
|
||||||
|
|
@ -125,7 +126,7 @@ impl HashStrategy {
|
||||||
/// to "only evaluate marker expressions that reference an extra name.")
|
/// to "only evaluate marker expressions that reference an extra name.")
|
||||||
pub fn from_requirements<'a>(
|
pub fn from_requirements<'a>(
|
||||||
requirements: impl Iterator<Item = (&'a UnresolvedRequirement, &'a [String])>,
|
requirements: impl Iterator<Item = (&'a UnresolvedRequirement, &'a [String])>,
|
||||||
markers: Option<&MarkerEnvironment>,
|
marker_env: Option<&ResolverMarkerEnvironment>,
|
||||||
mode: HashCheckingMode,
|
mode: HashCheckingMode,
|
||||||
) -> Result<Self, HashStrategyError> {
|
) -> Result<Self, HashStrategyError> {
|
||||||
let mut hashes = FxHashMap::<VersionId, Vec<HashDigest>>::default();
|
let mut hashes = FxHashMap::<VersionId, Vec<HashDigest>>::default();
|
||||||
|
|
@ -133,7 +134,9 @@ impl HashStrategy {
|
||||||
// For each requirement, map from name to allowed hashes. We use the last entry for each
|
// For each requirement, map from name to allowed hashes. We use the last entry for each
|
||||||
// package.
|
// package.
|
||||||
for (requirement, digests) in requirements {
|
for (requirement, digests) in requirements {
|
||||||
if !requirement.evaluate_markers(markers, &[]) {
|
if !requirement
|
||||||
|
.evaluate_markers(marker_env.map(ResolverMarkerEnvironment::markers), &[])
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,12 @@ pub(crate) fn pip_check(
|
||||||
.dimmed()
|
.dimmed()
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Determine the markers to use for resolution.
|
||||||
|
let markers = environment.interpreter().resolver_markers();
|
||||||
|
|
||||||
|
// Run the diagnostics.
|
||||||
let diagnostics: Vec<SitePackagesDiagnostic> =
|
let diagnostics: Vec<SitePackagesDiagnostic> =
|
||||||
site_packages.diagnostics()?.into_iter().collect();
|
site_packages.diagnostics(&markers)?.into_iter().collect();
|
||||||
|
|
||||||
if diagnostics.is_empty() {
|
if diagnostics.is_empty() {
|
||||||
writeln!(
|
writeln!(
|
||||||
|
|
|
||||||
|
|
@ -248,10 +248,7 @@ pub(crate) async fn pip_compile(
|
||||||
} else {
|
} else {
|
||||||
let (tags, markers) =
|
let (tags, markers) =
|
||||||
resolution_environment(python_version, python_platform, &interpreter)?;
|
resolution_environment(python_version, python_platform, &interpreter)?;
|
||||||
(
|
(Some(tags), ResolverMarkers::specific_environment(markers))
|
||||||
Some(tags),
|
|
||||||
ResolverMarkers::specific_environment((*markers).clone()),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate, but don't enforce hashes for the requirements.
|
// Generate, but don't enforce hashes for the requirements.
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,10 @@ pub(crate) fn pip_freeze(
|
||||||
|
|
||||||
// Validate that the environment is consistent.
|
// Validate that the environment is consistent.
|
||||||
if strict {
|
if strict {
|
||||||
for diagnostic in site_packages.diagnostics()? {
|
// Determine the markers to use for resolution.
|
||||||
|
let markers = environment.interpreter().resolver_markers();
|
||||||
|
|
||||||
|
for diagnostic in site_packages.diagnostics(&markers)? {
|
||||||
writeln!(
|
writeln!(
|
||||||
printer.stderr(),
|
printer.stderr(),
|
||||||
"{}{} {}",
|
"{}{} {}",
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ use uv_types::{BuildIsolation, HashStrategy};
|
||||||
|
|
||||||
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger, InstallLogger};
|
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger, InstallLogger};
|
||||||
use crate::commands::pip::operations::Modifications;
|
use crate::commands::pip::operations::Modifications;
|
||||||
use crate::commands::pip::{operations, resolution_environment};
|
use crate::commands::pip::{operations, resolution_markers, resolution_tags};
|
||||||
use crate::commands::{ExitStatus, SharedState};
|
use crate::commands::{ExitStatus, SharedState};
|
||||||
use crate::printer::Printer;
|
use crate::printer::Printer;
|
||||||
|
|
||||||
|
|
@ -183,6 +183,14 @@ pub(crate) async fn pip_install(
|
||||||
|
|
||||||
let _lock = environment.lock()?;
|
let _lock = environment.lock()?;
|
||||||
|
|
||||||
|
// Determine the markers to use for the resolution.
|
||||||
|
let interpreter = environment.interpreter();
|
||||||
|
let markers = resolution_markers(
|
||||||
|
python_version.as_ref(),
|
||||||
|
python_platform.as_ref(),
|
||||||
|
interpreter,
|
||||||
|
);
|
||||||
|
|
||||||
// Determine the set of installed packages.
|
// Determine the set of installed packages.
|
||||||
let site_packages = SitePackages::from_environment(&environment)?;
|
let site_packages = SitePackages::from_environment(&environment)?;
|
||||||
|
|
||||||
|
|
@ -190,7 +198,7 @@ pub(crate) async fn pip_install(
|
||||||
// Ideally, the resolver would be fast enough to let us remove this check. But right now, for large environments,
|
// Ideally, the resolver would be fast enough to let us remove this check. But right now, for large environments,
|
||||||
// it's an order of magnitude faster to validate the environment than to resolve the requirements.
|
// it's an order of magnitude faster to validate the environment than to resolve the requirements.
|
||||||
if reinstall.is_none() && upgrade.is_none() && source_trees.is_empty() && overrides.is_empty() {
|
if reinstall.is_none() && upgrade.is_none() && source_trees.is_empty() && overrides.is_empty() {
|
||||||
match site_packages.satisfies(&requirements, &constraints)? {
|
match site_packages.satisfies(&requirements, &constraints, &markers)? {
|
||||||
// If the requirements are already satisfied, we're done.
|
// If the requirements are already satisfied, we're done.
|
||||||
SatisfiesResult::Fresh {
|
SatisfiesResult::Fresh {
|
||||||
recursive_requirements,
|
recursive_requirements,
|
||||||
|
|
@ -216,8 +224,6 @@ pub(crate) async fn pip_install(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let interpreter = environment.interpreter();
|
|
||||||
|
|
||||||
// Determine the Python requirement, if the user requested a specific version.
|
// Determine the Python requirement, if the user requested a specific version.
|
||||||
let python_requirement = if let Some(python_version) = python_version.as_ref() {
|
let python_requirement = if let Some(python_version) = python_version.as_ref() {
|
||||||
PythonRequirement::from_python_version(interpreter, python_version)
|
PythonRequirement::from_python_version(interpreter, python_version)
|
||||||
|
|
@ -225,8 +231,12 @@ pub(crate) async fn pip_install(
|
||||||
PythonRequirement::from_interpreter(interpreter)
|
PythonRequirement::from_interpreter(interpreter)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Determine the environment for the resolution.
|
// Determine the tags to use for the resolution.
|
||||||
let (tags, markers) = resolution_environment(python_version, python_platform, interpreter)?;
|
let tags = resolution_tags(
|
||||||
|
python_version.as_ref(),
|
||||||
|
python_platform.as_ref(),
|
||||||
|
interpreter,
|
||||||
|
)?;
|
||||||
|
|
||||||
// Collect the set of required hashes.
|
// Collect the set of required hashes.
|
||||||
let hasher = if let Some(hash_checking) = hash_checking {
|
let hasher = if let Some(hash_checking) = hash_checking {
|
||||||
|
|
@ -262,7 +272,7 @@ pub(crate) async fn pip_install(
|
||||||
.cache(cache.clone())
|
.cache(cache.clone())
|
||||||
.index_urls(index_locations.index_urls())
|
.index_urls(index_locations.index_urls())
|
||||||
.index_strategy(index_strategy)
|
.index_strategy(index_strategy)
|
||||||
.markers(&markers)
|
.markers(interpreter.markers())
|
||||||
.platform(interpreter.platform())
|
.platform(interpreter.platform())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
@ -333,7 +343,7 @@ pub(crate) async fn pip_install(
|
||||||
&reinstall,
|
&reinstall,
|
||||||
&upgrade,
|
&upgrade,
|
||||||
Some(&tags),
|
Some(&tags),
|
||||||
ResolverMarkers::specific_environment((*markers).clone()),
|
ResolverMarkers::specific_environment(markers.clone()),
|
||||||
python_requirement,
|
python_requirement,
|
||||||
&client,
|
&client,
|
||||||
&flat_index,
|
&flat_index,
|
||||||
|
|
@ -366,6 +376,7 @@ pub(crate) async fn pip_install(
|
||||||
compile,
|
compile,
|
||||||
&index_locations,
|
&index_locations,
|
||||||
&hasher,
|
&hasher,
|
||||||
|
&markers,
|
||||||
&tags,
|
&tags,
|
||||||
&client,
|
&client,
|
||||||
&state.in_flight,
|
&state.in_flight,
|
||||||
|
|
@ -384,7 +395,7 @@ pub(crate) async fn pip_install(
|
||||||
|
|
||||||
// Notify the user of any environment diagnostics.
|
// Notify the user of any environment diagnostics.
|
||||||
if strict && !dry_run {
|
if strict && !dry_run {
|
||||||
operations::diagnose_environment(&resolution, &environment, printer)?;
|
operations::diagnose_environment(&resolution, &environment, &markers, printer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ExitStatus::Success)
|
Ok(ExitStatus::Success)
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,10 @@ pub(crate) fn pip_list(
|
||||||
|
|
||||||
// Validate that the environment is consistent.
|
// Validate that the environment is consistent.
|
||||||
if strict {
|
if strict {
|
||||||
for diagnostic in site_packages.diagnostics()? {
|
// Determine the markers to use for resolution.
|
||||||
|
let markers = environment.interpreter().resolver_markers();
|
||||||
|
|
||||||
|
for diagnostic in site_packages.diagnostics(&markers)? {
|
||||||
writeln!(
|
writeln!(
|
||||||
printer.stderr(),
|
printer.stderr(),
|
||||||
"{}{} {}",
|
"{}{} {}",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use pep508_rs::MarkerEnvironment;
|
|
||||||
use platform_tags::{Tags, TagsError};
|
use platform_tags::{Tags, TagsError};
|
||||||
|
use pypi_types::ResolverMarkerEnvironment;
|
||||||
use uv_configuration::TargetTriple;
|
use uv_configuration::TargetTriple;
|
||||||
use uv_python::{Interpreter, PythonVersion};
|
use uv_python::{Interpreter, PythonVersion};
|
||||||
|
|
||||||
|
|
@ -17,12 +17,65 @@ pub(crate) mod sync;
|
||||||
pub(crate) mod tree;
|
pub(crate) mod tree;
|
||||||
pub(crate) mod uninstall;
|
pub(crate) mod uninstall;
|
||||||
|
|
||||||
|
pub(crate) fn resolution_markers(
|
||||||
|
python_version: Option<&PythonVersion>,
|
||||||
|
python_platform: Option<&TargetTriple>,
|
||||||
|
interpreter: &Interpreter,
|
||||||
|
) -> ResolverMarkerEnvironment {
|
||||||
|
match (python_platform, python_version) {
|
||||||
|
(Some(python_platform), Some(python_version)) => ResolverMarkerEnvironment::from(
|
||||||
|
python_version.markers(&python_platform.markers(interpreter.markers())),
|
||||||
|
),
|
||||||
|
(Some(python_platform), None) => {
|
||||||
|
ResolverMarkerEnvironment::from(python_platform.markers(interpreter.markers()))
|
||||||
|
}
|
||||||
|
(None, Some(python_version)) => {
|
||||||
|
ResolverMarkerEnvironment::from(python_version.markers(interpreter.markers()))
|
||||||
|
}
|
||||||
|
(None, None) => interpreter.resolver_markers(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolution_tags<'env>(
|
||||||
|
python_version: Option<&PythonVersion>,
|
||||||
|
python_platform: Option<&TargetTriple>,
|
||||||
|
interpreter: &'env Interpreter,
|
||||||
|
) -> Result<Cow<'env, Tags>, TagsError> {
|
||||||
|
Ok(match (python_platform, python_version.as_ref()) {
|
||||||
|
(Some(python_platform), Some(python_version)) => Cow::Owned(Tags::from_env(
|
||||||
|
&python_platform.platform(),
|
||||||
|
(python_version.major(), python_version.minor()),
|
||||||
|
interpreter.implementation_name(),
|
||||||
|
interpreter.implementation_tuple(),
|
||||||
|
interpreter.manylinux_compatible(),
|
||||||
|
interpreter.gil_disabled(),
|
||||||
|
)?),
|
||||||
|
(Some(python_platform), None) => Cow::Owned(Tags::from_env(
|
||||||
|
&python_platform.platform(),
|
||||||
|
interpreter.python_tuple(),
|
||||||
|
interpreter.implementation_name(),
|
||||||
|
interpreter.implementation_tuple(),
|
||||||
|
interpreter.manylinux_compatible(),
|
||||||
|
interpreter.gil_disabled(),
|
||||||
|
)?),
|
||||||
|
(None, Some(python_version)) => Cow::Owned(Tags::from_env(
|
||||||
|
interpreter.platform(),
|
||||||
|
(python_version.major(), python_version.minor()),
|
||||||
|
interpreter.implementation_name(),
|
||||||
|
interpreter.implementation_tuple(),
|
||||||
|
interpreter.manylinux_compatible(),
|
||||||
|
interpreter.gil_disabled(),
|
||||||
|
)?),
|
||||||
|
(None, None) => Cow::Borrowed(interpreter.tags()?),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Determine the tags, markers, and interpreter to use for resolution.
|
/// Determine the tags, markers, and interpreter to use for resolution.
|
||||||
pub(crate) fn resolution_environment(
|
pub(crate) fn resolution_environment(
|
||||||
python_version: Option<PythonVersion>,
|
python_version: Option<PythonVersion>,
|
||||||
python_platform: Option<TargetTriple>,
|
python_platform: Option<TargetTriple>,
|
||||||
interpreter: &Interpreter,
|
interpreter: &Interpreter,
|
||||||
) -> Result<(Cow<'_, Tags>, Cow<'_, MarkerEnvironment>), TagsError> {
|
) -> Result<(Cow<'_, Tags>, ResolverMarkerEnvironment), TagsError> {
|
||||||
let tags = match (python_platform, python_version.as_ref()) {
|
let tags = match (python_platform, python_version.as_ref()) {
|
||||||
(Some(python_platform), Some(python_version)) => Cow::Owned(Tags::from_env(
|
(Some(python_platform), Some(python_version)) => Cow::Owned(Tags::from_env(
|
||||||
&python_platform.platform(),
|
&python_platform.platform(),
|
||||||
|
|
@ -53,12 +106,16 @@ pub(crate) fn resolution_environment(
|
||||||
|
|
||||||
// Apply the platform tags to the markers.
|
// Apply the platform tags to the markers.
|
||||||
let markers = match (python_platform, python_version) {
|
let markers = match (python_platform, python_version) {
|
||||||
(Some(python_platform), Some(python_version)) => {
|
(Some(python_platform), Some(python_version)) => ResolverMarkerEnvironment::from(
|
||||||
Cow::Owned(python_version.markers(&python_platform.markers(interpreter.markers())))
|
python_version.markers(&python_platform.markers(interpreter.markers())),
|
||||||
|
),
|
||||||
|
(Some(python_platform), None) => {
|
||||||
|
ResolverMarkerEnvironment::from(python_platform.markers(interpreter.markers()))
|
||||||
}
|
}
|
||||||
(Some(python_platform), None) => Cow::Owned(python_platform.markers(interpreter.markers())),
|
(None, Some(python_version)) => {
|
||||||
(None, Some(python_version)) => Cow::Owned(python_version.markers(interpreter.markers())),
|
ResolverMarkerEnvironment::from(python_version.markers(interpreter.markers()))
|
||||||
(None, None) => Cow::Borrowed(interpreter.markers()),
|
}
|
||||||
|
(None, None) => interpreter.resolver_markers(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((tags, markers))
|
Ok((tags, markers))
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ use distribution_types::{
|
||||||
};
|
};
|
||||||
use install_wheel_rs::linker::LinkMode;
|
use install_wheel_rs::linker::LinkMode;
|
||||||
use platform_tags::Tags;
|
use platform_tags::Tags;
|
||||||
use pypi_types::Requirement;
|
use pypi_types::{Requirement, ResolverMarkerEnvironment};
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::{BaseClientBuilder, RegistryClient};
|
use uv_client::{BaseClientBuilder, RegistryClient};
|
||||||
use uv_configuration::{
|
use uv_configuration::{
|
||||||
|
|
@ -199,7 +199,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
|
||||||
.chain(upgrade.constraints().cloned()),
|
.chain(upgrade.constraints().cloned()),
|
||||||
);
|
);
|
||||||
let overrides = Overrides::from_requirements(overrides);
|
let overrides = Overrides::from_requirements(overrides);
|
||||||
let preferences = Preferences::from_iter(preferences, markers.marker_environment());
|
let preferences = Preferences::from_iter(preferences, &markers);
|
||||||
|
|
||||||
// Determine any lookahead requirements.
|
// Determine any lookahead requirements.
|
||||||
let lookaheads = match options.dependency_mode {
|
let lookaheads = match options.dependency_mode {
|
||||||
|
|
@ -349,6 +349,7 @@ pub(crate) async fn install(
|
||||||
compile: bool,
|
compile: bool,
|
||||||
index_urls: &IndexLocations,
|
index_urls: &IndexLocations,
|
||||||
hasher: &HashStrategy,
|
hasher: &HashStrategy,
|
||||||
|
markers: &ResolverMarkerEnvironment,
|
||||||
tags: &Tags,
|
tags: &Tags,
|
||||||
client: &RegistryClient,
|
client: &RegistryClient,
|
||||||
in_flight: &InFlight,
|
in_flight: &InFlight,
|
||||||
|
|
@ -376,6 +377,7 @@ pub(crate) async fn install(
|
||||||
index_urls,
|
index_urls,
|
||||||
cache,
|
cache,
|
||||||
venv,
|
venv,
|
||||||
|
markers,
|
||||||
tags,
|
tags,
|
||||||
)
|
)
|
||||||
.context("Failed to determine installation plan")?;
|
.context("Failed to determine installation plan")?;
|
||||||
|
|
@ -661,10 +663,11 @@ pub(crate) fn diagnose_resolution(
|
||||||
pub(crate) fn diagnose_environment(
|
pub(crate) fn diagnose_environment(
|
||||||
resolution: &Resolution,
|
resolution: &Resolution,
|
||||||
venv: &PythonEnvironment,
|
venv: &PythonEnvironment,
|
||||||
|
markers: &ResolverMarkerEnvironment,
|
||||||
printer: Printer,
|
printer: Printer,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let site_packages = SitePackages::from_environment(venv)?;
|
let site_packages = SitePackages::from_environment(venv)?;
|
||||||
for diagnostic in site_packages.diagnostics()? {
|
for diagnostic in site_packages.diagnostics(markers)? {
|
||||||
// Only surface diagnostics that are "relevant" to the current resolution.
|
// Only surface diagnostics that are "relevant" to the current resolution.
|
||||||
if resolution
|
if resolution
|
||||||
.packages()
|
.packages()
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ pub(crate) fn pip_show(
|
||||||
let site_packages = SitePackages::from_environment(&environment)?;
|
let site_packages = SitePackages::from_environment(&environment)?;
|
||||||
|
|
||||||
// Determine the markers to use for resolution.
|
// Determine the markers to use for resolution.
|
||||||
let markers = environment.interpreter().markers();
|
let markers = environment.interpreter().resolver_markers();
|
||||||
|
|
||||||
// Sort and deduplicate the packages, which are keyed by name.
|
// Sort and deduplicate the packages, which are keyed by name.
|
||||||
packages.sort_unstable();
|
packages.sort_unstable();
|
||||||
|
|
@ -101,7 +101,7 @@ pub(crate) fn pip_show(
|
||||||
metadata
|
metadata
|
||||||
.requires_dist
|
.requires_dist
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|req| req.evaluate_markers(markers, &[]))
|
.filter(|req| req.evaluate_markers(&markers, &[]))
|
||||||
.map(|req| req.name)
|
.map(|req| req.name)
|
||||||
.sorted_unstable()
|
.sorted_unstable()
|
||||||
.dedup()
|
.dedup()
|
||||||
|
|
@ -119,7 +119,7 @@ pub(crate) fn pip_show(
|
||||||
let requires = metadata
|
let requires = metadata
|
||||||
.requires_dist
|
.requires_dist
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|req| req.evaluate_markers(markers, &[]))
|
.filter(|req| req.evaluate_markers(&markers, &[]))
|
||||||
.map(|req| req.name)
|
.map(|req| req.name)
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
if !requires.is_empty() {
|
if !requires.is_empty() {
|
||||||
|
|
@ -192,7 +192,7 @@ pub(crate) fn pip_show(
|
||||||
|
|
||||||
// Validate that the environment is consistent.
|
// Validate that the environment is consistent.
|
||||||
if strict {
|
if strict {
|
||||||
for diagnostic in site_packages.diagnostics()? {
|
for diagnostic in site_packages.diagnostics(&markers)? {
|
||||||
writeln!(
|
writeln!(
|
||||||
printer.stderr(),
|
printer.stderr(),
|
||||||
"{}{} {}",
|
"{}{} {}",
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ use uv_types::{BuildIsolation, HashStrategy};
|
||||||
|
|
||||||
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger};
|
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger};
|
||||||
use crate::commands::pip::operations::Modifications;
|
use crate::commands::pip::operations::Modifications;
|
||||||
use crate::commands::pip::{operations, resolution_environment};
|
use crate::commands::pip::{operations, resolution_markers, resolution_tags};
|
||||||
use crate::commands::{ExitStatus, SharedState};
|
use crate::commands::{ExitStatus, SharedState};
|
||||||
use crate::printer::Printer;
|
use crate::printer::Printer;
|
||||||
|
|
||||||
|
|
@ -183,8 +183,17 @@ pub(crate) async fn pip_sync(
|
||||||
PythonRequirement::from_interpreter(interpreter)
|
PythonRequirement::from_interpreter(interpreter)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Determine the environment for the resolution.
|
// Determine the markers and tags to use for resolution.
|
||||||
let (tags, markers) = resolution_environment(python_version, python_platform, interpreter)?;
|
let markers = resolution_markers(
|
||||||
|
python_version.as_ref(),
|
||||||
|
python_platform.as_ref(),
|
||||||
|
interpreter,
|
||||||
|
);
|
||||||
|
let tags = resolution_tags(
|
||||||
|
python_version.as_ref(),
|
||||||
|
python_platform.as_ref(),
|
||||||
|
interpreter,
|
||||||
|
)?;
|
||||||
|
|
||||||
// Collect the set of required hashes.
|
// Collect the set of required hashes.
|
||||||
let hasher = if let Some(hash_checking) = hash_checking {
|
let hasher = if let Some(hash_checking) = hash_checking {
|
||||||
|
|
@ -213,7 +222,7 @@ pub(crate) async fn pip_sync(
|
||||||
.cache(cache.clone())
|
.cache(cache.clone())
|
||||||
.index_urls(index_locations.index_urls())
|
.index_urls(index_locations.index_urls())
|
||||||
.index_strategy(index_strategy)
|
.index_strategy(index_strategy)
|
||||||
.markers(&markers)
|
.markers(interpreter.markers())
|
||||||
.platform(interpreter.platform())
|
.platform(interpreter.platform())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
@ -292,7 +301,7 @@ pub(crate) async fn pip_sync(
|
||||||
&reinstall,
|
&reinstall,
|
||||||
&upgrade,
|
&upgrade,
|
||||||
Some(&tags),
|
Some(&tags),
|
||||||
ResolverMarkers::specific_environment((*markers).clone()),
|
ResolverMarkers::specific_environment(markers.clone()),
|
||||||
python_requirement,
|
python_requirement,
|
||||||
&client,
|
&client,
|
||||||
&flat_index,
|
&flat_index,
|
||||||
|
|
@ -325,6 +334,7 @@ pub(crate) async fn pip_sync(
|
||||||
compile,
|
compile,
|
||||||
&index_locations,
|
&index_locations,
|
||||||
&hasher,
|
&hasher,
|
||||||
|
&markers,
|
||||||
&tags,
|
&tags,
|
||||||
&client,
|
&client,
|
||||||
&state.in_flight,
|
&state.in_flight,
|
||||||
|
|
@ -343,7 +353,7 @@ pub(crate) async fn pip_sync(
|
||||||
|
|
||||||
// Notify the user of any environment diagnostics.
|
// Notify the user of any environment diagnostics.
|
||||||
if strict && !dry_run {
|
if strict && !dry_run {
|
||||||
operations::diagnose_environment(&resolution, &environment, printer)?;
|
operations::diagnose_environment(&resolution, &environment, &markers, printer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ExitStatus::Success)
|
Ok(ExitStatus::Success)
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use distribution_types::{Diagnostic, Name};
|
use distribution_types::{Diagnostic, Name};
|
||||||
use pep508_rs::MarkerEnvironment;
|
use pypi_types::{RequirementSource, ResolverMarkerEnvironment};
|
||||||
use pypi_types::RequirementSource;
|
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_distribution::Metadata;
|
use uv_distribution::Metadata;
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
|
|
@ -60,6 +59,9 @@ pub(crate) fn pip_tree(
|
||||||
.push(metadata);
|
.push(metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine the markers to use for the resolution.
|
||||||
|
let markers = environment.interpreter().resolver_markers();
|
||||||
|
|
||||||
// Render the tree.
|
// Render the tree.
|
||||||
let rendered_tree = DisplayDependencyGraph::new(
|
let rendered_tree = DisplayDependencyGraph::new(
|
||||||
depth.into(),
|
depth.into(),
|
||||||
|
|
@ -68,7 +70,7 @@ pub(crate) fn pip_tree(
|
||||||
no_dedupe,
|
no_dedupe,
|
||||||
invert,
|
invert,
|
||||||
show_version_specifiers,
|
show_version_specifiers,
|
||||||
environment.interpreter().markers(),
|
&markers,
|
||||||
packages,
|
packages,
|
||||||
)
|
)
|
||||||
.render()
|
.render()
|
||||||
|
|
@ -87,7 +89,7 @@ pub(crate) fn pip_tree(
|
||||||
|
|
||||||
// Validate that the environment is consistent.
|
// Validate that the environment is consistent.
|
||||||
if strict {
|
if strict {
|
||||||
for diagnostic in site_packages.diagnostics()? {
|
for diagnostic in site_packages.diagnostics(&markers)? {
|
||||||
writeln!(
|
writeln!(
|
||||||
printer.stderr(),
|
printer.stderr(),
|
||||||
"{}{} {}",
|
"{}{} {}",
|
||||||
|
|
@ -129,7 +131,7 @@ impl DisplayDependencyGraph {
|
||||||
no_dedupe: bool,
|
no_dedupe: bool,
|
||||||
invert: bool,
|
invert: bool,
|
||||||
show_version_specifiers: bool,
|
show_version_specifiers: bool,
|
||||||
markers: &MarkerEnvironment,
|
markers: &ResolverMarkerEnvironment,
|
||||||
packages: IndexMap<PackageName, Vec<Metadata>>,
|
packages: IndexMap<PackageName, Vec<Metadata>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut requirements: FxHashMap<_, Vec<_>> = FxHashMap::default();
|
let mut requirements: FxHashMap<_, Vec<_>> = FxHashMap::default();
|
||||||
|
|
|
||||||
|
|
@ -534,7 +534,7 @@ pub(crate) async fn resolve_environment<'a>(
|
||||||
|
|
||||||
// Determine the tags, markers, and interpreter to use for resolution.
|
// Determine the tags, markers, and interpreter to use for resolution.
|
||||||
let tags = interpreter.tags()?;
|
let tags = interpreter.tags()?;
|
||||||
let markers = interpreter.markers();
|
let markers = interpreter.resolver_markers();
|
||||||
let python_requirement = PythonRequirement::from_interpreter(interpreter);
|
let python_requirement = PythonRequirement::from_interpreter(interpreter);
|
||||||
|
|
||||||
// Add all authenticated sources to the cache.
|
// Add all authenticated sources to the cache.
|
||||||
|
|
@ -549,7 +549,7 @@ pub(crate) async fn resolve_environment<'a>(
|
||||||
.index_urls(index_locations.index_urls())
|
.index_urls(index_locations.index_urls())
|
||||||
.index_strategy(index_strategy)
|
.index_strategy(index_strategy)
|
||||||
.keyring(keyring_provider)
|
.keyring(keyring_provider)
|
||||||
.markers(markers)
|
.markers(interpreter.markers())
|
||||||
.platform(interpreter.platform())
|
.platform(interpreter.platform())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
@ -629,7 +629,7 @@ pub(crate) async fn resolve_environment<'a>(
|
||||||
&reinstall,
|
&reinstall,
|
||||||
&upgrade,
|
&upgrade,
|
||||||
Some(tags),
|
Some(tags),
|
||||||
ResolverMarkers::specific_environment(markers.clone()),
|
ResolverMarkers::specific_environment(markers),
|
||||||
python_requirement,
|
python_requirement,
|
||||||
&client,
|
&client,
|
||||||
&flat_index,
|
&flat_index,
|
||||||
|
|
@ -673,10 +673,10 @@ pub(crate) async fn sync_environment(
|
||||||
|
|
||||||
let site_packages = SitePackages::from_environment(&venv)?;
|
let site_packages = SitePackages::from_environment(&venv)?;
|
||||||
|
|
||||||
// Determine the tags, markers, and interpreter to use for resolution.
|
// Determine the markers tags to use for resolution.
|
||||||
let interpreter = venv.interpreter();
|
let interpreter = venv.interpreter();
|
||||||
let tags = venv.interpreter().tags()?;
|
let tags = venv.interpreter().tags()?;
|
||||||
let markers = venv.interpreter().markers();
|
let markers = interpreter.resolver_markers();
|
||||||
|
|
||||||
// Add all authenticated sources to the cache.
|
// Add all authenticated sources to the cache.
|
||||||
for url in index_locations.urls() {
|
for url in index_locations.urls() {
|
||||||
|
|
@ -690,7 +690,7 @@ pub(crate) async fn sync_environment(
|
||||||
.index_urls(index_locations.index_urls())
|
.index_urls(index_locations.index_urls())
|
||||||
.index_strategy(index_strategy)
|
.index_strategy(index_strategy)
|
||||||
.keyring(keyring_provider)
|
.keyring(keyring_provider)
|
||||||
.markers(markers)
|
.markers(interpreter.markers())
|
||||||
.platform(interpreter.platform())
|
.platform(interpreter.platform())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
@ -748,6 +748,7 @@ pub(crate) async fn sync_environment(
|
||||||
compile_bytecode,
|
compile_bytecode,
|
||||||
index_locations,
|
index_locations,
|
||||||
&hasher,
|
&hasher,
|
||||||
|
&markers,
|
||||||
tags,
|
tags,
|
||||||
&client,
|
&client,
|
||||||
&state.in_flight,
|
&state.in_flight,
|
||||||
|
|
@ -827,10 +828,14 @@ pub(crate) async fn update_environment(
|
||||||
..
|
..
|
||||||
} = spec;
|
} = spec;
|
||||||
|
|
||||||
|
// Determine markers to use for resolution.
|
||||||
|
let interpreter = venv.interpreter();
|
||||||
|
let markers = venv.interpreter().resolver_markers();
|
||||||
|
|
||||||
// Check if the current environment satisfies the requirements
|
// Check if the current environment satisfies the requirements
|
||||||
let site_packages = SitePackages::from_environment(&venv)?;
|
let site_packages = SitePackages::from_environment(&venv)?;
|
||||||
if source_trees.is_empty() && reinstall.is_none() && upgrade.is_none() && overrides.is_empty() {
|
if source_trees.is_empty() && reinstall.is_none() && upgrade.is_none() && overrides.is_empty() {
|
||||||
match site_packages.satisfies(&requirements, &constraints)? {
|
match site_packages.satisfies(&requirements, &constraints, &markers)? {
|
||||||
// If the requirements are already satisfied, we're done.
|
// If the requirements are already satisfied, we're done.
|
||||||
SatisfiesResult::Fresh {
|
SatisfiesResult::Fresh {
|
||||||
recursive_requirements,
|
recursive_requirements,
|
||||||
|
|
@ -854,12 +859,6 @@ pub(crate) async fn update_environment(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the tags, markers, and interpreter to use for resolution.
|
|
||||||
let interpreter = venv.interpreter();
|
|
||||||
let tags = venv.interpreter().tags()?;
|
|
||||||
let markers = venv.interpreter().markers();
|
|
||||||
let python_requirement = PythonRequirement::from_interpreter(interpreter);
|
|
||||||
|
|
||||||
// Add all authenticated sources to the cache.
|
// Add all authenticated sources to the cache.
|
||||||
for url in index_locations.urls() {
|
for url in index_locations.urls() {
|
||||||
store_credentials_from_url(url);
|
store_credentials_from_url(url);
|
||||||
|
|
@ -872,7 +871,7 @@ pub(crate) async fn update_environment(
|
||||||
.index_urls(index_locations.index_urls())
|
.index_urls(index_locations.index_urls())
|
||||||
.index_strategy(*index_strategy)
|
.index_strategy(*index_strategy)
|
||||||
.keyring(*keyring_provider)
|
.keyring(*keyring_provider)
|
||||||
.markers(markers)
|
.markers(interpreter.markers())
|
||||||
.platform(interpreter.platform())
|
.platform(interpreter.platform())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
@ -901,6 +900,10 @@ pub(crate) async fn update_environment(
|
||||||
let hasher = HashStrategy::default();
|
let hasher = HashStrategy::default();
|
||||||
let preferences = Vec::default();
|
let preferences = Vec::default();
|
||||||
|
|
||||||
|
// Determine the tags to use for resolution.
|
||||||
|
let tags = venv.interpreter().tags()?;
|
||||||
|
let python_requirement = PythonRequirement::from_interpreter(interpreter);
|
||||||
|
|
||||||
// Resolve the flat indexes from `--find-links`.
|
// Resolve the flat indexes from `--find-links`.
|
||||||
let flat_index = {
|
let flat_index = {
|
||||||
let client = FlatIndexClient::new(&client, cache);
|
let client = FlatIndexClient::new(&client, cache);
|
||||||
|
|
@ -973,6 +976,7 @@ pub(crate) async fn update_environment(
|
||||||
*compile_bytecode,
|
*compile_bytecode,
|
||||||
index_locations,
|
index_locations,
|
||||||
&hasher,
|
&hasher,
|
||||||
|
&markers,
|
||||||
tags,
|
tags,
|
||||||
&client,
|
&client,
|
||||||
&state.in_flight,
|
&state.in_flight,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ use itertools::Itertools;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_cli::ExternalCommand;
|
use uv_cli::ExternalCommand;
|
||||||
use uv_client::{BaseClientBuilder, Connectivity};
|
use uv_client::{BaseClientBuilder, Connectivity};
|
||||||
|
|
@ -692,7 +691,11 @@ fn can_skip_ephemeral(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
match site_packages.satisfies(&spec.requirements, &spec.constraints) {
|
match site_packages.satisfies(
|
||||||
|
&spec.requirements,
|
||||||
|
&spec.constraints,
|
||||||
|
&base_interpreter.resolver_markers(),
|
||||||
|
) {
|
||||||
// If the requirements are already satisfied, we're done.
|
// If the requirements are already satisfied, we're done.
|
||||||
Ok(SatisfiesResult::Fresh {
|
Ok(SatisfiesResult::Fresh {
|
||||||
recursive_requirements,
|
recursive_requirements,
|
||||||
|
|
|
||||||
|
|
@ -173,11 +173,13 @@ pub(super) async fn do_sync(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine the markers to use for resolution.
|
||||||
|
let markers = venv.interpreter().resolver_markers();
|
||||||
|
|
||||||
// Validate that the platform is supported by the lockfile.
|
// Validate that the platform is supported by the lockfile.
|
||||||
let environments = lock.supported_environments();
|
let environments = lock.supported_environments();
|
||||||
if !environments.is_empty() {
|
if !environments.is_empty() {
|
||||||
let platform = venv.interpreter().markers();
|
if !environments.iter().any(|env| env.evaluate(&markers, &[])) {
|
||||||
if !environments.iter().any(|env| env.evaluate(platform, &[])) {
|
|
||||||
return Err(ProjectError::LockedPlatformIncompatibility(
|
return Err(ProjectError::LockedPlatformIncompatibility(
|
||||||
environments
|
environments
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -195,11 +197,11 @@ pub(super) async fn do_sync(
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
|
||||||
let markers = venv.interpreter().markers();
|
// Determine the tags to use for resolution.
|
||||||
let tags = venv.interpreter().tags()?;
|
let tags = venv.interpreter().tags()?;
|
||||||
|
|
||||||
// Read the lockfile.
|
// Read the lockfile.
|
||||||
let resolution = lock.to_resolution(project, markers, tags, extras, &dev)?;
|
let resolution = lock.to_resolution(project, &markers, tags, extras, &dev)?;
|
||||||
|
|
||||||
// If `--no-install-project` is set, remove the project itself.
|
// If `--no-install-project` is set, remove the project itself.
|
||||||
let resolution = apply_no_install_project(no_install_project, resolution, project);
|
let resolution = apply_no_install_project(no_install_project, resolution, project);
|
||||||
|
|
@ -222,7 +224,7 @@ pub(super) async fn do_sync(
|
||||||
.index_urls(index_locations.index_urls())
|
.index_urls(index_locations.index_urls())
|
||||||
.index_strategy(index_strategy)
|
.index_strategy(index_strategy)
|
||||||
.keyring(keyring_provider)
|
.keyring(keyring_provider)
|
||||||
.markers(markers)
|
.markers(venv.interpreter().markers())
|
||||||
.platform(venv.interpreter().platform())
|
.platform(venv.interpreter().platform())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
@ -284,6 +286,7 @@ pub(super) async fn do_sync(
|
||||||
compile_bytecode,
|
compile_bytecode,
|
||||||
index_locations,
|
index_locations,
|
||||||
&hasher,
|
&hasher,
|
||||||
|
&markers,
|
||||||
tags,
|
tags,
|
||||||
&client,
|
&client,
|
||||||
&state.in_flight,
|
&state.in_flight,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
@ -13,6 +12,7 @@ use uv_resolver::TreeDisplay;
|
||||||
use uv_workspace::{DiscoveryOptions, Workspace};
|
use uv_workspace::{DiscoveryOptions, Workspace};
|
||||||
|
|
||||||
use crate::commands::pip::loggers::DefaultResolveLogger;
|
use crate::commands::pip::loggers::DefaultResolveLogger;
|
||||||
|
use crate::commands::pip::resolution_markers;
|
||||||
use crate::commands::project::FoundInterpreter;
|
use crate::commands::project::FoundInterpreter;
|
||||||
use crate::commands::{project, ExitStatus};
|
use crate::commands::{project, ExitStatus};
|
||||||
use crate::printer::Printer;
|
use crate::printer::Printer;
|
||||||
|
|
@ -75,20 +75,17 @@ pub(crate) async fn tree(
|
||||||
.await?
|
.await?
|
||||||
.into_lock();
|
.into_lock();
|
||||||
|
|
||||||
// Apply the platform tags to the markers.
|
// Determine the markers to use for resolution.
|
||||||
let markers = match (python_platform, python_version) {
|
let markers = resolution_markers(
|
||||||
(Some(python_platform), Some(python_version)) => {
|
python_version.as_ref(),
|
||||||
Cow::Owned(python_version.markers(&python_platform.markers(interpreter.markers())))
|
python_platform.as_ref(),
|
||||||
}
|
&interpreter,
|
||||||
(Some(python_platform), None) => Cow::Owned(python_platform.markers(interpreter.markers())),
|
);
|
||||||
(None, Some(python_version)) => Cow::Owned(python_version.markers(interpreter.markers())),
|
|
||||||
(None, None) => Cow::Borrowed(interpreter.markers()),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render the tree.
|
// Render the tree.
|
||||||
let tree = TreeDisplay::new(
|
let tree = TreeDisplay::new(
|
||||||
&lock,
|
&lock,
|
||||||
(!universal).then(|| markers.as_ref()),
|
(!universal).then_some(&markers),
|
||||||
depth.into(),
|
depth.into(),
|
||||||
prune,
|
prune,
|
||||||
package,
|
package,
|
||||||
|
|
|
||||||
|
|
@ -514,7 +514,11 @@ async fn get_or_create_environment(
|
||||||
let constraints = [];
|
let constraints = [];
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
site_packages.satisfies(&requirements, &constraints),
|
site_packages.satisfies(
|
||||||
|
&requirements,
|
||||||
|
&constraints,
|
||||||
|
&interpreter.resolver_markers()
|
||||||
|
),
|
||||||
Ok(SatisfiesResult::Fresh { .. })
|
Ok(SatisfiesResult::Fresh { .. })
|
||||||
) {
|
) {
|
||||||
debug!("Using existing tool `{}`", from.name);
|
debug!("Using existing tool `{}`", from.name);
|
||||||
|
|
|
||||||
|
|
@ -6338,3 +6338,51 @@ fn no_extension() {
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Regression test for: <https://github.com/astral-sh/uv/pull/6646>
|
||||||
|
#[test]
|
||||||
|
fn switch_platform() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let requirements_txt = context.temp_dir.child("requirements.txt");
|
||||||
|
requirements_txt.write_str("iniconfig ; python_version == '3.12'")?;
|
||||||
|
|
||||||
|
// Install `iniconfig`.
|
||||||
|
uv_snapshot!(context.pip_install()
|
||||||
|
.arg("-r")
|
||||||
|
.arg("requirements.txt"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ iniconfig==2.0.0
|
||||||
|
"###);
|
||||||
|
|
||||||
|
requirements_txt
|
||||||
|
.write_str("iniconfig ; python_version == '3.12'\nanyio ; python_version < '3.12'")?;
|
||||||
|
|
||||||
|
// Add `anyio`, though it's only installed because of `--python-version`.
|
||||||
|
uv_snapshot!(context.pip_install()
|
||||||
|
.arg("-r")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--python-version")
|
||||||
|
.arg("3.11"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
Prepared 3 packages in [TIME]
|
||||||
|
Installed 3 packages in [TIME]
|
||||||
|
+ anyio==4.3.0
|
||||||
|
+ idna==3.6
|
||||||
|
+ sniffio==1.3.1
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue