Encode mutually-incompatible pairs of markers (#9444)

## Summary

This is an alternative to #9344. If accepted, I need to audit the
codebase and call sites to apply it everywhere, but the basic idea is:
rather than encoding mutually-incompatible pairs of markers in the
representation itself, we have an additional method on `MarkerTree` that
expands the false-y definition to take into account assumptions about
which markers can be true alongside others. We then check if the the
current marker implies that at least one of them is true.

So, for example, we know that `sys_platform == 'win32'` and
`platform_system == 'Darwin'` are mutually exclusive. When given a
marker expression like `python_version >= '3.7'`, we test if
`python_version >= '3.7'` and `sys_platform != 'win32' or
platform_system != 'Darwin'` are disjoint, i.e., if the following can't
be satisfied:

```
python_version >= '3.7' and (sys_platform != 'win32' or platform_system != 'Darwin')
```

Since, if this can't be satisfied, it implies that the left-hand
expression requires `sys_platform == 'win32'` and `platform_system ==
'Darwin'` to be true at the same time.

I think the main downsides here are:

1. We can't _simplify_ markers based on these implications. So we'd
still write markers like `sys_platform == 'win32' and platform_system !=
'Darwin'`, even though we know the latter expression is redundant.
2. It might be expensive? I'm not sure. I don't think we test for
falseness _that_ often though.

Closes #7760.
Closes #9275.
This commit is contained in:
Charlie Marsh
2024-12-06 20:51:44 -05:00
committed by GitHub
parent 3ca155ddd6
commit 508a6bc953
11 changed files with 631 additions and 120 deletions
+347 -13
View File
@@ -44,6 +44,7 @@
//! a [`NodeId`], which represents a potentially complemented reference to a [`Node`] in the interner,
//! or a terminal `true`/`false` node. Interning allows the reduction rule that isomorphic nodes are
//! merged to be applied globally.
use std::cmp::Ordering;
use std::fmt;
use std::ops::Bound;
@@ -60,8 +61,9 @@ use crate::marker::lowering::{
CanonicalMarkerValueExtra, CanonicalMarkerValueString, CanonicalMarkerValueVersion,
};
use crate::marker::MarkerValueExtra;
use crate::ExtraOperator;
use crate::{MarkerExpression, MarkerOperator, MarkerValueVersion};
use crate::{
ExtraOperator, MarkerExpression, MarkerOperator, MarkerValueString, MarkerValueVersion,
};
/// The global node interner.
pub(crate) static INTERNER: LazyLock<Interner> = LazyLock::new(Interner::default);
@@ -93,6 +95,9 @@ struct InternerState {
/// A cache for `AND` operations between two nodes.
/// Note that `OR` is implemented in terms of `AND`.
cache: FxHashMap<(NodeId, NodeId), NodeId>,
/// The [`NodeId`] for the disjunction of known, mutually incompatible markers.
exclusions: Option<NodeId>,
}
impl InternerShared {
@@ -295,14 +300,14 @@ impl InternerGuard<'_> {
self.create_node(var, children)
}
// Returns a decision node representing the disjunction of two nodes.
pub(crate) fn or(&mut self, x: NodeId, y: NodeId) -> NodeId {
/// Returns a decision node representing the disjunction of two nodes.
pub(crate) fn or(&mut self, xi: NodeId, yi: NodeId) -> NodeId {
// We take advantage of cheap negation here and implement OR in terms
// of it's De Morgan complement.
self.and(x.not(), y.not()).not()
self.and(xi.not(), yi.not()).not()
}
// Returns a decision node representing the conjunction of two nodes.
/// Returns a decision node representing the conjunction of two nodes.
pub(crate) fn and(&mut self, xi: NodeId, yi: NodeId) -> NodeId {
if xi.is_true() {
return yi;
@@ -328,6 +333,14 @@ impl InternerGuard<'_> {
let (x, y) = (self.shared.node(xi), self.shared.node(yi));
// Determine whether the conjunction _could_ contain a conflict.
//
// As an optimization, we only have to perform this check at the top-level, since these
// variables are given higher priority in the tree. In other words, if they're present, they
// _must_ be at the top; and if they're not at the top, we know they aren't present in any
// children.
let conflicts = x.var.is_conflicting_variable() && y.var.is_conflicting_variable();
// Perform Shannon Expansion of the higher order variable.
let (func, children) = match x.var.cmp(&y.var) {
// X is higher order than Y, apply Y to every child of X.
@@ -350,6 +363,18 @@ impl InternerGuard<'_> {
// Create the output node.
let node = self.create_node(func, children);
// If the node includes known incompatibilities, map it to `false`.
let node = if conflicts {
let exclusions = self.exclusions();
if self.disjointness(node, exclusions.not()) {
NodeId::FALSE
} else {
node
}
} else {
node
};
// Memoize the result of this operation.
//
// ADDs often contain duplicated subgraphs in distinct branches due to the restricted
@@ -380,17 +405,73 @@ impl InternerGuard<'_> {
}
let (x, y) = (self.shared.node(xi), self.shared.node(yi));
// Determine whether the conjunction _could_ contain a conflict.
//
// As an optimization, we only have to perform this check at the top-level, since these
// variables are given higher priority in the tree. In other words, if they're present, they
// _must_ be at the top; and if they're not at the top, we know they aren't present in any
// children.
if x.var.is_conflicting_variable() && y.var.is_conflicting_variable() {
return self.and(xi, yi).is_false();
}
// Perform Shannon Expansion of the higher order variable.
match x.var.cmp(&y.var) {
// X is higher order than Y, Y must be disjoint with every child of X.
Ordering::Less => x
.children
.nodes()
.all(|x| self.is_disjoint(x.negate(xi), yi)),
.all(|x| self.disjointness(x.negate(xi), yi)),
// Y is higher order than X, X must be disjoint with every child of Y.
Ordering::Greater => y
.children
.nodes()
.all(|y| self.is_disjoint(y.negate(yi), xi)),
.all(|y| self.disjointness(y.negate(yi), xi)),
// X and Y represent the same variable, their merged edges must be unsatisifiable.
Ordering::Equal => x.children.is_disjoint(xi, &y.children, yi, self),
}
}
/// Returns `true` if there is no environment in which both marker trees can apply,
/// i.e., their conjunction is always `false`.
fn disjointness(&mut self, xi: NodeId, yi: NodeId) -> bool {
// NOTE(charlie): This is equivalent to `is_disjoint`, with the exception that it doesn't
// perform the mutually-incompatible marker check. If it did, we'd create an infinite loop,
// since `is_disjoint` calls `and` (when relevant variables are present) which then calls
// `disjointness`.
// `false` is disjoint with any marker.
if xi.is_false() || yi.is_false() {
return true;
}
// `true` is not disjoint with any marker except `false`.
if xi.is_true() || yi.is_true() {
return false;
}
// `X` and `X` are not disjoint.
if xi == yi {
return false;
}
// `X` and `not X` are disjoint by definition.
if xi.not() == yi {
return true;
}
let (x, y) = (self.shared.node(xi), self.shared.node(yi));
// Perform Shannon Expansion of the higher order variable.
match x.var.cmp(&y.var) {
// X is higher order than Y, Y must be disjoint with every child of X.
Ordering::Less => x
.children
.nodes()
.all(|x| self.disjointness(x.negate(xi), yi)),
// Y is higher order than X, X must be disjoint with every child of Y.
Ordering::Greater => y
.children
.nodes()
.all(|y| self.disjointness(y.negate(yi), xi)),
// X and Y represent the same variable, their merged edges must be unsatisifiable.
Ordering::Equal => x.children.is_disjoint(xi, &y.children, yi, self),
}
@@ -667,6 +748,245 @@ impl InternerGuard<'_> {
self.create_node(node.var.clone(), Edges::Version { edges: new })
.negate(i)
}
/// The disjunction of known incompatible conditions.
///
/// For example, while the marker specification and grammar do not _forbid_ it, we know that
/// both `sys_platform == 'win32'` and `platform_system == 'Darwin'` will never true at the
/// same time.
///
/// This method thus encodes assumptions about the environment that are not guaranteed by the
/// PEP 508 specification alone.
fn exclusions(&mut self) -> NodeId {
/// Perform a disjunction operation between two nodes.
///
/// This is equivalent to [`InternerGuard::or`], with the exception that it does not
/// incorporate knowledge from outside the marker algebra.
fn disjunction(guard: &mut InternerGuard<'_>, xi: NodeId, yi: NodeId) -> NodeId {
// We take advantage of cheap negation here and implement OR in terms
// of it's De Morgan complement.
conjunction(guard, xi.not(), yi.not()).not()
}
/// Perform a conjunction operation between two nodes.
///
/// This is equivalent to [`InternerGuard::and`], with the exception that it does not
/// incorporate knowledge from outside the marker algebra.
fn conjunction(guard: &mut InternerGuard<'_>, xi: NodeId, yi: NodeId) -> NodeId {
if xi.is_true() {
return yi;
}
if yi.is_true() {
return xi;
}
if xi == yi {
return xi;
}
if xi.is_false() || yi.is_false() {
return NodeId::FALSE;
}
// `X and not X` is `false` by definition.
if xi.not() == yi {
return NodeId::FALSE;
}
let (x, y) = (guard.shared.node(xi), guard.shared.node(yi));
// Perform Shannon Expansion of the higher order variable.
let (func, children) = match x.var.cmp(&y.var) {
// X is higher order than Y, apply Y to every child of X.
Ordering::Less => {
let children = x.children.map(xi, |node| conjunction(guard, node, yi));
(x.var.clone(), children)
}
// Y is higher order than X, apply X to every child of Y.
Ordering::Greater => {
let children = y.children.map(yi, |node| conjunction(guard, node, xi));
(y.var.clone(), children)
}
// X and Y represent the same variable, merge their children.
Ordering::Equal => {
let children = x
.children
.apply(xi, &y.children, yi, |x, y| conjunction(guard, x, y));
(x.var.clone(), children)
}
};
// Create the output node.
guard.create_node(func, children)
}
if let Some(exclusions) = self.state.exclusions {
return exclusions;
}
let mut tree = NodeId::FALSE;
for (a, b) in [
// sys_platform == 'darwin' and platform_system == 'Windows'
(
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
value: "darwin".to_string(),
},
MarkerExpression::String {
key: MarkerValueString::PlatformSystem,
operator: MarkerOperator::Equal,
value: "Windows".to_string(),
},
),
// sys_platform == 'darwin' and platform_system == 'Linux'
(
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
value: "darwin".to_string(),
},
MarkerExpression::String {
key: MarkerValueString::PlatformSystem,
operator: MarkerOperator::Equal,
value: "Linux".to_string(),
},
),
// sys_platform == 'win32' and platform_system == 'Darwin'
(
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
value: "win32".to_string(),
},
MarkerExpression::String {
key: MarkerValueString::PlatformSystem,
operator: MarkerOperator::Equal,
value: "Darwin".to_string(),
},
),
// sys_platform == 'win32' and platform_system == 'Linux'
(
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
value: "win32".to_string(),
},
MarkerExpression::String {
key: MarkerValueString::PlatformSystem,
operator: MarkerOperator::Equal,
value: "Linux".to_string(),
},
),
// sys_platform == 'linux' and platform_system == 'Darwin'
(
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
value: "linux".to_string(),
},
MarkerExpression::String {
key: MarkerValueString::PlatformSystem,
operator: MarkerOperator::Equal,
value: "Darwin".to_string(),
},
),
// sys_platform == 'linux' and platform_system == 'Windows'
(
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
value: "linux".to_string(),
},
MarkerExpression::String {
key: MarkerValueString::PlatformSystem,
operator: MarkerOperator::Equal,
value: "Windows".to_string(),
},
),
// os_name == 'nt' and sys_platform == 'darwin'
(
MarkerExpression::String {
key: MarkerValueString::OsName,
operator: MarkerOperator::Equal,
value: "nt".to_string(),
},
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
value: "darwin".to_string(),
},
),
// os_name == 'nt' and sys_platform == 'linux'
(
MarkerExpression::String {
key: MarkerValueString::OsName,
operator: MarkerOperator::Equal,
value: "nt".to_string(),
},
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
value: "linux".to_string(),
},
),
// os_name == 'posix' and sys_platform == 'win32'
(
MarkerExpression::String {
key: MarkerValueString::OsName,
operator: MarkerOperator::Equal,
value: "posix".to_string(),
},
MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
value: "win32".to_string(),
},
),
// os_name == 'nt' and platform_system == 'Darwin'
(
MarkerExpression::String {
key: MarkerValueString::OsName,
operator: MarkerOperator::Equal,
value: "nt".to_string(),
},
MarkerExpression::String {
key: MarkerValueString::PlatformSystem,
operator: MarkerOperator::Equal,
value: "Darwin".to_string(),
},
),
// os_name == 'nt' and platform_system == 'Linux'
(
MarkerExpression::String {
key: MarkerValueString::OsName,
operator: MarkerOperator::Equal,
value: "nt".to_string(),
},
MarkerExpression::String {
key: MarkerValueString::PlatformSystem,
operator: MarkerOperator::Equal,
value: "Linux".to_string(),
},
),
// os_name == 'posix' and platform_system == 'Windows'
(
MarkerExpression::String {
key: MarkerValueString::OsName,
operator: MarkerOperator::Equal,
value: "posix".to_string(),
},
MarkerExpression::String {
key: MarkerValueString::PlatformSystem,
operator: MarkerOperator::Equal,
value: "Windows".to_string(),
},
),
] {
let a = self.expression(a);
let b = self.expression(b);
let a_and_b = conjunction(self, a, b);
tree = disjunction(self, tree, a_and_b);
}
self.state.exclusions = Some(tree);
tree
}
}
/// A unique variable for a decision node.
@@ -681,13 +1001,13 @@ impl InternerGuard<'_> {
/// impact.
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Debug)]
pub(crate) enum Variable {
/// A string marker, such as `os_name`.
String(CanonicalMarkerValueString),
/// A version marker, such as `python_version`.
///
/// This is the highest order variable as it typically contains the most complex
/// ranges, allowing us to merge ranges at the top-level.
Version(CanonicalMarkerValueVersion),
/// A string marker, such as `os_name`.
String(CanonicalMarkerValueString),
/// A variable representing a `<key> in <value>` expression for a particular
/// string marker and value.
In {
@@ -707,6 +1027,20 @@ pub(crate) enum Variable {
Extra(CanonicalMarkerValueExtra),
}
impl Variable {
/// Returns `true` if the variable is known to be involved in _at least_ one conflicting
/// marker pair.
///
/// For example, `sys_platform == 'win32'` and `platform_system == 'Darwin'` are known to
/// never be true at the same time.
fn is_conflicting_variable(&self) -> bool {
let Variable::String(marker) = self else {
return false;
};
marker.is_conflicting()
}
}
/// A decision node in an Algebraic Decision Diagram.
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub(crate) struct Node {
@@ -1072,8 +1406,8 @@ impl Edges {
low: right_low,
},
) => {
interner.is_disjoint(high.negate(parent), right_high.negate(right_parent))
&& interner.is_disjoint(low.negate(parent), right_low.negate(right_parent))
interner.disjointness(high.negate(parent), right_high.negate(right_parent))
&& interner.disjointness(low.negate(parent), right_low.negate(right_parent))
}
_ => unreachable!("cannot merge two `Edges` of different types"),
}
@@ -1100,7 +1434,7 @@ impl Edges {
}
// Ensure the intersection is disjoint.
if !interner.is_disjoint(
if !interner.disjointness(
left_child.negate(left_parent),
right_child.negate(right_parent),
) {
+25 -7
View File
@@ -32,13 +32,22 @@ impl From<CanonicalMarkerValueVersion> for MarkerValueVersion {
}
}
/// Those environment markers with an arbitrary string as value such as `sys_platform`
/// Those environment markers with an arbitrary string as value such as `sys_platform`.
///
/// As in [`crate::marker::algebra::Variable`], this `enum` also defines the variable ordering for
/// all ADDs, which is in turn used when translating the ADD to DNF. As such, modifying the ordering
/// will modify the output of marker expressions.
///
/// Critically, any variants that could be involved in a known-incompatible marker pair should
/// be at the top of the ordering, i.e., given the maximum priority.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum CanonicalMarkerValueString {
/// `implementation_name`
ImplementationName,
/// `os_name`
OsName,
/// `sys_platform`
SysPlatform,
/// `platform_system`
PlatformSystem,
/// `platform_machine`
PlatformMachine,
/// Deprecated `platform.machine` from <https://peps.python.org/pep-0345/#environment-markers>
@@ -46,12 +55,21 @@ pub enum CanonicalMarkerValueString {
PlatformPythonImplementation,
/// `platform_release`
PlatformRelease,
/// `platform_system`
PlatformSystem,
/// `platform_version`
PlatformVersion,
/// `sys_platform`
SysPlatform,
/// `implementation_name`
ImplementationName,
}
impl CanonicalMarkerValueString {
/// Returns `true` if the marker is known to be involved in _at least_ one conflicting
/// marker pair.
///
/// For example, `sys_platform == 'win32'` and `platform_system == 'Darwin'` are known to
/// never be true at the same time.
pub(crate) fn is_conflicting(self) -> bool {
self <= Self::PlatformSystem
}
}
impl From<MarkerValueString> for CanonicalMarkerValueString {
+17
View File
@@ -21,6 +21,7 @@ pub(crate) fn to_dnf(tree: MarkerTree) -> Vec<Vec<MarkerExpression>> {
let mut dnf = Vec::new();
collect_dnf(tree, &mut dnf, &mut Vec::new());
simplify(&mut dnf);
sort(&mut dnf);
dnf
}
@@ -278,6 +279,22 @@ fn simplify(dnf: &mut Vec<Vec<MarkerExpression>>) {
}
}
/// Sort the clauses in a DNF expression, for backwards compatibility. The goal is to avoid
/// unnecessary churn in the display output of the marker expressions, e.g., when modifying the
/// internal representations used in the marker algebra.
fn sort(dnf: &mut [Vec<MarkerExpression>]) {
// Sort each clause.
for clause in dnf.iter_mut() {
clause.sort_by_key(MarkerExpression::kind);
}
// Sort the clauses.
dnf.sort_by(|a, b| {
a.iter()
.map(MarkerExpression::kind)
.cmp(b.iter().map(MarkerExpression::kind))
});
}
/// Merge any edges that lead to identical subtrees into a single range.
pub(crate) fn collect_edges<'a, T>(
map: impl ExactSizeIterator<Item = (&'a Ranges<T>, MarkerTree)>,
+40 -24
View File
@@ -5,9 +5,10 @@ use std::str::FromStr;
use itertools::Itertools;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use version_ranges::Ranges;
use uv_normalize::ExtraName;
use uv_pep440::{Version, VersionParseError, VersionSpecifier};
use version_ranges::Ranges;
use super::algebra::{Edges, NodeId, Variable, INTERNER};
use super::simplify;
@@ -493,6 +494,19 @@ pub enum MarkerExpression {
},
}
/// The kind of a [`MarkerExpression`].
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub(crate) enum MarkerExpressionKind {
/// A version expression, e.g. `<version key> <version op> <quoted PEP 440 version>`.
Version(MarkerValueVersion),
/// A version "in" expression, e.g. `<version key> in <quoted list of PEP 440 versions>`.
VersionIn(MarkerValueVersion),
/// A string marker comparison, e.g. `sys_platform == '...'`.
String(MarkerValueString),
/// An extra expression, e.g. `extra == '...'`.
Extra,
}
/// The operator for an extra expression, either '==' or '!='.
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum ExtraOperator {
@@ -563,6 +577,16 @@ impl MarkerExpression {
pub fn from_str(s: &str) -> Result<Option<Self>, Pep508Error> {
MarkerExpression::parse_reporter(s, &mut TracingReporter)
}
/// Return the kind of this marker expression.
pub(crate) fn kind(&self) -> MarkerExpressionKind {
match self {
MarkerExpression::Version { key, .. } => MarkerExpressionKind::Version(*key),
MarkerExpression::VersionIn { key, .. } => MarkerExpressionKind::VersionIn(*key),
MarkerExpression::String { key, .. } => MarkerExpressionKind::String(*key),
MarkerExpression::Extra { .. } => MarkerExpressionKind::Extra,
}
}
}
impl Display for MarkerExpression {
@@ -691,13 +715,11 @@ impl MarkerTree {
}
/// Combine this marker tree with the one given via a conjunction.
#[allow(clippy::needless_pass_by_value)]
pub fn and(&mut self, tree: MarkerTree) {
self.0 = INTERNER.lock().and(self.0, tree.0);
}
/// Combine this marker tree with the one given via a disjunction.
#[allow(clippy::needless_pass_by_value)]
pub fn or(&mut self, tree: MarkerTree) {
self.0 = INTERNER.lock().or(self.0, tree.0);
}
@@ -707,7 +729,6 @@ impl MarkerTree {
///
/// If the marker set is always `true`, then it can be said that `self`
/// implies `consequent`.
#[allow(clippy::needless_pass_by_value)]
pub fn implies(&mut self, consequent: MarkerTree) {
// This could probably be optimized, but is clearly
// correct, since logical implication is `-P or Q`.
@@ -1014,7 +1035,6 @@ impl MarkerTree {
/// results of that simplification. (If `requires-python` changes, then one
/// should reconstitute all relevant markers from the source data.)
#[must_use]
#[allow(clippy::needless_pass_by_value)]
pub fn simplify_python_versions(
self,
lower: Bound<&Version>,
@@ -1035,7 +1055,6 @@ impl MarkerTree {
/// `python_full_version <= '3.10'`, this would result in a marker of
/// `python_full_version >= '3.8' and python_full_version <= '3.10'`.
#[must_use]
#[allow(clippy::needless_pass_by_value)]
pub fn complexify_python_versions(
self,
lower: Bound<&Version>,
@@ -2353,7 +2372,7 @@ mod test {
assert_simplifies(
"((extra == 'a' or extra == 'b') and extra == 'c') or extra == 'b'",
"(extra == 'a' and extra == 'c') or extra == 'b'",
"extra == 'b' or (extra == 'a' and extra == 'c')",
);
// post-normalization filtering
@@ -2575,13 +2594,13 @@ mod test {
or (implementation_name != 'pypy' and sys_platform == 'win32')
or (sys_platform == 'win32' and os_name != 'nt')
or (sys_platform != 'win32' and os_name == 'nt')",
"(os_name != 'nt' and sys_platform == 'win32') \
or (implementation_name != 'pypy' and os_name == 'nt') \
or (implementation_name == 'pypy' and os_name != 'nt') \
"(implementation_name == 'pypy' and sys_platform != 'win32') \
or (implementation_name != 'pypy' and sys_platform == 'win32') \
or (os_name != 'nt' and sys_platform == 'win32') \
or (os_name == 'nt' and sys_platform != 'win32')",
);
// This is another case we cannot simplify fully, depending on the variable order.
// This is a case we can simplify fully, but it's dependent on the variable order.
// The expression is equivalent to `sys_platform == 'x' or (os_name == 'Linux' and platform_system == 'win32')`.
assert_simplifies(
"(os_name == 'Linux' and platform_system == 'win32')
@@ -2590,14 +2609,14 @@ mod test {
or (os_name != 'Linux' and platform_system == 'win32' and sys_platform == 'x')
or (os_name == 'Linux' and platform_system != 'win32' and sys_platform == 'x')
or (os_name != 'Linux' and platform_system != 'win32' and sys_platform == 'x')",
"(os_name != 'Linux' and sys_platform == 'x') or (platform_system != 'win32' and sys_platform == 'x') or (os_name == 'Linux' and platform_system == 'win32')",
"(os_name == 'Linux' and platform_system == 'win32') or sys_platform == 'x'",
);
assert_simplifies("python_version > '3.7'", "python_full_version >= '3.8'");
assert_simplifies(
"(python_version <= '3.7' and os_name == 'Linux') or python_version > '3.7'",
"os_name == 'Linux' or python_full_version >= '3.8'",
"python_full_version >= '3.8' or os_name == 'Linux'",
);
// Again, the extra `<3.7` and `>=3.9` expressions cannot be seen as redundant due to them being interdependent.
@@ -2606,34 +2625,31 @@ mod test {
"(os_name == 'Linux' and sys_platform == 'win32') \
or (os_name != 'Linux' and sys_platform == 'win32' and python_version == '3.7') \
or (os_name != 'Linux' and sys_platform == 'win32' and python_version == '3.8')",
"(python_full_version < '3.7' and os_name == 'Linux' and sys_platform == 'win32') \
or (python_full_version >= '3.9' and os_name == 'Linux' and sys_platform == 'win32') \
or (python_full_version >= '3.7' and python_full_version < '3.9' and sys_platform == 'win32')",
"(python_full_version >= '3.7' and python_full_version < '3.9' and sys_platform == 'win32') or (os_name == 'Linux' and sys_platform == 'win32')",
);
assert_simplifies(
"(implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'darwin') or (os_name == 'nt' and sys_platform == 'win32')",
"(implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'darwin') or (os_name == 'nt' and sys_platform == 'win32')",
"os_name == 'nt' and sys_platform == 'win32'",
);
assert_simplifies(
"(sys_platform == 'darwin' or sys_platform == 'win32')
and ((implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'darwin') or (os_name == 'nt' and sys_platform == 'win32'))",
"(implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'darwin') or (os_name == 'nt' and sys_platform == 'win32')",
"(sys_platform == 'darwin' or sys_platform == 'win32') and ((implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'darwin') or (os_name == 'nt' and sys_platform == 'win32'))",
"os_name == 'nt' and sys_platform == 'win32'",
);
assert_simplifies(
"(sys_platform == 'darwin' or sys_platform == 'win32')
and ((platform_version != '1' and os_name == 'nt' and sys_platform == 'darwin') or (os_name == 'nt' and sys_platform == 'win32'))",
"(os_name == 'nt' and platform_version != '1' and sys_platform == 'darwin') or (os_name == 'nt' and sys_platform == 'win32')",
"os_name == 'nt' and sys_platform == 'win32'",
);
assert_simplifies(
"(os_name == 'nt' and sys_platform == 'win32') \
or (os_name != 'nt' and platform_version == '1' and (sys_platform == 'win32' or sys_platform == 'win64'))",
"(platform_version == '1' and sys_platform == 'win32') \
or (os_name != 'nt' and platform_version == '1' and sys_platform == 'win64') \
or (os_name == 'nt' and sys_platform == 'win32')",
"(os_name != 'nt' and platform_version == '1' and sys_platform == 'win64') \
or (os_name == 'nt' and sys_platform == 'win32') \
or (platform_version == '1' and sys_platform == 'win32')",
);
assert_simplifies(
+173 -11
View File
@@ -439,10 +439,10 @@ fn dependency_multiple_markers() -> Result<()> {
# This file was autogenerated by uv via the following command:
# uv export --cache-dir [CACHE_DIR]
-e .
attrs==23.2.0 ; sys_platform == 'win32' or python_full_version >= '3.12' \
attrs==23.2.0 ; python_full_version >= '3.12' or sys_platform == 'win32' \
--hash=sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30 \
--hash=sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1
cffi==1.16.0 ; (implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'win32') or (python_full_version >= '3.12' and implementation_name != 'pypy' and os_name == 'nt') \
cffi==1.16.0 ; (python_full_version >= '3.12' and implementation_name != 'pypy' and os_name == 'nt') or (implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'win32') \
--hash=sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab \
--hash=sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235 \
--hash=sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a \
@@ -453,22 +453,22 @@ fn dependency_multiple_markers() -> Result<()> {
exceptiongroup==1.2.0 ; python_full_version < '3.11' and sys_platform == 'win32' \
--hash=sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14 \
--hash=sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68
idna==3.6 ; sys_platform == 'win32' or python_full_version >= '3.12' \
idna==3.6 ; python_full_version >= '3.12' or sys_platform == 'win32' \
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
outcome==1.3.0.post0 ; sys_platform == 'win32' or python_full_version >= '3.12' \
outcome==1.3.0.post0 ; python_full_version >= '3.12' or sys_platform == 'win32' \
--hash=sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8 \
--hash=sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b
pycparser==2.21 ; (implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'win32') or (python_full_version >= '3.12' and implementation_name != 'pypy' and os_name == 'nt') \
pycparser==2.21 ; (python_full_version >= '3.12' and implementation_name != 'pypy' and os_name == 'nt') or (implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'win32') \
--hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \
--hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206
sniffio==1.3.1 ; sys_platform == 'win32' or python_full_version >= '3.12' \
sniffio==1.3.1 ; python_full_version >= '3.12' or sys_platform == 'win32' \
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
sortedcontainers==2.4.0 ; sys_platform == 'win32' or python_full_version >= '3.12' \
sortedcontainers==2.4.0 ; python_full_version >= '3.12' or sys_platform == 'win32' \
--hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \
--hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0
trio==0.25.0 ; sys_platform == 'win32' or python_full_version >= '3.12' \
trio==0.25.0 ; python_full_version >= '3.12' or sys_platform == 'win32' \
--hash=sha256:9b41f5993ad2c0e5f62d0acca320ec657fdb6b2a2c22b8c7aed6caf154475c4e \
--hash=sha256:e6458efe29cc543e557a91e614e2b51710eba2961669329ce9c862d50c6e8e81
@@ -501,7 +501,169 @@ fn dependency_conflicting_markers() -> Result<()> {
"#,
)?;
context.lock().assert().success();
uv_snapshot!(context.filters(), context.lock(), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 11 packages in [TIME]
"###);
let lock = context.read("uv.lock");
insta::with_settings!(
{
filters => context.filters(),
},
{
insta::assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12"
resolution-markers = [
"sys_platform == 'darwin'",
"sys_platform == 'win32'",
"sys_platform != 'darwin' and sys_platform != 'win32'",
]
[options]
exclude-newer = "2024-03-25T00:00:00Z"
[[package]]
name = "async-generator"
version = "1.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ce/b6/6fa6b3b598a03cba5e80f829e0dadbb49d7645f523d209b2fb7ea0bbb02a/async_generator-1.10.tar.gz", hash = "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144", size = 29870 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/71/52/39d20e03abd0ac9159c162ec24b93fbcaa111e8400308f2465432495ca2b/async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b", size = 18857 },
]
[[package]]
name = "attrs"
version = "23.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e3/fc/f800d51204003fa8ae392c4e8278f256206e7a919b708eef054f5f4b650d/attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", size = 780820 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/44/827b2a91a5816512fcaf3cc4ebc465ccd5d598c45cefa6703fcf4a79018f/attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1", size = 60752 },
]
[[package]]
name = "cffi"
version = "1.16.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/68/ce/95b0bae7968c65473e1298efb042e10cafc7bafc14d9e4f154008241c91d/cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", size = 512873 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c9/6e/751437067affe7ac0944b1ad4856ec11650da77f0dd8f305fae1117ef7bb/cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", size = 173564 },
{ url = "https://files.pythonhosted.org/packages/e9/63/e285470a4880a4f36edabe4810057bd4b562c6ddcc165eacf9c3c7210b40/cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", size = 181956 },
]
[[package]]
name = "idna"
version = "3.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/bf/3f/ea4b9117521a1e9c50344b909be7886dd00a519552724809bb1f486986c2/idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", size = 175426 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f", size = 61567 },
]
[[package]]
name = "outcome"
version = "1.3.0.post0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs", marker = "sys_platform == 'darwin' or sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/98/df/77698abfac98571e65ffeb0c1fba8ffd692ab8458d617a0eed7d9a8d38f2/outcome-1.3.0.post0.tar.gz", hash = "sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8", size = 21060 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/55/8b/5ab7257531a5d830fc8000c476e63c935488d74609b50f9384a643ec0a62/outcome-1.3.0.post0-py2.py3-none-any.whl", hash = "sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b", size = 10692 },
]
[[package]]
name = "project"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "trio", version = "0.10.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'win32'" },
{ name = "trio", version = "0.25.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'darwin'" },
]
[package.metadata]
requires-dist = [
{ name = "trio", marker = "sys_platform == 'darwin'", specifier = "==0.25.0" },
{ name = "trio", marker = "sys_platform == 'win32'", specifier = "==0.10.0" },
]
[[package]]
name = "pycparser"
version = "2.21"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206", size = 170877 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", size = 118697 },
]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
]
[[package]]
name = "sortedcontainers"
version = "2.4.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 },
]
[[package]]
name = "trio"
version = "0.10.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"sys_platform == 'win32'",
]
dependencies = [
{ name = "async-generator", marker = "sys_platform == 'win32'" },
{ name = "attrs", marker = "sys_platform == 'win32'" },
{ name = "cffi", marker = "os_name == 'nt' and sys_platform == 'win32'" },
{ name = "idna", marker = "sys_platform == 'win32'" },
{ name = "outcome", marker = "sys_platform == 'win32'" },
{ name = "sniffio", marker = "sys_platform == 'win32'" },
{ name = "sortedcontainers", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e6/20/37be7b5f47db6a9fbf905b5de5386e5b7193c45d07becb750db6f03cd117/trio-0.10.0.tar.gz", hash = "sha256:d323cc15f6406d15954af91e5e34af2001cc24163fdde29e3f88a227a1b53ab0", size = 402511 }
[[package]]
name = "trio"
version = "0.25.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"sys_platform == 'darwin'",
]
dependencies = [
{ name = "attrs", marker = "sys_platform == 'darwin'" },
{ name = "idna", marker = "sys_platform == 'darwin'" },
{ name = "outcome", marker = "sys_platform == 'darwin'" },
{ name = "sniffio", marker = "sys_platform == 'darwin'" },
{ name = "sortedcontainers", marker = "sys_platform == 'darwin'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b4/51/4f5ae37ec58768b9c30e5bc5b89431a7baf3fa9d0dda98983af6ef55eb47/trio-0.25.0.tar.gz", hash = "sha256:9b41f5993ad2c0e5f62d0acca320ec657fdb6b2a2c22b8c7aed6caf154475c4e", size = 551863 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/17/c9/f86f89f14d52f9f2f652ce24cb2f60141a51d087db1563f3fba94ba07346/trio-0.25.0-py3-none-any.whl", hash = "sha256:e6458efe29cc543e557a91e614e2b51710eba2961669329ce9c862d50c6e8e81", size = 467161 },
]
"###
);
}
);
uv_snapshot!(context.filters(), context.export(), @r###"
success: true
@@ -516,7 +678,7 @@ fn dependency_conflicting_markers() -> Result<()> {
attrs==23.2.0 ; sys_platform == 'darwin' or sys_platform == 'win32' \
--hash=sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30 \
--hash=sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1
cffi==1.16.0 ; (implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'darwin') or (os_name == 'nt' and sys_platform == 'win32') \
cffi==1.16.0 ; os_name == 'nt' and sys_platform == 'win32' \
--hash=sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235 \
--hash=sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b \
--hash=sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0
@@ -526,7 +688,7 @@ fn dependency_conflicting_markers() -> Result<()> {
outcome==1.3.0.post0 ; sys_platform == 'darwin' or sys_platform == 'win32' \
--hash=sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8 \
--hash=sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b
pycparser==2.21 ; (implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'darwin') or (os_name == 'nt' and sys_platform == 'win32') \
pycparser==2.21 ; os_name == 'nt' and sys_platform == 'win32' \
--hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \
--hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206
sniffio==1.3.1 ; sys_platform == 'darwin' or sys_platform == 'win32' \
+4 -28
View File
@@ -16539,7 +16539,6 @@ fn lock_multiple_sources_conflict() -> Result<()> {
Source markers must be disjoint, but the following markers overlap: `python_full_version == '3.12.*' and sys_platform == 'win32'` and `sys_platform == 'win32'`.
hint: replace `sys_platform == 'win32'` with `python_full_version != '3.12.*' and sys_platform == 'win32'`.
"###);
Ok(())
@@ -17138,7 +17137,7 @@ fn lock_multiple_sources_respect_marker() -> Result<()> {
----- stdout -----
----- stderr -----
Resolved 3 packages in [TIME]
Resolved 2 packages in [TIME]
"###);
let lock = context.read("uv.lock");
@@ -17150,11 +17149,6 @@ fn lock_multiple_sources_respect_marker() -> Result<()> {
lock, @r###"
version = 1
requires-python = ">=3.12"
resolution-markers = [
"platform_system == 'Windows' and sys_platform == 'darwin'",
"platform_system == 'Windows' and sys_platform != 'darwin'",
"platform_system != 'Windows'",
]
[options]
exclude-newer = "2024-03-25T00:00:00Z"
@@ -17163,39 +17157,21 @@ fn lock_multiple_sources_respect_marker() -> Result<()> {
name = "iniconfig"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"platform_system == 'Windows' and sys_platform != 'darwin'",
]
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
]
[[package]]
name = "iniconfig"
version = "2.0.0"
source = { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" }
resolution-markers = [
"platform_system == 'Windows' and sys_platform == 'darwin'",
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" },
]
[[package]]
name = "project"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "iniconfig", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "platform_system == 'Windows' and sys_platform != 'darwin'" },
{ name = "iniconfig", version = "2.0.0", source = { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" }, marker = "platform_system == 'Windows' and sys_platform == 'darwin'" },
{ name = "iniconfig", marker = "platform_system == 'Windows' and sys_platform != 'darwin'" },
]
[package.metadata]
requires-dist = [
{ name = "iniconfig", marker = "platform_system == 'Windows' and sys_platform != 'darwin'" },
{ name = "iniconfig", marker = "platform_system == 'Windows' and sys_platform == 'darwin'", url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" },
]
requires-dist = [{ name = "iniconfig", marker = "platform_system == 'Windows' and sys_platform != 'darwin'" }]
"###
);
});
@@ -17207,7 +17183,7 @@ fn lock_multiple_sources_respect_marker() -> Result<()> {
----- stdout -----
----- stderr -----
Resolved 3 packages in [TIME]
Resolved 2 packages in [TIME]
"###);
Ok(())
+12 -24
View File
@@ -7221,13 +7221,13 @@ fn universal_conflicting() -> Result<()> {
# via
# outcome
# trio
cffi==1.16.0 ; (implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'darwin') or (os_name == 'nt' and sys_platform == 'win32')
cffi==1.16.0 ; os_name == 'nt' and sys_platform == 'win32'
# via trio
idna==3.6 ; sys_platform == 'darwin' or sys_platform == 'win32'
# via trio
outcome==1.3.0.post0 ; sys_platform == 'darwin' or sys_platform == 'win32'
# via trio
pycparser==2.21 ; (implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'darwin') or (os_name == 'nt' and sys_platform == 'win32')
pycparser==2.21 ; os_name == 'nt' and sys_platform == 'win32'
# via cffi
sniffio==1.3.1 ; sys_platform == 'darwin' or sys_platform == 'win32'
# via trio
@@ -7823,7 +7823,7 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> {
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] requirements.in --universal
cmake==3.28.4 ; python_full_version < '3.11' or (python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux')
cmake==3.28.4 ; (python_full_version < '3.11' and platform_machine != 'x86_64') or (python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux') or (python_full_version < '3.11' and platform_system != 'Linux')
# via
# pytorch-triton-rocm
# triton
@@ -7836,7 +7836,7 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> {
# triton
jinja2==3.1.3
# via torch
lit==18.1.2 ; python_full_version < '3.11' or (python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux')
lit==18.1.2 ; (python_full_version < '3.11' and platform_machine != 'x86_64') or (python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux') or (python_full_version < '3.11' and platform_system != 'Linux')
# via
# pytorch-triton-rocm
# triton
@@ -8954,8 +8954,6 @@ fn universal_disjoint_extra() -> Result<()> {
# via flask
click==8.1.7 ; sys_platform == 'darwin' or sys_platform == 'linux'
# via flask
colorama==0.4.6 ; (platform_system == 'Windows' and sys_platform == 'darwin') or (platform_system == 'Windows' and sys_platform == 'linux')
# via click
flask==3.0.2 ; sys_platform == 'linux'
# via -r requirements.in
itsdangerous==2.1.2 ; sys_platform == 'darwin' or sys_platform == 'linux'
@@ -8972,7 +8970,7 @@ fn universal_disjoint_extra() -> Result<()> {
# via flask
----- stderr -----
Resolved 10 packages in [TIME]
Resolved 9 packages in [TIME]
"###
);
@@ -9003,8 +9001,6 @@ fn universal_disjoint_extra_no_strip() -> Result<()> {
# via flask
click==8.1.7 ; sys_platform == 'darwin' or sys_platform == 'linux'
# via flask
colorama==0.4.6 ; (platform_system == 'Windows' and sys_platform == 'darwin') or (platform_system == 'Windows' and sys_platform == 'linux')
# via click
flask==3.0.2 ; sys_platform == 'darwin' or sys_platform == 'linux'
# via -r requirements.in
flask[async]==3.0.2 ; sys_platform == 'linux'
@@ -9025,7 +9021,7 @@ fn universal_disjoint_extra_no_strip() -> Result<()> {
# via flask
----- stderr -----
Resolved 10 packages in [TIME]
Resolved 9 packages in [TIME]
"###
);
@@ -9053,7 +9049,7 @@ fn universal_overlap_extra_base() -> Result<()> {
# via flask
click==8.1.7
# via flask
colorama==0.4.6 ; platform_system == 'Windows'
colorama==0.4.6 ; platform_system == 'Windows' and sys_platform != 'darwin'
# via click
flask==3.0.2
# via -r requirements.in
@@ -9100,7 +9096,7 @@ fn universal_overlap_extra_base_no_strip() -> Result<()> {
# via flask
click==8.1.7
# via flask
colorama==0.4.6 ; platform_system == 'Windows'
colorama==0.4.6 ; platform_system == 'Windows' and sys_platform != 'darwin'
# via click
flask==3.0.2
# via -r requirements.in
@@ -9150,8 +9146,6 @@ fn universal_overlap_extras() -> Result<()> {
# via flask
click==8.1.7 ; sys_platform == 'darwin' or sys_platform == 'linux'
# via flask
colorama==0.4.6 ; (platform_system == 'Windows' and sys_platform == 'darwin') or (platform_system == 'Windows' and sys_platform == 'linux')
# via click
flask==3.0.2 ; sys_platform == 'darwin' or sys_platform == 'linux'
# via -r requirements.in
itsdangerous==2.1.2 ; sys_platform == 'darwin' or sys_platform == 'linux'
@@ -9168,7 +9162,7 @@ fn universal_overlap_extras() -> Result<()> {
# via flask
----- stderr -----
Resolved 10 packages in [TIME]
Resolved 9 packages in [TIME]
"###
);
@@ -9199,8 +9193,6 @@ fn universal_overlap_extras_no_strip() -> Result<()> {
# via flask
click==8.1.7 ; sys_platform == 'darwin' or sys_platform == 'linux'
# via flask
colorama==0.4.6 ; (platform_system == 'Windows' and sys_platform == 'darwin') or (platform_system == 'Windows' and sys_platform == 'linux')
# via click
flask[async]==3.0.2 ; sys_platform == 'darwin' or sys_platform == 'linux'
# via -r requirements.in
flask[dotenv]==3.0.2 ; sys_platform == 'darwin'
@@ -9219,7 +9211,7 @@ fn universal_overlap_extras_no_strip() -> Result<()> {
# via flask
----- stderr -----
Resolved 10 packages in [TIME]
Resolved 9 packages in [TIME]
"###
);
@@ -9249,8 +9241,6 @@ fn universal_identical_extras() -> Result<()> {
# via flask
click==8.1.7 ; sys_platform == 'darwin'
# via flask
colorama==0.4.6 ; platform_system == 'Windows' and sys_platform == 'darwin'
# via click
flask==3.0.2 ; sys_platform == 'darwin'
# via -r requirements.in
itsdangerous==2.1.2 ; sys_platform == 'darwin'
@@ -9267,7 +9257,7 @@ fn universal_identical_extras() -> Result<()> {
# via flask
----- stderr -----
Resolved 10 packages in [TIME]
Resolved 9 packages in [TIME]
"###
);
@@ -9298,8 +9288,6 @@ fn universal_identical_extras_no_strip() -> Result<()> {
# via flask
click==8.1.7 ; sys_platform == 'darwin'
# via flask
colorama==0.4.6 ; platform_system == 'Windows' and sys_platform == 'darwin'
# via click
flask[async, dotenv]==3.0.2 ; sys_platform == 'darwin'
# via -r requirements.in
itsdangerous==2.1.2 ; sys_platform == 'darwin'
@@ -9316,7 +9304,7 @@ fn universal_identical_extras_no_strip() -> Result<()> {
# via flask
----- stderr -----
Resolved 10 packages in [TIME]
Resolved 9 packages in [TIME]
"###
);
@@ -1,5 +1,5 @@
---
source: crates/uv/tests/ecosystem.rs
source: crates/uv/tests/it/ecosystem.rs
expression: lock
---
version = 1
@@ -1,5 +1,5 @@
---
source: crates/uv/tests/ecosystem.rs
source: crates/uv/tests/it/ecosystem.rs
expression: lock
---
version = 1
@@ -7,19 +7,19 @@ requires-python = ">=3.9.0"
resolution-markers = [
"python_full_version < '3.10' and platform_machine == 'arm64' and platform_system == 'Darwin'",
"python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system == 'Linux'",
"(python_full_version < '3.10' and platform_machine != 'aarch64' and platform_machine != 'arm64') or (python_full_version < '3.10' and platform_machine == 'aarch64' and platform_system != 'Linux') or (python_full_version < '3.10' and platform_machine == 'arm64' and platform_system != 'Darwin')",
"(python_full_version < '3.10' and platform_machine != 'arm64' and platform_system == 'Darwin') or (python_full_version < '3.10' and platform_machine != 'aarch64' and platform_system == 'Linux') or (python_full_version < '3.10' and platform_system != 'Darwin' and platform_system != 'Linux')",
"python_full_version == '3.10.*' and platform_system == 'Darwin'",
"python_full_version == '3.10.*' and platform_machine == 'aarch64' and platform_system == 'Linux'",
"(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')",
"(python_full_version == '3.10.*' and platform_machine != 'aarch64' and platform_system == 'Linux') or (python_full_version == '3.10.*' and platform_system != 'Darwin' and platform_system != 'Linux')",
"python_full_version == '3.11.*' and platform_system == 'Darwin'",
"python_full_version == '3.11.*' and platform_machine == 'aarch64' and platform_system == 'Linux'",
"(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')",
"(python_full_version == '3.11.*' and platform_machine != 'aarch64' and platform_system == 'Linux') or (python_full_version == '3.11.*' and platform_system != 'Darwin' and platform_system != 'Linux')",
"python_full_version == '3.12.*' and platform_system == 'Darwin'",
"python_full_version == '3.12.*' and platform_machine == 'aarch64' and platform_system == 'Linux'",
"(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')",
"(python_full_version == '3.12.*' and platform_machine != 'aarch64' and platform_system == 'Linux') or (python_full_version == '3.12.*' and platform_system != 'Darwin' and platform_system != 'Linux')",
"python_full_version >= '3.13' and platform_system == 'Darwin'",
"python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'",
"(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')",
"(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system == 'Linux') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')",
]
[options]
@@ -660,7 +660,7 @@ name = "colorlog"
version = "6.8.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "colorama", marker = "(python_full_version < '3.10' and platform_machine != 'arm64' and platform_system == 'Darwin' and sys_platform == 'win32') or (platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" },
]
sdist = { url = "https://files.pythonhosted.org/packages/db/38/2992ff192eaa7dd5a793f8b6570d6bbe887c4fbbf7e72702eb0a693a01c8/colorlog-6.8.2.tar.gz", hash = "sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44", size = 16529 }
wheels = [
@@ -875,7 +875,7 @@ name = "docker"
version = "7.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pywin32", marker = "sys_platform == 'win32'" },
{ name = "pywin32", marker = "(python_full_version < '3.10' and platform_machine != 'arm64' and platform_system == 'Darwin' and sys_platform == 'win32') or (platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" },
{ name = "requests" },
{ name = "urllib3" },
]
@@ -1440,7 +1440,7 @@ name = "humanfriendly"
version = "10.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyreadline3", marker = "sys_platform == 'win32'" },
{ name = "pyreadline3", marker = "(python_full_version < '3.10' and platform_machine != 'arm64' and platform_system == 'Darwin' and sys_platform == 'win32') or (platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" },
]
sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702 }
wheels = [
@@ -1652,7 +1652,7 @@ version = "5.7.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "platformdirs" },
{ name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" },
{ name = "pywin32", marker = "(python_full_version < '3.10' and platform_machine != 'arm64' and platform_python_implementation != 'PyPy' and platform_system == 'Darwin' and sys_platform == 'win32') or (platform_machine != 'aarch64' and platform_python_implementation != 'PyPy' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_python_implementation != 'PyPy' and platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" },
{ name = "traitlets" },
]
sdist = { url = "https://files.pythonhosted.org/packages/00/11/b56381fa6c3f4cc5d2cf54a7dbf98ad9aa0b339ef7a601d6053538b079a7/jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9", size = 87629 }
@@ -3204,7 +3204,7 @@ name = "pytest"
version = "7.4.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "colorama", marker = "(python_full_version < '3.10' and platform_machine != 'arm64' and platform_system == 'Darwin' and sys_platform == 'win32') or (platform_machine != 'aarch64' and platform_system == 'Linux' and sys_platform == 'win32') or (platform_system != 'Darwin' and platform_system != 'Linux' and sys_platform == 'win32')" },
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "iniconfig" },
{ name = "packaging" },
@@ -1,5 +1,5 @@
---
source: crates/uv/tests/ecosystem.rs
source: crates/uv/tests/it/ecosystem.rs
expression: lock
---
version = 1