mirror of https://github.com/astral-sh/uv
Add variant support to uv-pep508
This commit is contained in:
parent
de9f299b80
commit
4a90bef83e
|
|
@ -82,6 +82,17 @@ pub enum Pep508ErrorSource<T: Pep508Url = VerbatimUrl> {
|
|||
/// The version requirement is not supported.
|
||||
#[error("{0}")]
|
||||
UnsupportedRequirement(String),
|
||||
/// The operator is not supported with the variant marker.
|
||||
#[error(
|
||||
"The operator {0} is not supported with the marker {1}, only the `in` and `not in` operators are supported"
|
||||
)]
|
||||
ListOperator(MarkerOperator, MarkerValueList),
|
||||
/// The value is not a quoted string.
|
||||
#[error("Only quoted strings are supported with the variant marker {1}, not {0}")]
|
||||
ListValue(MarkerValue, MarkerValueList),
|
||||
/// The variant marker is on the left hand side of the expression.
|
||||
#[error("The marker {0} must be on the right hand side of the expression")]
|
||||
ListLValue(MarkerValueList),
|
||||
}
|
||||
|
||||
impl<T: Pep508Url> Display for Pep508Error<T> {
|
||||
|
|
@ -298,8 +309,13 @@ impl<T: Pep508Url> CacheKey for Requirement<T> {
|
|||
|
||||
impl<T: Pep508Url> Requirement<T> {
|
||||
/// Returns whether the markers apply for the given environment
|
||||
pub fn evaluate_markers(&self, env: &MarkerEnvironment, extras: &[ExtraName]) -> bool {
|
||||
self.marker.evaluate(env, extras)
|
||||
pub fn evaluate_markers(
|
||||
&self,
|
||||
env: &MarkerEnvironment,
|
||||
variants: Option<&[(String, String, String)]>,
|
||||
extras: &[ExtraName],
|
||||
) -> bool {
|
||||
self.marker.evaluate(env, variants, extras)
|
||||
}
|
||||
|
||||
/// Return the requirement with an additional marker added, to require the given extra.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use uv_normalize::{ExtraName, GroupName};
|
||||
|
||||
use crate::marker::tree::MarkerValueList;
|
||||
|
|
@ -163,13 +162,19 @@ impl Display for CanonicalMarkerValueExtra {
|
|||
|
||||
/// A key-value pair for `<value> in <key>` or `<value> not in <key>`, where the key is a list.
|
||||
///
|
||||
/// Used for PEP 751 markers.
|
||||
/// Used for PEP 751 and variant markers.
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
pub enum CanonicalMarkerListPair {
|
||||
/// A valid [`ExtraName`].
|
||||
Extras(ExtraName),
|
||||
/// A valid [`GroupName`].
|
||||
DependencyGroup(GroupName),
|
||||
/// A valid `variant_namespaces`.
|
||||
VariantNamespaces(String),
|
||||
/// A valid `variant_features`.
|
||||
VariantFeatures(String, String),
|
||||
/// A valid `variant_properties`.
|
||||
VariantProperties(String, String, String),
|
||||
/// For leniency, preserve invalid values.
|
||||
Arbitrary { key: MarkerValueList, value: String },
|
||||
}
|
||||
|
|
@ -180,6 +185,9 @@ impl CanonicalMarkerListPair {
|
|||
match self {
|
||||
Self::Extras(_) => MarkerValueList::Extras,
|
||||
Self::DependencyGroup(_) => MarkerValueList::DependencyGroups,
|
||||
Self::VariantNamespaces(_) => MarkerValueList::VariantNamespaces,
|
||||
Self::VariantFeatures(_, _) => MarkerValueList::VariantFeatures,
|
||||
Self::VariantProperties(_, _, _) => MarkerValueList::VariantProperties,
|
||||
Self::Arbitrary { key, .. } => *key,
|
||||
}
|
||||
}
|
||||
|
|
@ -189,6 +197,13 @@ impl CanonicalMarkerListPair {
|
|||
match self {
|
||||
Self::Extras(extra) => extra.to_string(),
|
||||
Self::DependencyGroup(group) => group.to_string(),
|
||||
Self::VariantNamespaces(namespace) => namespace.clone(),
|
||||
Self::VariantFeatures(namespace, property) => {
|
||||
format!("{namespace} :: {property}")
|
||||
}
|
||||
Self::VariantProperties(namespace, property, value) => {
|
||||
format!("{namespace} :: {property} :: {value}")
|
||||
}
|
||||
Self::Arbitrary { value, .. } => value.clone(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,6 +181,9 @@ pub(crate) fn parse_marker_key_op_value<T: Pep508Url>(
|
|||
let r_value = parse_marker_value(cursor, reporter)?;
|
||||
let len = cursor.pos() - start;
|
||||
|
||||
// TODO(konsti): Catch incorrect variant markers in all places, now that we have the
|
||||
// opportunity to check from the beginning.
|
||||
|
||||
// Convert a `<marker_value> <marker_op> <marker_value>` expression into its
|
||||
// typed equivalent.
|
||||
let expr = match l_value {
|
||||
|
|
@ -307,7 +310,7 @@ pub(crate) fn parse_marker_key_op_value<T: Pep508Url>(
|
|||
Ok(name) => CanonicalMarkerListPair::Extras(name),
|
||||
Err(err) => {
|
||||
reporter.report(
|
||||
MarkerWarningKind::ExtrasInvalidComparison,
|
||||
MarkerWarningKind::ListInvalidComparison,
|
||||
format!("Expected extra name (found `{l_string}`): {err}"),
|
||||
);
|
||||
CanonicalMarkerListPair::Arbitrary {
|
||||
|
|
@ -322,7 +325,7 @@ pub(crate) fn parse_marker_key_op_value<T: Pep508Url>(
|
|||
Ok(name) => CanonicalMarkerListPair::DependencyGroup(name),
|
||||
Err(err) => {
|
||||
reporter.report(
|
||||
MarkerWarningKind::ExtrasInvalidComparison,
|
||||
MarkerWarningKind::ListInvalidComparison,
|
||||
format!("Expected dependency group name (found `{l_string}`): {err}"),
|
||||
);
|
||||
CanonicalMarkerListPair::Arbitrary {
|
||||
|
|
@ -332,6 +335,53 @@ pub(crate) fn parse_marker_key_op_value<T: Pep508Url>(
|
|||
}
|
||||
}
|
||||
}
|
||||
MarkerValueList::VariantNamespaces => {
|
||||
// TODO(konsti): Validate
|
||||
CanonicalMarkerListPair::VariantNamespaces(l_string.trim().to_string())
|
||||
}
|
||||
MarkerValueList::VariantFeatures => {
|
||||
if let Some((namespace, feature)) = l_string.split_once("::") {
|
||||
// TODO(konsti): Validate
|
||||
CanonicalMarkerListPair::VariantFeatures(
|
||||
namespace.trim().to_string(),
|
||||
feature.trim().to_string(),
|
||||
)
|
||||
} else {
|
||||
reporter.report(
|
||||
MarkerWarningKind::ListInvalidComparison,
|
||||
format!("Expected variant feature with two components seperated by `::`, found `{l_string}`"),
|
||||
);
|
||||
CanonicalMarkerListPair::Arbitrary {
|
||||
key,
|
||||
value: l_string.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
MarkerValueList::VariantProperties => {
|
||||
let mut components = l_string.split("::");
|
||||
if let (Some(namespace), Some(feature), Some(property), None) = (
|
||||
components.next(),
|
||||
components.next(),
|
||||
components.next(),
|
||||
components.next(),
|
||||
) {
|
||||
// TODO(konsti): Validate
|
||||
CanonicalMarkerListPair::VariantProperties(
|
||||
namespace.trim().to_string(),
|
||||
feature.trim().to_string(),
|
||||
property.trim().to_string(),
|
||||
)
|
||||
} else {
|
||||
reporter.report(
|
||||
MarkerWarningKind::ListInvalidComparison,
|
||||
format!("Expected variant property with three components seperated by `::`, found `{l_string}`"),
|
||||
);
|
||||
CanonicalMarkerListPair::Arbitrary {
|
||||
key,
|
||||
value: l_string.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Some(MarkerExpression::List { pair, operator })
|
||||
|
|
|
|||
|
|
@ -33,12 +33,9 @@ pub enum MarkerWarningKind {
|
|||
/// Doing an operation other than `==` and `!=` on a quoted string with `extra`, such as
|
||||
/// `extra > "perf"` or `extra == os_name`
|
||||
ExtraInvalidComparison,
|
||||
/// Doing an operation other than `in` and `not in` on a quoted string with `extra`, such as
|
||||
/// `extras > "perf"` or `extras == os_name`
|
||||
ExtrasInvalidComparison,
|
||||
/// Doing an operation other than `in` and `not in` on a quoted string with `dependency_groups`,
|
||||
/// such as `dependency_groups > "perf"` or `dependency_groups == os_name`
|
||||
DependencyGroupsInvalidComparison,
|
||||
/// Doing an operation other than `in` and `not in` on a list marker, such as
|
||||
/// `extras > "perf"` or `dependency_groups == os_name`
|
||||
ListInvalidComparison,
|
||||
/// Comparing a string valued marker and a string lexicographically, such as `"3.9" > "3.10"`
|
||||
LexicographicComparison,
|
||||
/// Comparing two markers, such as `os_name != sys_implementation`
|
||||
|
|
@ -128,9 +125,15 @@ impl Display for MarkerValueString {
|
|||
|
||||
/// Those markers with exclusively `in` and `not in` operators.
|
||||
///
|
||||
/// Contains PEP 751 lockfile markers.
|
||||
/// Contains the PEP 751 lockfile marker and the variant markers.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
pub enum MarkerValueList {
|
||||
/// `variant_namespaces`
|
||||
VariantNamespaces,
|
||||
/// `variant_features`
|
||||
VariantFeatures,
|
||||
/// `variant_properties`
|
||||
VariantProperties,
|
||||
/// `extras`. This one is special because it's a list, and user-provided
|
||||
Extras,
|
||||
/// `dependency_groups`. This one is special because it's a list, and user-provided
|
||||
|
|
@ -142,6 +145,9 @@ impl Display for MarkerValueList {
|
|||
match self {
|
||||
Self::Extras => f.write_str("extras"),
|
||||
Self::DependencyGroups => f.write_str("dependency_groups"),
|
||||
Self::VariantNamespaces => f.write_str("variant_namespaces"),
|
||||
Self::VariantFeatures => f.write_str("variant_features"),
|
||||
Self::VariantProperties => f.write_str("variant_properties"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -200,6 +206,9 @@ impl FromStr for MarkerValue {
|
|||
"sys.platform" => Self::MarkerEnvString(MarkerValueString::SysPlatformDeprecated),
|
||||
"extras" => Self::MarkerEnvList(MarkerValueList::Extras),
|
||||
"dependency_groups" => Self::MarkerEnvList(MarkerValueList::DependencyGroups),
|
||||
"variant_namespaces" => Self::MarkerEnvList(MarkerValueList::VariantNamespaces),
|
||||
"variant_features" => Self::MarkerEnvList(MarkerValueList::VariantFeatures),
|
||||
"variant_properties" => Self::MarkerEnvList(MarkerValueList::VariantProperties),
|
||||
"extra" => Self::Extra,
|
||||
_ => return Err(format!("Invalid key: {s}")),
|
||||
};
|
||||
|
|
@ -531,7 +540,7 @@ pub enum MarkerExpression {
|
|||
operator: MarkerOperator,
|
||||
value: ArcStr,
|
||||
},
|
||||
/// `'...' in <key>`, a PEP 751 expression.
|
||||
/// `'...' in <key>`, either PEP 751 or a variant expression.
|
||||
List {
|
||||
pair: CanonicalMarkerListPair,
|
||||
operator: ContainerOperator,
|
||||
|
|
@ -552,7 +561,8 @@ pub(crate) enum MarkerExpressionKind {
|
|||
VersionIn(MarkerValueVersion),
|
||||
/// A string marker comparison, e.g. `sys_platform == '...'`.
|
||||
String(MarkerValueString),
|
||||
/// A list `in` or `not in` expression, e.g. `'...' in dependency_groups`.
|
||||
/// A list `in` or `not in` expression, e.g. `'...' in dependency_groups` or
|
||||
/// `'gpu :: cuda :: cu128' in variant_properties`.
|
||||
List(MarkerValueList),
|
||||
/// An extra expression, e.g. `extra == '...'`.
|
||||
Extra,
|
||||
|
|
@ -996,10 +1006,16 @@ impl MarkerTree {
|
|||
}
|
||||
|
||||
/// Does this marker apply in the given environment?
|
||||
pub fn evaluate(self, env: &MarkerEnvironment, extras: &[ExtraName]) -> bool {
|
||||
pub fn evaluate(
|
||||
self,
|
||||
env: &MarkerEnvironment,
|
||||
variants: Option<&[(String, String, String)]>,
|
||||
extras: &[ExtraName],
|
||||
) -> bool {
|
||||
self.evaluate_reporter_impl(
|
||||
env,
|
||||
ExtrasEnvironment::from_extras(extras),
|
||||
variants,
|
||||
&mut TracingReporter,
|
||||
)
|
||||
}
|
||||
|
|
@ -1010,12 +1026,14 @@ impl MarkerTree {
|
|||
pub fn evaluate_pep751(
|
||||
self,
|
||||
env: &MarkerEnvironment,
|
||||
variants: Option<&[(String, String, String)]>,
|
||||
extras: &[ExtraName],
|
||||
groups: &[GroupName],
|
||||
) -> bool {
|
||||
self.evaluate_reporter_impl(
|
||||
env,
|
||||
ExtrasEnvironment::from_pep751(extras, groups),
|
||||
variants,
|
||||
&mut TracingReporter,
|
||||
)
|
||||
}
|
||||
|
|
@ -1030,6 +1048,7 @@ impl MarkerTree {
|
|||
pub fn evaluate_optional_environment(
|
||||
self,
|
||||
env: Option<&MarkerEnvironment>,
|
||||
variants: Option<&[(String, String, String)]>,
|
||||
extras: &[ExtraName],
|
||||
) -> bool {
|
||||
match env {
|
||||
|
|
@ -1037,6 +1056,7 @@ impl MarkerTree {
|
|||
Some(env) => self.evaluate_reporter_impl(
|
||||
env,
|
||||
ExtrasEnvironment::from_extras(extras),
|
||||
variants,
|
||||
&mut TracingReporter,
|
||||
),
|
||||
}
|
||||
|
|
@ -1048,15 +1068,22 @@ impl MarkerTree {
|
|||
self,
|
||||
env: &MarkerEnvironment,
|
||||
extras: &[ExtraName],
|
||||
variants: Option<&[(String, String, String)]>,
|
||||
reporter: &mut impl Reporter,
|
||||
) -> bool {
|
||||
self.evaluate_reporter_impl(env, ExtrasEnvironment::from_extras(extras), reporter)
|
||||
self.evaluate_reporter_impl(
|
||||
env,
|
||||
ExtrasEnvironment::from_extras(extras),
|
||||
variants,
|
||||
reporter,
|
||||
)
|
||||
}
|
||||
|
||||
fn evaluate_reporter_impl(
|
||||
self,
|
||||
env: &MarkerEnvironment,
|
||||
extras: ExtrasEnvironment,
|
||||
variants: Option<&[(String, String, String)]>,
|
||||
reporter: &mut impl Reporter,
|
||||
) -> bool {
|
||||
match self.kind() {
|
||||
|
|
@ -1065,7 +1092,7 @@ impl MarkerTree {
|
|||
MarkerTreeKind::Version(marker) => {
|
||||
for (range, tree) in marker.edges() {
|
||||
if range.contains(env.get_version(marker.key())) {
|
||||
return tree.evaluate_reporter_impl(env, extras, reporter);
|
||||
return tree.evaluate_reporter_impl(env, extras, variants, reporter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1092,27 +1119,56 @@ impl MarkerTree {
|
|||
}
|
||||
|
||||
if range.contains(l_string) {
|
||||
return tree.evaluate_reporter_impl(env, extras, reporter);
|
||||
return tree.evaluate_reporter_impl(env, extras, variants, reporter);
|
||||
}
|
||||
}
|
||||
}
|
||||
MarkerTreeKind::In(marker) => {
|
||||
return marker
|
||||
.edge(marker.value().contains(env.get_string(marker.key())))
|
||||
.evaluate_reporter_impl(env, extras, reporter);
|
||||
.evaluate_reporter_impl(env, extras, variants, reporter);
|
||||
}
|
||||
MarkerTreeKind::Contains(marker) => {
|
||||
return marker
|
||||
.edge(env.get_string(marker.key()).contains(marker.value()))
|
||||
.evaluate_reporter_impl(env, extras, reporter);
|
||||
}
|
||||
MarkerTreeKind::Extra(marker) => {
|
||||
return marker
|
||||
.edge(extras.extra().contains(marker.name().extra()))
|
||||
.evaluate_reporter_impl(env, extras, reporter);
|
||||
.evaluate_reporter_impl(env, extras, variants, reporter);
|
||||
}
|
||||
MarkerTreeKind::List(marker) => {
|
||||
let edge = match marker.pair() {
|
||||
CanonicalMarkerListPair::VariantNamespaces(marker_namespace) => {
|
||||
let Some(variants) = variants else {
|
||||
// If we're not limiting to specific variants, we're solving universally.
|
||||
return true;
|
||||
};
|
||||
|
||||
variants
|
||||
.iter()
|
||||
.any(|(namespace, _feature, _property)| namespace == marker_namespace)
|
||||
}
|
||||
CanonicalMarkerListPair::VariantFeatures(marker_namespace, marker_feature) => {
|
||||
let Some(variants) = variants else {
|
||||
return true;
|
||||
};
|
||||
|
||||
variants.iter().any(|(namespace, feature, _property)| {
|
||||
namespace == marker_namespace && feature == marker_feature
|
||||
})
|
||||
}
|
||||
CanonicalMarkerListPair::VariantProperties(
|
||||
marker_namespace,
|
||||
marker_feature,
|
||||
marker_property,
|
||||
) => {
|
||||
let Some(variants) = variants else {
|
||||
return true;
|
||||
};
|
||||
|
||||
variants.iter().any(|(namespace, feature, property)| {
|
||||
namespace == marker_namespace
|
||||
&& feature == marker_feature
|
||||
&& property == marker_property
|
||||
})
|
||||
}
|
||||
CanonicalMarkerListPair::Extras(extra) => extras.extras().contains(extra),
|
||||
CanonicalMarkerListPair::DependencyGroup(dependency_group) => {
|
||||
extras.dependency_groups().contains(dependency_group)
|
||||
|
|
@ -1123,7 +1179,12 @@ impl MarkerTree {
|
|||
|
||||
return marker
|
||||
.edge(edge)
|
||||
.evaluate_reporter_impl(env, extras, reporter);
|
||||
.evaluate_reporter_impl(env, extras, variants, reporter);
|
||||
}
|
||||
MarkerTreeKind::Extra(marker) => {
|
||||
return marker
|
||||
.edge(extras.extra().contains(marker.name().extra()))
|
||||
.evaluate_reporter_impl(env, extras, variants, reporter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2150,12 +2211,12 @@ mod test {
|
|||
let marker3 = MarkerTree::from_str(
|
||||
"python_version == \"2.7\" and (sys_platform == \"win32\" or sys_platform == \"linux\")",
|
||||
).unwrap();
|
||||
assert!(marker1.evaluate(&env27, &[]));
|
||||
assert!(!marker1.evaluate(&env37, &[]));
|
||||
assert!(marker2.evaluate(&env27, &[]));
|
||||
assert!(marker2.evaluate(&env37, &[]));
|
||||
assert!(marker3.evaluate(&env27, &[]));
|
||||
assert!(!marker3.evaluate(&env37, &[]));
|
||||
assert!(marker1.evaluate(&env27, None, &[]));
|
||||
assert!(!marker1.evaluate(&env37, None, &[]));
|
||||
assert!(marker2.evaluate(&env27, None, &[]));
|
||||
assert!(marker2.evaluate(&env37, None, &[]));
|
||||
assert!(marker3.evaluate(&env27, None, &[]));
|
||||
assert!(!marker3.evaluate(&env37, None, &[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2177,48 +2238,48 @@ mod test {
|
|||
let env37 = env37();
|
||||
|
||||
let marker = MarkerTree::from_str("python_version in \"2.7 3.2 3.3\"").unwrap();
|
||||
assert!(marker.evaluate(&env27, &[]));
|
||||
assert!(!marker.evaluate(&env37, &[]));
|
||||
assert!(marker.evaluate(&env27, None, &[]));
|
||||
assert!(!marker.evaluate(&env37, None, &[]));
|
||||
|
||||
let marker = MarkerTree::from_str("python_version in \"2.7 3.7\"").unwrap();
|
||||
assert!(marker.evaluate(&env27, &[]));
|
||||
assert!(marker.evaluate(&env37, &[]));
|
||||
assert!(marker.evaluate(&env27, None, &[]));
|
||||
assert!(marker.evaluate(&env37, None, &[]));
|
||||
|
||||
let marker = MarkerTree::from_str("python_version in \"2.4 3.8 4.0\"").unwrap();
|
||||
assert!(!marker.evaluate(&env27, &[]));
|
||||
assert!(!marker.evaluate(&env37, &[]));
|
||||
assert!(!marker.evaluate(&env27, None, &[]));
|
||||
assert!(!marker.evaluate(&env37, None, &[]));
|
||||
|
||||
let marker = MarkerTree::from_str("python_version not in \"2.7 3.2 3.3\"").unwrap();
|
||||
assert!(!marker.evaluate(&env27, &[]));
|
||||
assert!(marker.evaluate(&env37, &[]));
|
||||
assert!(!marker.evaluate(&env27, None, &[]));
|
||||
assert!(marker.evaluate(&env37, None, &[]));
|
||||
|
||||
let marker = MarkerTree::from_str("python_version not in \"2.7 3.7\"").unwrap();
|
||||
assert!(!marker.evaluate(&env27, &[]));
|
||||
assert!(!marker.evaluate(&env37, &[]));
|
||||
assert!(!marker.evaluate(&env27, None, &[]));
|
||||
assert!(!marker.evaluate(&env37, None, &[]));
|
||||
|
||||
let marker = MarkerTree::from_str("python_version not in \"2.4 3.8 4.0\"").unwrap();
|
||||
assert!(marker.evaluate(&env27, &[]));
|
||||
assert!(marker.evaluate(&env37, &[]));
|
||||
assert!(marker.evaluate(&env27, None, &[]));
|
||||
assert!(marker.evaluate(&env37, None, &[]));
|
||||
|
||||
let marker = MarkerTree::from_str("python_full_version in \"2.7\"").unwrap();
|
||||
assert!(marker.evaluate(&env27, &[]));
|
||||
assert!(!marker.evaluate(&env37, &[]));
|
||||
assert!(marker.evaluate(&env27, None, &[]));
|
||||
assert!(!marker.evaluate(&env37, None, &[]));
|
||||
|
||||
let marker = MarkerTree::from_str("implementation_version in \"2.7 3.2 3.3\"").unwrap();
|
||||
assert!(marker.evaluate(&env27, &[]));
|
||||
assert!(!marker.evaluate(&env37, &[]));
|
||||
assert!(marker.evaluate(&env27, None, &[]));
|
||||
assert!(!marker.evaluate(&env37, None, &[]));
|
||||
|
||||
let marker = MarkerTree::from_str("implementation_version in \"2.7 3.7\"").unwrap();
|
||||
assert!(marker.evaluate(&env27, &[]));
|
||||
assert!(marker.evaluate(&env37, &[]));
|
||||
assert!(marker.evaluate(&env27, None, &[]));
|
||||
assert!(marker.evaluate(&env37, None, &[]));
|
||||
|
||||
let marker = MarkerTree::from_str("implementation_version not in \"2.7 3.7\"").unwrap();
|
||||
assert!(!marker.evaluate(&env27, &[]));
|
||||
assert!(!marker.evaluate(&env37, &[]));
|
||||
assert!(!marker.evaluate(&env27, None, &[]));
|
||||
assert!(!marker.evaluate(&env37, None, &[]));
|
||||
|
||||
let marker = MarkerTree::from_str("implementation_version not in \"2.4 3.8 4.0\"").unwrap();
|
||||
assert!(marker.evaluate(&env27, &[]));
|
||||
assert!(marker.evaluate(&env37, &[]));
|
||||
assert!(marker.evaluate(&env27, None, &[]));
|
||||
assert!(marker.evaluate(&env37, None, &[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -2227,7 +2288,7 @@ mod test {
|
|||
fn warnings1() {
|
||||
let env37 = env37();
|
||||
let compare_keys = MarkerTree::from_str("platform_version == sys_platform").unwrap();
|
||||
compare_keys.evaluate(&env37, &[]);
|
||||
compare_keys.evaluate(&env37, None, &[]);
|
||||
logs_contain(
|
||||
"Comparing two markers with each other doesn't make any sense, will evaluate to false",
|
||||
);
|
||||
|
|
@ -2239,7 +2300,7 @@ mod test {
|
|||
fn warnings2() {
|
||||
let env37 = env37();
|
||||
let non_pep440 = MarkerTree::from_str("python_version >= '3.9.'").unwrap();
|
||||
non_pep440.evaluate(&env37, &[]);
|
||||
non_pep440.evaluate(&env37, None, &[]);
|
||||
logs_contain(
|
||||
"Expected PEP 440 version to compare with python_version, found `3.9.`, \
|
||||
will evaluate to false: after parsing `3.9`, found `.`, which is \
|
||||
|
|
@ -2253,7 +2314,7 @@ mod test {
|
|||
fn warnings3() {
|
||||
let env37 = env37();
|
||||
let string_string = MarkerTree::from_str("'b' >= 'a'").unwrap();
|
||||
string_string.evaluate(&env37, &[]);
|
||||
string_string.evaluate(&env37, None, &[]);
|
||||
logs_contain(
|
||||
"Comparing two quoted strings with each other doesn't make sense: 'b' >= 'a', will evaluate to false",
|
||||
);
|
||||
|
|
@ -2265,7 +2326,7 @@ mod test {
|
|||
fn warnings4() {
|
||||
let env37 = env37();
|
||||
let string_string = MarkerTree::from_str(r"os.name == 'posix' and platform.machine == 'x86_64' and platform.python_implementation == 'CPython' and 'Ubuntu' in platform.version and sys.platform == 'linux'").unwrap();
|
||||
string_string.evaluate(&env37, &[]);
|
||||
string_string.evaluate(&env37, None, &[]);
|
||||
logs_assert(|lines: &[&str]| {
|
||||
let lines: Vec<_> = lines
|
||||
.iter()
|
||||
|
|
@ -2297,18 +2358,18 @@ mod test {
|
|||
let env37 = env37();
|
||||
let result = MarkerTree::from_str("python_version > '3.6'")
|
||||
.unwrap()
|
||||
.evaluate(&env37, &[]);
|
||||
.evaluate(&env37, None, &[]);
|
||||
assert!(result);
|
||||
|
||||
let result = MarkerTree::from_str("'3.6' > python_version")
|
||||
.unwrap()
|
||||
.evaluate(&env37, &[]);
|
||||
.evaluate(&env37, None, &[]);
|
||||
assert!(!result);
|
||||
|
||||
// Meaningless expressions are ignored, so this is always true.
|
||||
let result = MarkerTree::from_str("'3.*' == python_version")
|
||||
.unwrap()
|
||||
.evaluate(&env37, &[]);
|
||||
.evaluate(&env37, None, &[]);
|
||||
assert!(result);
|
||||
}
|
||||
|
||||
|
|
@ -2317,12 +2378,12 @@ mod test {
|
|||
let env37 = env37();
|
||||
let result = MarkerTree::from_str("'nux' in sys_platform")
|
||||
.unwrap()
|
||||
.evaluate(&env37, &[]);
|
||||
.evaluate(&env37, None, &[]);
|
||||
assert!(result);
|
||||
|
||||
let result = MarkerTree::from_str("sys_platform in 'nux'")
|
||||
.unwrap()
|
||||
.evaluate(&env37, &[]);
|
||||
.evaluate(&env37, None, &[]);
|
||||
assert!(!result);
|
||||
}
|
||||
|
||||
|
|
@ -2331,7 +2392,7 @@ mod test {
|
|||
let env37 = env37();
|
||||
let result = MarkerTree::from_str("python_version == '3.7.*'")
|
||||
.unwrap()
|
||||
.evaluate(&env37, &[]);
|
||||
.evaluate(&env37, None, &[]);
|
||||
assert!(result);
|
||||
}
|
||||
|
||||
|
|
@ -2340,7 +2401,7 @@ mod test {
|
|||
let env37 = env37();
|
||||
let result = MarkerTree::from_str("python_version ~= '3.7'")
|
||||
.unwrap()
|
||||
.evaluate(&env37, &[]);
|
||||
.evaluate(&env37, None, &[]);
|
||||
assert!(result);
|
||||
}
|
||||
|
||||
|
|
@ -3709,4 +3770,146 @@ mod test {
|
|||
assert!(!marker.evaluate_only_extras(std::slice::from_ref(&b)));
|
||||
assert!(marker.evaluate_only_extras(&[a.clone(), b.clone()]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marker_evaluation_variants() {
|
||||
let env37 = env37();
|
||||
let gpu_namespaces = [("gpu".to_string(), "cuda".to_string(), "12.4".to_string())];
|
||||
let cpu_namespaces = [("cpu".to_string(), String::new(), String::new())];
|
||||
|
||||
// namespace variant markers
|
||||
let marker1 = m("'gpu' in variant_namespaces");
|
||||
let marker2 = m("'gpu' not in variant_namespaces");
|
||||
|
||||
// If no variants are provided, we solve universally.
|
||||
assert!(marker1.evaluate(&env37, None, &[]));
|
||||
assert!(marker2.evaluate(&env37, None, &[]));
|
||||
|
||||
assert!(marker1.evaluate(&env37, Some(&gpu_namespaces), &[]));
|
||||
assert!(!marker1.evaluate(&env37, Some(&cpu_namespaces), &[]));
|
||||
assert!(!marker2.evaluate(&env37, Some(&gpu_namespaces), &[]));
|
||||
assert!(marker2.evaluate(&env37, Some(&cpu_namespaces), &[]));
|
||||
|
||||
// property variant markers
|
||||
let marker3 = m("'gpu :: cuda' in variant_features");
|
||||
let marker4 = m("'gpu :: rocm' in variant_features");
|
||||
|
||||
assert!(marker3.evaluate(&env37, None, &[]));
|
||||
assert!(marker4.evaluate(&env37, None, &[]));
|
||||
|
||||
assert!(marker3.evaluate(&env37, Some(&gpu_namespaces), &[]));
|
||||
assert!(!marker3.evaluate(&env37, Some(&cpu_namespaces), &[]));
|
||||
assert!(!marker4.evaluate(&env37, Some(&gpu_namespaces), &[]));
|
||||
assert!(!marker4.evaluate(&env37, Some(&cpu_namespaces), &[]));
|
||||
|
||||
// feature variant markers
|
||||
let marker5 = m("'gpu :: cuda :: 12.4' in variant_properties");
|
||||
let marker6 = m("'gpu :: cuda :: 12.8' in variant_properties");
|
||||
|
||||
assert!(marker5.evaluate(&env37, None, &[]));
|
||||
assert!(marker6.evaluate(&env37, None, &[]));
|
||||
|
||||
assert!(marker5.evaluate(&env37, Some(&gpu_namespaces), &[]));
|
||||
assert!(!marker5.evaluate(&env37, Some(&cpu_namespaces), &[]));
|
||||
assert!(!marker6.evaluate(&env37, Some(&gpu_namespaces), &[]));
|
||||
assert!(!marker6.evaluate(&env37, Some(&cpu_namespaces), &[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marker_evaluation_variants_combined() {
|
||||
let env37 = env37();
|
||||
let namespaces = [
|
||||
("gpu".to_string(), "cuda".to_string(), "12.4".to_string()),
|
||||
("gpu".to_string(), "cuda".to_string(), "12.6".to_string()),
|
||||
("cpu".to_string(), "x86_64".to_string(), "v1".to_string()),
|
||||
("cpu".to_string(), "x86_64".to_string(), "v2".to_string()),
|
||||
("cpu".to_string(), "x86_64".to_string(), "v3".to_string()),
|
||||
];
|
||||
|
||||
let marker1 = m("'gpu' in variant_namespaces \
|
||||
and 'cpu:: x86_64 :: v3' in variant_properties \
|
||||
and python_version >= '3.7' \
|
||||
and 'gpu :: rocm' not in variant_features");
|
||||
assert!(marker1.evaluate(&env37, None, &[]));
|
||||
assert!(marker1.evaluate(&env37, Some(&namespaces), &[]));
|
||||
|
||||
let marker2 = m("python_version >= '3.7' and 'gpu' not in variant_namespaces");
|
||||
assert!(marker2.evaluate(&env37, None, &[]));
|
||||
assert!(!marker2.evaluate(&env37, Some(&namespaces), &[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variant_to_string() {
|
||||
let assert_roundtrips = |marker| {
|
||||
assert_eq!(m(marker).try_to_string().unwrap(), marker);
|
||||
};
|
||||
assert_roundtrips("'gpu' in variant_namespaces");
|
||||
assert_roundtrips("'gpu' not in variant_namespaces");
|
||||
assert_roundtrips("'gpu :: cuda' in variant_properties");
|
||||
assert_roundtrips("'gpu :: cuda' not in variant_properties");
|
||||
assert_roundtrips("'gpu :: cuda :: 12.4' in variant_features");
|
||||
assert_roundtrips("'gpu :: cuda :: 12.8' not in variant_features");
|
||||
|
||||
// TODO(konsti): Implement normalization and test it.
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variant_errors() {
|
||||
let err = MarkerExpression::from_str(r"variant_namespaces in 'gpu'")
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
assert_snapshot!(
|
||||
err,
|
||||
@r"
|
||||
The marker variant_namespaces must be on the right hand side of the expression
|
||||
variant_namespaces in 'gpu'
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
"
|
||||
);
|
||||
let err = MarkerExpression::from_str(r"'gpu :: cuda' == variant_properties")
|
||||
.unwrap_err()
|
||||
.to_string();
|
||||
assert_snapshot!(
|
||||
err,
|
||||
@r"
|
||||
The operator == is not supported with the marker variant_properties, only the `in` and `not in` operators are supported
|
||||
'gpu :: cuda' == variant_properties
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
"
|
||||
);
|
||||
// TODO(konsti): Test all cases systematically
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variant_invalid() {
|
||||
let env37 = env37();
|
||||
let namespaces = [("gpu".to_string(), "cuda".to_string(), "cuda126".to_string())];
|
||||
|
||||
let marker = m(r"'gpu :: cuda :: cuda126 :: gtx1080' in variant_properties");
|
||||
assert!(!marker.evaluate(&env37, Some(&namespaces), &[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn torch_variant_marker() {
|
||||
let env37 = env37();
|
||||
let cu126 = [("nvidia".to_string(), "ctk".to_string(), "12.6".to_string())];
|
||||
let cu126_2 = [
|
||||
("nvidia".to_string(), "ctk".to_string(), "12.6".to_string()),
|
||||
(
|
||||
"nvidia".to_string(),
|
||||
"cuda_version".to_string(),
|
||||
">=12.6,<13".to_string(),
|
||||
),
|
||||
];
|
||||
let cu128 = [("nvidia".to_string(), "ctk".to_string(), "12.8".to_string())];
|
||||
|
||||
let marker = m(
|
||||
" platform_machine == 'x86_64' and sys_platform == 'linux' and 'nvidia :: ctk :: 12.6' in variant_properties",
|
||||
);
|
||||
|
||||
assert!(marker.evaluate(&env37, None, &[]));
|
||||
assert!(marker.evaluate(&env37, Some(&cu126), &[]));
|
||||
assert!(marker.evaluate(&env37, Some(&cu126_2), &[]));
|
||||
assert!(!marker.evaluate(&env37, Some(&cu128), &[]));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,17 +82,24 @@ pub struct UnnamedRequirement<ReqUrl: UnnamedRequirementUrl = VerbatimUrl> {
|
|||
|
||||
impl<Url: UnnamedRequirementUrl> UnnamedRequirement<Url> {
|
||||
/// Returns whether the markers apply for the given environment
|
||||
pub fn evaluate_markers(&self, env: &MarkerEnvironment, extras: &[ExtraName]) -> bool {
|
||||
self.evaluate_optional_environment(Some(env), extras)
|
||||
pub fn evaluate_markers(
|
||||
&self,
|
||||
env: &MarkerEnvironment,
|
||||
variants: Option<&[(String, String, String)]>,
|
||||
extras: &[ExtraName],
|
||||
) -> bool {
|
||||
self.evaluate_optional_environment(Some(env), variants, extras)
|
||||
}
|
||||
|
||||
/// Returns whether the markers apply for the given environment
|
||||
pub fn evaluate_optional_environment(
|
||||
&self,
|
||||
env: Option<&MarkerEnvironment>,
|
||||
variants: Option<&[(String, String, String)]>,
|
||||
extras: &[ExtraName],
|
||||
) -> bool {
|
||||
self.marker.evaluate_optional_environment(env, extras)
|
||||
self.marker
|
||||
.evaluate_optional_environment(env, variants, extras)
|
||||
}
|
||||
|
||||
/// Set the source file containing the requirement.
|
||||
|
|
|
|||
Loading…
Reference in New Issue