mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
pep508: provide extra-only marker evaluation
We provide a new API on a `Requirement` that specifically ignores the marker environment and only evaluates a requirement's marker expression with respect to extras. Any marker expressions that reference the marker environment automatically evaluate to true. Instead of duplicating the evaluation code, we just make a marker environment optional on the lower level APIs. In theory, we could just writer a separate evaluation routine that ignores everything except extras, but the evaluator has a fair bit of other stuff in it (such as emitting warnings) that would be good to keep DRY IMO.
This commit is contained in:
parent
624f92b3f8
commit
21f5999b57
2 changed files with 51 additions and 11 deletions
|
@ -41,6 +41,23 @@ impl Requirement {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns whether the markers apply only for the given extras.
|
||||
///
|
||||
/// When `env` is `None`, this specifically evaluates all marker
|
||||
/// expressions based on the environment to `true`. That is, this provides
|
||||
/// environment independent marker evaluation.
|
||||
pub fn evaluate_optional_environment(
|
||||
&self,
|
||||
env: Option<&MarkerEnvironment>,
|
||||
extras: &[ExtraName],
|
||||
) -> bool {
|
||||
if let Some(marker) = &self.marker {
|
||||
marker.evaluate_optional_environment(env, extras)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a [`pep508_rs::Requirement`] to a [`Requirement`].
|
||||
pub fn from_pep508(requirement: pep508_rs::Requirement) -> Result<Self, Box<ParsedUrlError>> {
|
||||
let source = match requirement.version_or_url {
|
||||
|
|
|
@ -556,9 +556,12 @@ pub struct MarkerExpression {
|
|||
|
||||
impl MarkerExpression {
|
||||
/// Evaluate a <`marker_value`> <`marker_op`> <`marker_value`> expression
|
||||
///
|
||||
/// When `env` is `None`, all expressions that reference the environment
|
||||
/// will evaluate as `true`.
|
||||
fn evaluate(
|
||||
&self,
|
||||
env: &MarkerEnvironment,
|
||||
env: Option<&MarkerEnvironment>,
|
||||
extras: &[ExtraName],
|
||||
reporter: &mut impl FnMut(MarkerWarningKind, String, &Self),
|
||||
) -> bool {
|
||||
|
@ -607,9 +610,10 @@ impl MarkerExpression {
|
|||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let l_version = env.get_version(l_key);
|
||||
specifier.contains(l_version)
|
||||
env.map_or(true, |env| {
|
||||
let l_version = env.get_version(l_key);
|
||||
specifier.contains(l_version)
|
||||
})
|
||||
}
|
||||
// This is half the same block as above inverted
|
||||
MarkerValue::MarkerEnvString(l_key) => {
|
||||
|
@ -623,8 +627,10 @@ impl MarkerExpression {
|
|||
MarkerValue::QuotedString(r_string) => r_string,
|
||||
};
|
||||
|
||||
let l_string = env.get_string(l_key);
|
||||
self.compare_strings(l_string, r_string, reporter)
|
||||
env.map_or(true, |env| {
|
||||
let l_string = env.get_string(l_key);
|
||||
self.compare_strings(l_string, r_string, reporter)
|
||||
})
|
||||
}
|
||||
// `extra == '...'`
|
||||
MarkerValue::Extra => {
|
||||
|
@ -652,6 +658,8 @@ impl MarkerExpression {
|
|||
match &self.r_value {
|
||||
// The only sound choice for this is `<quoted PEP 440 version> <version op>` <version key>
|
||||
MarkerValue::MarkerEnvVersion(r_key) => {
|
||||
let Some(env) = env else { return true };
|
||||
|
||||
let l_version = match Version::from_str(l_string) {
|
||||
Ok(l_version) => l_version,
|
||||
Err(err) => {
|
||||
|
@ -693,10 +701,10 @@ impl MarkerExpression {
|
|||
specifier.contains(&l_version)
|
||||
}
|
||||
// This is half the same block as above inverted
|
||||
MarkerValue::MarkerEnvString(r_key) => {
|
||||
MarkerValue::MarkerEnvString(r_key) => env.map_or(true, |env| {
|
||||
let r_string = env.get_string(r_key);
|
||||
self.compare_strings(l_string, r_string, reporter)
|
||||
}
|
||||
}),
|
||||
// `'...' == extra`
|
||||
MarkerValue::Extra => match ExtraName::from_str(l_string) {
|
||||
Ok(l_extra) => self.marker_compare(&l_extra, extras, reporter),
|
||||
|
@ -950,6 +958,21 @@ impl FromStr for MarkerTree {
|
|||
impl MarkerTree {
|
||||
/// Does this marker apply in the given environment?
|
||||
pub fn evaluate(&self, env: &MarkerEnvironment, extras: &[ExtraName]) -> bool {
|
||||
self.evaluate_optional_environment(Some(env), extras)
|
||||
}
|
||||
|
||||
/// Evaluates this marker tree against an optional environment and a
|
||||
/// possibly empty sequence of extras.
|
||||
///
|
||||
/// When an environment is not provided, all marker expressions based on
|
||||
/// the environment evaluate to `true`. That is, this provides environment
|
||||
/// independent marker evaluation. In practice, this means only the extras
|
||||
/// are evaluated when an environment is not provided.
|
||||
pub fn evaluate_optional_environment(
|
||||
&self,
|
||||
env: Option<&MarkerEnvironment>,
|
||||
extras: &[ExtraName],
|
||||
) -> bool {
|
||||
let mut reporter = |_kind, _message, _marker_expression: &MarkerExpression| {
|
||||
#[cfg(feature = "tracing")]
|
||||
{
|
||||
|
@ -1059,12 +1082,12 @@ impl MarkerTree {
|
|||
reporter: &mut impl FnMut(MarkerWarningKind, String, &MarkerExpression),
|
||||
) -> bool {
|
||||
self.report_deprecated_options(reporter);
|
||||
self.evaluate_reporter_impl(env, extras, reporter)
|
||||
self.evaluate_reporter_impl(Some(env), extras, reporter)
|
||||
}
|
||||
|
||||
fn evaluate_reporter_impl(
|
||||
&self,
|
||||
env: &MarkerEnvironment,
|
||||
env: Option<&MarkerEnvironment>,
|
||||
extras: &[ExtraName],
|
||||
reporter: &mut impl FnMut(MarkerWarningKind, String, &MarkerExpression),
|
||||
) -> bool {
|
||||
|
@ -1117,7 +1140,7 @@ impl MarkerTree {
|
|||
warnings.push((kind, warning, marker.to_string()));
|
||||
};
|
||||
self.report_deprecated_options(&mut reporter);
|
||||
let result = self.evaluate_reporter_impl(env, extras, &mut reporter);
|
||||
let result = self.evaluate_reporter_impl(Some(env), extras, &mut reporter);
|
||||
(result, warnings)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue