mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-01 12:24:15 +00:00
Handle universal vs. fork markers with ResolverMarkers (#5099)
* Use a dedicated `ResolverMarkers` check in the fork state. This is better than the `MarkerTree::And(Vec::new())` check. * Report the timing correct naming universal resolution instead of two spaces around an empty string when there are no markers. * On resolution error, show the split that we're in. I'm not sure how to word this, since we're doing a universal resolution until we fork, so the trace may contain information from requirements that are not part of this fork.
This commit is contained in:
parent
fe403576c5
commit
a6dfd3953a
19 changed files with 220 additions and 133 deletions
|
|
@ -90,7 +90,7 @@ mod resolver {
|
|||
use uv_python::PythonEnvironment;
|
||||
use uv_resolver::{
|
||||
FlatIndex, InMemoryIndex, Manifest, OptionsBuilder, PythonRequirement, ResolutionGraph,
|
||||
Resolver,
|
||||
Resolver, ResolverMarkers,
|
||||
};
|
||||
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
|
||||
|
||||
|
|
@ -175,7 +175,7 @@ mod resolver {
|
|||
manifest,
|
||||
options,
|
||||
&python_requirement,
|
||||
Some(&MARKERS),
|
||||
ResolverMarkers::SpecificEnvironment(MARKERS.clone()),
|
||||
Some(&TAGS),
|
||||
&flat_index,
|
||||
&index,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ use uv_installer::{Installer, Plan, Planner, Preparer, SitePackages};
|
|||
use uv_python::{Interpreter, PythonEnvironment};
|
||||
use uv_resolver::{
|
||||
ExcludeNewer, FlatIndex, InMemoryIndex, Manifest, OptionsBuilder, PythonRequirement, Resolver,
|
||||
ResolverMarkers,
|
||||
};
|
||||
use uv_types::{BuildContext, BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
|
||||
|
||||
|
|
@ -146,7 +147,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
|||
.index_strategy(self.index_strategy)
|
||||
.build(),
|
||||
&python_requirement,
|
||||
Some(markers),
|
||||
ResolverMarkers::SpecificEnvironment(markers.clone()),
|
||||
Some(tags),
|
||||
self.flat_index,
|
||||
self.index,
|
||||
|
|
|
|||
|
|
@ -7,13 +7,12 @@ use thiserror::Error;
|
|||
use tracing::trace;
|
||||
|
||||
use distribution_types::{BuiltDist, Dist, DistributionMetadata, GitSourceDist, SourceDist};
|
||||
use pep508_rs::MarkerEnvironment;
|
||||
use pypi_types::{Requirement, RequirementSource};
|
||||
use uv_configuration::{Constraints, Overrides};
|
||||
use uv_distribution::{DistributionDatabase, Reporter};
|
||||
use uv_git::GitUrl;
|
||||
use uv_normalize::GroupName;
|
||||
use uv_resolver::{InMemoryIndex, MetadataResponse};
|
||||
use uv_resolver::{InMemoryIndex, MetadataResponse, ResolverMarkers};
|
||||
use uv_types::{BuildContext, HashStrategy, RequestedRequirements};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
@ -98,7 +97,7 @@ impl<'a, Context: BuildContext> LookaheadResolver<'a, Context> {
|
|||
/// to "only evaluate marker expressions that reference an extra name.")
|
||||
pub async fn resolve(
|
||||
self,
|
||||
markers: Option<&MarkerEnvironment>,
|
||||
markers: &ResolverMarkers,
|
||||
) -> Result<Vec<RequestedRequirements>, LookaheadError> {
|
||||
let mut results = Vec::new();
|
||||
let mut futures = FuturesUnordered::new();
|
||||
|
|
@ -108,7 +107,7 @@ impl<'a, Context: BuildContext> LookaheadResolver<'a, Context> {
|
|||
let mut queue: VecDeque<_> = self
|
||||
.constraints
|
||||
.apply(self.overrides.apply(self.requirements))
|
||||
.filter(|requirement| requirement.evaluate_markers(markers, &[]))
|
||||
.filter(|requirement| requirement.evaluate_markers(markers.marker_environment(), &[]))
|
||||
.map(|requirement| (*requirement).clone())
|
||||
.collect();
|
||||
|
||||
|
|
@ -127,7 +126,9 @@ impl<'a, Context: BuildContext> LookaheadResolver<'a, Context> {
|
|||
.constraints
|
||||
.apply(self.overrides.apply(lookahead.requirements()))
|
||||
{
|
||||
if requirement.evaluate_markers(markers, lookahead.extras()) {
|
||||
if requirement
|
||||
.evaluate_markers(markers.marker_environment(), lookahead.extras())
|
||||
{
|
||||
queue.push_back((*requirement).clone());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use crate::pubgrub::{
|
|||
PubGrubPackage, PubGrubPackageInner, PubGrubReportFormatter, PubGrubSpecifierError,
|
||||
};
|
||||
use crate::python_requirement::PythonRequirement;
|
||||
use crate::resolver::{IncompletePackage, UnavailablePackage, UnavailableReason};
|
||||
use crate::resolver::{IncompletePackage, ResolverMarkers, UnavailablePackage, UnavailableReason};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ResolveError {
|
||||
|
|
@ -50,10 +50,10 @@ pub enum ResolveError {
|
|||
ConflictingOverrideUrls(PackageName, String, String),
|
||||
|
||||
#[error("Requirements contain conflicting URLs for package `{0}`:\n- {}", _1.join("\n- "))]
|
||||
ConflictingUrls(PackageName, Vec<String>),
|
||||
ConflictingUrlsUniversal(PackageName, Vec<String>),
|
||||
|
||||
#[error("Requirements contain conflicting URLs for package `{package_name}` in split `{fork_markers}`:\n- {}", urls.join("\n- "))]
|
||||
ConflictingUrlsInFork {
|
||||
ConflictingUrlsFork {
|
||||
package_name: PackageName,
|
||||
urls: Vec<String>,
|
||||
fork_markers: MarkerTree,
|
||||
|
|
@ -125,9 +125,21 @@ pub struct NoSolutionError {
|
|||
unavailable_packages: FxHashMap<PackageName, UnavailablePackage>,
|
||||
incomplete_packages: FxHashMap<PackageName, BTreeMap<Version, IncompletePackage>>,
|
||||
fork_urls: ForkUrls,
|
||||
markers: ResolverMarkers,
|
||||
}
|
||||
|
||||
impl NoSolutionError {
|
||||
pub fn header(&self) -> String {
|
||||
match &self.markers {
|
||||
ResolverMarkers::Universal | ResolverMarkers::SpecificEnvironment(_) => {
|
||||
"No solution found when resolving dependencies:".to_string()
|
||||
}
|
||||
ResolverMarkers::Fork(markers) => {
|
||||
format!("No solution found when resolving dependencies for split ({markers}):")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(
|
||||
error: pubgrub::error::NoSolutionError<UvDependencyProvider>,
|
||||
available_versions: FxHashMap<PubGrubPackage, BTreeSet<Version>>,
|
||||
|
|
@ -137,6 +149,7 @@ impl NoSolutionError {
|
|||
unavailable_packages: FxHashMap<PackageName, UnavailablePackage>,
|
||||
incomplete_packages: FxHashMap<PackageName, BTreeMap<Version, IncompletePackage>>,
|
||||
fork_urls: ForkUrls,
|
||||
markers: ResolverMarkers,
|
||||
) -> Self {
|
||||
Self {
|
||||
error,
|
||||
|
|
@ -147,6 +160,7 @@ impl NoSolutionError {
|
|||
unavailable_packages,
|
||||
incomplete_packages,
|
||||
fork_urls,
|
||||
markers,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ use std::collections::hash_map::Entry;
|
|||
use rustc_hash::FxHashMap;
|
||||
|
||||
use distribution_types::Verbatim;
|
||||
use pep508_rs::MarkerTree;
|
||||
use pypi_types::VerbatimParsedUrl;
|
||||
use uv_normalize::PackageName;
|
||||
|
||||
use crate::resolver::ResolverMarkers;
|
||||
use crate::ResolveError;
|
||||
|
||||
/// See [`crate::resolver::SolveState`].
|
||||
|
|
@ -29,7 +29,7 @@ impl ForkUrls {
|
|||
&mut self,
|
||||
package_name: &PackageName,
|
||||
url: &VerbatimParsedUrl,
|
||||
fork_markers: &MarkerTree,
|
||||
fork_markers: &ResolverMarkers,
|
||||
) -> Result<(), ResolveError> {
|
||||
match self.0.entry(package_name.clone()) {
|
||||
Entry::Occupied(previous) => {
|
||||
|
|
@ -39,17 +39,20 @@ impl ForkUrls {
|
|||
url.verbatim.verbatim().to_string(),
|
||||
];
|
||||
conflicting_url.sort();
|
||||
return if fork_markers.is_universal() {
|
||||
Err(ResolveError::ConflictingUrls(
|
||||
package_name.clone(),
|
||||
conflicting_url,
|
||||
))
|
||||
} else {
|
||||
Err(ResolveError::ConflictingUrlsInFork {
|
||||
package_name: package_name.clone(),
|
||||
urls: conflicting_url,
|
||||
fork_markers: fork_markers.clone(),
|
||||
})
|
||||
return match fork_markers {
|
||||
ResolverMarkers::Universal | ResolverMarkers::SpecificEnvironment(_) => {
|
||||
Err(ResolveError::ConflictingUrlsUniversal(
|
||||
package_name.clone(),
|
||||
conflicting_url,
|
||||
))
|
||||
}
|
||||
ResolverMarkers::Fork(fork_markers) => {
|
||||
Err(ResolveError::ConflictingUrlsFork {
|
||||
package_name: package_name.clone(),
|
||||
urls: conflicting_url,
|
||||
fork_markers: fork_markers.clone(),
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ pub use resolution::{AnnotationStyle, DisplayResolutionGraph, ResolutionGraph};
|
|||
pub use resolution_mode::ResolutionMode;
|
||||
pub use resolver::{
|
||||
BuildId, DefaultResolverProvider, InMemoryIndex, MetadataResponse, PackageVersionsResult,
|
||||
Reporter as ResolverReporter, Resolver, ResolverProvider, VersionsResponse,
|
||||
Reporter as ResolverReporter, Resolver, ResolverMarkers, ResolverProvider, VersionsResponse,
|
||||
WheelMetadataResult,
|
||||
};
|
||||
pub use version_map::VersionMap;
|
||||
|
|
|
|||
|
|
@ -7,12 +7,13 @@ use petgraph::Direction;
|
|||
use rustc_hash::{FxBuildHasher, FxHashMap};
|
||||
|
||||
use distribution_types::{DistributionMetadata, Name, SourceAnnotation, SourceAnnotations};
|
||||
use pep508_rs::MarkerEnvironment;
|
||||
use pep508_rs::MarkerTree;
|
||||
use uv_normalize::PackageName;
|
||||
|
||||
use crate::resolution::{RequirementsTxtDist, ResolutionGraphNode};
|
||||
use crate::{marker, ResolutionGraph};
|
||||
use crate::{marker, ResolutionGraph, ResolverMarkers};
|
||||
|
||||
static UNIVERSAL_MARKERS: ResolverMarkers = ResolverMarkers::Universal;
|
||||
|
||||
/// A [`std::fmt::Display`] implementation for the resolution graph.
|
||||
#[derive(Debug)]
|
||||
|
|
@ -21,7 +22,7 @@ pub struct DisplayResolutionGraph<'a> {
|
|||
/// The underlying graph.
|
||||
resolution: &'a ResolutionGraph,
|
||||
/// The marker environment, used to determine the markers that apply to each package.
|
||||
marker_env: Option<&'a MarkerEnvironment>,
|
||||
marker_env: &'a ResolverMarkers,
|
||||
/// The packages to exclude from the output.
|
||||
no_emit_packages: &'a [PackageName],
|
||||
/// Whether to include hashes in the output.
|
||||
|
|
@ -50,7 +51,7 @@ impl<'a> From<&'a ResolutionGraph> for DisplayResolutionGraph<'a> {
|
|||
fn from(resolution: &'a ResolutionGraph) -> Self {
|
||||
Self::new(
|
||||
resolution,
|
||||
None,
|
||||
&UNIVERSAL_MARKERS,
|
||||
&[],
|
||||
false,
|
||||
false,
|
||||
|
|
@ -67,7 +68,7 @@ impl<'a> DisplayResolutionGraph<'a> {
|
|||
#[allow(clippy::fn_params_excessive_bools)]
|
||||
pub fn new(
|
||||
underlying: &'a ResolutionGraph,
|
||||
marker_env: Option<&'a MarkerEnvironment>,
|
||||
marker_env: &'a ResolverMarkers,
|
||||
no_emit_packages: &'a [PackageName],
|
||||
show_hashes: bool,
|
||||
include_extras: bool,
|
||||
|
|
@ -97,12 +98,9 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
|||
let sources = if self.include_annotations {
|
||||
let mut sources = SourceAnnotations::default();
|
||||
|
||||
for requirement in self
|
||||
.resolution
|
||||
.requirements
|
||||
.iter()
|
||||
.filter(|requirement| requirement.evaluate_markers(self.marker_env, &[]))
|
||||
{
|
||||
for requirement in self.resolution.requirements.iter().filter(|requirement| {
|
||||
requirement.evaluate_markers(self.marker_env.marker_environment(), &[])
|
||||
}) {
|
||||
if let Some(origin) = &requirement.origin {
|
||||
sources.add(
|
||||
&requirement.name,
|
||||
|
|
@ -115,7 +113,9 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
|||
.resolution
|
||||
.constraints
|
||||
.requirements()
|
||||
.filter(|requirement| requirement.evaluate_markers(self.marker_env, &[]))
|
||||
.filter(|requirement| {
|
||||
requirement.evaluate_markers(self.marker_env.marker_environment(), &[])
|
||||
})
|
||||
{
|
||||
if let Some(origin) = &requirement.origin {
|
||||
sources.add(
|
||||
|
|
@ -129,7 +129,9 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
|||
.resolution
|
||||
.overrides
|
||||
.requirements()
|
||||
.filter(|requirement| requirement.evaluate_markers(self.marker_env, &[]))
|
||||
.filter(|requirement| {
|
||||
requirement.evaluate_markers(self.marker_env.marker_environment(), &[])
|
||||
})
|
||||
{
|
||||
if let Some(origin) = &requirement.origin {
|
||||
sources.add(
|
||||
|
|
|
|||
|
|
@ -28,9 +28,10 @@ use distribution_types::{
|
|||
};
|
||||
pub(crate) use locals::ForkLocals;
|
||||
use pep440_rs::{Version, MIN_VERSION};
|
||||
use pep508_rs::{MarkerEnvironment, MarkerTree};
|
||||
use pep508_rs::MarkerTree;
|
||||
use platform_tags::Tags;
|
||||
use pypi_types::{Metadata23, Requirement, VerbatimParsedUrl};
|
||||
pub use resolver_markers::ResolverMarkers;
|
||||
pub(crate) use urls::Urls;
|
||||
use uv_configuration::{Constraints, Overrides};
|
||||
use uv_distribution::{ArchiveMetadata, DistributionDatabase};
|
||||
|
|
@ -73,6 +74,7 @@ mod index;
|
|||
mod locals;
|
||||
mod provider;
|
||||
mod reporter;
|
||||
mod resolver_markers;
|
||||
mod urls;
|
||||
|
||||
pub struct Resolver<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider> {
|
||||
|
|
@ -94,8 +96,7 @@ struct ResolverState<InstalledPackages: InstalledPackagesProvider> {
|
|||
urls: Urls,
|
||||
dependency_mode: DependencyMode,
|
||||
hasher: HashStrategy,
|
||||
/// When not set, the resolver is in "universal" mode.
|
||||
markers: Option<MarkerEnvironment>,
|
||||
markers: ResolverMarkers,
|
||||
python_requirement: PythonRequirement,
|
||||
/// This is derived from `PythonRequirement` once at initialization
|
||||
/// time. It's used in universal mode to filter our dependencies with
|
||||
|
|
@ -140,7 +141,7 @@ impl<'a, Context: BuildContext, InstalledPackages: InstalledPackagesProvider>
|
|||
manifest: Manifest,
|
||||
options: Options,
|
||||
python_requirement: &'a PythonRequirement,
|
||||
markers: Option<&'a MarkerEnvironment>,
|
||||
markers: ResolverMarkers,
|
||||
tags: Option<&'a Tags>,
|
||||
flat_index: &'a FlatIndex,
|
||||
index: &'a InMemoryIndex,
|
||||
|
|
@ -156,7 +157,11 @@ impl<'a, Context: BuildContext, InstalledPackages: InstalledPackagesProvider>
|
|||
python_requirement
|
||||
.target()
|
||||
.and_then(|target| target.as_requires_python()),
|
||||
AllowedYanks::from_manifest(&manifest, markers, options.dependency_mode),
|
||||
AllowedYanks::from_manifest(
|
||||
&manifest,
|
||||
markers.marker_environment(),
|
||||
options.dependency_mode,
|
||||
),
|
||||
hasher,
|
||||
options.exclude_newer,
|
||||
build_context.build_options(),
|
||||
|
|
@ -184,7 +189,7 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
|
|||
manifest: Manifest,
|
||||
options: Options,
|
||||
hasher: &HashStrategy,
|
||||
markers: Option<&MarkerEnvironment>,
|
||||
markers: ResolverMarkers,
|
||||
python_requirement: &PythonRequirement,
|
||||
index: &InMemoryIndex,
|
||||
git: &GitResolver,
|
||||
|
|
@ -196,9 +201,18 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
|
|||
git: git.clone(),
|
||||
unavailable_packages: DashMap::default(),
|
||||
incomplete_packages: DashMap::default(),
|
||||
selector: CandidateSelector::for_resolution(options, &manifest, markers),
|
||||
selector: CandidateSelector::for_resolution(
|
||||
options,
|
||||
&manifest,
|
||||
markers.marker_environment(),
|
||||
),
|
||||
dependency_mode: options.dependency_mode,
|
||||
urls: Urls::from_manifest(&manifest, markers, git, options.dependency_mode)?,
|
||||
urls: Urls::from_manifest(
|
||||
&manifest,
|
||||
markers.marker_environment(),
|
||||
git,
|
||||
options.dependency_mode,
|
||||
)?,
|
||||
project: manifest.project,
|
||||
requirements: manifest.requirements,
|
||||
constraints: manifest.constraints,
|
||||
|
|
@ -207,12 +221,12 @@ impl<Provider: ResolverProvider, InstalledPackages: InstalledPackagesProvider>
|
|||
preferences: manifest.preferences,
|
||||
exclusions: manifest.exclusions,
|
||||
hasher: hasher.clone(),
|
||||
markers: markers.cloned(),
|
||||
requires_python: if markers.is_some() {
|
||||
requires_python: if markers.marker_environment().is_some() {
|
||||
None
|
||||
} else {
|
||||
python_requirement.to_marker_tree()
|
||||
},
|
||||
markers,
|
||||
python_requirement: python_requirement.clone(),
|
||||
reporter: None,
|
||||
installed_packages,
|
||||
|
|
@ -296,7 +310,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
fork_locals: ForkLocals::default(),
|
||||
priorities: PubGrubPriorities::default(),
|
||||
added_dependencies: FxHashMap::default(),
|
||||
markers: MarkerTree::And(vec![]),
|
||||
markers: self.markers.clone(),
|
||||
python_requirement: self.python_requirement.clone(),
|
||||
requires_python: self.requires_python.clone(),
|
||||
};
|
||||
|
|
@ -305,14 +319,14 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
let mut resolutions = vec![];
|
||||
|
||||
'FORK: while let Some(mut state) = forked_states.pop() {
|
||||
if !state.markers.is_universal() {
|
||||
if let ResolverMarkers::Fork(markers) = &state.markers {
|
||||
if let Some(requires_python) = state.requires_python.as_ref() {
|
||||
debug!(
|
||||
"Solving split {} (requires-python: {})",
|
||||
state.markers, requires_python
|
||||
markers, requires_python
|
||||
);
|
||||
} else {
|
||||
debug!("Solving split {}", state.markers);
|
||||
debug!("Solving split {}", markers);
|
||||
}
|
||||
}
|
||||
let start = Instant::now();
|
||||
|
|
@ -321,7 +335,8 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
if let Err(err) = state.pubgrub.unit_propagation(state.next.clone()) {
|
||||
return Err(self.convert_no_solution_err(
|
||||
err,
|
||||
state.fork_urls.clone(),
|
||||
state.fork_urls,
|
||||
state.markers,
|
||||
&visited,
|
||||
&index_locations,
|
||||
));
|
||||
|
|
@ -348,7 +363,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
prefetcher.log_tried_versions();
|
||||
}
|
||||
debug!(
|
||||
"Split {} took {:.3}s",
|
||||
"Split {} resolution took {:.3}s",
|
||||
state.markers,
|
||||
start.elapsed().as_secs_f32()
|
||||
);
|
||||
|
|
@ -540,15 +555,15 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
cur_state = Some(forked_state.clone());
|
||||
}
|
||||
|
||||
forked_state.markers.and(fork.markers);
|
||||
forked_state.markers = normalize(forked_state.markers, None)
|
||||
.unwrap_or(MarkerTree::And(Vec::new()));
|
||||
let combined_markers = forked_state.markers.and(fork.markers);
|
||||
let combined_markers = normalize(combined_markers, None)
|
||||
.expect("Fork markers are universal");
|
||||
|
||||
// If the fork contains a narrowed Python requirement, apply it.
|
||||
let python_requirement = requires_python_marker(
|
||||
&forked_state.markers,
|
||||
)
|
||||
.and_then(|marker| forked_state.python_requirement.narrow(&marker));
|
||||
let python_requirement = requires_python_marker(&combined_markers)
|
||||
.and_then(|marker| {
|
||||
forked_state.python_requirement.narrow(&marker)
|
||||
});
|
||||
if let Some(python_requirement) = python_requirement {
|
||||
if let Some(target) = python_requirement.target() {
|
||||
debug!("Narrowed `requires-python` bound to: {target}");
|
||||
|
|
@ -562,6 +577,8 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
forked_state.python_requirement = python_requirement;
|
||||
}
|
||||
|
||||
forked_state.markers = ResolverMarkers::Fork(combined_markers);
|
||||
|
||||
forked_state.add_package_version_dependencies(
|
||||
for_package.as_deref(),
|
||||
&version,
|
||||
|
|
@ -584,18 +601,11 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
}
|
||||
forked_states.push(forked_state);
|
||||
}
|
||||
if markers.is_universal() {
|
||||
debug!(
|
||||
"Pre-fork split universal took {:.3}s",
|
||||
start.elapsed().as_secs_f32()
|
||||
);
|
||||
} else {
|
||||
debug!(
|
||||
"Pre-fork split {} took {:.3}s",
|
||||
markers,
|
||||
start.elapsed().as_secs_f32()
|
||||
);
|
||||
}
|
||||
debug!(
|
||||
"Pre-fork split {} took {:.3}s",
|
||||
markers,
|
||||
start.elapsed().as_secs_f32()
|
||||
);
|
||||
continue 'FORK;
|
||||
}
|
||||
}
|
||||
|
|
@ -1083,7 +1093,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
version: &Version,
|
||||
fork_urls: &ForkUrls,
|
||||
fork_locals: &ForkLocals,
|
||||
markers: &MarkerTree,
|
||||
markers: &ResolverMarkers,
|
||||
requires_python: Option<&MarkerTree>,
|
||||
) -> Result<ForkedDependencies, ResolveError> {
|
||||
let result = self.get_dependencies(
|
||||
|
|
@ -1094,13 +1104,13 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
markers,
|
||||
requires_python,
|
||||
);
|
||||
if self.markers.is_some() {
|
||||
return result.map(|deps| match deps {
|
||||
match markers {
|
||||
ResolverMarkers::SpecificEnvironment(_) => result.map(|deps| match deps {
|
||||
Dependencies::Available(deps) => ForkedDependencies::Unforked(deps),
|
||||
Dependencies::Unavailable(err) => ForkedDependencies::Unavailable(err),
|
||||
});
|
||||
}),
|
||||
ResolverMarkers::Universal | ResolverMarkers::Fork(_) => Ok(result?.fork()),
|
||||
}
|
||||
Ok(result?.fork())
|
||||
}
|
||||
|
||||
/// Given a candidate package and version, return its dependencies.
|
||||
|
|
@ -1111,7 +1121,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
version: &Version,
|
||||
fork_urls: &ForkUrls,
|
||||
fork_locals: &ForkLocals,
|
||||
markers: &MarkerTree,
|
||||
markers: &ResolverMarkers,
|
||||
requires_python: Option<&MarkerTree>,
|
||||
) -> Result<Dependencies, ResolveError> {
|
||||
let url = package.name().and_then(|name| fork_urls.get(name));
|
||||
|
|
@ -1383,7 +1393,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
extra: Option<&'a ExtraName>,
|
||||
dev: Option<&'a GroupName>,
|
||||
name: Option<&PackageName>,
|
||||
markers: &'a MarkerTree,
|
||||
markers: &'a ResolverMarkers,
|
||||
requires_python: Option<&'a MarkerTree>,
|
||||
) -> Vec<Cow<'a, Requirement>> {
|
||||
// Start with the requirements for the current extra of the package (for an extra
|
||||
|
|
@ -1448,7 +1458,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
&'data self,
|
||||
dependencies: impl IntoIterator<Item = &'data Requirement> + 'parameters,
|
||||
extra: Option<&'parameters ExtraName>,
|
||||
markers: &'parameters MarkerTree,
|
||||
markers: &'parameters ResolverMarkers,
|
||||
requires_python: Option<&'parameters MarkerTree>,
|
||||
) -> impl Iterator<Item = Cow<'data, Requirement>> + 'parameters
|
||||
where
|
||||
|
|
@ -1469,31 +1479,31 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
return false;
|
||||
}
|
||||
|
||||
// If we're in universal mode, `fork_markers` might correspond to a
|
||||
// non-trivial marker expression that provoked the resolver to fork.
|
||||
// In that case, we should ignore any dependency that cannot possibly
|
||||
// satisfy the markers that provoked the fork.
|
||||
if !possible_to_satisfy_markers(markers, requirement) {
|
||||
trace!("skipping {requirement} because of context resolver markers {markers}");
|
||||
return false;
|
||||
// If we're in a fork in universal mode, ignore any dependency that isn't part of
|
||||
// this fork (but will be part of another fork).
|
||||
if let ResolverMarkers::Fork(markers) = markers {
|
||||
if !possible_to_satisfy_markers(markers, requirement) {
|
||||
trace!("skipping {requirement} because of context resolver markers {markers}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If the requirement isn't relevant for the current platform, skip it.
|
||||
match extra {
|
||||
Some(source_extra) => {
|
||||
// Only include requirements that are relevant for the current extra.
|
||||
if requirement.evaluate_markers(self.markers.as_ref(), &[]) {
|
||||
if requirement.evaluate_markers(markers.marker_environment(), &[]) {
|
||||
return false;
|
||||
}
|
||||
if !requirement.evaluate_markers(
|
||||
self.markers.as_ref(),
|
||||
markers.marker_environment(),
|
||||
std::slice::from_ref(source_extra),
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if !requirement.evaluate_markers(self.markers.as_ref(), &[]) {
|
||||
if !requirement.evaluate_markers(markers.marker_environment(), &[]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1516,23 +1526,27 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
return false;
|
||||
}
|
||||
|
||||
if !possible_to_satisfy_markers(markers, constraint) {
|
||||
trace!("skipping {constraint} because of context resolver markers {markers}");
|
||||
return false;
|
||||
// If we're in a fork in universal mode, ignore any dependency that isn't part of
|
||||
// this fork (but will be part of another fork).
|
||||
if let ResolverMarkers::Fork(markers) = markers {
|
||||
if !possible_to_satisfy_markers(markers, constraint) {
|
||||
trace!("skipping {constraint} because of context resolver markers {markers}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If the constraint isn't relevant for the current platform, skip it.
|
||||
match extra {
|
||||
Some(source_extra) => {
|
||||
if !constraint.evaluate_markers(
|
||||
self.markers.as_ref(),
|
||||
markers.marker_environment(),
|
||||
std::slice::from_ref(source_extra),
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if !constraint.evaluate_markers(self.markers.as_ref(), &[]) {
|
||||
if !constraint.evaluate_markers(markers.marker_environment(), &[]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1835,6 +1849,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
&self,
|
||||
mut err: pubgrub::error::NoSolutionError<UvDependencyProvider>,
|
||||
fork_urls: ForkUrls,
|
||||
markers: ResolverMarkers,
|
||||
visited: &FxHashSet<PackageName>,
|
||||
index_locations: &IndexLocations,
|
||||
) -> ResolveError {
|
||||
|
|
@ -1897,6 +1912,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
|
|||
unavailable_packages,
|
||||
incomplete_packages,
|
||||
fork_urls,
|
||||
markers,
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -1976,7 +1992,7 @@ struct ForkState {
|
|||
/// completely disjoint marker expression (i.e., it can never be true given
|
||||
/// that the marker expression that provoked the fork is true), then that
|
||||
/// dependency is completely ignored.
|
||||
markers: MarkerTree,
|
||||
markers: ResolverMarkers,
|
||||
/// The Python requirement for this fork. Defaults to the Python requirement for
|
||||
/// the resolution, but may be narrowed if a `python_version` marker is present
|
||||
/// in a given fork.
|
||||
|
|
|
|||
50
crates/uv-resolver/src/resolver/resolver_markers.rs
Normal file
50
crates/uv-resolver/src/resolver/resolver_markers.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
use pep508_rs::{MarkerEnvironment, MarkerTree};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Whether we're solving for a specific environment, universally or for a specific fork.
|
||||
pub enum ResolverMarkers {
|
||||
/// We're solving for this specific environment only.
|
||||
SpecificEnvironment(MarkerEnvironment),
|
||||
/// We're doing a universal resolution for all environments (a python version
|
||||
/// constraint is expressed separately).
|
||||
Universal,
|
||||
/// We're in a fork of the universal resolution solving only for specific markers.
|
||||
Fork(MarkerTree),
|
||||
}
|
||||
|
||||
impl ResolverMarkers {
|
||||
/// Add the markers of an initial or subsequent fork to the current markers.
|
||||
pub(crate) fn and(self, other: MarkerTree) -> MarkerTree {
|
||||
match self {
|
||||
ResolverMarkers::Universal => other,
|
||||
ResolverMarkers::Fork(mut current) => {
|
||||
current.and(other);
|
||||
current
|
||||
}
|
||||
ResolverMarkers::SpecificEnvironment(_) => {
|
||||
unreachable!("Specific environment mode must not fork")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If solving for a specific environment, return this environment
|
||||
pub fn marker_environment(&self) -> Option<&MarkerEnvironment> {
|
||||
match self {
|
||||
ResolverMarkers::Universal | ResolverMarkers::Fork(_) => None,
|
||||
ResolverMarkers::SpecificEnvironment(env) => Some(env),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ResolverMarkers {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ResolverMarkers::Universal => f.write_str("universal"),
|
||||
ResolverMarkers::SpecificEnvironment(_) => f.write_str("specific environment"),
|
||||
ResolverMarkers::Fork(markers) => {
|
||||
write!(f, "({markers})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -174,7 +174,7 @@ impl Urls {
|
|||
.chain(iter::once(verbatim_url.verbatim().to_string()))
|
||||
.collect();
|
||||
conflicting_urls.sort();
|
||||
return Err(ResolveError::ConflictingUrls(
|
||||
return Err(ResolveError::ConflictingUrlsUniversal(
|
||||
package_name.clone(),
|
||||
conflicting_urls,
|
||||
));
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ use uv_requirements::{
|
|||
use uv_resolver::{
|
||||
AnnotationStyle, DependencyMode, DisplayResolutionGraph, ExcludeNewer, FlatIndex,
|
||||
InMemoryIndex, OptionsBuilder, PreReleaseMode, PythonRequirement, RequiresPython,
|
||||
ResolutionMode,
|
||||
ResolutionMode, ResolverMarkers,
|
||||
};
|
||||
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
|
||||
use uv_warnings::warn_user;
|
||||
|
|
@ -230,11 +230,14 @@ pub(crate) async fn pip_compile(
|
|||
|
||||
// Determine the environment for the resolution.
|
||||
let (tags, markers) = if universal {
|
||||
(None, None)
|
||||
(None, ResolverMarkers::Universal)
|
||||
} else {
|
||||
let (tags, markers) =
|
||||
resolution_environment(python_version, python_platform, &interpreter)?;
|
||||
(Some(tags), Some(markers))
|
||||
(
|
||||
Some(tags),
|
||||
ResolverMarkers::SpecificEnvironment((*markers).clone()),
|
||||
)
|
||||
};
|
||||
|
||||
// Generate, but don't enforce hashes for the requirements.
|
||||
|
|
@ -335,7 +338,7 @@ pub(crate) async fn pip_compile(
|
|||
&Reinstall::None,
|
||||
&upgrade,
|
||||
tags.as_deref(),
|
||||
markers.as_deref(),
|
||||
markers.clone(),
|
||||
python_requirement,
|
||||
&client,
|
||||
&flat_index,
|
||||
|
|
@ -351,8 +354,7 @@ pub(crate) async fn pip_compile(
|
|||
{
|
||||
Ok(resolution) => resolution,
|
||||
Err(operations::Error::Resolve(uv_resolver::ResolveError::NoSolution(err))) => {
|
||||
let report = miette::Report::msg(format!("{err}"))
|
||||
.context("No solution found when resolving dependencies:");
|
||||
let report = miette::Report::msg(format!("{err}")).context(err.header());
|
||||
eprint!("{report:?}");
|
||||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
|
|
@ -384,7 +386,7 @@ pub(crate) async fn pip_compile(
|
|||
}
|
||||
|
||||
if include_marker_expression {
|
||||
if let Some(markers) = markers.as_deref() {
|
||||
if let ResolverMarkers::SpecificEnvironment(markers) = &markers {
|
||||
let relevant_markers = resolution.marker_tree(&top_level_index, markers)?;
|
||||
writeln!(
|
||||
writer,
|
||||
|
|
@ -457,7 +459,7 @@ pub(crate) async fn pip_compile(
|
|||
"{}",
|
||||
DisplayResolutionGraph::new(
|
||||
&resolution,
|
||||
markers.as_deref(),
|
||||
&markers,
|
||||
&no_emit_packages,
|
||||
generate_hashes,
|
||||
include_extras,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ use uv_python::{
|
|||
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||
use uv_resolver::{
|
||||
DependencyMode, ExcludeNewer, FlatIndex, OptionsBuilder, PreReleaseMode, PythonRequirement,
|
||||
ResolutionMode,
|
||||
ResolutionMode, ResolverMarkers,
|
||||
};
|
||||
use uv_types::{BuildIsolation, HashStrategy};
|
||||
|
||||
|
|
@ -326,7 +326,7 @@ pub(crate) async fn pip_install(
|
|||
&reinstall,
|
||||
&upgrade,
|
||||
Some(&tags),
|
||||
Some(&markers),
|
||||
ResolverMarkers::SpecificEnvironment((*markers).clone()),
|
||||
python_requirement,
|
||||
&client,
|
||||
&flat_index,
|
||||
|
|
@ -342,8 +342,7 @@ pub(crate) async fn pip_install(
|
|||
{
|
||||
Ok(resolution) => Resolution::from(resolution),
|
||||
Err(operations::Error::Resolve(uv_resolver::ResolveError::NoSolution(err))) => {
|
||||
let report = miette::Report::msg(format!("{err}"))
|
||||
.context("No solution found when resolving dependencies:");
|
||||
let report = miette::Report::msg(format!("{err}")).context(err.header());
|
||||
eprint!("{report:?}");
|
||||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ use distribution_types::{
|
|||
DistributionMetadata, IndexLocations, InstalledMetadata, LocalDist, Name, Resolution,
|
||||
};
|
||||
use install_wheel_rs::linker::LinkMode;
|
||||
use pep508_rs::MarkerEnvironment;
|
||||
use platform_tags::Tags;
|
||||
use pypi_types::Requirement;
|
||||
use uv_cache::Cache;
|
||||
|
|
@ -37,7 +36,7 @@ use uv_requirements::{
|
|||
};
|
||||
use uv_resolver::{
|
||||
DependencyMode, Exclusions, FlatIndex, InMemoryIndex, Manifest, Options, Preference,
|
||||
Preferences, PythonRequirement, ResolutionGraph, Resolver,
|
||||
Preferences, PythonRequirement, ResolutionGraph, Resolver, ResolverMarkers,
|
||||
};
|
||||
use uv_types::{HashStrategy, InFlight, InstalledPackagesProvider};
|
||||
use uv_warnings::warn_user;
|
||||
|
|
@ -88,7 +87,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
|
|||
reinstall: &Reinstall,
|
||||
upgrade: &Upgrade,
|
||||
tags: Option<&Tags>,
|
||||
markers: Option<&MarkerEnvironment>,
|
||||
markers: ResolverMarkers,
|
||||
python_requirement: PythonRequirement,
|
||||
client: &RegistryClient,
|
||||
flat_index: &FlatIndex,
|
||||
|
|
@ -188,7 +187,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
|
|||
.chain(upgrade.constraints().cloned()),
|
||||
);
|
||||
let overrides = Overrides::from_requirements(overrides);
|
||||
let preferences = Preferences::from_iter(preferences, markers);
|
||||
let preferences = Preferences::from_iter(preferences, markers.marker_environment());
|
||||
|
||||
// Determine any lookahead requirements.
|
||||
let lookaheads = match options.dependency_mode {
|
||||
|
|
@ -203,7 +202,7 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
|
|||
DistributionDatabase::new(client, build_dispatch, concurrency.downloads, preview),
|
||||
)
|
||||
.with_reporter(ResolverReporter::from(printer))
|
||||
.resolve(markers)
|
||||
.resolve(&markers)
|
||||
.await?
|
||||
}
|
||||
DependencyMode::Direct => Vec::new(),
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use uv_python::{
|
|||
use uv_requirements::{RequirementsSource, RequirementsSpecification};
|
||||
use uv_resolver::{
|
||||
DependencyMode, ExcludeNewer, FlatIndex, OptionsBuilder, PreReleaseMode, PythonRequirement,
|
||||
ResolutionMode,
|
||||
ResolutionMode, ResolverMarkers,
|
||||
};
|
||||
use uv_types::{BuildIsolation, HashStrategy};
|
||||
|
||||
|
|
@ -281,7 +281,7 @@ pub(crate) async fn pip_sync(
|
|||
&reinstall,
|
||||
&upgrade,
|
||||
Some(&tags),
|
||||
Some(&markers),
|
||||
ResolverMarkers::SpecificEnvironment((*markers).clone()),
|
||||
python_requirement,
|
||||
&client,
|
||||
&flat_index,
|
||||
|
|
@ -297,8 +297,7 @@ pub(crate) async fn pip_sync(
|
|||
{
|
||||
Ok(resolution) => Resolution::from(resolution),
|
||||
Err(operations::Error::Resolve(uv_resolver::ResolveError::NoSolution(err))) => {
|
||||
let report = miette::Report::msg(format!("{err}"))
|
||||
.context("No solution found when resolving dependencies:");
|
||||
let report = miette::Report::msg(format!("{err}")).context(err.header());
|
||||
eprint!("{report:?}");
|
||||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ use uv_git::ResolvedRepositoryReference;
|
|||
use uv_normalize::PackageName;
|
||||
use uv_python::{Interpreter, PythonFetch, PythonPreference, PythonRequest};
|
||||
use uv_requirements::upgrade::{read_lock_requirements, LockedRequirements};
|
||||
use uv_resolver::{FlatIndex, Lock, OptionsBuilder, PythonRequirement, RequiresPython};
|
||||
use uv_resolver::{
|
||||
FlatIndex, Lock, OptionsBuilder, PythonRequirement, RequiresPython, ResolverMarkers,
|
||||
};
|
||||
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy};
|
||||
use uv_warnings::{warn_user, warn_user_once};
|
||||
|
||||
|
|
@ -88,8 +90,7 @@ pub(crate) async fn lock(
|
|||
Err(ProjectError::Operation(pip::operations::Error::Resolve(
|
||||
uv_resolver::ResolveError::NoSolution(err),
|
||||
))) => {
|
||||
let report = miette::Report::msg(format!("{err}"))
|
||||
.context("No solution found when resolving dependencies:");
|
||||
let report = miette::Report::msg(format!("{err}")).context(err.header());
|
||||
eprint!("{report:?}");
|
||||
Ok(ExitStatus::Failure)
|
||||
}
|
||||
|
|
@ -274,7 +275,7 @@ pub(super) async fn do_lock(
|
|||
&Reinstall::default(),
|
||||
upgrade,
|
||||
None,
|
||||
None,
|
||||
ResolverMarkers::Universal,
|
||||
python_requirement.clone(),
|
||||
&client,
|
||||
&flat_index,
|
||||
|
|
@ -350,7 +351,7 @@ pub(super) async fn do_lock(
|
|||
&Reinstall::default(),
|
||||
upgrade,
|
||||
None,
|
||||
None,
|
||||
ResolverMarkers::Universal,
|
||||
python_requirement,
|
||||
&client,
|
||||
&flat_index,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ use uv_python::{
|
|||
PythonInstallation, PythonPreference, PythonRequest, VersionRequest,
|
||||
};
|
||||
use uv_requirements::{NamedRequirementsResolver, RequirementsSpecification};
|
||||
use uv_resolver::{FlatIndex, OptionsBuilder, PythonRequirement, RequiresPython, ResolutionGraph};
|
||||
use uv_resolver::{
|
||||
FlatIndex, OptionsBuilder, PythonRequirement, RequiresPython, ResolutionGraph, ResolverMarkers,
|
||||
};
|
||||
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy};
|
||||
use uv_warnings::warn_user;
|
||||
|
||||
|
|
@ -485,7 +487,7 @@ pub(crate) async fn resolve_environment<'a>(
|
|||
&reinstall,
|
||||
&upgrade,
|
||||
Some(tags),
|
||||
Some(markers),
|
||||
ResolverMarkers::SpecificEnvironment(markers.clone()),
|
||||
python_requirement,
|
||||
&client,
|
||||
&flat_index,
|
||||
|
|
@ -737,7 +739,7 @@ pub(crate) async fn update_environment(
|
|||
reinstall,
|
||||
upgrade,
|
||||
Some(tags),
|
||||
Some(markers),
|
||||
ResolverMarkers::SpecificEnvironment(markers.clone()),
|
||||
python_requirement,
|
||||
&client,
|
||||
&flat_index,
|
||||
|
|
|
|||
|
|
@ -92,8 +92,7 @@ pub(crate) async fn sync(
|
|||
Err(ProjectError::Operation(pip::operations::Error::Resolve(
|
||||
uv_resolver::ResolveError::NoSolution(err),
|
||||
))) => {
|
||||
let report = miette::Report::msg(format!("{err}"))
|
||||
.context("No solution found when resolving dependencies:");
|
||||
let report = miette::Report::msg(format!("{err}")).context(err.header());
|
||||
anstream::eprint!("{report:?}");
|
||||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
|
|
@ -133,8 +132,7 @@ pub(crate) async fn sync(
|
|||
Err(ProjectError::Operation(pip::operations::Error::Resolve(
|
||||
uv_resolver::ResolveError::NoSolution(err),
|
||||
))) => {
|
||||
let report = miette::Report::msg(format!("{err}"))
|
||||
.context("No solution found when resolving dependencies:");
|
||||
let report = miette::Report::msg(format!("{err}")).context(err.header());
|
||||
anstream::eprint!("{report:?}");
|
||||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -362,7 +362,7 @@ fn conflict_in_fork() -> Result<()> {
|
|||
|
||||
----- stderr -----
|
||||
warning: `uv lock` is experimental and may change without warning.
|
||||
× No solution found when resolving dependencies:
|
||||
× No solution found when resolving dependencies for split (sys_platform == 'darwin'):
|
||||
╰─▶ Because only package-b==1.0.0 is available and package-b==1.0.0 depends on package-d==1, we can conclude that all versions of package-b depend on package-d==1.
|
||||
And because package-c==1.0.0 depends on package-d==2 and only package-c==1.0.0 is available, we can conclude that all versions of package-b and all versions of package-c are incompatible.
|
||||
And because package-a{sys_platform == 'darwin'}==1.0.0 depends on package-b and package-c, we can conclude that package-a{sys_platform == 'darwin'}==1.0.0 cannot be used.
|
||||
|
|
|
|||
|
|
@ -6872,7 +6872,7 @@ fn universal_requires_python() -> Result<()> {
|
|||
|
||||
----- stderr -----
|
||||
warning: The requested Python version 3.8 is not available; 3.12.[X] will be used to build dependencies instead.
|
||||
× No solution found when resolving dependencies:
|
||||
× No solution found when resolving dependencies for split (python_version >= '3.9'):
|
||||
╰─▶ Because only the following versions of numpy{python_version >= '3.9'} are available:
|
||||
numpy{python_version >= '3.9'}<=1.26.0
|
||||
numpy{python_version >= '3.9'}==1.26.1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue