mirror of https://github.com/astral-sh/uv
Requirements with platform markers
This commit is contained in:
parent
3ca777673f
commit
efb9a93306
|
|
@ -358,6 +358,18 @@ impl Requirement {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns whether the markers apply for the given environment
|
||||
pub fn evaluate_markers2(&self, env: &MarkerEnvironment, extras: &[ExtraName]) -> bool {
|
||||
if let Some(marker) = &self.marker {
|
||||
marker.evaluate_extras_and_python_version(
|
||||
&extras.into_iter().cloned().collect(),
|
||||
&[env.python_version.version.clone()],
|
||||
)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether the requirement would be satisfied, independent of environment markers, i.e.
|
||||
/// if there is potentially an environment that could activate this requirement.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ pub(crate) async fn resolve_cli(args: ResolveCliArgs) -> Result<()> {
|
|||
let graphviz = Dot::with_attr_getters(
|
||||
resolution_graph.petgraph(),
|
||||
&[DotConfig::NodeNoLabel, DotConfig::EdgeNoLabel],
|
||||
&|_graph, edge_ref| format!("label={:?}", edge_ref.weight().to_string()),
|
||||
&|_graph, edge_ref| format!("label={:?}", edge_ref.weight().0.to_string()),
|
||||
&|_graph, (_node_index, dist)| {
|
||||
format!("label={:?}", dist.to_string().replace("==", "\n"))
|
||||
},
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ impl Preferences {
|
|||
requirements
|
||||
.iter()
|
||||
.filter_map(|requirement| {
|
||||
if !requirement.evaluate_markers(markers, &[]) {
|
||||
if !requirement.evaluate_markers2(markers, &[]) {
|
||||
return None;
|
||||
}
|
||||
let Some(VersionOrUrl::VersionSpecifier(version_specifiers)) =
|
||||
|
|
|
|||
|
|
@ -65,10 +65,10 @@ impl PreReleaseStrategy {
|
|||
.iter()
|
||||
.chain(manifest.constraints.iter())
|
||||
.chain(manifest.overrides.iter())
|
||||
.filter(|requirement| requirement.evaluate_markers(markers, &[]))
|
||||
.filter(|requirement| requirement.evaluate_markers2(markers, &[]))
|
||||
.chain(manifest.editables.iter().flat_map(|(editable, metadata)| {
|
||||
metadata.requires_dist.iter().filter(|requirement| {
|
||||
requirement.evaluate_markers(markers, &editable.extras)
|
||||
requirement.evaluate_markers2(markers, &editable.extras)
|
||||
})
|
||||
}))
|
||||
.filter(|requirement| {
|
||||
|
|
@ -94,10 +94,10 @@ impl PreReleaseStrategy {
|
|||
.iter()
|
||||
.chain(manifest.constraints.iter())
|
||||
.chain(manifest.overrides.iter())
|
||||
.filter(|requirement| requirement.evaluate_markers(markers, &[]))
|
||||
.filter(|requirement| requirement.evaluate_markers2(markers, &[]))
|
||||
.chain(manifest.editables.iter().flat_map(|(editable, metadata)| {
|
||||
metadata.requires_dist.iter().filter(|requirement| {
|
||||
requirement.evaluate_markers(markers, &editable.extras)
|
||||
requirement.evaluate_markers2(markers, &editable.extras)
|
||||
})
|
||||
}))
|
||||
.filter(|requirement| {
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ impl PubGrubDependencies {
|
|||
for requirement in overrides.apply(requirements) {
|
||||
// If the requirement isn't relevant for the current platform, skip it.
|
||||
if let Some(extra) = source_extra {
|
||||
if !requirement.evaluate_markers(env, std::slice::from_ref(extra)) {
|
||||
if !requirement.evaluate_markers2(env, std::slice::from_ref(extra)) {
|
||||
continue;
|
||||
}
|
||||
} else if !requirement.evaluate_markers(env, &[]) {
|
||||
} else if !requirement.evaluate_markers2(env, &[]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -68,10 +68,10 @@ impl PubGrubDependencies {
|
|||
for constraint in constraints.get(&requirement.name).into_iter().flatten() {
|
||||
// If the requirement isn't relevant for the current platform, skip it.
|
||||
if let Some(extra) = source_extra {
|
||||
if !constraint.evaluate_markers(env, std::slice::from_ref(extra)) {
|
||||
if !constraint.evaluate_markers2(env, std::slice::from_ref(extra)) {
|
||||
continue;
|
||||
}
|
||||
} else if !constraint.evaluate_markers(env, &[]) {
|
||||
} else if !constraint.evaluate_markers2(env, &[]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use url::Url;
|
|||
use distribution_types::{Dist, DistributionMetadata, LocalEditable, Name, PackageId, Verbatim};
|
||||
use once_map::OnceMap;
|
||||
use pep440_rs::Version;
|
||||
use pep508_rs::MarkerTree;
|
||||
use pypi_types::{Hashes, Metadata21};
|
||||
use uv_normalize::{ExtraName, PackageName};
|
||||
|
||||
|
|
@ -42,7 +43,8 @@ pub enum AnnotationStyle {
|
|||
#[derive(Debug)]
|
||||
pub struct ResolutionGraph {
|
||||
/// The underlying graph.
|
||||
petgraph: petgraph::graph::Graph<Dist, Range<Version>, petgraph::Directed>,
|
||||
petgraph:
|
||||
petgraph::graph::Graph<Dist, (Range<Version>, Option<MarkerTree>), petgraph::Directed>,
|
||||
/// The metadata for every distribution in this resolution.
|
||||
hashes: FxHashMap<PackageName, Vec<Hashes>>,
|
||||
/// The set of editable requirements in this resolution.
|
||||
|
|
@ -64,7 +66,11 @@ impl ResolutionGraph {
|
|||
) -> Result<Self, ResolveError> {
|
||||
// TODO(charlie): petgraph is a really heavy and unnecessary dependency here. We should
|
||||
// write our own graph, given that our requirements are so simple.
|
||||
let mut petgraph = petgraph::graph::Graph::with_capacity(selection.len(), selection.len());
|
||||
let mut petgraph: petgraph::graph::Graph<
|
||||
Dist,
|
||||
(Range<Version>, Option<MarkerTree>),
|
||||
petgraph::Directed,
|
||||
> = petgraph::graph::Graph::with_capacity(selection.len(), selection.len());
|
||||
let mut hashes =
|
||||
FxHashMap::with_capacity_and_hasher(selection.len(), BuildHasherDefault::default());
|
||||
let mut diagnostics = Vec::new();
|
||||
|
|
@ -207,13 +213,13 @@ impl ResolutionGraph {
|
|||
for (package, version) in selection {
|
||||
for id in &state.incompatibilities[package] {
|
||||
if let Kind::FromDependencyOf(
|
||||
self_package,
|
||||
self_package2,
|
||||
self_version,
|
||||
dependency_package,
|
||||
dependency_range,
|
||||
) = &state.incompatibility_store[*id].kind
|
||||
{
|
||||
let PubGrubPackage::Package(self_package, _, _) = self_package else {
|
||||
let PubGrubPackage::Package(self_package, _, _) = self_package2 else {
|
||||
continue;
|
||||
};
|
||||
let PubGrubPackage::Package(dependency_package, _, _) = dependency_package
|
||||
|
|
@ -226,13 +232,39 @@ impl ResolutionGraph {
|
|||
continue;
|
||||
}
|
||||
|
||||
let dist =
|
||||
PubGrubDistribution::from_registry(self_package, &selection[self_package2]);
|
||||
let requires_dist =
|
||||
if let Some((_editable, metadata)) = editables.get(self_package) {
|
||||
metadata.requires_dist.clone()
|
||||
} else {
|
||||
distributions
|
||||
.get(&dist.package_id())
|
||||
.expect("Missing metadata")
|
||||
.requires_dist
|
||||
.clone()
|
||||
};
|
||||
|
||||
let markers: Vec<MarkerTree> = requires_dist
|
||||
.iter()
|
||||
.filter(|req| &req.name == dependency_package)
|
||||
.filter_map(|req| req.marker.clone())
|
||||
.collect();
|
||||
|
||||
let markers_joined = match markers.as_slice() {
|
||||
[] => None,
|
||||
[marker_tree] => Some(marker_tree.clone()),
|
||||
// The package gets installed if any marker is fulfilled.
|
||||
_ => Some(MarkerTree::Or(markers)),
|
||||
};
|
||||
|
||||
if self_version.contains(version) {
|
||||
let self_index = &inverse[self_package];
|
||||
let dependency_index = &inverse[dependency_package];
|
||||
petgraph.update_edge(
|
||||
*self_index,
|
||||
*dependency_index,
|
||||
dependency_range.clone(),
|
||||
(dependency_range.clone(), markers_joined),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -270,7 +302,10 @@ impl ResolutionGraph {
|
|||
}
|
||||
|
||||
/// Return the underlying graph.
|
||||
pub fn petgraph(&self) -> &petgraph::graph::Graph<Dist, Range<Version>, petgraph::Directed> {
|
||||
pub fn petgraph(
|
||||
&self,
|
||||
) -> &petgraph::graph::Graph<Dist, (Range<Version>, Option<MarkerTree>), petgraph::Directed>
|
||||
{
|
||||
&self.petgraph
|
||||
}
|
||||
}
|
||||
|
|
@ -406,6 +441,16 @@ impl std::fmt::Display for DisplayResolutionGraph<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
let markers: Vec<_> = self
|
||||
.resolution
|
||||
.petgraph
|
||||
.edges_directed(index, Direction::Incoming)
|
||||
.filter_map(|edge| edge.weight().1.clone())
|
||||
.collect();
|
||||
if !markers.is_empty() {
|
||||
line.push_str(&format!(" ; {}", MarkerTree::Or(markers)));
|
||||
}
|
||||
|
||||
// Determine the annotation comment and separator (between comment and requirement).
|
||||
let mut annotation = None;
|
||||
|
||||
|
|
|
|||
|
|
@ -45,10 +45,10 @@ impl ResolutionStrategy {
|
|||
manifest
|
||||
.requirements
|
||||
.iter()
|
||||
.filter(|requirement| requirement.evaluate_markers(markers, &[]))
|
||||
.filter(|requirement| requirement.evaluate_markers2(markers, &[]))
|
||||
.chain(manifest.editables.iter().flat_map(|(editable, metadata)| {
|
||||
metadata.requires_dist.iter().filter(|requirement| {
|
||||
requirement.evaluate_markers(markers, &editable.extras)
|
||||
requirement.evaluate_markers2(markers, &editable.extras)
|
||||
})
|
||||
}))
|
||||
.map(|requirement| requirement.name.clone())
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ impl Urls {
|
|||
.iter()
|
||||
.chain(manifest.constraints.iter())
|
||||
{
|
||||
if !requirement.evaluate_markers(markers, &[]) {
|
||||
if !requirement.evaluate_markers2(markers, &[]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ impl Urls {
|
|||
}
|
||||
|
||||
for requirement in &metadata.requires_dist {
|
||||
if !requirement.evaluate_markers(markers, &editable.extras) {
|
||||
if !requirement.evaluate_markers2(markers, &editable.extras) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ impl Urls {
|
|||
// Add any overrides. Conflicts here are fine, as the overrides are meant to be
|
||||
// authoritative.
|
||||
for requirement in &manifest.overrides {
|
||||
if !requirement.evaluate_markers(markers, &[]) {
|
||||
if !requirement.evaluate_markers2(markers, &[]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ impl AllowedYanks {
|
|||
.chain(manifest.constraints.iter())
|
||||
.chain(manifest.overrides.iter())
|
||||
.chain(manifest.preferences.iter())
|
||||
.filter(|requirement| requirement.evaluate_markers(markers, &[]))
|
||||
.filter(|requirement| requirement.evaluate_markers2(markers, &[]))
|
||||
.chain(manifest.editables.iter().flat_map(|(editable, metadata)| {
|
||||
metadata
|
||||
.requires_dist
|
||||
.iter()
|
||||
.filter(|requirement| requirement.evaluate_markers(markers, &editable.extras))
|
||||
.filter(|requirement| requirement.evaluate_markers2(markers, &editable.extras))
|
||||
}))
|
||||
{
|
||||
let Some(pep508_rs::VersionOrUrl::VersionSpecifier(specifiers)) =
|
||||
|
|
|
|||
|
|
@ -163,6 +163,15 @@ pub(crate) async fn pip_sync(
|
|||
)
|
||||
.await?;
|
||||
|
||||
let requirements: Vec<_> = requirements
|
||||
.into_iter()
|
||||
.filter(|req| {
|
||||
req.marker.as_ref().map_or(true, |marker| {
|
||||
marker.evaluate(venv.interpreter().markers(), &[])
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Partition into those that should be linked from the cache (`local`), those that need to be
|
||||
// downloaded (`remote`), and those that should be removed (`extraneous`).
|
||||
let Plan {
|
||||
|
|
|
|||
Loading…
Reference in New Issue