Introduce a generic type for list operations (#14792)

We currently have two marker keys that a list, `extras` and
`dependency_groups`, both from PEP 751. With the variants PEP, we will
add three more. This change is broken out of the wheel variants PR to
introduce generic marker list support, plus a change to use
`ContainerOperator` in more places.
This commit is contained in:
konsti 2025-07-21 18:21:46 +02:00 committed by GitHub
parent d052427c37
commit f3dc457d2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 276 additions and 478 deletions

View File

@ -32,8 +32,8 @@ pub use marker::{
CanonicalMarkerValueExtra, CanonicalMarkerValueString, CanonicalMarkerValueVersion, CanonicalMarkerValueExtra, CanonicalMarkerValueString, CanonicalMarkerValueVersion,
ContainsMarkerTree, ExtraMarkerTree, ExtraOperator, InMarkerTree, MarkerEnvironment, ContainsMarkerTree, ExtraMarkerTree, ExtraOperator, InMarkerTree, MarkerEnvironment,
MarkerEnvironmentBuilder, MarkerExpression, MarkerOperator, MarkerTree, MarkerTreeContents, MarkerEnvironmentBuilder, MarkerExpression, MarkerOperator, MarkerTree, MarkerTreeContents,
MarkerTreeKind, MarkerValue, MarkerValueExtra, MarkerValueString, MarkerValueVersion, MarkerTreeKind, MarkerValue, MarkerValueExtra, MarkerValueList, MarkerValueString,
MarkerWarningKind, StringMarkerTree, StringVersion, VersionMarkerTree, MarkerValueVersion, MarkerWarningKind, StringMarkerTree, StringVersion, VersionMarkerTree,
}; };
pub use origin::RequirementOrigin; pub use origin::RequirementOrigin;
#[cfg(feature = "non-pep508-extensions")] #[cfg(feature = "non-pep508-extensions")]

View File

@ -59,10 +59,10 @@ use uv_pep440::{Operator, Version, VersionSpecifier, release_specifier_to_range}
use crate::marker::MarkerValueExtra; use crate::marker::MarkerValueExtra;
use crate::marker::lowering::{ use crate::marker::lowering::{
CanonicalMarkerValueDependencyGroup, CanonicalMarkerValueExtra, CanonicalMarkerValueString, CanonicalMarkerListPair, CanonicalMarkerValueExtra, CanonicalMarkerValueString,
CanonicalMarkerValueVersion, CanonicalMarkerValueVersion,
}; };
use crate::marker::tree::{ContainerOperator, MarkerValueDependencyGroup}; use crate::marker::tree::ContainerOperator;
use crate::{ use crate::{
ExtraOperator, MarkerExpression, MarkerOperator, MarkerValueString, MarkerValueVersion, ExtraOperator, MarkerExpression, MarkerOperator, MarkerValueString, MarkerValueVersion,
}; };
@ -188,19 +188,19 @@ impl InternerGuard<'_> {
MarkerExpression::VersionIn { MarkerExpression::VersionIn {
key, key,
versions, versions,
negated, operator,
} => match key { } => match key {
MarkerValueVersion::ImplementationVersion => ( MarkerValueVersion::ImplementationVersion => (
Variable::Version(CanonicalMarkerValueVersion::ImplementationVersion), Variable::Version(CanonicalMarkerValueVersion::ImplementationVersion),
Edges::from_versions(&versions, negated), Edges::from_versions(&versions, operator),
), ),
MarkerValueVersion::PythonFullVersion => ( MarkerValueVersion::PythonFullVersion => (
Variable::Version(CanonicalMarkerValueVersion::PythonFullVersion), Variable::Version(CanonicalMarkerValueVersion::PythonFullVersion),
Edges::from_versions(&versions, negated), Edges::from_versions(&versions, operator),
), ),
// Normalize `python_version` markers to `python_full_version` nodes. // Normalize `python_version` markers to `python_full_version` nodes.
MarkerValueVersion::PythonVersion => { MarkerValueVersion::PythonVersion => {
match Edges::from_python_versions(versions, negated) { match Edges::from_python_versions(versions, operator) {
Ok(edges) => ( Ok(edges) => (
Variable::Version(CanonicalMarkerValueVersion::PythonFullVersion), Variable::Version(CanonicalMarkerValueVersion::PythonFullVersion),
edges, edges,
@ -315,6 +315,10 @@ impl InternerGuard<'_> {
}; };
(Variable::String(key), Edges::from_string(operator, value)) (Variable::String(key), Edges::from_string(operator, value))
} }
MarkerExpression::List { pair, operator } => (
Variable::List(pair),
Edges::from_bool(operator == ContainerOperator::In),
),
// A variable representing the existence or absence of a particular extra. // A variable representing the existence or absence of a particular extra.
MarkerExpression::Extra { MarkerExpression::Extra {
name: MarkerValueExtra::Extra(extra), name: MarkerValueExtra::Extra(extra),
@ -335,48 +339,6 @@ impl InternerGuard<'_> {
name: MarkerValueExtra::Arbitrary(_), name: MarkerValueExtra::Arbitrary(_),
.. ..
} => return NodeId::FALSE, } => return NodeId::FALSE,
// A variable representing the existence or absence of a particular extra, in the
// context of a PEP 751 lockfile.
MarkerExpression::Extras {
name: MarkerValueExtra::Extra(extra),
operator: ContainerOperator::In,
} => (
Variable::Extras(CanonicalMarkerValueExtra::Extra(extra)),
Edges::from_bool(true),
),
MarkerExpression::Extras {
name: MarkerValueExtra::Extra(extra),
operator: ContainerOperator::NotIn,
} => (
Variable::Extras(CanonicalMarkerValueExtra::Extra(extra)),
Edges::from_bool(false),
),
// Invalid `extras` names are always `false`.
MarkerExpression::Extras {
name: MarkerValueExtra::Arbitrary(_),
..
} => return NodeId::FALSE,
// A variable representing the existence or absence of a particular extra, in the
// context of a PEP 751 lockfile.
MarkerExpression::DependencyGroups {
name: MarkerValueDependencyGroup::Group(group),
operator: ContainerOperator::In,
} => (
Variable::DependencyGroups(CanonicalMarkerValueDependencyGroup::Group(group)),
Edges::from_bool(true),
),
MarkerExpression::DependencyGroups {
name: MarkerValueDependencyGroup::Group(group),
operator: ContainerOperator::NotIn,
} => (
Variable::DependencyGroups(CanonicalMarkerValueDependencyGroup::Group(group)),
Edges::from_bool(false),
),
// Invalid `dependency_group` names are always `false`.
MarkerExpression::DependencyGroups {
name: MarkerValueDependencyGroup::Arbitrary(_),
..
} => return NodeId::FALSE,
}; };
self.create_node(var, children) self.create_node(var, children)
@ -1090,18 +1052,12 @@ pub(crate) enum Variable {
/// We keep extras at the leaves of the tree, so when simplifying extras we can /// We keep extras at the leaves of the tree, so when simplifying extras we can
/// trivially remove the leaves without having to reconstruct the entire tree. /// trivially remove the leaves without having to reconstruct the entire tree.
Extra(CanonicalMarkerValueExtra), Extra(CanonicalMarkerValueExtra),
/// A variable representing the existence or absence of a given extra, in the context of a /// A variable representing whether a `<value> in <key>` or `<value> not in <key>`
/// PEP 751 lockfile marker. /// expression, where the key is a list.
/// ///
/// We keep extras at the leaves of the tree, so when simplifying extras we can /// We keep extras and groups at the leaves of the tree, so when simplifying extras we can
/// trivially remove the leaves without having to reconstruct the entire tree. /// trivially remove the leaves without having to reconstruct the entire tree.
Extras(CanonicalMarkerValueExtra), List(CanonicalMarkerListPair),
/// A variable representing the existence or absence of a given dependency group, in the context of a
/// PEP 751 lockfile marker.
///
/// We keep groups at the leaves of the tree, so when simplifying groups we can
/// trivially remove the leaves without having to reconstruct the entire tree.
DependencyGroups(CanonicalMarkerValueDependencyGroup),
} }
impl Variable { impl Variable {
@ -1279,7 +1235,10 @@ impl Edges {
/// Returns an [`Edges`] where values in the given range are `true`. /// Returns an [`Edges`] where values in the given range are `true`.
/// ///
/// Only for use when the `key` is a `PythonVersion`. Normalizes to `PythonFullVersion`. /// Only for use when the `key` is a `PythonVersion`. Normalizes to `PythonFullVersion`.
fn from_python_versions(versions: Vec<Version>, negated: bool) -> Result<Edges, NodeId> { fn from_python_versions(
versions: Vec<Version>,
operator: ContainerOperator,
) -> Result<Edges, NodeId> {
let mut range: Ranges<Version> = versions let mut range: Ranges<Version> = versions
.into_iter() .into_iter()
.map(|version| { .map(|version| {
@ -1290,7 +1249,7 @@ impl Edges {
.flatten_ok() .flatten_ok()
.collect::<Result<Ranges<_>, NodeId>>()?; .collect::<Result<Ranges<_>, NodeId>>()?;
if negated { if operator == ContainerOperator::NotIn {
range = range.complement(); range = range.complement();
} }
@ -1300,7 +1259,7 @@ impl Edges {
} }
/// Returns an [`Edges`] where values in the given range are `true`. /// Returns an [`Edges`] where values in the given range are `true`.
fn from_versions(versions: &[Version], negated: bool) -> Edges { fn from_versions(versions: &[Version], operator: ContainerOperator) -> Edges {
let mut range: Ranges<Version> = versions let mut range: Ranges<Version> = versions
.iter() .iter()
.map(|version| { .map(|version| {
@ -1311,7 +1270,7 @@ impl Edges {
}) })
.collect(); .collect();
if negated { if operator == ContainerOperator::NotIn {
range = range.complement(); range = range.complement();
} }

View File

@ -2,7 +2,7 @@ use std::fmt::{Display, Formatter};
use uv_normalize::{ExtraName, GroupName}; use uv_normalize::{ExtraName, GroupName};
use crate::marker::tree::MarkerValueDependencyGroup; use crate::marker::tree::MarkerValueList;
use crate::{MarkerValueExtra, MarkerValueString, MarkerValueVersion}; use crate::{MarkerValueExtra, MarkerValueString, MarkerValueVersion};
/// Those environment markers with a PEP 440 version as value such as `python_version` /// Those environment markers with a PEP 440 version as value such as `python_version`
@ -161,34 +161,35 @@ impl Display for CanonicalMarkerValueExtra {
} }
} }
/// The [`GroupName`] value used in `dependency_group` markers. /// A key-value pair for `<value> in <key>` or `<value> not in <key>`, where the key is a list.
///
/// Used for PEP 751 markers.
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum CanonicalMarkerValueDependencyGroup { pub enum CanonicalMarkerListPair {
/// A valid [`ExtraName`].
Extras(ExtraName),
/// A valid [`GroupName`]. /// A valid [`GroupName`].
Group(GroupName), DependencyGroup(GroupName),
/// For leniency, preserve invalid values.
Arbitrary { key: MarkerValueList, value: String },
} }
impl CanonicalMarkerValueDependencyGroup { impl CanonicalMarkerListPair {
/// Returns the [`GroupName`] value. /// The key (RHS) of the marker expression.
pub fn group(&self) -> &GroupName { pub(crate) fn key(&self) -> MarkerValueList {
match self { match self {
Self::Group(group) => group, Self::Extras(_) => MarkerValueList::Extras,
} Self::DependencyGroup(_) => MarkerValueList::DependencyGroups,
Self::Arbitrary { key, .. } => *key,
} }
} }
impl From<CanonicalMarkerValueDependencyGroup> for MarkerValueDependencyGroup { /// The value (LHS) of the marker expression.
fn from(value: CanonicalMarkerValueDependencyGroup) -> Self { pub(crate) fn value(&self) -> String {
match value {
CanonicalMarkerValueDependencyGroup::Group(group) => Self::Group(group),
}
}
}
impl Display for CanonicalMarkerValueDependencyGroup {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::Group(group) => group.fmt(f), Self::Extras(extra) => extra.to_string(),
Self::DependencyGroup(group) => group.to_string(),
Self::Arbitrary { value, .. } => value.clone(),
} }
} }
} }

View File

@ -23,8 +23,8 @@ pub use lowering::{
pub use tree::{ pub use tree::{
ContainsMarkerTree, ExtraMarkerTree, ExtraOperator, InMarkerTree, MarkerExpression, ContainsMarkerTree, ExtraMarkerTree, ExtraOperator, InMarkerTree, MarkerExpression,
MarkerOperator, MarkerTree, MarkerTreeContents, MarkerTreeDebugGraph, MarkerTreeKind, MarkerOperator, MarkerTree, MarkerTreeContents, MarkerTreeDebugGraph, MarkerTreeKind,
MarkerValue, MarkerValueExtra, MarkerValueString, MarkerValueVersion, MarkerWarningKind, MarkerValue, MarkerValueExtra, MarkerValueList, MarkerValueString, MarkerValueVersion,
StringMarkerTree, StringVersion, VersionMarkerTree, MarkerWarningKind, StringMarkerTree, StringVersion, VersionMarkerTree,
}; };
/// `serde` helpers for [`MarkerTree`]. /// `serde` helpers for [`MarkerTree`].

View File

@ -5,7 +5,8 @@ use uv_pep440::{Version, VersionPattern, VersionSpecifier};
use crate::cursor::Cursor; use crate::cursor::Cursor;
use crate::marker::MarkerValueExtra; use crate::marker::MarkerValueExtra;
use crate::marker::tree::{ContainerOperator, MarkerValueContains, MarkerValueDependencyGroup}; use crate::marker::lowering::CanonicalMarkerListPair;
use crate::marker::tree::{ContainerOperator, MarkerValueList};
use crate::{ use crate::{
ExtraOperator, MarkerExpression, MarkerOperator, MarkerTree, MarkerValue, MarkerValueString, ExtraOperator, MarkerExpression, MarkerOperator, MarkerTree, MarkerValue, MarkerValueString,
MarkerValueVersion, MarkerWarningKind, Pep508Error, Pep508ErrorSource, Pep508Url, Reporter, MarkerValueVersion, MarkerWarningKind, Pep508Error, Pep508ErrorSource, Pep508Url, Reporter,
@ -169,6 +170,7 @@ pub(crate) fn parse_marker_key_op_value<T: Pep508Url>(
reporter: &mut impl Reporter, reporter: &mut impl Reporter,
) -> Result<Option<MarkerExpression>, Pep508Error<T>> { ) -> Result<Option<MarkerExpression>, Pep508Error<T>> {
cursor.eat_whitespace(); cursor.eat_whitespace();
let start = cursor.pos();
let l_value = parse_marker_value(cursor, reporter)?; let l_value = parse_marker_value(cursor, reporter)?;
cursor.eat_whitespace(); cursor.eat_whitespace();
// "not in" and "in" must be preceded by whitespace. We must already have matched a whitespace // "not in" and "in" must be preceded by whitespace. We must already have matched a whitespace
@ -177,6 +179,7 @@ pub(crate) fn parse_marker_key_op_value<T: Pep508Url>(
let operator = parse_marker_operator(cursor)?; let operator = parse_marker_operator(cursor)?;
cursor.eat_whitespace(); cursor.eat_whitespace();
let r_value = parse_marker_value(cursor, reporter)?; let r_value = parse_marker_value(cursor, reporter)?;
let len = cursor.pos() - start;
// Convert a `<marker_value> <marker_op> <marker_value>` expression into its // Convert a `<marker_value> <marker_op> <marker_value>` expression into its
// typed equivalent. // typed equivalent.
@ -211,7 +214,7 @@ pub(crate) fn parse_marker_key_op_value<T: Pep508Url>(
MarkerValue::Extra MarkerValue::Extra
| MarkerValue::MarkerEnvVersion(_) | MarkerValue::MarkerEnvVersion(_)
| MarkerValue::MarkerEnvString(_) | MarkerValue::MarkerEnvString(_)
| MarkerValue::MarkerEnvContains(_) => { | MarkerValue::MarkerEnvList(_) => {
reporter.report( reporter.report(
MarkerWarningKind::MarkerMarkerComparison, MarkerWarningKind::MarkerMarkerComparison,
"Comparing two markers with each other doesn't make any sense, "Comparing two markers with each other doesn't make any sense,
@ -239,12 +242,23 @@ pub(crate) fn parse_marker_key_op_value<T: Pep508Url>(
value, value,
}) })
} }
// `extras in "test"` or `dependency_groups not in "dev"` are invalid.
MarkerValue::MarkerEnvList(key) => {
return Err(Pep508Error {
message: Pep508ErrorSource::String(format!(
"The marker {key} must be on the right hand side of the expression"
)),
start,
len,
input: cursor.to_string(),
});
}
// `extra == '...'` // `extra == '...'`
MarkerValue::Extra => { MarkerValue::Extra => {
let value = match r_value { let value = match r_value {
MarkerValue::MarkerEnvVersion(_) MarkerValue::MarkerEnvVersion(_)
| MarkerValue::MarkerEnvString(_) | MarkerValue::MarkerEnvString(_)
| MarkerValue::MarkerEnvContains(_) | MarkerValue::MarkerEnvList(_)
| MarkerValue::Extra => { | MarkerValue::Extra => {
reporter.report( reporter.report(
MarkerWarningKind::ExtraInvalidComparison, MarkerWarningKind::ExtraInvalidComparison,
@ -274,16 +288,56 @@ pub(crate) fn parse_marker_key_op_value<T: Pep508Url>(
operator: operator.invert(), operator: operator.invert(),
value: l_string, value: l_string,
}), }),
// `"test" in extras` or `"dev" in dependency_groups`
MarkerValue::MarkerEnvList(key) => {
let operator =
ContainerOperator::from_marker_operator(operator).ok_or_else(|| {
Pep508Error {
message: Pep508ErrorSource::String(format!(
"The operator {operator} is not supported with the marker {key}, only the `in` and `not in` operators are supported"
)),
start,
len,
input: cursor.to_string(),
}
})?;
let pair = match key {
// `'...' in extras`
MarkerValueList::Extras => match ExtraName::from_str(&l_string) {
Ok(name) => CanonicalMarkerListPair::Extras(name),
Err(err) => {
reporter.report(
MarkerWarningKind::ExtrasInvalidComparison,
format!("Expected extra name (found `{l_string}`): {err}"),
);
CanonicalMarkerListPair::Arbitrary {
key,
value: l_string.to_string(),
}
}
},
// `'...' in dependency_groups`
MarkerValueList::DependencyGroups => {
match GroupName::from_str(&l_string) {
Ok(name) => CanonicalMarkerListPair::DependencyGroup(name),
Err(err) => {
reporter.report(
MarkerWarningKind::ExtrasInvalidComparison,
format!("Expected dependency group name (found `{l_string}`): {err}"),
);
CanonicalMarkerListPair::Arbitrary {
key,
value: l_string.to_string(),
}
}
}
}
};
Some(MarkerExpression::List { pair, operator })
}
// `'...' == extra` // `'...' == extra`
MarkerValue::Extra => parse_extra_expr(operator, &l_string, reporter), MarkerValue::Extra => parse_extra_expr(operator, &l_string, reporter),
// `'...' in extras`
MarkerValue::MarkerEnvContains(MarkerValueContains::Extras) => {
parse_extras_expr(operator, &l_string, reporter)
}
// `'...' in dependency_groups`
MarkerValue::MarkerEnvContains(MarkerValueContains::DependencyGroups) => {
parse_dependency_groups_expr(operator, &l_string, reporter)
}
// `'...' == '...'`, doesn't make much sense // `'...' == '...'`, doesn't make much sense
MarkerValue::QuotedString(_) => { MarkerValue::QuotedString(_) => {
// Not even pypa/packaging 22.0 supports this // Not even pypa/packaging 22.0 supports this
@ -300,16 +354,6 @@ pub(crate) fn parse_marker_key_op_value<T: Pep508Url>(
} }
} }
} }
MarkerValue::MarkerEnvContains(key) => {
reporter.report(
MarkerWarningKind::Pep440Error,
format!(
"The `{key}` marker must be used as '...' in {key}' or '... not in {key}',
found `{key} {operator} {r_value}`, will be ignored"
),
);
return Ok(None);
}
}; };
Ok(expr) Ok(expr)
@ -340,10 +384,7 @@ fn parse_version_in_expr(
value: &str, value: &str,
reporter: &mut impl Reporter, reporter: &mut impl Reporter,
) -> Option<MarkerExpression> { ) -> Option<MarkerExpression> {
if !matches!(operator, MarkerOperator::In | MarkerOperator::NotIn) { let operator = ContainerOperator::from_marker_operator(operator)?;
return None;
}
let negated = matches!(operator, MarkerOperator::NotIn);
let mut cursor = Cursor::new(value); let mut cursor = Cursor::new(value);
let mut versions = Vec::new(); let mut versions = Vec::new();
@ -379,7 +420,7 @@ fn parse_version_in_expr(
Some(MarkerExpression::VersionIn { Some(MarkerExpression::VersionIn {
key, key,
versions, versions,
negated, operator,
}) })
} }
@ -519,68 +560,6 @@ fn parse_extra_expr(
None None
} }
/// Creates an instance of [`MarkerExpression::Extras`] with the given values, falling back to
/// [`MarkerExpression::Arbitrary`] on failure.
fn parse_extras_expr(
operator: MarkerOperator,
value: &str,
reporter: &mut impl Reporter,
) -> Option<MarkerExpression> {
let name = match ExtraName::from_str(value) {
Ok(name) => MarkerValueExtra::Extra(name),
Err(err) => {
reporter.report(
MarkerWarningKind::ExtrasInvalidComparison,
format!("Expected extra name (found `{value}`): {err}"),
);
MarkerValueExtra::Arbitrary(value.to_string())
}
};
if let Some(operator) = ContainerOperator::from_marker_operator(operator) {
return Some(MarkerExpression::Extras { operator, name });
}
reporter.report(
MarkerWarningKind::ExtrasInvalidComparison,
"Comparing `extras` with any operator other than `in` or `not in` is wrong and will be ignored"
.to_string(),
);
None
}
/// Creates an instance of [`MarkerExpression::DependencyGroups`] with the given values, falling
/// back to [`MarkerExpression::Arbitrary`] on failure.
fn parse_dependency_groups_expr(
operator: MarkerOperator,
value: &str,
reporter: &mut impl Reporter,
) -> Option<MarkerExpression> {
let name = match GroupName::from_str(value) {
Ok(name) => MarkerValueDependencyGroup::Group(name),
Err(err) => {
reporter.report(
MarkerWarningKind::ExtrasInvalidComparison,
format!("Expected extra name (found `{value}`): {err}"),
);
MarkerValueDependencyGroup::Arbitrary(value.to_string())
}
};
if let Some(operator) = ContainerOperator::from_marker_operator(operator) {
return Some(MarkerExpression::DependencyGroups { operator, name });
}
reporter.report(
MarkerWarningKind::ExtrasInvalidComparison,
"Comparing `extras` with any operator other than `in` or `not in` is wrong and will be ignored"
.to_string(),
);
None
}
/// ```text /// ```text
/// marker_expr = marker_var:l marker_op:o marker_var:r -> (o, l, r) /// marker_expr = marker_var:l marker_op:o marker_var:r -> (o, l, r)
/// | wsp* '(' marker:m wsp* ')' -> m /// | wsp* '(' marker:m wsp* ')' -> m

View File

@ -162,6 +162,22 @@ fn collect_dnf(
path.pop(); path.pop();
} }
} }
MarkerTreeKind::List(marker) => {
for (is_high, tree) in marker.children() {
let expr = MarkerExpression::List {
pair: marker.pair().clone(),
operator: if is_high {
ContainerOperator::In
} else {
ContainerOperator::NotIn
},
};
path.push(expr);
collect_dnf(tree, dnf, path);
path.pop();
}
}
MarkerTreeKind::Extra(marker) => { MarkerTreeKind::Extra(marker) => {
for (value, tree) in marker.children() { for (value, tree) in marker.children() {
let operator = if value { let operator = if value {
@ -175,42 +191,6 @@ fn collect_dnf(
operator, operator,
}; };
path.push(expr);
collect_dnf(tree, dnf, path);
path.pop();
}
}
MarkerTreeKind::Extras(marker) => {
for (value, tree) in marker.children() {
let operator = if value {
ContainerOperator::In
} else {
ContainerOperator::NotIn
};
let expr = MarkerExpression::Extras {
name: marker.name().clone().into(),
operator,
};
path.push(expr);
collect_dnf(tree, dnf, path);
path.pop();
}
}
MarkerTreeKind::DependencyGroups(marker) => {
for (value, tree) in marker.children() {
let operator = if value {
ContainerOperator::In
} else {
ContainerOperator::NotIn
};
let expr = MarkerExpression::DependencyGroups {
name: marker.name().clone().into(),
operator,
};
path.push(expr); path.push(expr);
collect_dnf(tree, dnf, path); collect_dnf(tree, dnf, path);
path.pop(); path.pop();
@ -433,18 +413,18 @@ fn is_negation(left: &MarkerExpression, right: &MarkerExpression) -> bool {
MarkerExpression::VersionIn { MarkerExpression::VersionIn {
key, key,
versions, versions,
negated, operator,
} => { } => {
let MarkerExpression::VersionIn { let MarkerExpression::VersionIn {
key: key2, key: key2,
versions: versions2, versions: versions2,
negated: negated2, operator: operator2,
} = right } = right
else { else {
return false; return false;
}; };
key == key2 && versions == versions2 && negated != negated2 key == key2 && versions == versions2 && operator != operator2
} }
MarkerExpression::String { MarkerExpression::String {
key, key,
@ -477,27 +457,16 @@ fn is_negation(left: &MarkerExpression, right: &MarkerExpression) -> bool {
name == name2 && operator.negate() == *operator2 name == name2 && operator.negate() == *operator2
} }
MarkerExpression::Extras { name, operator } => { MarkerExpression::List { pair, operator } => {
let MarkerExpression::Extras { let MarkerExpression::List {
name: name2, pair: pair2,
operator: operator2, operator: operator2,
} = right } = right
else { else {
return false; return false;
}; };
name == name2 && *operator == operator2.negate() pair == pair2 && operator != operator2
}
MarkerExpression::DependencyGroups { name, operator } => {
let MarkerExpression::DependencyGroups {
name: name2,
operator: operator2,
} = right
else {
return false;
};
name == name2 && *operator == operator2.negate()
} }
} }
} }

View File

@ -16,12 +16,12 @@ use super::algebra::{Edges, INTERNER, NodeId, Variable};
use super::simplify; use super::simplify;
use crate::cursor::Cursor; use crate::cursor::Cursor;
use crate::marker::lowering::{ use crate::marker::lowering::{
CanonicalMarkerValueDependencyGroup, CanonicalMarkerValueExtra, CanonicalMarkerValueString, CanonicalMarkerListPair, CanonicalMarkerValueString, CanonicalMarkerValueVersion,
CanonicalMarkerValueVersion,
}; };
use crate::marker::parse; use crate::marker::parse;
use crate::{ use crate::{
MarkerEnvironment, Pep508Error, Pep508ErrorSource, Pep508Url, Reporter, TracingReporter, CanonicalMarkerValueExtra, MarkerEnvironment, Pep508Error, Pep508ErrorSource, Pep508Url,
Reporter, TracingReporter,
}; };
/// Ways in which marker evaluation can fail /// Ways in which marker evaluation can fail
@ -126,16 +126,18 @@ impl Display for MarkerValueString {
} }
} }
/// Those markers with exclusively `in` and `not in` operators (PEP 751) /// Those markers with exclusively `in` and `not in` operators.
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] ///
pub enum MarkerValueContains { /// Contains PEP 751 lockfile markers.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum MarkerValueList {
/// `extras`. This one is special because it's a list, and user-provided /// `extras`. This one is special because it's a list, and user-provided
Extras, Extras,
/// `dependency_groups`. This one is special because it's a list, and user-provided /// `dependency_groups`. This one is special because it's a list, and user-provided
DependencyGroups, DependencyGroups,
} }
impl Display for MarkerValueContains { impl Display for MarkerValueList {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Extras => f.write_str("extras"), Self::Extras => f.write_str("extras"),
@ -153,10 +155,10 @@ pub enum MarkerValue {
MarkerEnvVersion(MarkerValueVersion), MarkerEnvVersion(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`
MarkerEnvString(MarkerValueString), MarkerEnvString(MarkerValueString),
/// Those markers with exclusively `in` and `not in` operators
MarkerEnvList(MarkerValueList),
/// `extra`. This one is special because it's a list, and user-provided /// `extra`. This one is special because it's a list, and user-provided
Extra, Extra,
/// Those markers with exclusively `in` and `not in` operators (PEP 751)
MarkerEnvContains(MarkerValueContains),
/// Not a constant, but a user given quoted string with a value inside such as '3.8' or "windows" /// Not a constant, but a user given quoted string with a value inside such as '3.8' or "windows"
QuotedString(ArcStr), QuotedString(ArcStr),
} }
@ -196,9 +198,9 @@ impl FromStr for MarkerValue {
"python_version" => Self::MarkerEnvVersion(MarkerValueVersion::PythonVersion), "python_version" => Self::MarkerEnvVersion(MarkerValueVersion::PythonVersion),
"sys_platform" => Self::MarkerEnvString(MarkerValueString::SysPlatform), "sys_platform" => Self::MarkerEnvString(MarkerValueString::SysPlatform),
"sys.platform" => Self::MarkerEnvString(MarkerValueString::SysPlatformDeprecated), "sys.platform" => Self::MarkerEnvString(MarkerValueString::SysPlatformDeprecated),
"extras" => Self::MarkerEnvList(MarkerValueList::Extras),
"dependency_groups" => Self::MarkerEnvList(MarkerValueList::DependencyGroups),
"extra" => Self::Extra, "extra" => Self::Extra,
"extras" => Self::MarkerEnvContains(MarkerValueContains::Extras),
"dependency_groups" => Self::MarkerEnvContains(MarkerValueContains::DependencyGroups),
_ => return Err(format!("Invalid key: {s}")), _ => return Err(format!("Invalid key: {s}")),
}; };
Ok(value) Ok(value)
@ -210,8 +212,8 @@ impl Display for MarkerValue {
match self { match self {
Self::MarkerEnvVersion(marker_value_version) => marker_value_version.fmt(f), Self::MarkerEnvVersion(marker_value_version) => marker_value_version.fmt(f),
Self::MarkerEnvString(marker_value_string) => marker_value_string.fmt(f), Self::MarkerEnvString(marker_value_string) => marker_value_string.fmt(f),
Self::MarkerEnvList(marker_value_contains) => marker_value_contains.fmt(f),
Self::Extra => f.write_str("extra"), Self::Extra => f.write_str("extra"),
Self::MarkerEnvContains(marker_value_contains) => marker_value_contains.fmt(f),
Self::QuotedString(value) => write!(f, "'{value}'"), Self::QuotedString(value) => write!(f, "'{value}'"),
} }
} }
@ -499,24 +501,6 @@ impl Display for MarkerValueExtra {
} }
} }
/// The [`GroupName`] value used in `dependency_group` markers.
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum MarkerValueDependencyGroup {
/// A valid [`GroupName`].
Group(GroupName),
/// An invalid name, preserved as an arbitrary string.
Arbitrary(String),
}
impl Display for MarkerValueDependencyGroup {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Group(group) => group.fmt(f),
Self::Arbitrary(string) => string.fmt(f),
}
}
}
/// Represents one clause such as `python_version > "3.8"`. /// Represents one clause such as `python_version > "3.8"`.
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[allow(missing_docs)] #[allow(missing_docs)]
@ -540,7 +524,7 @@ pub enum MarkerExpression {
VersionIn { VersionIn {
key: MarkerValueVersion, key: MarkerValueVersion,
versions: Vec<Version>, versions: Vec<Version>,
negated: bool, operator: ContainerOperator,
}, },
/// An string marker comparison, e.g. `sys_platform == '...'`. /// An string marker comparison, e.g. `sys_platform == '...'`.
/// ///
@ -550,21 +534,16 @@ pub enum MarkerExpression {
operator: MarkerOperator, operator: MarkerOperator,
value: ArcStr, value: ArcStr,
}, },
/// `'...' in <key>`, a PEP 751 expression.
List {
pair: CanonicalMarkerListPair,
operator: ContainerOperator,
},
/// `extra <extra op> '...'` or `'...' <extra op> extra`. /// `extra <extra op> '...'` or `'...' <extra op> extra`.
Extra { Extra {
name: MarkerValueExtra, name: MarkerValueExtra,
operator: ExtraOperator, operator: ExtraOperator,
}, },
/// `'...' in extras`
Extras {
name: MarkerValueExtra,
operator: ContainerOperator,
},
/// `'...' in dependency_groups`
DependencyGroups {
name: MarkerValueDependencyGroup,
operator: ContainerOperator,
},
} }
/// The kind of a [`MarkerExpression`]. /// The kind of a [`MarkerExpression`].
@ -572,16 +551,14 @@ pub enum MarkerExpression {
pub(crate) enum MarkerExpressionKind { pub(crate) enum MarkerExpressionKind {
/// A version expression, e.g. `<version key> <version op> <quoted PEP 440 version>`. /// A version expression, e.g. `<version key> <version op> <quoted PEP 440 version>`.
Version(MarkerValueVersion), Version(MarkerValueVersion),
/// A version "in" expression, e.g. `<version key> in <quoted list of PEP 440 versions>`. /// A version `in` expression, e.g. `<version key> in <quoted list of PEP 440 versions>`.
VersionIn(MarkerValueVersion), VersionIn(MarkerValueVersion),
/// A string marker comparison, e.g. `sys_platform == '...'`. /// A string marker comparison, e.g. `sys_platform == '...'`.
String(MarkerValueString), String(MarkerValueString),
/// A list `in` or `not in` expression, e.g. `'...' in dependency_groups`.
List(MarkerValueList),
/// An extra expression, e.g. `extra == '...'`. /// An extra expression, e.g. `extra == '...'`.
Extra, Extra,
/// An extras expression, e.g. `'...' in extras`.
Extras,
/// A dependency groups expression, e.g. `'...' in dependency_groups`.
DependencyGroups,
} }
/// The operator for an extra expression, either '==' or '!='. /// The operator for an extra expression, either '==' or '!='.
@ -624,7 +601,7 @@ impl Display for ExtraOperator {
} }
/// The operator for a container expression, either 'in' or 'not in'. /// The operator for a container expression, either 'in' or 'not in'.
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum ContainerOperator { pub enum ContainerOperator {
/// `in` /// `in`
In, In,
@ -643,14 +620,6 @@ impl ContainerOperator {
_ => None, _ => None,
} }
} }
/// Negates this operator.
pub(crate) fn negate(&self) -> ContainerOperator {
match *self {
ContainerOperator::In => ContainerOperator::NotIn,
ContainerOperator::NotIn => ContainerOperator::In,
}
}
} }
impl Display for ContainerOperator { impl Display for ContainerOperator {
@ -700,9 +669,8 @@ impl MarkerExpression {
MarkerExpression::Version { key, .. } => MarkerExpressionKind::Version(*key), MarkerExpression::Version { key, .. } => MarkerExpressionKind::Version(*key),
MarkerExpression::VersionIn { key, .. } => MarkerExpressionKind::VersionIn(*key), MarkerExpression::VersionIn { key, .. } => MarkerExpressionKind::VersionIn(*key),
MarkerExpression::String { key, .. } => MarkerExpressionKind::String(*key), MarkerExpression::String { key, .. } => MarkerExpressionKind::String(*key),
MarkerExpression::List { pair, .. } => MarkerExpressionKind::List(pair.key()),
MarkerExpression::Extra { .. } => MarkerExpressionKind::Extra, MarkerExpression::Extra { .. } => MarkerExpressionKind::Extra,
MarkerExpression::Extras { .. } => MarkerExpressionKind::Extras,
MarkerExpression::DependencyGroups { .. } => MarkerExpressionKind::DependencyGroups,
} }
} }
} }
@ -721,11 +689,10 @@ impl Display for MarkerExpression {
MarkerExpression::VersionIn { MarkerExpression::VersionIn {
key, key,
versions, versions,
negated, operator,
} => { } => {
let op = if *negated { "not in" } else { "in" };
let versions = versions.iter().map(ToString::to_string).join(" "); let versions = versions.iter().map(ToString::to_string).join(" ");
write!(f, "{key} {op} '{versions}'") write!(f, "{key} {operator} '{versions}'")
} }
MarkerExpression::String { MarkerExpression::String {
key, key,
@ -741,15 +708,12 @@ impl Display for MarkerExpression {
write!(f, "{key} {operator} '{value}'") write!(f, "{key} {operator} '{value}'")
} }
MarkerExpression::List { pair, operator } => {
write!(f, "'{}' {} {}", pair.value(), operator, pair.key())
}
MarkerExpression::Extra { operator, name } => { MarkerExpression::Extra { operator, name } => {
write!(f, "extra {operator} '{name}'") write!(f, "extra {operator} '{name}'")
} }
MarkerExpression::Extras { operator, name } => {
write!(f, "'{name}' {operator} extras")
}
MarkerExpression::DependencyGroups { operator, name } => {
write!(f, "'{name}' {operator} dependency_groups")
}
} }
} }
} }
@ -777,24 +741,24 @@ impl<'a> ExtrasEnvironment<'a> {
/// Returns the `extra` names in this environment. /// Returns the `extra` names in this environment.
fn extra(&self) -> &[ExtraName] { fn extra(&self) -> &[ExtraName] {
match self { match self {
ExtrasEnvironment::Extras(extra) => extra, Self::Extras(extra) => extra,
ExtrasEnvironment::Pep751(..) => &[], Self::Pep751(..) => &[],
} }
} }
/// Returns the `extras` names in this environment, as in a PEP 751 lockfile. /// Returns the `extras` names in this environment, as in a PEP 751 lockfile.
fn extras(&self) -> &[ExtraName] { fn extras(&self) -> &[ExtraName] {
match self { match self {
ExtrasEnvironment::Extras(..) => &[], Self::Extras(..) => &[],
ExtrasEnvironment::Pep751(extras, ..) => extras, Self::Pep751(extras, ..) => extras,
} }
} }
/// Returns the `dependency_group` group names in this environment, as in a PEP 751 lockfile. /// Returns the `dependency_group` group names in this environment, as in a PEP 751 lockfile.
fn dependency_groups(&self) -> &[GroupName] { fn dependency_groups(&self) -> &[GroupName] {
match self { match self {
ExtrasEnvironment::Extras(..) => &[], Self::Extras(..) => &[],
ExtrasEnvironment::Pep751(.., groups) => groups, Self::Pep751(.., groups) => groups,
} }
} }
} }
@ -1006,6 +970,16 @@ impl MarkerTree {
low: low.negate(self.0), low: low.negate(self.0),
}) })
} }
Variable::List(key) => {
let Edges::Boolean { low, high } = node.children else {
unreachable!()
};
MarkerTreeKind::List(ListMarkerTree {
pair: key,
high: high.negate(self.0),
low: low.negate(self.0),
})
}
Variable::Extra(name) => { Variable::Extra(name) => {
let Edges::Boolean { low, high } = node.children else { let Edges::Boolean { low, high } = node.children else {
unreachable!() unreachable!()
@ -1016,26 +990,6 @@ impl MarkerTree {
low: low.negate(self.0), low: low.negate(self.0),
}) })
} }
Variable::Extras(name) => {
let Edges::Boolean { low, high } = node.children else {
unreachable!()
};
MarkerTreeKind::Extras(ExtrasMarkerTree {
name,
high: high.negate(self.0),
low: low.negate(self.0),
})
}
Variable::DependencyGroups(name) => {
let Edges::Boolean { low, high } = node.children else {
unreachable!()
};
MarkerTreeKind::DependencyGroups(DependencyGroupsMarkerTree {
name,
high: high.negate(self.0),
low: low.negate(self.0),
})
}
} }
} }
@ -1160,14 +1114,18 @@ impl MarkerTree {
.edge(extras.extra().contains(marker.name().extra())) .edge(extras.extra().contains(marker.name().extra()))
.evaluate_reporter_impl(env, extras, reporter); .evaluate_reporter_impl(env, extras, reporter);
} }
MarkerTreeKind::Extras(marker) => { MarkerTreeKind::List(marker) => {
return marker let edge = match marker.pair() {
.edge(extras.extras().contains(marker.name().extra())) CanonicalMarkerListPair::Extras(extra) => extras.extras().contains(extra),
.evaluate_reporter_impl(env, extras, reporter); CanonicalMarkerListPair::DependencyGroup(dependency_group) => {
extras.dependency_groups().contains(dependency_group)
} }
MarkerTreeKind::DependencyGroups(marker) => { // Invalid marker expression
CanonicalMarkerListPair::Arbitrary { .. } => return false,
};
return marker return marker
.edge(extras.dependency_groups().contains(marker.name().group())) .edge(edge)
.evaluate_reporter_impl(env, extras, reporter); .evaluate_reporter_impl(env, extras, reporter);
} }
} }
@ -1194,15 +1152,12 @@ impl MarkerTree {
MarkerTreeKind::Contains(marker) => marker MarkerTreeKind::Contains(marker) => marker
.children() .children()
.any(|(_, tree)| tree.evaluate_extras(extras)), .any(|(_, tree)| tree.evaluate_extras(extras)),
MarkerTreeKind::List(marker) => marker
.children()
.any(|(_, tree)| tree.evaluate_extras(extras)),
MarkerTreeKind::Extra(marker) => marker MarkerTreeKind::Extra(marker) => marker
.edge(extras.contains(marker.name().extra())) .edge(extras.contains(marker.name().extra()))
.evaluate_extras(extras), .evaluate_extras(extras),
MarkerTreeKind::Extras(marker) => marker
.children()
.any(|(_, tree)| tree.evaluate_extras(extras)),
MarkerTreeKind::DependencyGroups(marker) => marker
.children()
.any(|(_, tree)| tree.evaluate_extras(extras)),
} }
} }
@ -1430,6 +1385,11 @@ impl MarkerTree {
imp(tree, f); imp(tree, f);
} }
} }
MarkerTreeKind::List(kind) => {
for (_, tree) in kind.children() {
imp(tree, f);
}
}
MarkerTreeKind::Extra(kind) => { MarkerTreeKind::Extra(kind) => {
if kind.low.is_false() { if kind.low.is_false() {
f(MarkerOperator::Equal, kind.name().extra()); f(MarkerOperator::Equal, kind.name().extra());
@ -1440,16 +1400,6 @@ impl MarkerTree {
imp(tree, f); imp(tree, f);
} }
} }
MarkerTreeKind::Extras(kind) => {
for (_, tree) in kind.children() {
imp(tree, f);
}
}
MarkerTreeKind::DependencyGroups(kind) => {
for (_, tree) in kind.children() {
imp(tree, f);
}
}
} }
} }
imp(self, &mut f); imp(self, &mut f);
@ -1557,6 +1507,21 @@ impl MarkerTree {
write!(f, "{} not in {} -> ", kind.value(), kind.key())?; write!(f, "{} not in {} -> ", kind.value(), kind.key())?;
kind.edge(false).fmt_graph(f, level + 1)?; kind.edge(false).fmt_graph(f, level + 1)?;
} }
MarkerTreeKind::List(kind) => {
writeln!(f)?;
for _ in 0..level {
write!(f, " ")?;
}
write!(f, "{} in {} -> ", kind.value(), kind.key())?;
kind.edge(true).fmt_graph(f, level + 1)?;
writeln!(f)?;
for _ in 0..level {
write!(f, " ")?;
}
write!(f, "{} not in {} -> ", kind.value(), kind.key())?;
kind.edge(false).fmt_graph(f, level + 1)?;
}
MarkerTreeKind::Extra(kind) => { MarkerTreeKind::Extra(kind) => {
writeln!(f)?; writeln!(f)?;
for _ in 0..level { for _ in 0..level {
@ -1572,36 +1537,6 @@ impl MarkerTree {
write!(f, "extra != {} -> ", kind.name())?; write!(f, "extra != {} -> ", kind.name())?;
kind.edge(false).fmt_graph(f, level + 1)?; kind.edge(false).fmt_graph(f, level + 1)?;
} }
MarkerTreeKind::Extras(kind) => {
writeln!(f)?;
for _ in 0..level {
write!(f, " ")?;
}
write!(f, "{} in extras -> ", kind.name())?;
kind.edge(true).fmt_graph(f, level + 1)?;
writeln!(f)?;
for _ in 0..level {
write!(f, " ")?;
}
write!(f, "{} not in extras -> ", kind.name())?;
kind.edge(false).fmt_graph(f, level + 1)?;
}
MarkerTreeKind::DependencyGroups(kind) => {
writeln!(f)?;
for _ in 0..level {
write!(f, " ")?;
}
write!(f, "{} in dependency_groups -> ", kind.name())?;
kind.edge(true).fmt_graph(f, level + 1)?;
writeln!(f)?;
for _ in 0..level {
write!(f, " ")?;
}
write!(f, "{} not in dependency_groups -> ", kind.name())?;
kind.edge(false).fmt_graph(f, level + 1)?;
}
} }
Ok(()) Ok(())
@ -1671,12 +1606,10 @@ pub enum MarkerTreeKind<'a> {
In(InMarkerTree<'a>), In(InMarkerTree<'a>),
/// A string expression with the `contains` operator. /// A string expression with the `contains` operator.
Contains(ContainsMarkerTree<'a>), Contains(ContainsMarkerTree<'a>),
/// A string expression (e.g., `extra == 'dev'`). /// A `in` or `not in` expression.
List(ListMarkerTree<'a>),
/// An extra expression (e.g., `extra == 'dev'`).
Extra(ExtraMarkerTree<'a>), Extra(ExtraMarkerTree<'a>),
/// A string expression (e.g., `'dev' in extras`).
Extras(ExtrasMarkerTree<'a>),
/// A string expression (e.g., `'dev' in dependency_groups`).
DependencyGroups(DependencyGroupsMarkerTree<'a>),
} }
/// A version marker node, such as `python_version < '3.7'`. /// A version marker node, such as `python_version < '3.7'`.
@ -1851,6 +1784,59 @@ impl Ord for ContainsMarkerTree<'_> {
} }
} }
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct ListMarkerTree<'a> {
// No separate canonical type, the type is already canonical.
pair: &'a CanonicalMarkerListPair,
high: NodeId,
low: NodeId,
}
impl ListMarkerTree<'_> {
/// The key-value pair for this expression
pub fn pair(&self) -> &CanonicalMarkerListPair {
self.pair
}
/// The key (RHS) for this expression.
pub fn key(&self) -> MarkerValueList {
self.pair.key()
}
/// The value (LHS) for this expression.
pub fn value(&self) -> String {
self.pair.value()
}
/// The edges of this node, corresponding to the boolean evaluation of the expression.
pub fn children(&self) -> impl Iterator<Item = (bool, MarkerTree)> {
[(true, MarkerTree(self.high)), (false, MarkerTree(self.low))].into_iter()
}
/// Returns the subtree associated with the given edge value.
pub fn edge(&self, value: bool) -> MarkerTree {
if value {
MarkerTree(self.high)
} else {
MarkerTree(self.low)
}
}
}
impl PartialOrd for ListMarkerTree<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ListMarkerTree<'_> {
fn cmp(&self, other: &Self) -> Ordering {
self.pair()
.cmp(other.pair())
.then_with(|| self.children().cmp(other.children()))
}
}
/// A node representing the existence or absence of a given extra, such as `extra == 'bar'`. /// A node representing the existence or absence of a given extra, such as `extra == 'bar'`.
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
pub struct ExtraMarkerTree<'a> { pub struct ExtraMarkerTree<'a> {
@ -1894,93 +1880,6 @@ impl Ord for ExtraMarkerTree<'_> {
} }
} }
/// A node representing the existence or absence of a given extra, such as `'bar' in extras`.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct ExtrasMarkerTree<'a> {
name: &'a CanonicalMarkerValueExtra,
high: NodeId,
low: NodeId,
}
impl ExtrasMarkerTree<'_> {
/// Returns the name of the extra in this expression.
pub fn name(&self) -> &CanonicalMarkerValueExtra {
self.name
}
/// The edges of this node, corresponding to the boolean evaluation of the expression.
pub fn children(&self) -> impl Iterator<Item = (bool, MarkerTree)> {
[(true, MarkerTree(self.high)), (false, MarkerTree(self.low))].into_iter()
}
/// Returns the subtree associated with the given edge value.
pub fn edge(&self, value: bool) -> MarkerTree {
if value {
MarkerTree(self.high)
} else {
MarkerTree(self.low)
}
}
}
impl PartialOrd for ExtrasMarkerTree<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ExtrasMarkerTree<'_> {
fn cmp(&self, other: &Self) -> Ordering {
self.name()
.cmp(other.name())
.then_with(|| self.children().cmp(other.children()))
}
}
/// A node representing the existence or absence of a given dependency group, such as
/// `'bar' in dependency_groups`.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct DependencyGroupsMarkerTree<'a> {
name: &'a CanonicalMarkerValueDependencyGroup,
high: NodeId,
low: NodeId,
}
impl DependencyGroupsMarkerTree<'_> {
/// Returns the name of the group in this expression.
pub fn name(&self) -> &CanonicalMarkerValueDependencyGroup {
self.name
}
/// The edges of this node, corresponding to the boolean evaluation of the expression.
pub fn children(&self) -> impl Iterator<Item = (bool, MarkerTree)> {
[(true, MarkerTree(self.high)), (false, MarkerTree(self.low))].into_iter()
}
/// Returns the subtree associated with the given edge value.
pub fn edge(&self, value: bool) -> MarkerTree {
if value {
MarkerTree(self.high)
} else {
MarkerTree(self.low)
}
}
}
impl PartialOrd for DependencyGroupsMarkerTree<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for DependencyGroupsMarkerTree<'_> {
fn cmp(&self, other: &Self) -> Ordering {
self.name()
.cmp(other.name())
.then_with(|| self.children().cmp(other.children()))
}
}
/// A marker tree that contains at least one expression. /// A marker tree that contains at least one expression.
/// ///
/// See [`MarkerTree::contents`] for details. /// See [`MarkerTree::contents`] for details.
@ -2090,7 +1989,7 @@ mod test {
implementation_name: "", implementation_name: "",
implementation_version: "3.7", implementation_version: "3.7",
os_name: "linux", os_name: "linux",
platform_machine: "", platform_machine: "x86_64",
platform_python_implementation: "", platform_python_implementation: "",
platform_release: "", platform_release: "",
platform_system: "", platform_system: "",

View File

@ -62,6 +62,7 @@ impl VerbatimUrl {
/// ///
/// If no root directory is provided, relative paths are resolved against the current working /// If no root directory is provided, relative paths are resolved against the current working
/// directory. /// directory.
#[cfg(feature = "non-pep508-extensions")] // PEP 508 arguably only allows absolute file URLs.
pub fn from_url_or_path( pub fn from_url_or_path(
input: &str, input: &str,
root_dir: Option<&Path>, root_dir: Option<&Path>,

View File

@ -54,12 +54,7 @@ pub(crate) fn requires_python(tree: MarkerTree) -> Option<RequiresPythonRange> {
collect_python_markers(tree, markers, range); collect_python_markers(tree, markers, range);
} }
} }
MarkerTreeKind::Extras(marker) => { MarkerTreeKind::List(marker) => {
for (_, tree) in marker.children() {
collect_python_markers(tree, markers, range);
}
}
MarkerTreeKind::DependencyGroups(marker) => {
for (_, tree) in marker.children() { for (_, tree) in marker.children() {
collect_python_markers(tree, markers, range); collect_python_markers(tree, markers, range);
} }

View File

@ -698,12 +698,7 @@ impl ResolverOutput {
add_marker_params_from_tree(tree, set); add_marker_params_from_tree(tree, set);
} }
} }
MarkerTreeKind::Extras(marker) => { MarkerTreeKind::List(marker) => {
for (_, tree) in marker.children() {
add_marker_params_from_tree(tree, set);
}
}
MarkerTreeKind::DependencyGroups(marker) => {
for (_, tree) in marker.children() { for (_, tree) in marker.children() {
add_marker_params_from_tree(tree, set); add_marker_params_from_tree(tree, set);
} }