Remove uses of `Option<MarkerTree>` (#5978)

## Summary

Follow up to https://github.com/astral-sh/uv/pull/5898. This should fix
some of the failures in https://github.com/astral-sh/uv/pull/5887 where
`uv lock --locked` is failing due to `Some(true)` and `None` markers not
comparing equal.
This commit is contained in:
Ibraheem Ahmed 2024-08-10 13:23:29 -04:00 committed by GitHub
parent 4eced1bd0c
commit f5110f7b5e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 228 additions and 330 deletions

View File

@ -1,4 +1,5 @@
use distribution_filename::DistExtension;
use pep508_rs::MarkerTree;
use pypi_types::{HashDigest, Requirement, RequirementSource};
use std::collections::BTreeMap;
use uv_normalize::{ExtraName, GroupName, PackageName};
@ -211,7 +212,7 @@ impl From<&ResolvedDist> for Requirement {
Requirement {
name: resolved_dist.name().clone(),
extras: vec![],
marker: None,
marker: MarkerTree::TRUE,
source,
origin: None,
}

View File

@ -56,7 +56,7 @@ pub use verbatim_url::{
};
mod cursor;
mod marker;
pub mod marker;
mod origin;
#[cfg(feature = "non-pep508-extensions")]
mod unnamed;
@ -149,7 +149,7 @@ pub struct Requirement<T: Pep508Url = VerbatimUrl> {
/// The markers such as `python_version > "3.8"` in
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`.
/// Those are a nested and/or tree.
pub marker: Option<MarkerTree>,
pub marker: MarkerTree,
/// The source file containing the requirement.
pub origin: Option<RequirementOrigin>,
}
@ -190,7 +190,7 @@ impl<T: Pep508Url + Display> Display for Requirement<T> {
}
}
}
if let Some(marker) = self.marker.as_ref().and_then(MarkerTree::contents) {
if let Some(marker) = self.marker.contents() {
write!(f, " ; {marker}")?;
}
Ok(())
@ -256,10 +256,7 @@ impl PyRequirement {
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`
#[getter]
pub fn marker(&self) -> Option<String> {
self.marker
.as_ref()
.and_then(MarkerTree::contents)
.map(|marker| marker.to_string())
self.marker.try_to_string()
}
/// Parses a PEP 440 string
@ -375,11 +372,7 @@ impl PyRequirement {
impl<T: Pep508Url> Requirement<T> {
/// Returns whether the markers apply for the given environment
pub fn evaluate_markers(&self, env: &MarkerEnvironment, extras: &[ExtraName]) -> bool {
if let Some(marker) = &self.marker {
marker.evaluate(env, extras)
} else {
true
}
self.marker.evaluate(env, extras)
}
/// Returns whether the requirement would be satisfied, independent of environment markers, i.e.
@ -393,11 +386,8 @@ impl<T: Pep508Url> Requirement<T> {
extras: &HashSet<ExtraName>,
python_versions: &[Version],
) -> bool {
if let Some(marker) = &self.marker {
marker.evaluate_extras_and_python_version(extras, python_versions)
} else {
true
}
self.marker
.evaluate_extras_and_python_version(extras, python_versions)
}
/// Returns whether the markers apply for the given environment.
@ -406,11 +396,7 @@ impl<T: Pep508Url> Requirement<T> {
env: &MarkerEnvironment,
extras: &[ExtraName],
) -> (bool, Vec<MarkerWarning>) {
if let Some(marker) = &self.marker {
marker.evaluate_collect_warnings(env, extras)
} else {
(true, Vec::new())
}
self.marker.evaluate_collect_warnings(env, extras)
}
/// Return the requirement with an additional marker added, to require the given extra.
@ -418,26 +404,14 @@ impl<T: Pep508Url> Requirement<T> {
/// For example, given `flask >= 2.0.2`, calling `with_extra_marker("dotenv")` would return
/// `flask >= 2.0.2 ; extra == "dotenv"`.
#[must_use]
pub fn with_extra_marker(self, extra: &ExtraName) -> Self {
let marker = match self.marker {
Some(mut marker) => {
let extra = MarkerTree::expression(MarkerExpression::Extra {
operator: ExtraOperator::Equal,
name: extra.clone(),
});
marker.and(extra);
marker
}
None => MarkerTree::expression(MarkerExpression::Extra {
pub fn with_extra_marker(mut self, extra: &ExtraName) -> Self {
self.marker
.and(MarkerTree::expression(MarkerExpression::Extra {
operator: ExtraOperator::Equal,
name: extra.clone(),
}),
};
}));
Self {
marker: Some(marker),
..self
}
self
}
/// Set the source file containing the requirement.
@ -1053,6 +1027,7 @@ fn parse_pep508_requirement<T: Pep508Url>(
} else {
None
};
// wsp*
cursor.eat_whitespace();
if let Some((pos, char)) = cursor.next() {
@ -1085,7 +1060,7 @@ fn parse_pep508_requirement<T: Pep508Url>(
name,
extras,
version_or_url: requirement_kind,
marker,
marker: marker.unwrap_or_default(),
origin: None,
})
}
@ -1222,14 +1197,14 @@ mod tests {
.into_iter()
.collect(),
)),
marker: Some(MarkerTree::expression(MarkerExpression::Version {
marker: MarkerTree::expression(MarkerExpression::Version {
key: MarkerValueVersion::PythonVersion,
specifier: VersionSpecifier::from_pattern(
pep440_rs::Operator::LessThan,
"2.7".parse().unwrap(),
)
.unwrap(),
})),
}),
origin: None,
};
assert_eq!(requests, expected);
@ -1455,7 +1430,7 @@ mod tests {
let expected = Requirement {
name: PackageName::from_str("pip").unwrap(),
extras: vec![],
marker: None,
marker: MarkerTree::TRUE,
version_or_url: Some(VersionOrUrl::Url(Url::parse(url).unwrap())),
origin: None,
};

View File

@ -21,3 +21,24 @@ pub use tree::{
MarkerOperator, MarkerTree, MarkerTreeContents, MarkerTreeKind, MarkerValue, MarkerValueString,
MarkerValueVersion, MarkerWarningKind, StringMarkerTree, StringVersion, VersionMarkerTree,
};
/// `serde` helpers for [`MarkerTree`].
pub mod ser {
use super::MarkerTree;
use serde::Serialize;
/// A helper for `serde(skip_serializing_if)`.
pub fn is_empty(marker: &MarkerTree) -> bool {
marker.contents().is_none()
}
/// A helper for `serde(serialize_with)`.
///
/// Note this will panic if `marker.contents()` is `None`, and so should be paired with `is_empty`.
pub fn serialize<S>(marker: &MarkerTree, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
marker.contents().unwrap().serialize(s)
}
}

View File

@ -564,6 +564,12 @@ impl Display for MarkerExpression {
#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct MarkerTree(NodeId);
impl Default for MarkerTree {
fn default() -> Self {
MarkerTree::TRUE
}
}
impl<'de> Deserialize<'de> for MarkerTree {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where

View File

@ -75,7 +75,7 @@ pub struct UnnamedRequirement<Url: UnnamedRequirementUrl = VerbatimUrl> {
/// The markers such as `python_version > "3.8"` in
/// `requests [security,tests] >= 2.8.1, == 2.8.* ; python_version > "3.8"`.
/// Those are a nested and/or tree.
pub marker: Option<MarkerTree>,
pub marker: MarkerTree,
/// The source file containing the requirement.
pub origin: Option<RequirementOrigin>,
}
@ -92,11 +92,7 @@ impl<Url: UnnamedRequirementUrl> UnnamedRequirement<Url> {
env: Option<&MarkerEnvironment>,
extras: &[ExtraName],
) -> bool {
if let Some(marker) = &self.marker {
marker.evaluate_optional_environment(env, extras)
} else {
true
}
self.marker.evaluate_optional_environment(env, extras)
}
/// Set the source file containing the requirement.
@ -136,7 +132,7 @@ impl<Url: UnnamedRequirementUrl> Display for UnnamedRequirement<Url> {
.join(",")
)?;
}
if let Some(marker) = self.marker.as_ref().and_then(MarkerTree::contents) {
if let Some(marker) = self.marker.contents() {
write!(f, " ; {marker}")?;
}
Ok(())
@ -207,7 +203,7 @@ fn parse_unnamed_requirement<Url: UnnamedRequirementUrl>(
Ok(UnnamedRequirement {
url,
extras,
marker,
marker: marker.unwrap_or_default(),
origin: None,
})
}

View File

@ -3,12 +3,13 @@ use std::path::{Path, PathBuf};
use std::str::FromStr;
use distribution_filename::DistExtension;
use serde::Serialize;
use thiserror::Error;
use url::Url;
use pep440_rs::VersionSpecifiers;
use pep508_rs::{MarkerEnvironment, MarkerTree, RequirementOrigin, VerbatimUrl, VersionOrUrl};
use pep508_rs::{
marker, MarkerEnvironment, MarkerTree, RequirementOrigin, VerbatimUrl, VersionOrUrl,
};
use uv_fs::PortablePathBuf;
use uv_git::{GitReference, GitSha, GitUrl};
use uv_normalize::{ExtraName, PackageName};
@ -40,28 +41,17 @@ pub struct Requirement {
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub extras: Vec<ExtraName>,
#[serde(
skip_serializing_if = "marker_is_empty",
serialize_with = "serialize_marker",
skip_serializing_if = "marker::ser::is_empty",
serialize_with = "marker::ser::serialize",
default
)]
pub marker: Option<MarkerTree>,
pub marker: MarkerTree,
#[serde(flatten)]
pub source: RequirementSource,
#[serde(skip)]
pub origin: Option<RequirementOrigin>,
}
fn marker_is_empty(marker: &Option<MarkerTree>) -> bool {
marker.as_ref().and_then(MarkerTree::contents).is_none()
}
fn serialize_marker<S>(marker: &Option<MarkerTree>, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
marker.as_ref().unwrap().contents().unwrap().serialize(s)
}
impl Requirement {
/// Returns whether the markers apply for the given environment.
///
@ -69,11 +59,7 @@ impl Requirement {
/// expressions based on the environment to `true`. That is, this provides
/// environment independent marker evaluation.
pub fn evaluate_markers(&self, env: Option<&MarkerEnvironment>, extras: &[ExtraName]) -> bool {
if let Some(marker) = &self.marker {
marker.evaluate_optional_environment(env, extras)
} else {
true
}
self.marker.evaluate_optional_environment(env, extras)
}
/// Returns `true` if the requirement is editable.
@ -256,7 +242,7 @@ impl Display for Requirement {
write!(f, " @ {url}")?;
}
}
if let Some(marker) = self.marker.as_ref().and_then(MarkerTree::contents) {
if let Some(marker) = self.marker.contents() {
write!(f, " ; {marker}")?;
}
Ok(())
@ -715,7 +701,7 @@ impl TryFrom<RequirementSourceWire> for RequirementSource {
mod tests {
use std::path::{Path, PathBuf};
use pep508_rs::VerbatimUrl;
use pep508_rs::{MarkerTree, VerbatimUrl};
use crate::{Requirement, RequirementSource};
@ -724,7 +710,7 @@ mod tests {
let requirement = Requirement {
name: "foo".parse().unwrap(),
extras: vec![],
marker: None,
marker: MarkerTree::TRUE,
source: RequirementSource::Registry {
specifier: ">1,<2".parse().unwrap(),
index: None,
@ -744,7 +730,7 @@ mod tests {
let requirement = Requirement {
name: "foo".parse().unwrap(),
extras: vec![],
marker: None,
marker: MarkerTree::TRUE,
source: RequirementSource::Directory {
install_path: PathBuf::from(path),
lock_path: PathBuf::from(path),

View File

@ -1691,7 +1691,7 @@ mod test {
),
extras: [],
version_or_url: None,
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/subdir/sibling.txt",
@ -1755,7 +1755,7 @@ mod test {
),
extras: [],
version_or_url: None,
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/requirements.txt",
@ -1864,7 +1864,7 @@ mod test {
},
},
extras: [],
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/grandchild.txt",
@ -1978,7 +1978,7 @@ mod test {
),
extras: [],
version_or_url: None,
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/./sibling.txt",
@ -2007,7 +2007,7 @@ mod test {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/requirements.txt",
@ -2038,7 +2038,7 @@ mod test {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/requirements.txt",
@ -2069,7 +2069,7 @@ mod test {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/requirements.txt",
@ -2098,7 +2098,7 @@ mod test {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/requirements.txt",

View File

@ -23,7 +23,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/basic.txt",
@ -52,7 +52,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/basic.txt",
@ -81,7 +81,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/basic.txt",
@ -110,7 +110,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/basic.txt",
@ -139,7 +139,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/basic.txt",
@ -168,7 +168,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/basic.txt",

View File

@ -23,7 +23,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/constraints-a.txt",
@ -52,7 +52,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/constraints-b.txt",
@ -76,7 +76,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/constraints-b.txt",

View File

@ -23,7 +23,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/constraints-b.txt",
@ -52,7 +52,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/constraints-b.txt",

View File

@ -23,7 +23,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/for-poetry.txt",
@ -52,7 +52,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/for-poetry.txt",
@ -70,7 +70,7 @@ RequirementsTxt {
),
extras: [],
version_or_url: None,
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/for-poetry.txt",
@ -107,7 +107,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/for-poetry.txt",

View File

@ -12,7 +12,7 @@ RequirementsTxt {
),
extras: [],
version_or_url: None,
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/include-b.txt",
@ -41,7 +41,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/include-a.txt",

View File

@ -12,7 +12,7 @@ RequirementsTxt {
),
extras: [],
version_or_url: None,
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/include-b.txt",

View File

@ -23,9 +23,7 @@ RequirementsTxt {
),
),
),
marker: Some(
python_version >= '3.8' and python_version < '4.0',
),
marker: python_version >= '3.8' and python_version < '4.0',
origin: Some(
File(
"<REQUIREMENTS_DIR>/poetry-with-hashes.txt",
@ -56,9 +54,7 @@ RequirementsTxt {
),
),
),
marker: Some(
python_version >= '3.8' and python_version < '4.0',
),
marker: python_version >= '3.8' and python_version < '4.0',
origin: Some(
File(
"<REQUIREMENTS_DIR>/poetry-with-hashes.txt",
@ -89,9 +85,7 @@ RequirementsTxt {
),
),
),
marker: Some(
python_version >= '3.8' and python_version < '4.0' and platform_system == 'Windows',
),
marker: python_version >= '3.8' and python_version < '4.0' and platform_system == 'Windows',
origin: Some(
File(
"<REQUIREMENTS_DIR>/poetry-with-hashes.txt",
@ -122,9 +116,7 @@ RequirementsTxt {
),
),
),
marker: Some(
python_version >= '3.8' and python_version < '4.0',
),
marker: python_version >= '3.8' and python_version < '4.0',
origin: Some(
File(
"<REQUIREMENTS_DIR>/poetry-with-hashes.txt",
@ -156,9 +148,7 @@ RequirementsTxt {
),
),
),
marker: Some(
python_version >= '3.8' and python_version < '4.0',
),
marker: python_version >= '3.8' and python_version < '4.0',
origin: Some(
File(
"<REQUIREMENTS_DIR>/poetry-with-hashes.txt",

View File

@ -23,7 +23,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/small.txt",
@ -52,7 +52,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/small.txt",

View File

@ -12,7 +12,7 @@ RequirementsTxt {
),
extras: [],
version_or_url: None,
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/whitespace.txt",
@ -83,7 +83,7 @@ RequirementsTxt {
},
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/whitespace.txt",

View File

@ -23,7 +23,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/basic.txt",
@ -52,7 +52,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/basic.txt",
@ -81,7 +81,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/basic.txt",
@ -110,7 +110,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/basic.txt",
@ -139,7 +139,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/basic.txt",
@ -168,7 +168,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/basic.txt",

View File

@ -23,7 +23,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/constraints-a.txt",
@ -52,7 +52,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/constraints-b.txt",
@ -76,7 +76,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/constraints-b.txt",

View File

@ -23,7 +23,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/constraints-b.txt",
@ -52,7 +52,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/constraints-b.txt",

View File

@ -23,7 +23,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/for-poetry.txt",
@ -52,7 +52,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/for-poetry.txt",
@ -70,7 +70,7 @@ RequirementsTxt {
),
extras: [],
version_or_url: None,
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/for-poetry.txt",
@ -107,7 +107,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/for-poetry.txt",

View File

@ -12,7 +12,7 @@ RequirementsTxt {
),
extras: [],
version_or_url: None,
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/include-b.txt",
@ -41,7 +41,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/include-a.txt",

View File

@ -12,7 +12,7 @@ RequirementsTxt {
),
extras: [],
version_or_url: None,
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/include-b.txt",

View File

@ -23,9 +23,7 @@ RequirementsTxt {
),
),
),
marker: Some(
python_version >= '3.8' and python_version < '4.0',
),
marker: python_version >= '3.8' and python_version < '4.0',
origin: Some(
File(
"<REQUIREMENTS_DIR>/poetry-with-hashes.txt",
@ -56,9 +54,7 @@ RequirementsTxt {
),
),
),
marker: Some(
python_version >= '3.8' and python_version < '4.0',
),
marker: python_version >= '3.8' and python_version < '4.0',
origin: Some(
File(
"<REQUIREMENTS_DIR>/poetry-with-hashes.txt",
@ -89,9 +85,7 @@ RequirementsTxt {
),
),
),
marker: Some(
python_version >= '3.8' and python_version < '4.0' and platform_system == 'Windows',
),
marker: python_version >= '3.8' and python_version < '4.0' and platform_system == 'Windows',
origin: Some(
File(
"<REQUIREMENTS_DIR>/poetry-with-hashes.txt",
@ -122,9 +116,7 @@ RequirementsTxt {
),
),
),
marker: Some(
python_version >= '3.8' and python_version < '4.0',
),
marker: python_version >= '3.8' and python_version < '4.0',
origin: Some(
File(
"<REQUIREMENTS_DIR>/poetry-with-hashes.txt",
@ -156,9 +148,7 @@ RequirementsTxt {
),
),
),
marker: Some(
python_version >= '3.8' and python_version < '4.0',
),
marker: python_version >= '3.8' and python_version < '4.0',
origin: Some(
File(
"<REQUIREMENTS_DIR>/poetry-with-hashes.txt",

View File

@ -23,7 +23,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/small.txt",
@ -52,7 +52,7 @@ RequirementsTxt {
),
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/small.txt",

View File

@ -44,7 +44,7 @@ RequirementsTxt {
},
},
extras: [],
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/bare-url.txt",
@ -98,7 +98,7 @@ RequirementsTxt {
"dev",
),
],
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/bare-url.txt",
@ -148,7 +148,7 @@ RequirementsTxt {
},
},
extras: [],
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/bare-url.txt",

View File

@ -53,7 +53,7 @@ RequirementsTxt {
"dev",
),
],
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -110,7 +110,7 @@ RequirementsTxt {
"dev",
),
],
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -167,9 +167,7 @@ RequirementsTxt {
"dev",
),
],
marker: Some(
python_version >= '3.9' and os_name == 'posix',
),
marker: python_version >= '3.9' and os_name == 'posix',
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -226,9 +224,7 @@ RequirementsTxt {
"dev",
),
],
marker: Some(
python_version >= '3.9' and os_name == 'posix',
),
marker: python_version >= '3.9' and os_name == 'posix',
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -278,9 +274,7 @@ RequirementsTxt {
},
},
extras: [],
marker: Some(
python_version >= '3.9' and os_name == 'posix',
),
marker: python_version >= '3.9' and os_name == 'posix',
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -330,7 +324,7 @@ RequirementsTxt {
},
},
extras: [],
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",

View File

@ -12,7 +12,7 @@ RequirementsTxt {
),
extras: [],
version_or_url: None,
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/whitespace.txt",
@ -83,7 +83,7 @@ RequirementsTxt {
},
),
),
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/whitespace.txt",

View File

@ -44,7 +44,7 @@ RequirementsTxt {
},
},
extras: [],
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/bare-url.txt",
@ -98,7 +98,7 @@ RequirementsTxt {
"dev",
),
],
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/bare-url.txt",
@ -148,7 +148,7 @@ RequirementsTxt {
},
},
extras: [],
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/bare-url.txt",

View File

@ -53,7 +53,7 @@ RequirementsTxt {
"dev",
),
],
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -110,7 +110,7 @@ RequirementsTxt {
"dev",
),
],
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -167,9 +167,7 @@ RequirementsTxt {
"dev",
),
],
marker: Some(
python_version >= '3.9' and os_name == 'posix',
),
marker: python_version >= '3.9' and os_name == 'posix',
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -226,9 +224,7 @@ RequirementsTxt {
"dev",
),
],
marker: Some(
python_version >= '3.9' and os_name == 'posix',
),
marker: python_version >= '3.9' and os_name == 'posix',
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -278,9 +274,7 @@ RequirementsTxt {
},
},
extras: [],
marker: Some(
python_version >= '3.9' and os_name == 'posix',
),
marker: python_version >= '3.9' and os_name == 'posix',
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -330,7 +324,7 @@ RequirementsTxt {
},
},
extras: [],
marker: None,
marker: true,
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",

View File

@ -60,11 +60,7 @@ impl Constraints {
// ASSUMPTION: There is one `extra = "..."`, and it's either the only marker or part
// of the main conjunction.
let Some(extra_expression) = requirement
.marker
.as_ref()
.and_then(MarkerTree::top_level_extra)
else {
let Some(extra_expression) = requirement.marker.top_level_extra() else {
// Case 2: A non-optional dependency with constraint(s).
return Either::Right(Either::Right(
std::iter::once(requirement).chain(constraints.iter().map(Cow::Borrowed)),
@ -79,11 +75,9 @@ impl Constraints {
constraints.iter().cloned().map(move |constraint| {
// Add the extra to the override marker.
let mut joint_marker = MarkerTree::expression(extra_expression.clone());
if let Some(marker) = &constraint.marker {
joint_marker.and(marker.clone());
}
joint_marker.and(constraint.marker.clone());
Cow::Owned(Requirement {
marker: Some(joint_marker.clone()),
marker: joint_marker,
..constraint
})
}),

View File

@ -50,11 +50,7 @@ impl Overrides {
// ASSUMPTION: There is one `extra = "..."`, and it's either the only marker or part
// of the main conjunction.
let Some(extra_expression) = requirement
.marker
.as_ref()
.and_then(MarkerTree::top_level_extra)
else {
let Some(extra_expression) = requirement.marker.top_level_extra() else {
// Case 2: A non-optional dependency with override(s).
return Either::Right(Either::Right(overrides.iter().map(Cow::Borrowed)));
};
@ -67,11 +63,9 @@ impl Overrides {
move |override_requirement| {
// Add the extra to the override marker.
let mut joint_marker = MarkerTree::expression(extra_expression.clone());
if let Some(marker) = &override_requirement.marker {
joint_marker.and(marker.clone());
}
joint_marker.and(override_requirement.marker.clone());
Cow::Owned(Requirement {
marker: Some(joint_marker.clone()),
marker: joint_marker,
..override_requirement.clone()
})
},

View File

@ -104,10 +104,7 @@ impl<'a, Context: BuildContext> SourceTreeResolver<'a, Context> {
.into_iter()
.map(|requirement| Requirement {
origin: Some(origin.clone()),
marker: requirement
.marker
.map(|marker| marker.simplify_extras(extras))
.filter(|marker| !marker.is_true()),
marker: requirement.marker.simplify_extras(extras),
..requirement
})
.collect();
@ -117,7 +114,7 @@ impl<'a, Context: BuildContext> SourceTreeResolver<'a, Context> {
// Find the first recursive requirement.
// TODO(charlie): Respect markers on recursive extras.
let Some(index) = requirements.iter().position(|requirement| {
requirement.name == metadata.name && requirement.marker.is_none()
requirement.name == metadata.name && requirement.marker.is_true()
}) else {
break;
};
@ -129,9 +126,8 @@ impl<'a, Context: BuildContext> SourceTreeResolver<'a, Context> {
for requirement in &mut requirements {
requirement.marker = requirement
.marker
.take()
.map(|marker| marker.simplify_extras(&recursive.extras))
.filter(|marker| !marker.is_true());
.clone()
.simplify_extras(&recursive.extras);
}
}

View File

@ -37,7 +37,7 @@ use cache_key::CanonicalUrl;
use distribution_types::{
FlatIndexLocation, IndexUrl, UnresolvedRequirement, UnresolvedRequirementSpecification,
};
use pep508_rs::{UnnamedRequirement, UnnamedRequirementUrl};
use pep508_rs::{MarkerTree, UnnamedRequirement, UnnamedRequirementUrl};
use pypi_types::Requirement;
use pypi_types::VerbatimParsedUrl;
use requirements_txt::{RequirementsTxt, RequirementsTxtRequirement};
@ -193,7 +193,7 @@ impl RequirementsSpecification {
requirement: UnresolvedRequirement::Unnamed(UnnamedRequirement {
url: VerbatimParsedUrl::parse_absolute_path(path)?,
extras: vec![],
marker: None,
marker: MarkerTree::TRUE,
origin: None,
}),
hashes: vec![],

View File

@ -94,30 +94,24 @@ impl Lock {
for package in &mut lock.packages {
for dep in &mut package.dependencies {
if let Some(marker) = &mut dep.marker {
*marker = marker.clone().simplify_python_versions(
python_version.clone(),
python_full_version.clone(),
);
}
dep.marker = dep.marker.clone().simplify_python_versions(
python_version.clone(),
python_full_version.clone(),
);
}
for dep in package.optional_dependencies.values_mut().flatten() {
if let Some(marker) = &mut dep.marker {
*marker = marker.clone().simplify_python_versions(
python_version.clone(),
python_full_version.clone(),
);
}
dep.marker = dep.marker.clone().simplify_python_versions(
python_version.clone(),
python_full_version.clone(),
);
}
for dep in package.dev_dependencies.values_mut().flatten() {
if let Some(marker) = &mut dep.marker {
*marker = marker.clone().simplify_python_versions(
python_version.clone(),
python_full_version.clone(),
);
}
dep.marker = dep.marker.clone().simplify_python_versions(
python_version.clone(),
python_full_version.clone(),
);
}
}
}
@ -146,8 +140,8 @@ impl Lock {
else {
continue;
};
let marker = edge.weight().as_ref();
locked_dist.add_dependency(dependency_dist, marker);
let marker = edge.weight().clone();
locked_dist.add_dependency(dependency_dist, marker.unwrap_or_default());
}
let id = locked_dist.id.clone();
if let Some(locked_dist) = locked_dists.insert(id, locked_dist) {
@ -178,8 +172,12 @@ impl Lock {
else {
continue;
};
let marker = edge.weight().as_ref();
locked_dist.add_optional_dependency(extra.clone(), dependency_dist, marker);
let marker = edge.weight().clone();
locked_dist.add_optional_dependency(
extra.clone(),
dependency_dist,
marker.unwrap_or_default(),
);
}
}
if let Some(group) = dist.dev.as_ref() {
@ -196,8 +194,12 @@ impl Lock {
else {
continue;
};
let marker = edge.weight().as_ref();
locked_dist.add_dev_dependency(group.clone(), dependency_dist, marker);
let marker = edge.weight().clone();
locked_dist.add_dev_dependency(
group.clone(),
dependency_dist,
marker.unwrap_or_default(),
);
}
}
}
@ -488,11 +490,7 @@ impl Lock {
))
};
for dep in deps {
if dep
.marker
.as_ref()
.map_or(true, |marker| marker.evaluate(marker_env, &[]))
{
if dep.marker.evaluate(marker_env, &[]) {
let dep_dist = self.find_by_id(&dep.package_id);
if seen.insert((&dep.package_id, None)) {
queue.push_back((dep_dist, None));
@ -772,7 +770,7 @@ impl Package {
}
/// Add the [`AnnotatedDist`] as a dependency of the [`Package`].
fn add_dependency(&mut self, annotated_dist: &AnnotatedDist, marker: Option<&MarkerTree>) {
fn add_dependency(&mut self, annotated_dist: &AnnotatedDist, marker: MarkerTree) {
let new_dep = Dependency::from_annotated_dist(annotated_dist, marker);
for existing_dep in &mut self.dependencies {
if existing_dep.package_id == new_dep.package_id
@ -790,7 +788,7 @@ impl Package {
&mut self,
extra: ExtraName,
annotated_dist: &AnnotatedDist,
marker: Option<&MarkerTree>,
marker: MarkerTree,
) {
self.optional_dependencies
.entry(extra)
@ -803,7 +801,7 @@ impl Package {
&mut self,
dev: GroupName,
annotated_dist: &AnnotatedDist,
marker: Option<&MarkerTree>,
marker: MarkerTree,
) {
self.dev_dependencies
.entry(dev)
@ -1072,15 +1070,11 @@ impl Package {
for dep in deps {
if let Some(mut dep) = dep.to_requirement(workspace_root, &mut dependency_extras)? {
// Add back the extra marker expression.
let marker = MarkerTree::expression(MarkerExpression::Extra {
operator: ExtraOperator::Equal,
name: extra.clone(),
});
match dep.marker {
Some(ref mut tree) => tree.and(marker),
None => dep.marker = Some(marker),
}
dep.marker
.and(MarkerTree::expression(MarkerExpression::Extra {
operator: ExtraOperator::Equal,
name: extra.clone(),
}));
requires_dist.push(dep);
}
@ -2282,17 +2276,13 @@ impl TryFrom<WheelWire> for Wheel {
struct Dependency {
package_id: PackageId,
extra: BTreeSet<ExtraName>,
marker: Option<MarkerTree>,
marker: MarkerTree,
}
impl Dependency {
fn from_annotated_dist(
annotated_dist: &AnnotatedDist,
marker: Option<&MarkerTree>,
) -> Dependency {
fn from_annotated_dist(annotated_dist: &AnnotatedDist, marker: MarkerTree) -> Dependency {
let package_id = PackageId::from_annotated_dist(annotated_dist);
let extra = annotated_dist.extra.iter().cloned().collect();
let marker = marker.cloned();
Dependency {
package_id,
extra,
@ -2389,7 +2379,7 @@ impl Dependency {
.collect::<Array>();
table.insert("extra", value(extra_array));
}
if let Some(marker) = self.marker.as_ref().and_then(MarkerTree::contents) {
if let Some(marker) = self.marker.contents() {
table.insert("marker", value(marker.to_string()));
}
@ -2425,7 +2415,8 @@ struct DependencyWire {
package_id: PackageIdForDependency,
#[serde(default)]
extra: BTreeSet<ExtraName>,
marker: Option<MarkerTree>,
#[serde(default)]
marker: MarkerTree,
}
impl DependencyWire {
@ -2816,10 +2807,8 @@ impl<'env> TreeDisplay<'env> {
// Skip dependencies that don't apply to the current environment.
if let Some(environment_markers) = markers {
if let Some(dependency_markers) = dependency.marker.as_ref() {
if !dependency_markers.evaluate(environment_markers, &[]) {
continue;
}
if !dependency.marker.evaluate(environment_markers, &[]) {
continue;
}
}
@ -2847,10 +2836,8 @@ impl<'env> TreeDisplay<'env> {
// Skip dependencies that don't apply to the current environment.
if let Some(environment_markers) = markers {
if let Some(dependency_markers) = dependency.marker.as_ref() {
if !dependency_markers.evaluate(environment_markers, &[]) {
continue;
}
if !dependency.marker.evaluate(environment_markers, &[]) {
continue;
}
}
@ -2884,10 +2871,8 @@ impl<'env> TreeDisplay<'env> {
// Skip dependencies that don't apply to the current environment.
if let Some(environment_markers) = markers {
if let Some(dependency_markers) = dependency.marker.as_ref() {
if !dependency_markers.evaluate(environment_markers, &[]) {
continue;
}
if !dependency.marker.evaluate(environment_markers, &[]) {
continue;
}
}

View File

@ -23,7 +23,7 @@ pub struct Preference {
name: PackageName,
version: Version,
/// The markers on the requirement itself (those after the semicolon).
marker: Option<MarkerTree>,
marker: MarkerTree,
/// If coming from a package with diverging versions, the markers of the forks this preference
/// is part of, otherwise `None`.
fork_markers: Option<BTreeSet<MarkerTree>>,
@ -77,7 +77,7 @@ impl Preference {
Self {
name: dist.name().clone(),
version: version.clone(),
marker: None,
marker: MarkerTree::TRUE,
// Installed distributions don't have fork annotations.
fork_markers: None,
hashes: Vec::new(),
@ -89,7 +89,7 @@ impl Preference {
Self {
name: package.id.name.clone(),
version: package.id.version.clone(),
marker: None,
marker: MarkerTree::TRUE,
fork_markers: package.fork_markers().cloned(),
hashes: Vec::new(),
}
@ -128,11 +128,7 @@ impl Preferences {
for preference in preferences {
// Filter non-matching preferences when resolving for an environment.
if let Some(markers) = markers {
if !preference
.marker
.as_ref()
.map_or(true, |marker| marker.evaluate(markers, &[]))
{
if !preference.marker.evaluate(markers, &[]) {
trace!("Excluding {preference} from preferences due to unmatched markers");
continue;
}

View File

@ -94,16 +94,14 @@ impl PubGrubPackage {
pub(crate) fn from_package(
name: PackageName,
extra: Option<ExtraName>,
marker: Option<MarkerTree>,
marker: MarkerTree,
) -> Self {
// Remove all extra expressions from the marker, since we track extras
// separately. This also avoids an issue where packages added via
// extras end up having two distinct marker expressions, which in turn
// makes them two distinct packages. This results in PubGrub being
// unable to unify version constraints across such packages.
let marker = marker
.map(|m| m.simplify_extras_with(|_| true))
.and_then(|marker| marker.contents());
let marker = marker.simplify_extras_with(|_| true).contents();
if let Some(extra) = extra {
Self(Arc::new(PubGrubPackageInner::Extra {
name,

View File

@ -587,10 +587,7 @@ impl ResolutionGraph {
.constraints
.apply(self.overrides.apply(archive.metadata.requires_dist.iter()))
{
let Some(ref marker_tree) = req.marker else {
continue;
};
add_marker_params_from_tree(marker_tree, &mut seen_marker_values);
add_marker_params_from_tree(&req.marker, &mut seen_marker_values);
}
}
@ -599,10 +596,7 @@ impl ResolutionGraph {
.constraints
.apply(self.overrides.apply(self.requirements.iter()))
{
let Some(ref marker_tree) = direct_req.marker else {
continue;
};
add_marker_params_from_tree(marker_tree, &mut seen_marker_values);
add_marker_params_from_tree(&direct_req.marker, &mut seen_marker_values);
}
// Generate the final marker expression as a conjunction of

View File

@ -15,7 +15,7 @@ pub(crate) struct ForkMap<T>(FxHashMap<PackageName, Vec<Entry<T>>>);
#[derive(Debug, Clone)]
struct Entry<T> {
value: T,
marker: Option<MarkerTree>,
marker: MarkerTree,
}
impl<T> Default for ForkMap<T> {
@ -67,12 +67,7 @@ impl<T> ForkMap<T> {
// with the current fork, i.e. the markers are not disjoint.
ResolverMarkers::Fork(fork) => values
.iter()
.filter(|entry| {
!entry
.marker
.as_ref()
.is_some_and(|marker| fork.is_disjoint(marker))
})
.filter(|entry| !fork.is_disjoint(&entry.marker))
.map(|entry| &entry.value)
.collect(),

View File

@ -1598,12 +1598,11 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
// If the requirement is `requests ; sys_platform == 'darwin'` and the
// constraint is `requests ; python_version == '3.6'`, the constraint
// should only apply when _both_ markers are true.
if let Some(marker) = requirement.marker.as_ref() {
let marker = constraint.marker.as_ref().map(|m| {
let mut marker = marker.clone();
marker.and(m.clone());
marker
}).or_else(|| Some(marker.clone()));
if requirement.marker.is_true() {
Cow::Borrowed(constraint)
} else {
let mut marker = constraint.marker.clone();
marker.and(requirement.marker.clone());
Cow::Owned(Requirement {
name: constraint.name.clone(),
@ -1612,8 +1611,6 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
origin: constraint.origin.clone(),
marker
})
} else {
Cow::Borrowed(constraint)
}
})
)
@ -3120,8 +3117,5 @@ fn satisfies_requires_python(
/// Returns true if and only if the given requirement's marker expression has a
/// possible true value given the `markers` expression given.
fn possible_to_satisfy_markers(markers: &MarkerTree, requirement: &Requirement) -> bool {
let Some(marker) = requirement.marker.as_ref() else {
return true;
};
!markers.is_disjoint(marker)
!markers.is_disjoint(&requirement.marker)
}

View File

@ -99,7 +99,7 @@ Ok(
),
},
extra: {},
marker: None,
marker: true,
},
],
optional_dependencies: {},

View File

@ -99,7 +99,7 @@ Ok(
),
},
extra: {},
marker: None,
marker: true,
},
],
optional_dependencies: {},

View File

@ -99,7 +99,7 @@ Ok(
),
},
extra: {},
marker: None,
marker: true,
},
],
optional_dependencies: {},

View File

@ -522,8 +522,8 @@ fn update_requirement(old: &mut Requirement, new: &Requirement, has_source: bool
}
// Update the marker expression.
if let Some(marker) = new.marker.clone() {
old.marker = Some(marker);
if new.marker.contents().is_some() {
old.marker = new.marker.clone();
}
}

View File

@ -8,7 +8,7 @@ use glob::{glob, GlobError, PatternError};
use rustc_hash::FxHashSet;
use tracing::{debug, trace, warn};
use pep508_rs::{RequirementOrigin, VerbatimUrl};
use pep508_rs::{MarkerTree, RequirementOrigin, VerbatimUrl};
use pypi_types::{Requirement, RequirementSource};
use uv_fs::{absolutize_path, normalize_path, relative_to, Simplified};
use uv_normalize::{GroupName, PackageName, DEV_DEPENDENCIES};
@ -282,7 +282,7 @@ impl Workspace {
Some(Requirement {
name: project.name.clone(),
extras,
marker: None,
marker: MarkerTree::TRUE,
source: RequirementSource::Directory {
install_path: member.root.clone(),
lock_path: member

View File

@ -139,12 +139,11 @@ impl DisplayDependencyGraph {
// Add all transitive requirements.
for metadata in packages.values().flatten() {
// Ignore any optional dependencies.
for required in metadata.requires_dist.iter().filter(|requirement| {
requirement
.marker
.as_ref()
.map_or(true, |m| m.evaluate(markers, &[]))
}) {
for required in metadata
.requires_dist
.iter()
.filter(|requirement| requirement.marker.evaluate(markers, &[]))
{
let dependency = if invert {
Dependency::Inverted(
required.name.clone(),