mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-13 14:06:22 +00:00
Avoid need for universal markers in requirements.txt
export (#10171)
This commit is contained in:
parent
0b5c0220b5
commit
79dce7391e
2 changed files with 63 additions and 43 deletions
|
@ -5,6 +5,7 @@ use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
use uv_normalize::{ExtraName, GroupName, PackageName};
|
use uv_normalize::{ExtraName, GroupName, PackageName};
|
||||||
|
use uv_pep508::MarkerTree;
|
||||||
use uv_pypi_types::{ConflictItem, Conflicts};
|
use uv_pypi_types::{ConflictItem, Conflicts};
|
||||||
|
|
||||||
use crate::resolution::ResolutionGraphNode;
|
use crate::resolution::ResolutionGraphNode;
|
||||||
|
@ -17,10 +18,10 @@ use crate::universal_marker::UniversalMarker;
|
||||||
/// marker), we re-queue the node and update all its children. This implicitly handles cycles,
|
/// marker), we re-queue the node and update all its children. This implicitly handles cycles,
|
||||||
/// whenever we re-reach a node through a cycle the marker we have is a more
|
/// whenever we re-reach a node through a cycle the marker we have is a more
|
||||||
/// specific marker/longer path, so we don't update the node and don't re-queue it.
|
/// specific marker/longer path, so we don't update the node and don't re-queue it.
|
||||||
pub(crate) fn marker_reachability<T>(
|
pub(crate) fn marker_reachability<Node, Edge: Reachable + Copy + PartialEq>(
|
||||||
graph: &Graph<T, UniversalMarker>,
|
graph: &Graph<Node, Edge>,
|
||||||
fork_markers: &[UniversalMarker],
|
fork_markers: &[Edge],
|
||||||
) -> FxHashMap<NodeIndex, UniversalMarker> {
|
) -> FxHashMap<NodeIndex, Edge> {
|
||||||
// Note that we build including the virtual packages due to how we propagate markers through
|
// Note that we build including the virtual packages due to how we propagate markers through
|
||||||
// the graph, even though we then only read the markers for base packages.
|
// the graph, even though we then only read the markers for base packages.
|
||||||
let mut reachability = FxHashMap::with_capacity_and_hasher(graph.node_count(), FxBuildHasher);
|
let mut reachability = FxHashMap::with_capacity_and_hasher(graph.node_count(), FxBuildHasher);
|
||||||
|
@ -42,11 +43,11 @@ pub(crate) fn marker_reachability<T>(
|
||||||
// The root nodes are always applicable, unless the user has restricted resolver
|
// The root nodes are always applicable, unless the user has restricted resolver
|
||||||
// environments with `tool.uv.environments`.
|
// environments with `tool.uv.environments`.
|
||||||
let root_markers = if fork_markers.is_empty() {
|
let root_markers = if fork_markers.is_empty() {
|
||||||
UniversalMarker::TRUE
|
Edge::true_marker()
|
||||||
} else {
|
} else {
|
||||||
fork_markers
|
fork_markers
|
||||||
.iter()
|
.iter()
|
||||||
.fold(UniversalMarker::FALSE, |mut acc, marker| {
|
.fold(Edge::false_marker(), |mut acc, marker| {
|
||||||
acc.or(*marker);
|
acc.or(*marker);
|
||||||
acc
|
acc
|
||||||
})
|
})
|
||||||
|
@ -264,3 +265,54 @@ pub(crate) fn simplify_conflict_markers(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait for types that can be used as markers in the dependency graph.
|
||||||
|
pub(crate) trait Reachable {
|
||||||
|
/// The marker representing the "true" value.
|
||||||
|
fn true_marker() -> Self;
|
||||||
|
|
||||||
|
/// The marker representing the "false" value.
|
||||||
|
fn false_marker() -> Self;
|
||||||
|
|
||||||
|
/// Perform a logical AND operation with another marker.
|
||||||
|
fn and(&mut self, other: Self);
|
||||||
|
|
||||||
|
/// Perform a logical OR operation with another marker.
|
||||||
|
fn or(&mut self, other: Self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Reachable for UniversalMarker {
|
||||||
|
fn true_marker() -> Self {
|
||||||
|
Self::TRUE
|
||||||
|
}
|
||||||
|
|
||||||
|
fn false_marker() -> Self {
|
||||||
|
Self::FALSE
|
||||||
|
}
|
||||||
|
|
||||||
|
fn and(&mut self, other: Self) {
|
||||||
|
self.and(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn or(&mut self, other: Self) {
|
||||||
|
self.or(other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Reachable for MarkerTree {
|
||||||
|
fn true_marker() -> Self {
|
||||||
|
Self::TRUE
|
||||||
|
}
|
||||||
|
|
||||||
|
fn false_marker() -> Self {
|
||||||
|
Self::FALSE
|
||||||
|
}
|
||||||
|
|
||||||
|
fn and(&mut self, other: Self) {
|
||||||
|
self.and(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn or(&mut self, other: Self) {
|
||||||
|
self.or(other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ use uv_pypi_types::{ParsedArchiveUrl, ParsedGitUrl};
|
||||||
|
|
||||||
use crate::graph_ops::marker_reachability;
|
use crate::graph_ops::marker_reachability;
|
||||||
use crate::lock::{Package, PackageId, Source};
|
use crate::lock::{Package, PackageId, Source};
|
||||||
use crate::universal_marker::{ConflictMarker, UniversalMarker};
|
|
||||||
use crate::{Installable, LockError};
|
use crate::{Installable, LockError};
|
||||||
|
|
||||||
/// An export of a [`Lock`] that renders in `requirements.txt` format.
|
/// An export of a [`Lock`] that renders in `requirements.txt` format.
|
||||||
|
@ -70,7 +69,7 @@ impl<'lock> RequirementsTxtExport<'lock> {
|
||||||
|
|
||||||
// Add an edge from the root.
|
// Add an edge from the root.
|
||||||
let index = inverse[&dist.id];
|
let index = inverse[&dist.id];
|
||||||
petgraph.add_edge(root, index, UniversalMarker::TRUE);
|
petgraph.add_edge(root, index, MarkerTree::TRUE);
|
||||||
|
|
||||||
// Push its dependencies on the queue.
|
// Push its dependencies on the queue.
|
||||||
queue.push_back((dist, None));
|
queue.push_back((dist, None));
|
||||||
|
@ -110,17 +109,7 @@ impl<'lock> RequirementsTxtExport<'lock> {
|
||||||
petgraph.add_edge(
|
petgraph.add_edge(
|
||||||
root,
|
root,
|
||||||
dep_index,
|
dep_index,
|
||||||
UniversalMarker::new(
|
|
||||||
dep.simplified_marker.as_simplified_marker_tree(),
|
dep.simplified_marker.as_simplified_marker_tree(),
|
||||||
// OK because we've verified above that we do not have any
|
|
||||||
// conflicting extras/groups.
|
|
||||||
//
|
|
||||||
// So why use `UniversalMarker`? Because that's what
|
|
||||||
// `marker_reachability` wants and it (probably) isn't
|
|
||||||
// worth inventing a new abstraction so that it can accept
|
|
||||||
// graphs with either `MarkerTree` or `UniversalMarker`.
|
|
||||||
ConflictMarker::TRUE,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Push its dependencies on the queue.
|
// Push its dependencies on the queue.
|
||||||
|
@ -205,21 +194,7 @@ impl<'lock> RequirementsTxtExport<'lock> {
|
||||||
|
|
||||||
// Add an edge from the root.
|
// Add an edge from the root.
|
||||||
let dep_index = inverse[&dist.id];
|
let dep_index = inverse[&dist.id];
|
||||||
petgraph.add_edge(
|
petgraph.add_edge(root, dep_index, marker);
|
||||||
root,
|
|
||||||
dep_index,
|
|
||||||
UniversalMarker::new(
|
|
||||||
marker,
|
|
||||||
// OK because we've verified above that we do not have any
|
|
||||||
// conflicting extras/groups.
|
|
||||||
//
|
|
||||||
// So why use `UniversalMarker`? Because that's what
|
|
||||||
// `marker_reachability` wants and it (probably) isn't
|
|
||||||
// worth inventing a new abstraction so that it can accept
|
|
||||||
// graphs with either `MarkerTree` or `UniversalMarker`.
|
|
||||||
ConflictMarker::TRUE,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Push its dependencies on the queue.
|
// Push its dependencies on the queue.
|
||||||
if seen.insert((&dist.id, None)) {
|
if seen.insert((&dist.id, None)) {
|
||||||
|
@ -267,12 +242,7 @@ impl<'lock> RequirementsTxtExport<'lock> {
|
||||||
petgraph.add_edge(
|
petgraph.add_edge(
|
||||||
index,
|
index,
|
||||||
dep_index,
|
dep_index,
|
||||||
UniversalMarker::new(
|
|
||||||
dep.simplified_marker.as_simplified_marker_tree(),
|
dep.simplified_marker.as_simplified_marker_tree(),
|
||||||
// See note above for other `UniversalMarker::new` for
|
|
||||||
// why this is OK.
|
|
||||||
ConflictMarker::TRUE,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Push its dependencies on the queue.
|
// Push its dependencies on the queue.
|
||||||
|
@ -305,9 +275,7 @@ impl<'lock> RequirementsTxtExport<'lock> {
|
||||||
})
|
})
|
||||||
.map(|(index, package)| Requirement {
|
.map(|(index, package)| Requirement {
|
||||||
package,
|
package,
|
||||||
// As above, we've verified that there are no conflicting extras/groups
|
marker: reachability.remove(&index).unwrap_or_default(),
|
||||||
// specified, so it's safe to completely ignore the conflict marker.
|
|
||||||
marker: reachability.remove(&index).unwrap_or_default().pep508(),
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue