diff --git a/crates/uv-pep508/src/marker/algebra.rs b/crates/uv-pep508/src/marker/algebra.rs index 4b0b1d07ef8f..af249934ed92 100644 --- a/crates/uv-pep508/src/marker/algebra.rs +++ b/crates/uv-pep508/src/marker/algebra.rs @@ -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 = 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, } 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.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), + } + } + + /// 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.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), } @@ -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 ` in ` 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), ) { diff --git a/crates/uv-pep508/src/marker/lowering.rs b/crates/uv-pep508/src/marker/lowering.rs index aeb5a53b9a94..16139a65d486 100644 --- a/crates/uv-pep508/src/marker/lowering.rs +++ b/crates/uv-pep508/src/marker/lowering.rs @@ -32,13 +32,22 @@ impl From 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 @@ -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 for CanonicalMarkerValueString { diff --git a/crates/uv-pep508/src/marker/simplify.rs b/crates/uv-pep508/src/marker/simplify.rs index f855f6053918..35a33b6ce4b8 100644 --- a/crates/uv-pep508/src/marker/simplify.rs +++ b/crates/uv-pep508/src/marker/simplify.rs @@ -21,6 +21,7 @@ pub(crate) fn to_dnf(tree: MarkerTree) -> Vec> { 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>) { } } +/// 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]) { + // 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, MarkerTree)>, diff --git a/crates/uv-pep508/src/marker/tree.rs b/crates/uv-pep508/src/marker/tree.rs index 057955916b83..b8afb69e53a6 100644 --- a/crates/uv-pep508/src/marker/tree.rs +++ b/crates/uv-pep508/src/marker/tree.rs @@ -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(MarkerValueVersion), + /// A version "in" expression, e.g. ` in `. + 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, 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( diff --git a/crates/uv/tests/it/export.rs b/crates/uv/tests/it/export.rs index 73274923d270..9a2181a4093a 100644 --- a/crates/uv/tests/it/export.rs +++ b/crates/uv/tests/it/export.rs @@ -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' \ diff --git a/crates/uv/tests/it/lock.rs b/crates/uv/tests/it/lock.rs index 7ed90cb0673b..67bcdc03741d 100644 --- a/crates/uv/tests/it/lock.rs +++ b/crates/uv/tests/it/lock.rs @@ -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(()) diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index d0087ebe98f8..198bea7195c4 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -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] "### ); diff --git a/crates/uv/tests/it/snapshots/it__ecosystem__black-lock-file.snap b/crates/uv/tests/it/snapshots/it__ecosystem__black-lock-file.snap index 197286702d33..8577bc686437 100644 --- a/crates/uv/tests/it/snapshots/it__ecosystem__black-lock-file.snap +++ b/crates/uv/tests/it/snapshots/it__ecosystem__black-lock-file.snap @@ -1,5 +1,5 @@ --- -source: crates/uv/tests/ecosystem.rs +source: crates/uv/tests/it/ecosystem.rs expression: lock --- version = 1 diff --git a/crates/uv/tests/it/snapshots/it__ecosystem__home-assistant-core-lock-file.snap b/crates/uv/tests/it/snapshots/it__ecosystem__home-assistant-core-lock-file.snap index b3342075b642..0bb3a8abd098 100644 --- a/crates/uv/tests/it/snapshots/it__ecosystem__home-assistant-core-lock-file.snap +++ b/crates/uv/tests/it/snapshots/it__ecosystem__home-assistant-core-lock-file.snap @@ -1,5 +1,5 @@ --- -source: crates/uv/tests/ecosystem.rs +source: crates/uv/tests/it/ecosystem.rs expression: lock --- version = 1 diff --git a/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap b/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap index c91826241a67..ef2424c91030 100644 --- a/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap +++ b/crates/uv/tests/it/snapshots/it__ecosystem__transformers-lock-file.snap @@ -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" }, diff --git a/crates/uv/tests/it/snapshots/it__ecosystem__warehouse-lock-file.snap b/crates/uv/tests/it/snapshots/it__ecosystem__warehouse-lock-file.snap index a8dd7118a200..61601b9bc85a 100644 --- a/crates/uv/tests/it/snapshots/it__ecosystem__warehouse-lock-file.snap +++ b/crates/uv/tests/it/snapshots/it__ecosystem__warehouse-lock-file.snap @@ -1,5 +1,5 @@ --- -source: crates/uv/tests/ecosystem.rs +source: crates/uv/tests/it/ecosystem.rs expression: lock --- version = 1