Normalize python_version markers to python_full_version (#6126)

## Summary

Normalize all `python_version` markers to their equivalent
`python_full_version` form. This avoids false positives in forking
because we currently cannot detect any relationships between the two
forms. It also avoids subtle bugs due to the truncating semantics of
`python_version`. For example, given `requires-python = ">3.12"`, we
currently simplify the marker `python_version <= 3.12` to `false`.
However, the version `3.12.1` will be truncated to `3.12` for
`python_version` comparisons, and thus it satisfies the python
requirement and evaluates to `true`.

It is possible to simplify back to `python_version` when writing markers
to the lockfile. However, the equivalent `python_full_version` markers
are often clearer and easier to simplify, so I lean towards leaving them
as `python_full_version`.

There are *a lot* of snapshot updates from this change. I'd like more
eyes on the transformation logic in `python_version_to_full_version` to
ensure that they are all correct.

Resolves https://github.com/astral-sh/uv/issues/6125.
This commit is contained in:
Ibraheem Ahmed 2024-08-15 21:42:15 -04:00 committed by GitHub
parent 1311127991
commit e6ddce0246
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 1021 additions and 844 deletions

View file

@ -119,6 +119,11 @@ impl Operator {
_ => None,
}
}
/// Returns `true` if this operator represents a wildcard.
pub fn is_star(self) -> bool {
matches!(self, Self::EqualStar | Self::NotEqualStar)
}
}
impl FromStr for Operator {

View file

@ -416,6 +416,23 @@ impl VersionSpecifier {
version,
}
}
/// `==<version>.*`
pub fn equals_star_version(version: Version) -> Self {
Self {
operator: Operator::EqualStar,
version,
}
}
/// `!=<version>.*`
pub fn not_equals_star_version(version: Version) -> Self {
Self {
operator: Operator::NotEqualStar,
version,
}
}
/// `!=<version>`
pub fn not_equals_version(version: Version) -> Self {
Self {
@ -465,19 +482,36 @@ impl VersionSpecifier {
&self.version
}
/// Get the operator and version parts of this specifier.
pub fn into_parts(self) -> (Operator, Version) {
(self.operator, self.version)
}
/// Whether the version marker includes a prerelease.
pub fn any_prerelease(&self) -> bool {
self.version.any_prerelease()
}
/// Returns the version specifiers whose union represents the given range.
pub fn from_bounds(
///
/// This function is not applicable to ranges involving pre-release versions.
pub fn from_release_only_bounds(
bounds: (&Bound<Version>, &Bound<Version>),
) -> impl Iterator<Item = VersionSpecifier> {
let (b1, b2) = match bounds {
(Bound::Included(v1), Bound::Included(v2)) if v1 == v2 => {
(Some(VersionSpecifier::equals_version(v1.clone())), None)
}
// `v >= 3.7 && v < 3.8` is equivalent to `v == 3.7.*`
(Bound::Included(v1), Bound::Excluded(v2))
if v1.release().len() == 2
&& v2.release() == [v1.release()[0], v1.release()[1] + 1] =>
{
(
Some(VersionSpecifier::equals_star_version(v1.clone())),
None,
)
}
(lower, upper) => (
VersionSpecifier::from_lower_bound(lower),
VersionSpecifier::from_upper_bound(upper),

View file

@ -1172,7 +1172,7 @@ mod tests {
#[test]
fn basic_examples() {
let input = r"requests[security,tests]>=2.8.1,==2.8.* ; python_version < '2.7'";
let input = r"requests[security,tests]>=2.8.1,==2.8.* ; python_full_version < '2.7'";
let requests = Requirement::<Url>::from_str(input).unwrap();
assert_eq!(input, requests.to_string());
let expected = Requirement {
@ -1198,7 +1198,7 @@ mod tests {
.collect(),
)),
marker: MarkerTree::expression(MarkerExpression::Version {
key: MarkerValueVersion::PythonVersion,
key: MarkerValueVersion::PythonFullVersion,
specifier: VersionSpecifier::from_pattern(
pep440_rs::Operator::LessThan,
"2.7".parse().unwrap(),
@ -1788,10 +1788,16 @@ mod tests {
#[test]
fn no_space_after_operator() {
let requirement = Requirement::<Url>::from_str("pytest;python_version<='4.0'").unwrap();
assert_eq!(requirement.to_string(), "pytest ; python_version <= '4.0'");
assert_eq!(
requirement.to_string(),
"pytest ; python_full_version < '4.1'"
);
let requirement = Requirement::<Url>::from_str("pytest;'4.0'>=python_version").unwrap();
assert_eq!(requirement.to_string(), "pytest ; python_version <= '4.0'");
assert_eq!(
requirement.to_string(),
"pytest ; python_full_version < '4.1'"
);
}
#[test]

View file

@ -51,6 +51,7 @@ use std::sync::Mutex;
use std::sync::MutexGuard;
use itertools::Either;
use pep440_rs::Operator;
use pep440_rs::{Version, VersionSpecifier};
use pubgrub::Range;
use rustc_hash::FxHashMap;
@ -156,10 +157,21 @@ impl InternerGuard<'_> {
/// Returns a decision node for a single marker expression.
pub(crate) fn expression(&mut self, expr: MarkerExpression) -> NodeId {
let (var, children) = match expr {
// Normalize `python_version` markers to `python_full_version` nodes.
MarkerExpression::Version {
key: MarkerValueVersion::PythonVersion,
specifier,
} => match python_version_to_full_version(normalize_specifier(specifier)) {
Ok(specifier) => (
Variable::Version(MarkerValueVersion::PythonFullVersion),
Edges::from_specifier(specifier),
),
Err(node) => return node,
},
// A variable representing the output of a version key. Edges correspond
// to disjoint version ranges.
MarkerExpression::Version { key, specifier } => {
(Variable::Version(key), Edges::from_specifier(&specifier))
(Variable::Version(key), Edges::from_specifier(specifier))
}
// The `in` and `contains` operators are a bit different than other operators.
// In particular, they do not represent a particular value for the corresponding
@ -532,18 +544,9 @@ impl Edges {
}
/// Returns the [`Edges`] for a version specifier.
fn from_specifier(specifier: &VersionSpecifier) -> Edges {
// The decision diagram relies on the assumption that the negation of a marker tree is
// the complement of the marker space. However, pre-release versions violate this assumption.
// For example, the marker `python_full_version > '3.9' or python_full_version <= '3.9'`
// does not match `python_full_version == 3.9.0a0`. However, it's negation,
// `python_full_version > '3.9' and python_full_version <= '3.9'` also does not include
// `3.9.0a0`, and is actually `false`.
//
// For this reason we ignore pre-release versions entirely when evaluating markers.
// Note that `python_version` cannot take on pre-release values so this is necessary for
// simplifying ranges, but for `python_full_version` this decision is a semantic change.
let specifier = PubGrubSpecifier::from_release_specifier(specifier).unwrap();
fn from_specifier(specifier: VersionSpecifier) -> Edges {
let specifier =
PubGrubSpecifier::from_release_specifier(&normalize_specifier(specifier)).unwrap();
Edges::Version {
edges: Edges::from_range(&specifier.into()),
}
@ -748,6 +751,110 @@ impl Edges {
}
}
// Normalize a [`VersionSpecifier`] before adding it to the tree.
fn normalize_specifier(specifier: VersionSpecifier) -> VersionSpecifier {
let (operator, version) = specifier.into_parts();
// The decision diagram relies on the assumption that the negation of a marker tree is
// the complement of the marker space. However, pre-release versions violate this assumption.
// For example, the marker `python_full_version > '3.9' or python_full_version <= '3.9'`
// does not match `python_full_version == 3.9.0a0`. However, it's negation,
// `python_full_version > '3.9' and python_full_version <= '3.9'` also does not include
// `3.9.0a0`, and is actually `false`.
//
// For this reason we ignore pre-release versions entirely when evaluating markers.
// Note that `python_version` cannot take on pre-release values so this is necessary for
// simplifying ranges, but for `python_full_version` this decision is a semantic change.
let mut release = version.release();
// Strip any trailing `0`s.
//
// The [`Version`] type ignores trailing `0`s for equality, but still preserves them in it's
// [`Display`] output. We must normalize all versions by stripping trailing `0`s to remove the
// distinction between versions like `3.9` and `3.9.0`, whose output will depend on which form
// was added to the global marker interner first.
//
// Note that we cannot strip trailing `0`s for star equality, as `==3.0.*` is different from `==3.*`.
if !operator.is_star() {
if let Some(end) = release.iter().rposition(|segment| *segment != 0) {
if end > 0 {
release = &release[..=end];
}
}
}
VersionSpecifier::from_version(operator, Version::new(release)).unwrap()
}
/// Returns the equivalent `python_full_version` specifier for a `python_version` comparison.
///
/// Returns `Err` with a constant node if the equivalent comparison is always `true` or `false`.
fn python_version_to_full_version(specifier: VersionSpecifier) -> Result<VersionSpecifier, NodeId> {
let major_minor = match *specifier.version().release() {
// `python_version == 3.*` is equivalent to `python_full_version == 3.*`
// and adding a trailing `0` would be incorrect.
[_major] if specifier.operator().is_star() => return Ok(specifier),
// Note that `python_version == 3` matches `3.0.1`, `3.0.2`, etc.
[major] => Some((major, 0)),
[major, minor] => Some((major, minor)),
_ => None,
};
if let Some((major, minor)) = major_minor {
let version = Version::new([major, minor]);
Ok(match specifier.operator() {
// `python_version == 3.7` is equivalent to `python_full_version == 3.7.*`.
Operator::Equal | Operator::ExactEqual => {
VersionSpecifier::equals_star_version(version)
}
// `python_version != 3.7` is equivalent to `python_full_version != 3.7.*`.
Operator::NotEqual => VersionSpecifier::not_equals_star_version(version),
// `python_version > 3.7` is equivalent to `python_full_version >= 3.8`.
Operator::GreaterThan => {
VersionSpecifier::greater_than_equal_version(Version::new([major, minor + 1]))
}
// `python_version < 3.7` is equivalent to `python_full_version < 3.7`.
Operator::LessThan => specifier,
// `python_version >= 3.7` is equivalent to `python_full_version >= 3.7`.
Operator::GreaterThanEqual => specifier,
// `python_version <= 3.7` is equivalent to `python_full_version < 3.8`.
Operator::LessThanEqual => {
VersionSpecifier::less_than_version(Version::new([major, minor + 1]))
}
// `==3.7.*`, `!=3.7.*`, `~=3.7` already represent the equivalent `python_full_version`
// comparison.
Operator::EqualStar | Operator::NotEqualStar | Operator::TildeEqual => specifier,
})
} else {
let &[major, minor, ..] = specifier.version().release() else {
unreachable!()
};
Ok(match specifier.operator() {
// `python_version` cannot have more than two release segments, so equality is impossible.
Operator::Equal | Operator::ExactEqual | Operator::EqualStar | Operator::TildeEqual => {
return Err(NodeId::FALSE)
}
// Similarly, inequalities are always `true`.
Operator::NotEqual | Operator::NotEqualStar => return Err(NodeId::TRUE),
// `python_version {<,<=} 3.7.8` is equivalent to `python_full_version < 3.8`.
Operator::LessThan | Operator::LessThanEqual => {
VersionSpecifier::less_than_version(Version::new([major, minor + 1]))
}
// `python_version {>,>=} 3.7.8` is equivalent to `python_full_version >= 3.8`.
Operator::GreaterThan | Operator::GreaterThanEqual => {
VersionSpecifier::greater_than_equal_version(Version::new([major, minor + 1]))
}
})
}
}
/// Compares the start of two ranges that are known to be disjoint.
fn compare_disjoint_range_start<T>(range1: &Range<T>, range2: &Range<T>) -> Ordering
where

View file

@ -3,7 +3,7 @@ use std::ops::Bound;
use indexmap::IndexMap;
use itertools::Itertools;
use pep440_rs::VersionSpecifier;
use pep440_rs::{Version, VersionSpecifier};
use pubgrub::Range;
use rustc_hash::FxBuildHasher;
@ -61,9 +61,21 @@ fn collect_dnf(
continue;
}
// Detect whether the range for this edge can be simplified as a star inequality.
if let Some(specifier) = star_range_inequality(&range) {
path.push(MarkerExpression::Version {
key: marker.key().clone(),
specifier,
});
collect_dnf(&tree, dnf, path);
path.pop();
continue;
}
for bounds in range.iter() {
let current = path.len();
for specifier in VersionSpecifier::from_bounds(bounds) {
for specifier in VersionSpecifier::from_release_only_bounds(bounds) {
path.push(MarkerExpression::Version {
key: marker.key().clone(),
specifier,
@ -307,7 +319,7 @@ where
/// Returns `Some` if the expression can be simplified as an inequality consisting
/// of the given values.
///
/// For example, `os_name < 'Linux'` and `os_name > 'Linux'` can be simplified to
/// For example, `os_name < 'Linux' or os_name > 'Linux'` can be simplified to
/// `os_name != 'Linux'`.
fn range_inequality<T>(range: &Range<T>) -> Option<Vec<&T>>
where
@ -328,6 +340,25 @@ where
Some(excluded)
}
/// Returns `Some` if the version expression can be simplified as a star inequality with the given
/// specifier.
///
/// For example, `python_full_version < '3.8' or python_full_version >= '3.9'` can be simplified to
/// `python_full_version != '3.8.*'`.
fn star_range_inequality(range: &Range<Version>) -> Option<VersionSpecifier> {
let (b1, b2) = range.iter().collect_tuple()?;
match (b1, b2) {
((Bound::Unbounded, Bound::Excluded(v1)), (Bound::Included(v2), Bound::Unbounded))
if v1.release().len() == 2
&& v2.release() == [v1.release()[0], v1.release()[1] + 1] =>
{
Some(VersionSpecifier::not_equals_star_version(v1.clone()))
}
_ => None,
}
}
/// Returns `true` if the LHS is the negation of the RHS, or vice versa.
fn is_negation(left: &MarkerExpression, right: &MarkerExpression) -> bool {
match left {

View file

@ -1067,15 +1067,11 @@ impl MarkerTree {
/// the marker will be simplified to `sys_platform == 'linux'`.
#[must_use]
#[allow(clippy::needless_pass_by_value)]
pub fn simplify_python_versions(
self,
python_version: Range<Version>,
full_python_version: Range<Version>,
) -> MarkerTree {
pub fn simplify_python_versions(self, python_version: Range<Version>) -> MarkerTree {
MarkerTree(INTERNER.lock().restrict_versions(self.0, &|var| match var {
Variable::Version(MarkerValueVersion::PythonVersion) => Some(python_version.clone()),
// Note that `python_version` is normalized to `python_full_version`.
Variable::Version(MarkerValueVersion::PythonFullVersion) => {
Some(full_python_version.clone())
Some(python_version.clone())
}
_ => None,
}))
@ -1452,62 +1448,49 @@ mod test {
assert_eq!(
m("(python_version <= '3.11' and sys_platform == 'win32') or python_version > '3.11'")
.simplify_python_versions(
Range::from_range_bounds((
Bound::Excluded(Version::new([3, 12])),
Bound::Unbounded,
)),
Range::from_range_bounds((
Bound::Excluded(Version::new([3, 12])),
Bound::Unbounded,
)),
),
.simplify_python_versions(Range::from_range_bounds((
Bound::Excluded(Version::new([3, 12])),
Bound::Unbounded,
))),
MarkerTree::TRUE
);
assert_eq!(
m("python_version < '3.10'")
.simplify_python_versions(
Range::from_range_bounds((
Bound::Excluded(Version::new([3, 7])),
Bound::Unbounded,
)),
Range::from_range_bounds((
Bound::Excluded(Version::new([3, 7])),
Bound::Unbounded,
)),
)
.simplify_python_versions(Range::from_range_bounds((
Bound::Excluded(Version::new([3, 7])),
Bound::Unbounded,
)))
.try_to_string()
.unwrap(),
"python_version < '3.10'"
"python_full_version < '3.10'"
);
// Note that `3.12.1` will still match.
assert_eq!(
m("python_version <= '3.12'")
.simplify_python_versions(Range::from_range_bounds((
Bound::Excluded(Version::new([3, 12])),
Bound::Unbounded,
)))
.try_to_string()
.unwrap(),
"python_full_version < '3.13'"
);
assert_eq!(
m("python_version <= '3.12'").simplify_python_versions(
Range::from_range_bounds((
Bound::Excluded(Version::new([3, 12])),
Bound::Unbounded,
)),
Range::from_range_bounds((
Bound::Excluded(Version::new([3, 12])),
Bound::Unbounded,
)),
),
m("python_full_version <= '3.12'").simplify_python_versions(Range::from_range_bounds(
(Bound::Excluded(Version::new([3, 12])), Bound::Unbounded,)
)),
MarkerTree::FALSE
);
assert_eq!(
m("python_full_version <= '3.12.1'")
.simplify_python_versions(
Range::from_range_bounds((
Bound::Excluded(Version::new([3, 12])),
Bound::Unbounded,
)),
Range::from_range_bounds((
Bound::Excluded(Version::new([3, 12])),
Bound::Unbounded,
)),
)
.simplify_python_versions(Range::from_range_bounds((
Bound::Excluded(Version::new([3, 12])),
Bound::Unbounded,
)))
.try_to_string()
.unwrap(),
"python_full_version <= '3.12.1'"
@ -1727,7 +1710,7 @@ mod test {
.contents()
.unwrap()
.to_string(),
"python_full_version >= '3.7' and python_version <= '3.7' and 'nt' in os_name",
"python_full_version == '3.7.*' and 'nt' in os_name",
);
}
@ -1820,60 +1803,75 @@ mod test {
#[test]
fn test_marker_simplification() {
assert_simplifies("python_version == '3.9'", "python_full_version == '3.9.*'");
assert_simplifies(
"python_version == '3.9' or python_version == '3.9'",
"python_version == '3.9'",
"python_version == '3.9.0'",
"python_full_version == '3.9.*'",
);
assert_simplifies("python_version != '3.9'", "python_full_version != '3.9.*'");
assert_simplifies("python_version >= '3.9.0'", "python_full_version >= '3.9'");
assert_simplifies("python_version <= '3.9.0'", "python_full_version < '3.10'");
assert_simplifies(
"python_version == '3.*'",
"python_full_version >= '3' and python_full_version < '4'",
);
assert_simplifies(
"python_version == '3.0.*'",
"python_full_version == '3.0.*'",
);
assert_simplifies(
"python_version < '3.17' or python_version < '3.18'",
"python_version < '3.18'",
"python_full_version < '3.18'",
);
assert_simplifies(
"python_version > '3.17' or python_version > '3.18' or python_version > '3.12'",
"python_version > '3.12'",
"python_full_version >= '3.13'",
);
// a quirk of how pubgrub works, but this is considered part of normalization
assert_simplifies(
"python_version > '3.17.post4' or python_version > '3.18.post4'",
"python_version > '3.17'",
"python_full_version >= '3.18'",
);
assert_simplifies(
"python_version < '3.17' and python_version < '3.18'",
"python_version < '3.17'",
"python_full_version < '3.17'",
);
assert_simplifies(
"python_version <= '3.18' and python_version == '3.18'",
"python_version == '3.18'",
"python_full_version == '3.18.*'",
);
assert_simplifies(
"python_version <= '3.18' or python_version == '3.18'",
"python_version <= '3.18'",
"python_full_version < '3.19'",
);
assert_simplifies(
"python_version <= '3.15' or (python_version <= '3.17' and python_version < '3.16')",
"python_version < '3.16'",
"python_full_version < '3.16'",
);
assert_simplifies(
"(python_version > '3.17' or python_version > '3.16') and python_version > '3.15'",
"python_version > '3.16'",
"python_full_version >= '3.17'",
);
assert_simplifies(
"(python_version > '3.17' or python_version > '3.16') and python_version > '3.15' and implementation_version == '1'",
"implementation_version == '1' and python_version > '3.16'",
"implementation_version == '1' and python_full_version >= '3.17'",
);
assert_simplifies(
"('3.17' < python_version or '3.16' < python_version) and '3.15' < python_version and implementation_version == '1'",
"implementation_version == '1' and python_version > '3.16'",
"implementation_version == '1' and python_full_version >= '3.17'",
);
assert_simplifies("extra == 'a' or extra == 'a'", "extra == 'a'");
@ -1929,7 +1927,7 @@ mod test {
// post-normalization filtering
assert_simplifies(
"(python_version < '3.1' or python_version < '3.2') and (python_version < '3.2' or python_version == '3.3')",
"python_version < '3.2'",
"python_full_version < '3.2'",
);
// normalize out redundant ranges
@ -1949,17 +1947,17 @@ mod test {
assert_simplifies(
"python_version != '3.10' or python_version > '3.12'",
"python_version != '3.10'",
"python_full_version != '3.10.*'",
);
assert_simplifies(
"python_version != '3.8' and python_version < '3.10'",
"python_version < '3.8' or (python_version > '3.8' and python_version < '3.10')",
"python_full_version < '3.8' or python_full_version == '3.9.*'",
);
assert_simplifies(
"python_version != '3.8' and python_version != '3.9'",
"python_version != '3.8' and python_version != '3.9'",
"python_full_version < '3.8' or python_full_version >= '3.10'",
);
// normalize out redundant expressions
@ -2089,20 +2087,22 @@ mod test {
"(os_name != 'Linux' and sys_platform == 'x') or (platform_system != 'win32' and sys_platform == 'x') or (os_name == 'Linux' and platform_system == 'win32')"
);
assert_simplifies("python_version > '3.7'", "python_version > '3.7'");
assert_simplifies("python_version > '3.7'", "python_full_version >= '3.8'");
assert_simplifies(
"(python_version <= '3.7' and os_name == 'Linux') or python_version > '3.7'",
"os_name == 'Linux' or python_version > '3.7'",
"os_name == 'Linux' or python_full_version >= '3.8'",
);
// Again, the extra `<3.7` and `>=3.9` expressions cannot be seen as redundant due to them being interdependent.
// TODO(ibraheem): We might be able to simplify these by checking for the negation of the combined ranges before we split them.
assert_simplifies(
"(os_name == 'Linux' and sys_platform == 'win32') \
or (os_name != 'Linux' and sys_platform == 'win32' and python_version == '3.7') \
or (os_name != 'Linux' and sys_platform == 'win32' and python_version == '3.8')",
"(os_name == 'Linux' and sys_platform == 'win32') \
or (python_version == '3.7' and sys_platform == 'win32') \
or (python_version == '3.8' and sys_platform == 'win32')",
"(python_full_version < '3.7' and os_name == 'Linux' and sys_platform == 'win32') \
or (python_full_version >= '3.9' and os_name == 'Linux' and sys_platform == 'win32') \
or (python_full_version >= '3.7' and python_full_version < '3.9' and sys_platform == 'win32')"
);
assert_simplifies(
@ -2139,10 +2139,10 @@ mod test {
#[test]
fn test_requires_python() {
fn simplified(marker: &str) -> MarkerTree {
m(marker).simplify_python_versions(
Range::from_range_bounds((Bound::Included(Version::new([3, 8])), Bound::Unbounded)),
Range::from_range_bounds((Bound::Included(Version::new([3, 8])), Bound::Unbounded)),
)
m(marker).simplify_python_versions(Range::from_range_bounds((
Bound::Included(Version::new([3, 8])),
Bound::Unbounded,
)))
}
assert_eq!(simplified("python_version >= '3.8'"), MarkerTree::TRUE);
@ -2160,14 +2160,14 @@ mod test {
simplified("python_version == '3.8'")
.try_to_string()
.unwrap(),
"python_version == '3.8'"
"python_full_version < '3.9'"
);
assert_eq!(
simplified("python_version <= '3.10'")
.try_to_string()
.unwrap(),
"python_version <= '3.10'"
"python_full_version < '3.11'"
);
}
@ -2190,7 +2190,7 @@ mod test {
// is always `true` and not disjoint.
assert!(!is_disjoint(
"python_version == 'Linux'",
"python_version == '3.7.1'"
"python_full_version == '3.7.1'"
));
}
@ -2198,14 +2198,14 @@ mod test {
fn test_version_disjointness() {
assert!(!is_disjoint(
"os_name == 'Linux'",
"python_version == '3.7.1'"
"python_full_version == '3.7.1'"
));
test_version_bounds_disjointness("python_version");
test_version_bounds_disjointness("python_full_version");
assert!(!is_disjoint(
"python_version == '3.7.*'",
"python_version == '3.7.1'"
"python_full_version == '3.7.*'",
"python_full_version == '3.7.1'"
));
}
@ -2217,7 +2217,7 @@ mod test {
));
assert!(!is_disjoint(
"implementation_version == '3.7.0'",
"python_version == '3.7.1'"
"python_full_version == '3.7.1'"
));
// basic version bounds checking should still work with lexicographical comparisons
@ -2355,8 +2355,8 @@ mod test {
}
fn assert_simplifies(left: &str, right: &str) {
assert_eq!(m(left), m(right));
assert_eq!(m(left).try_to_string().unwrap(), right);
assert_eq!(m(left), m(right), "{left} != {right}");
assert_eq!(m(left).try_to_string().unwrap(), right, "{left} != {right}");
}
fn assert_true(marker: &str) {

View file

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

View file

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

View file

@ -167,7 +167,7 @@ RequirementsTxt {
"dev",
),
],
marker: python_version >= '3.9' and os_name == 'posix',
marker: python_full_version >= '3.9' and os_name == 'posix',
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -224,7 +224,7 @@ RequirementsTxt {
"dev",
),
],
marker: python_version >= '3.9' and os_name == 'posix',
marker: python_full_version >= '3.9' and os_name == 'posix',
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -274,7 +274,7 @@ RequirementsTxt {
},
},
extras: [],
marker: python_version >= '3.9' and os_name == 'posix',
marker: python_full_version >= '3.9' and os_name == 'posix',
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",

View file

@ -167,7 +167,7 @@ RequirementsTxt {
"dev",
),
],
marker: python_version >= '3.9' and os_name == 'posix',
marker: python_full_version >= '3.9' and os_name == 'posix',
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -224,7 +224,7 @@ RequirementsTxt {
"dev",
),
],
marker: python_version >= '3.9' and os_name == 'posix',
marker: python_full_version >= '3.9' and os_name == 'posix',
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",
@ -274,7 +274,7 @@ RequirementsTxt {
},
},
extras: [],
marker: python_version >= '3.9' and os_name == 'posix',
marker: python_full_version >= '3.9' and os_name == 'posix',
origin: Some(
File(
"<REQUIREMENTS_DIR>/editable.txt",

View file

@ -97,7 +97,7 @@ impl RequiresPython {
// Convert back to PEP 440 specifiers.
let specifiers = range
.iter()
.flat_map(VersionSpecifier::from_bounds)
.flat_map(VersionSpecifier::from_release_only_bounds)
.collect();
Ok(Some(Self { specifiers, bound }))

View file

@ -1515,7 +1515,6 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
// supported by the root, skip it.
let requirement = if let Some(requires_python) = python_requirement.target().and_then(|target| target.as_requires_python()).filter(|_| !requirement.marker.is_true()) {
let marker = requirement.marker.clone().simplify_python_versions(
Range::from(requires_python.bound_major_minor().clone()),
Range::from(requires_python.bound().clone()),
);
@ -1582,7 +1581,6 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
// supported by the root, skip it.
let constraint = if let Some(requires_python) = python_requirement.target().and_then(|target| target.as_requires_python()).filter(|_| !constraint.marker.is_true()) {
let mut marker = constraint.marker.clone().simplify_python_versions(
Range::from(requires_python.bound_major_minor().clone()),
Range::from(requires_python.bound().clone()),
);
marker.and(requirement.marker.clone());
@ -2888,10 +2886,7 @@ fn simplify_python(marker: MarkerTree, python_requirement: &PythonRequirement) -
.target()
.and_then(|target| target.as_requires_python())
{
marker.simplify_python_versions(
Range::from(requires_python.bound_major_minor().clone()),
Range::from(requires_python.bound().clone()),
)
marker.simplify_python_versions(Range::from(requires_python.bound().clone()))
} else {
marker
}

View file

@ -67,7 +67,7 @@ fn branching_urls_overlapping() -> Result<()> {
----- stdout -----
----- stderr -----
error: Requirements contain conflicting URLs for package `iniconfig` in split `python_version < '3.12'`:
error: Requirements contain conflicting URLs for package `iniconfig` in split `python_full_version < '3.12'`:
- https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl
- https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl
"###
@ -133,7 +133,7 @@ fn root_package_splits_but_transitive_conflict() -> Result<()> {
----- stdout -----
----- stderr -----
error: Requirements contain conflicting URLs for package `iniconfig` in split `python_version < '3.12'`:
error: Requirements contain conflicting URLs for package `iniconfig` in split `python_full_version < '3.12'`:
- https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl
- https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl
"###
@ -209,8 +209,8 @@ fn root_package_splits_transitive_too() -> Result<()> {
version = 1
requires-python = ">=3.11, <3.13"
environment-markers = [
"python_version < '3.12'",
"python_version >= '3.12'",
"python_full_version < '3.12'",
"python_full_version >= '3.12'",
]
[options]
@ -221,15 +221,15 @@ fn root_package_splits_transitive_too() -> Result<()> {
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "anyio", version = "4.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_version < '3.12'" },
{ name = "anyio", version = "4.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_version >= '3.12'" },
{ name = "anyio", version = "4.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "anyio", version = "4.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "b" },
]
[package.metadata]
requires-dist = [
{ name = "anyio", marker = "python_version < '3.12'", specifier = "==4.2.0" },
{ name = "anyio", marker = "python_version >= '3.12'", specifier = "==4.3.0" },
{ name = "anyio", marker = "python_full_version < '3.12'", specifier = "==4.2.0" },
{ name = "anyio", marker = "python_full_version >= '3.12'", specifier = "==4.3.0" },
{ name = "b", directory = "b" },
]
@ -238,11 +238,11 @@ fn root_package_splits_transitive_too() -> Result<()> {
version = "4.2.0"
source = { registry = "https://pypi.org/simple" }
environment-markers = [
"python_version < '3.12'",
"python_full_version < '3.12'",
]
dependencies = [
{ name = "idna", marker = "python_version < '3.12'" },
{ name = "sniffio", marker = "python_version < '3.12'" },
{ name = "idna", marker = "python_full_version < '3.12'" },
{ name = "sniffio", marker = "python_full_version < '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2d/b8/7333d87d5f03247215d86a86362fd3e324111788c6cdd8d2e6196a6ba833/anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f", size = 158770 }
wheels = [
@ -254,11 +254,11 @@ fn root_package_splits_transitive_too() -> Result<()> {
version = "4.3.0"
source = { registry = "https://pypi.org/simple" }
environment-markers = [
"python_version >= '3.12'",
"python_full_version >= '3.12'",
]
dependencies = [
{ name = "idna", marker = "python_version >= '3.12'" },
{ name = "sniffio", marker = "python_version >= '3.12'" },
{ name = "idna", marker = "python_full_version >= '3.12'" },
{ name = "sniffio", marker = "python_full_version >= '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6", size = 159642 }
wheels = [
@ -270,14 +270,14 @@ fn root_package_splits_transitive_too() -> Result<()> {
version = "0.1.0"
source = { directory = "b" }
dependencies = [
{ name = "b1", marker = "python_version < '3.12'" },
{ name = "b2", marker = "python_version >= '3.12'" },
{ name = "b1", marker = "python_full_version < '3.12'" },
{ name = "b2", marker = "python_full_version >= '3.12'" },
]
[package.metadata]
requires-dist = [
{ name = "b1", marker = "python_version < '3.12'", directory = "../b1" },
{ name = "b2", marker = "python_version >= '3.12'", directory = "../b2" },
{ name = "b1", marker = "python_full_version < '3.12'", directory = "../b1" },
{ name = "b2", marker = "python_full_version >= '3.12'", directory = "../b2" },
]
[[package]]
@ -285,7 +285,7 @@ fn root_package_splits_transitive_too() -> Result<()> {
version = "0.1.0"
source = { directory = "../b1" }
dependencies = [
{ name = "iniconfig", version = "1.1.1", source = { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl" }, marker = "python_version < '3.12'" },
{ name = "iniconfig", version = "1.1.1", source = { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl" }, marker = "python_full_version < '3.12'" },
]
[package.metadata]
@ -296,7 +296,7 @@ fn root_package_splits_transitive_too() -> Result<()> {
version = "0.1.0"
source = { directory = "../b2" }
dependencies = [
{ name = "iniconfig", version = "2.0.0", source = { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" }, marker = "python_version >= '3.12'" },
{ name = "iniconfig", version = "2.0.0", source = { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" }, marker = "python_full_version >= '3.12'" },
]
[package.metadata]
@ -316,7 +316,7 @@ fn root_package_splits_transitive_too() -> Result<()> {
version = "1.1.1"
source = { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl" }
environment-markers = [
"python_version < '3.12'",
"python_full_version < '3.12'",
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3" },
@ -327,7 +327,7 @@ fn root_package_splits_transitive_too() -> Result<()> {
version = "2.0.0"
source = { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" }
environment-markers = [
"python_version >= '3.12'",
"python_full_version >= '3.12'",
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" },
@ -404,8 +404,8 @@ fn root_package_splits_other_dependencies_too() -> Result<()> {
version = 1
requires-python = ">=3.11, <3.13"
environment-markers = [
"python_version < '3.12'",
"python_version >= '3.12'",
"python_full_version < '3.12'",
"python_full_version >= '3.12'",
]
[options]
@ -416,18 +416,18 @@ fn root_package_splits_other_dependencies_too() -> Result<()> {
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "anyio", version = "4.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_version < '3.12'" },
{ name = "anyio", version = "4.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_version >= '3.12'" },
{ name = "b1", marker = "python_version < '3.12'" },
{ name = "b2", marker = "python_version >= '3.12'" },
{ name = "anyio", version = "4.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "anyio", version = "4.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "b1", marker = "python_full_version < '3.12'" },
{ name = "b2", marker = "python_full_version >= '3.12'" },
]
[package.metadata]
requires-dist = [
{ name = "anyio", marker = "python_version < '3.12'", specifier = "==4.2.0" },
{ name = "anyio", marker = "python_version >= '3.12'", specifier = "==4.3.0" },
{ name = "b1", marker = "python_version < '3.12'", directory = "b1" },
{ name = "b2", marker = "python_version >= '3.12'", directory = "b2" },
{ name = "anyio", marker = "python_full_version < '3.12'", specifier = "==4.2.0" },
{ name = "anyio", marker = "python_full_version >= '3.12'", specifier = "==4.3.0" },
{ name = "b1", marker = "python_full_version < '3.12'", directory = "b1" },
{ name = "b2", marker = "python_full_version >= '3.12'", directory = "b2" },
]
[[package]]
@ -435,11 +435,11 @@ fn root_package_splits_other_dependencies_too() -> Result<()> {
version = "4.2.0"
source = { registry = "https://pypi.org/simple" }
environment-markers = [
"python_version < '3.12'",
"python_full_version < '3.12'",
]
dependencies = [
{ name = "idna", marker = "python_version < '3.12'" },
{ name = "sniffio", marker = "python_version < '3.12'" },
{ name = "idna", marker = "python_full_version < '3.12'" },
{ name = "sniffio", marker = "python_full_version < '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2d/b8/7333d87d5f03247215d86a86362fd3e324111788c6cdd8d2e6196a6ba833/anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f", size = 158770 }
wheels = [
@ -451,11 +451,11 @@ fn root_package_splits_other_dependencies_too() -> Result<()> {
version = "4.3.0"
source = { registry = "https://pypi.org/simple" }
environment-markers = [
"python_version >= '3.12'",
"python_full_version >= '3.12'",
]
dependencies = [
{ name = "idna", marker = "python_version >= '3.12'" },
{ name = "sniffio", marker = "python_version >= '3.12'" },
{ name = "idna", marker = "python_full_version >= '3.12'" },
{ name = "sniffio", marker = "python_full_version >= '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6", size = 159642 }
wheels = [
@ -467,7 +467,7 @@ fn root_package_splits_other_dependencies_too() -> Result<()> {
version = "0.1.0"
source = { directory = "b1" }
dependencies = [
{ name = "iniconfig", version = "1.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_version < '3.12'" },
{ name = "iniconfig", version = "1.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
]
[package.metadata]
@ -478,7 +478,7 @@ fn root_package_splits_other_dependencies_too() -> Result<()> {
version = "0.1.0"
source = { directory = "b2" }
dependencies = [
{ name = "iniconfig", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_version >= '3.12'" },
{ name = "iniconfig", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
]
[package.metadata]
@ -498,7 +498,7 @@ fn root_package_splits_other_dependencies_too() -> Result<()> {
version = "1.1.1"
source = { registry = "https://pypi.org/simple" }
environment-markers = [
"python_version < '3.12'",
"python_full_version < '3.12'",
]
sdist = { url = "https://files.pythonhosted.org/packages/23/a2/97899f6bd0e873fed3a7e67ae8d3a08b21799430fb4da15cfedf10d6e2c2/iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32", size = 8104 }
wheels = [
@ -510,7 +510,7 @@ fn root_package_splits_other_dependencies_too() -> Result<()> {
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
environment-markers = [
"python_version >= '3.12'",
"python_full_version >= '3.12'",
]
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
wheels = [
@ -565,8 +565,8 @@ fn branching_between_registry_and_direct_url() -> Result<()> {
version = 1
requires-python = ">=3.11, <3.13"
environment-markers = [
"python_version < '3.12'",
"python_version >= '3.12'",
"python_full_version < '3.12'",
"python_full_version >= '3.12'",
]
[options]
@ -577,14 +577,14 @@ fn branching_between_registry_and_direct_url() -> Result<()> {
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "iniconfig", version = "1.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_version < '3.12'" },
{ name = "iniconfig", version = "2.0.0", source = { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" }, marker = "python_version >= '3.12'" },
{ name = "iniconfig", version = "1.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "iniconfig", version = "2.0.0", source = { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" }, marker = "python_full_version >= '3.12'" },
]
[package.metadata]
requires-dist = [
{ name = "iniconfig", marker = "python_version < '3.12'", specifier = "==1.1.1" },
{ name = "iniconfig", marker = "python_version >= '3.12'", url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" },
{ name = "iniconfig", marker = "python_full_version < '3.12'", specifier = "==1.1.1" },
{ name = "iniconfig", marker = "python_full_version >= '3.12'", url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" },
]
[[package]]
@ -592,7 +592,7 @@ fn branching_between_registry_and_direct_url() -> Result<()> {
version = "1.1.1"
source = { registry = "https://pypi.org/simple" }
environment-markers = [
"python_version < '3.12'",
"python_full_version < '3.12'",
]
sdist = { url = "https://files.pythonhosted.org/packages/23/a2/97899f6bd0e873fed3a7e67ae8d3a08b21799430fb4da15cfedf10d6e2c2/iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32", size = 8104 }
wheels = [
@ -604,7 +604,7 @@ fn branching_between_registry_and_direct_url() -> Result<()> {
version = "2.0.0"
source = { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" }
environment-markers = [
"python_version >= '3.12'",
"python_full_version >= '3.12'",
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" },
@ -650,8 +650,8 @@ fn branching_urls_of_different_sources_disjoint() -> Result<()> {
version = 1
requires-python = ">=3.11, <3.13"
environment-markers = [
"python_version < '3.12'",
"python_version >= '3.12'",
"python_full_version < '3.12'",
"python_full_version >= '3.12'",
]
[options]
@ -662,14 +662,14 @@ fn branching_urls_of_different_sources_disjoint() -> Result<()> {
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "iniconfig", version = "1.1.1", source = { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl" }, marker = "python_version < '3.12'" },
{ name = "iniconfig", version = "2.0.0", source = { git = "https://github.com/pytest-dev/iniconfig?rev=93f5930e668c0d1ddf4597e38dd0dea4e2665e7a#93f5930e668c0d1ddf4597e38dd0dea4e2665e7a" }, marker = "python_version >= '3.12'" },
{ name = "iniconfig", version = "1.1.1", source = { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl" }, marker = "python_full_version < '3.12'" },
{ name = "iniconfig", version = "2.0.0", source = { git = "https://github.com/pytest-dev/iniconfig?rev=93f5930e668c0d1ddf4597e38dd0dea4e2665e7a#93f5930e668c0d1ddf4597e38dd0dea4e2665e7a" }, marker = "python_full_version >= '3.12'" },
]
[package.metadata]
requires-dist = [
{ name = "iniconfig", marker = "python_version < '3.12'", url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl" },
{ name = "iniconfig", marker = "python_version >= '3.12'", git = "https://github.com/pytest-dev/iniconfig?rev=93f5930e668c0d1ddf4597e38dd0dea4e2665e7a#93f5930e668c0d1ddf4597e38dd0dea4e2665e7a" },
{ name = "iniconfig", marker = "python_full_version < '3.12'", url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl" },
{ name = "iniconfig", marker = "python_full_version >= '3.12'", git = "https://github.com/pytest-dev/iniconfig?rev=93f5930e668c0d1ddf4597e38dd0dea4e2665e7a#93f5930e668c0d1ddf4597e38dd0dea4e2665e7a" },
]
[[package]]
@ -677,7 +677,7 @@ fn branching_urls_of_different_sources_disjoint() -> Result<()> {
version = "1.1.1"
source = { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl" }
environment-markers = [
"python_version < '3.12'",
"python_full_version < '3.12'",
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3" },
@ -688,7 +688,7 @@ fn branching_urls_of_different_sources_disjoint() -> Result<()> {
version = "2.0.0"
source = { git = "https://github.com/pytest-dev/iniconfig?rev=93f5930e668c0d1ddf4597e38dd0dea4e2665e7a#93f5930e668c0d1ddf4597e38dd0dea4e2665e7a" }
environment-markers = [
"python_version >= '3.12'",
"python_full_version >= '3.12'",
]
"###);
@ -723,7 +723,7 @@ fn branching_urls_of_different_sources_conflict() -> Result<()> {
----- stdout -----
----- stderr -----
error: Requirements contain conflicting URLs for package `iniconfig` in split `python_version < '3.12'`:
error: Requirements contain conflicting URLs for package `iniconfig` in split `python_full_version < '3.12'`:
- git+https://github.com/pytest-dev/iniconfig@93f5930e668c0d1ddf4597e38dd0dea4e2665e7a
- https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl
"###

View file

@ -1770,7 +1770,7 @@ fn update() -> Result<()> {
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"requests[security,socks,use-chardet-on-py3]==2.31.0 ; python_version > '3.7'",
"requests[security,socks,use-chardet-on-py3]==2.31.0 ; python_full_version >= '3.8'",
]
"###
);
@ -1808,7 +1808,7 @@ fn update() -> Result<()> {
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"requests[security,socks,use-chardet-on-py3]==2.31.0 ; python_version > '3.7'",
"requests[security,socks,use-chardet-on-py3]==2.31.0 ; python_full_version >= '3.8'",
]
[tool.uv.sources]
@ -1890,7 +1890,7 @@ fn update() -> Result<()> {
]
[package.metadata]
requires-dist = [{ name = "requests", extras = ["security", "socks", "use-chardet-on-py3"], marker = "python_version > '3.7'", git = "https://github.com/psf/requests?tag=v2.32.3" }]
requires-dist = [{ name = "requests", extras = ["security", "socks", "use-chardet-on-py3"], marker = "python_full_version >= '3.8'", git = "https://github.com/psf/requests?tag=v2.32.3" }]
[[package]]
name = "pysocks"

View file

@ -881,7 +881,7 @@ fn lock_wheel_url() -> Result<()> {
requires-dist = [
{ name = "anyio", extras = ["trio"], marker = "extra == 'test'" },
{ name = "coverage", extras = ["toml"], marker = "extra == 'test'", specifier = ">=7" },
{ name = "exceptiongroup", marker = "python_version < '3.11'", specifier = ">=1.0.2" },
{ name = "exceptiongroup", marker = "python_full_version < '3.11'", specifier = ">=1.0.2" },
{ name = "exceptiongroup", marker = "extra == 'test'", specifier = ">=1.2.0" },
{ name = "hypothesis", marker = "extra == 'test'", specifier = ">=4.0" },
{ name = "idna", specifier = ">=2.8" },
@ -895,7 +895,7 @@ fn lock_wheel_url() -> Result<()> {
{ name = "sphinx-rtd-theme", marker = "extra == 'doc'" },
{ name = "trio", marker = "extra == 'trio'", specifier = ">=0.23" },
{ name = "trustme", marker = "extra == 'test'" },
{ name = "typing-extensions", marker = "python_version < '3.11'", specifier = ">=4.1" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'", specifier = ">=4.1" },
{ name = "uvloop", marker = "platform_python_implementation == 'CPython' and platform_system != 'Windows' and extra == 'test'", specifier = ">=0.17" },
]
@ -1026,7 +1026,7 @@ fn lock_sdist_url() -> Result<()> {
requires-dist = [
{ name = "anyio", extras = ["trio"], marker = "extra == 'test'" },
{ name = "coverage", extras = ["toml"], marker = "extra == 'test'", specifier = ">=7" },
{ name = "exceptiongroup", marker = "python_version < '3.11'", specifier = ">=1.0.2" },
{ name = "exceptiongroup", marker = "python_full_version < '3.11'", specifier = ">=1.0.2" },
{ name = "exceptiongroup", marker = "extra == 'test'", specifier = ">=1.2.0" },
{ name = "hypothesis", marker = "extra == 'test'", specifier = ">=4.0" },
{ name = "idna", specifier = ">=2.8" },
@ -1040,7 +1040,7 @@ fn lock_sdist_url() -> Result<()> {
{ name = "sphinx-rtd-theme", marker = "extra == 'doc'" },
{ name = "trio", marker = "extra == 'trio'", specifier = ">=0.23" },
{ name = "trustme", marker = "extra == 'test'" },
{ name = "typing-extensions", marker = "python_version < '3.11'", specifier = ">=4.1" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'", specifier = ">=4.1" },
{ name = "uvloop", marker = "platform_python_implementation == 'CPython' and platform_system != 'Windows' and extra == 'test'", specifier = ">=0.17" },
]
@ -1611,8 +1611,8 @@ fn lock_conditional_dependency_extra() -> Result<()> {
version = 1
requires-python = ">=3.7"
environment-markers = [
"python_version < '3.10'",
"python_version >= '3.10'",
"python_full_version < '3.10'",
"python_full_version >= '3.10'",
]
[options]
@ -1739,13 +1739,13 @@ fn lock_conditional_dependency_extra() -> Result<()> {
source = { editable = "." }
dependencies = [
{ name = "requests" },
{ name = "requests", extra = ["socks"], marker = "python_version < '3.10'" },
{ name = "requests", extra = ["socks"], marker = "python_full_version < '3.10'" },
]
[package.metadata]
requires-dist = [
{ name = "requests" },
{ name = "requests", extras = ["socks"], marker = "python_version < '3.10'" },
{ name = "requests", extras = ["socks"], marker = "python_full_version < '3.10'" },
]
[[package]]
@ -1774,7 +1774,7 @@ fn lock_conditional_dependency_extra() -> Result<()> {
[package.optional-dependencies]
socks = [
{ name = "pysocks", marker = "python_version < '3.10'" },
{ name = "pysocks", marker = "python_full_version < '3.10'" },
]
[[package]]
@ -2747,7 +2747,7 @@ fn lock_requires_python() -> Result<()> {
version = "23.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "importlib-metadata", marker = "python_version < '3.8'" },
{ name = "importlib-metadata", marker = "python_full_version < '3.8'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e3/fc/f800d51204003fa8ae392c4e8278f256206e7a919b708eef054f5f4b650d/attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", size = 780820 }
wheels = [
@ -2760,8 +2760,8 @@ fn lock_requires_python() -> Result<()> {
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
{ name = "exceptiongroup", marker = "python_version < '3.11'" },
{ name = "typing-extensions", marker = "python_version < '3.11'" },
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/68/d4/27f9fd840e74d51b6d6a024d39ff495b56ffde71d28eb82758b7b85d0617/cattrs-23.1.2.tar.gz", hash = "sha256:db1c821b8c537382b2c7c66678c3790091ca0275ac486c76f3c8f3920e83c657", size = 39998 }
wheels = [
@ -2782,7 +2782,7 @@ fn lock_requires_python() -> Result<()> {
version = "6.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_version < '3.8'" },
{ name = "typing-extensions", marker = "python_full_version < '3.8'" },
{ name = "zipp" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a3/82/f6e29c8d5c098b6be61460371c2c5591f4a335923639edec43b3830650a4/importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4", size = 53569 }
@ -2899,7 +2899,7 @@ fn lock_requires_python() -> Result<()> {
version = "23.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "importlib-metadata", marker = "python_version < '3.8'" },
{ name = "importlib-metadata", marker = "python_full_version < '3.8'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e3/fc/f800d51204003fa8ae392c4e8278f256206e7a919b708eef054f5f4b650d/attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", size = 780820 }
wheels = [
@ -2912,8 +2912,8 @@ fn lock_requires_python() -> Result<()> {
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
{ name = "exceptiongroup", marker = "python_version < '3.11'" },
{ name = "typing-extensions", marker = "python_version < '3.11'" },
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/68/d4/27f9fd840e74d51b6d6a024d39ff495b56ffde71d28eb82758b7b85d0617/cattrs-23.1.2.tar.gz", hash = "sha256:db1c821b8c537382b2c7c66678c3790091ca0275ac486c76f3c8f3920e83c657", size = 39998 }
wheels = [
@ -2934,7 +2934,7 @@ fn lock_requires_python() -> Result<()> {
version = "6.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_version < '3.8'" },
{ name = "typing-extensions", marker = "python_full_version < '3.8'" },
{ name = "zipp" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a3/82/f6e29c8d5c098b6be61460371c2c5591f4a335923639edec43b3830650a4/importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4", size = 53569 }
@ -3175,7 +3175,7 @@ fn lock_requires_python_wheels() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.12, <3.13"
requires-python = "==3.12.*"
[options]
exclude-newer = "2024-03-25 00:00:00 UTC"
@ -3260,7 +3260,7 @@ fn lock_requires_python_wheels() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.11, <3.12"
requires-python = "==3.11.*"
[options]
exclude-newer = "2024-03-25 00:00:00 UTC"
@ -3370,7 +3370,7 @@ fn lock_requires_python_star() -> Result<()> {
assert_snapshot!(
lock, @r###"
version = 1
requires-python = ">=3.11, <3.12"
requires-python = "==3.11.*"
[options]
exclude-newer = "2024-03-25 00:00:00 UTC"
@ -3711,12 +3711,10 @@ fn lock_python_version_marker_complement() -> Result<()> {
version = 1
requires-python = ">=3.8"
environment-markers = [
"python_full_version <= '3.10' and python_version > '3.10'",
"python_full_version <= '3.10' and python_version == '3.10'",
"python_full_version <= '3.10' and python_version < '3.10'",
"python_full_version > '3.10' and python_version > '3.10'",
"python_full_version > '3.10' and python_version == '3.10'",
"python_full_version > '3.10' and python_version < '3.10'",
"python_full_version < '3.10'",
"python_full_version == '3.10'",
"python_full_version > '3.10' and python_full_version < '3.11'",
"python_full_version >= '3.11'",
]
[options]
@ -3752,10 +3750,10 @@ fn lock_python_version_marker_complement() -> Result<()> {
[package.metadata]
requires-dist = [
{ name = "attrs", marker = "python_version <= '3.10'" },
{ name = "attrs", marker = "python_version > '3.10'" },
{ name = "iniconfig", marker = "python_version < '3.10'" },
{ name = "iniconfig", marker = "python_version >= '3.10'" },
{ name = "attrs", marker = "python_full_version < '3.11'" },
{ name = "attrs", marker = "python_full_version >= '3.11'" },
{ name = "iniconfig", marker = "python_full_version < '3.10'" },
{ name = "iniconfig", marker = "python_full_version >= '3.10'" },
{ name = "typing-extensions", marker = "python_full_version <= '3.10'" },
{ name = "typing-extensions", marker = "python_full_version > '3.10'" },
]
@ -3966,7 +3964,7 @@ fn lock_conditional_unconditional() -> Result<()> {
[package.metadata]
requires-dist = [
{ name = "iniconfig" },
{ name = "iniconfig", marker = "python_version < '3.12'" },
{ name = "iniconfig", marker = "python_full_version < '3.12'" },
]
"###
);
@ -4045,7 +4043,7 @@ fn lock_multiple_markers() -> Result<()> {
[package.metadata]
requires-dist = [
{ name = "iniconfig", marker = "implementation_name == 'cpython'" },
{ name = "iniconfig", marker = "python_version < '3.12'" },
{ name = "iniconfig", marker = "python_full_version < '3.12'" },
]
"###
);
@ -9747,9 +9745,9 @@ fn lock_narrowed_python_version() -> Result<()> {
version = 1
requires-python = ">=3.7"
environment-markers = [
"python_version < '3.9'",
"python_version >= '3.9' and python_version <= '3.10'",
"python_version > '3.10'",
"python_full_version < '3.9'",
"python_full_version >= '3.9' and python_full_version < '3.11'",
"python_full_version >= '3.11'",
]
[options]
@ -9760,7 +9758,7 @@ fn lock_narrowed_python_version() -> Result<()> {
version = "0.1.0"
source = { directory = "dependency" }
dependencies = [
{ name = "iniconfig", marker = "python_version < '3.9' or python_version > '3.10'" },
{ name = "iniconfig", marker = "python_full_version < '3.9' or python_full_version >= '3.11'" },
]
[package.metadata]
@ -9780,13 +9778,13 @@ fn lock_narrowed_python_version() -> Result<()> {
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "dependency", marker = "python_version < '3.9' or python_version > '3.10'" },
{ name = "dependency", marker = "python_full_version < '3.9' or python_full_version >= '3.11'" },
]
[package.metadata]
requires-dist = [
{ name = "dependency", marker = "python_version < '3.9'", directory = "dependency" },
{ name = "dependency", marker = "python_version > '3.10'", directory = "dependency" },
{ name = "dependency", marker = "python_full_version < '3.9'", directory = "dependency" },
{ name = "dependency", marker = "python_full_version >= '3.11'", directory = "dependency" },
]
"###
);
@ -9883,7 +9881,7 @@ fn lock_exclude_unnecessary_python_forks() -> Result<()> {
[package.metadata]
requires-dist = [
{ name = "anyio", marker = "sys_platform == 'darwin'" },
{ name = "anyio", marker = "python_version > '3.10'" },
{ name = "anyio", marker = "python_full_version >= '3.11'" },
]
[[package]]

View file

@ -893,9 +893,9 @@ fn fork_incomplete_markers() -> Result<()> {
version = 1
requires-python = ">=3.8"
environment-markers = [
"python_version < '3.10'",
"python_version >= '3.10' and python_version < '3.11'",
"python_version >= '3.11'",
"python_full_version < '3.10'",
"python_full_version == '3.10.*'",
"python_full_version >= '3.11'",
]
[[package]]
@ -903,7 +903,7 @@ fn fork_incomplete_markers() -> Result<()> {
version = "1.0.0"
source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }
environment-markers = [
"python_version < '3.10'",
"python_full_version < '3.10'",
]
sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_a-1.0.0.tar.gz", hash = "sha256:dd56de2e560b3f95c529c44cbdae55d9b1ada826ddd3e19d3ea45438224ad603" }
wheels = [
@ -915,7 +915,7 @@ fn fork_incomplete_markers() -> Result<()> {
version = "2.0.0"
source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }
environment-markers = [
"python_version >= '3.11'",
"python_full_version >= '3.11'",
]
sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_a-2.0.0.tar.gz", hash = "sha256:580f1454a172036c89f5cfbefe52f175b011806a61f243eb476526bcc186e0be" }
wheels = [
@ -927,7 +927,7 @@ fn fork_incomplete_markers() -> Result<()> {
version = "1.0.0"
source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }
dependencies = [
{ name = "package-c", marker = "python_version == '3.10'" },
{ name = "package-c", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_b-1.0.0.tar.gz", hash = "sha256:c4deba44768923473d077bdc0e177033fcb6e6fd406d56830d7ee6f4ffad68c1" }
wheels = [
@ -948,15 +948,15 @@ fn fork_incomplete_markers() -> Result<()> {
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "python_version < '3.10'" },
{ name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "python_version >= '3.11'" },
{ name = "package-a", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "python_full_version < '3.10'" },
{ name = "package-a", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "python_full_version >= '3.11'" },
{ name = "package-b" },
]
[package.metadata]
requires-dist = [
{ name = "package-a", marker = "python_version < '3.10'", specifier = "==1" },
{ name = "package-a", marker = "python_version >= '3.11'", specifier = "==2" },
{ name = "package-a", marker = "python_full_version < '3.10'", specifier = "==1" },
{ name = "package-a", marker = "python_full_version >= '3.11'", specifier = "==2" },
{ name = "package-b" },
]
"###
@ -2978,9 +2978,9 @@ fn fork_overlapping_markers_basic() -> Result<()> {
version = 1
requires-python = ">=3.8"
environment-markers = [
"python_version < '3.10'",
"python_version >= '3.10' and python_version < '3.11'",
"python_version >= '3.11'",
"python_full_version < '3.10'",
"python_full_version == '3.10.*'",
"python_full_version >= '3.11'",
]
[[package]]
@ -3002,9 +3002,9 @@ fn fork_overlapping_markers_basic() -> Result<()> {
[package.metadata]
requires-dist = [
{ name = "package-a", marker = "python_version < '3.10'", specifier = ">=1.0.0" },
{ name = "package-a", marker = "python_version >= '3.10'", specifier = ">=1.1.0" },
{ name = "package-a", marker = "python_version >= '3.11'", specifier = ">=1.2.0" },
{ name = "package-a", marker = "python_full_version < '3.10'", specifier = ">=1.0.0" },
{ name = "package-a", marker = "python_full_version >= '3.10'", specifier = ">=1.1.0" },
{ name = "package-a", marker = "python_full_version >= '3.11'", specifier = ">=1.2.0" },
]
"###
);
@ -4326,11 +4326,11 @@ fn fork_requires_python_patch_overlap() -> Result<()> {
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "package-a", marker = "python_version == '3.10'" },
{ name = "package-a", marker = "python_full_version < '3.11'" },
]
[package.metadata]
requires-dist = [{ name = "package-a", marker = "python_version == '3.10'", specifier = "==1.0.0" }]
requires-dist = [{ name = "package-a", marker = "python_full_version == '3.10.*'", specifier = "==1.0.0" }]
"###
);
});
@ -4412,7 +4412,7 @@ fn fork_requires_python() -> Result<()> {
source = { editable = "." }
[package.metadata]
requires-dist = [{ name = "package-a", marker = "python_version == '3.9'", specifier = "==1.0.0" }]
requires-dist = [{ name = "package-a", marker = "python_full_version == '3.9.*'", specifier = "==1.0.0" }]
"###
);
});

View file

@ -6443,11 +6443,11 @@ fn no_strip_markers() -> Result<()> {
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] requirements.in --no-strip-markers --python-platform linux
anyio==4.3.0 ; python_version > '3.11'
anyio==4.3.0 ; python_full_version >= '3.12'
# via -r requirements.in
idna==3.6 ; python_version > '3.11'
idna==3.6 ; python_full_version >= '3.12'
# via anyio
sniffio==1.3.1 ; python_version > '3.11'
sniffio==1.3.1 ; python_full_version >= '3.12'
# via anyio
----- stderr -----
@ -6479,23 +6479,23 @@ fn no_strip_markers_multiple_markers() -> Result<()> {
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] requirements.in --no-strip-markers --python-platform windows
attrs==23.2.0 ; sys_platform == 'win32' or python_version > '3.11'
attrs==23.2.0 ; sys_platform == 'win32' or python_full_version >= '3.12'
# via
# outcome
# trio
cffi==1.16.0 ; (implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'win32') or (python_version > '3.11' and implementation_name != 'pypy' and os_name == 'nt')
cffi==1.16.0 ; (implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'win32') or (python_full_version >= '3.12' and implementation_name != 'pypy' and os_name == 'nt')
# via trio
idna==3.6 ; sys_platform == 'win32' or python_version > '3.11'
idna==3.6 ; sys_platform == 'win32' or python_full_version >= '3.12'
# via trio
outcome==1.3.0.post0 ; sys_platform == 'win32' or python_version > '3.11'
outcome==1.3.0.post0 ; sys_platform == 'win32' or python_full_version >= '3.12'
# via trio
pycparser==2.21 ; (implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'win32') or (python_version > '3.11' and implementation_name != 'pypy' and os_name == 'nt')
pycparser==2.21 ; (implementation_name != 'pypy' and os_name == 'nt' and sys_platform == 'win32') or (python_full_version >= '3.12' and implementation_name != 'pypy' and os_name == 'nt')
# via cffi
sniffio==1.3.1 ; sys_platform == 'win32' or python_version > '3.11'
sniffio==1.3.1 ; sys_platform == 'win32' or python_full_version >= '3.12'
# via trio
sortedcontainers==2.4.0 ; sys_platform == 'win32' or python_version > '3.11'
sortedcontainers==2.4.0 ; sys_platform == 'win32' or python_full_version >= '3.12'
# via trio
trio==0.25.0 ; sys_platform == 'win32' or python_version > '3.11'
trio==0.25.0 ; sys_platform == 'win32' or python_full_version >= '3.12'
# via -r requirements.in
----- stderr -----
@ -6524,23 +6524,23 @@ fn no_strip_markers_transitive_marker() -> Result<()> {
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] requirements.in --no-strip-markers --python-platform windows
attrs==23.2.0 ; python_version > '3.11'
attrs==23.2.0 ; python_full_version >= '3.12'
# via
# outcome
# trio
cffi==1.16.0 ; python_version > '3.11' and implementation_name != 'pypy' and os_name == 'nt'
cffi==1.16.0 ; python_full_version >= '3.12' and implementation_name != 'pypy' and os_name == 'nt'
# via trio
idna==3.6 ; python_version > '3.11'
idna==3.6 ; python_full_version >= '3.12'
# via trio
outcome==1.3.0.post0 ; python_version > '3.11'
outcome==1.3.0.post0 ; python_full_version >= '3.12'
# via trio
pycparser==2.21 ; python_version > '3.11' and implementation_name != 'pypy' and os_name == 'nt'
pycparser==2.21 ; python_full_version >= '3.12' and implementation_name != 'pypy' and os_name == 'nt'
# via cffi
sniffio==1.3.1 ; python_version > '3.11'
sniffio==1.3.1 ; python_full_version >= '3.12'
# via trio
sortedcontainers==2.4.0 ; python_version > '3.11'
sortedcontainers==2.4.0 ; python_full_version >= '3.12'
# via trio
trio==0.25.0 ; python_version > '3.11'
trio==0.25.0 ; python_full_version >= '3.12'
# via -r requirements.in
----- stderr -----
@ -6816,9 +6816,9 @@ fn universal_multi_version() -> Result<()> {
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] requirements.in -c constraints.txt --universal
iniconfig==1.0.0 ; python_version == '3.12'
iniconfig==1.0.0 ; python_full_version < '3.13'
# via -r requirements.in
iniconfig==2.0.0 ; python_version > '3.12'
iniconfig==2.0.0 ; python_full_version >= '3.13'
# via -r requirements.in
----- stderr -----
@ -7222,7 +7222,7 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> {
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] requirements.in --universal
cmake==3.28.4 ; python_version >= '3.11' and python_version <= '3.12' and platform_machine == 'x86_64' and platform_system == 'Linux'
cmake==3.28.4 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux'
# via triton
.
# via -r requirements.in
@ -7232,7 +7232,7 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> {
# triton
jinja2==3.1.3
# via torch
lit==18.1.2 ; python_version >= '3.11' and python_version <= '3.12' and platform_machine == 'x86_64' and platform_system == 'Linux'
lit==18.1.2 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux'
# via triton
markupsafe==2.1.5
# via jinja2
@ -7242,20 +7242,20 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> {
# via torch
sympy==1.12
# via torch
torch==2.0.0 ; python_version < '3.11'
torch==2.0.0 ; python_full_version < '3.11'
# via
# -r requirements.in
# example
torch==2.0.0+cpu ; python_version > '3.12'
torch==2.0.0+cpu ; python_full_version >= '3.13'
# via
# -r requirements.in
# example
torch==2.0.0+cu118 ; python_version >= '3.11' and python_version <= '3.12'
torch==2.0.0+cu118 ; python_full_version >= '3.11' and python_full_version < '3.13'
# via
# -r requirements.in
# example
# triton
triton==2.0.0 ; python_version >= '3.11' and python_version <= '3.12' and platform_machine == 'x86_64' and platform_system == 'Linux'
triton==2.0.0 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux'
# via torch
typing-extensions==4.10.0
# via torch
@ -7954,9 +7954,9 @@ fn universal_requires_python() -> Result<()> {
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] requirements.in -p 3.8 --universal
numpy==1.24.4 ; python_version < '3.9'
numpy==1.24.4 ; python_full_version < '3.9'
# via -r requirements.in
numpy==1.26.4 ; python_version >= '3.9'
numpy==1.26.4 ; python_full_version >= '3.9'
# via -r requirements.in
----- stderr -----
@ -7993,35 +7993,35 @@ fn universal_requires_python_incomplete() -> Result<()> {
----- stderr -----
warning: The requested Python version 3.7 is not available; 3.12.[X] will be used to build dependencies instead.
× No solution found when resolving dependencies:
Because only the following versions of uv{python_version >= '3.8'} are available:
uv{python_version >= '3.8'}==0.0.5
uv{python_version >= '3.8'}==0.1.0
uv{python_version >= '3.8'}==0.1.1
uv{python_version >= '3.8'}==0.1.2
uv{python_version >= '3.8'}==0.1.3
uv{python_version >= '3.8'}==0.1.4
uv{python_version >= '3.8'}==0.1.5
uv{python_version >= '3.8'}==0.1.6
uv{python_version >= '3.8'}==0.1.7
uv{python_version >= '3.8'}==0.1.8
uv{python_version >= '3.8'}==0.1.9
uv{python_version >= '3.8'}==0.1.10
uv{python_version >= '3.8'}==0.1.11
uv{python_version >= '3.8'}==0.1.12
uv{python_version >= '3.8'}==0.1.13
uv{python_version >= '3.8'}==0.1.14
uv{python_version >= '3.8'}==0.1.15
uv{python_version >= '3.8'}==0.1.16
uv{python_version >= '3.8'}==0.1.17
uv{python_version >= '3.8'}==0.1.18
uv{python_version >= '3.8'}==0.1.19
uv{python_version >= '3.8'}==0.1.20
uv{python_version >= '3.8'}==0.1.21
uv{python_version >= '3.8'}==0.1.22
uv{python_version >= '3.8'}==0.1.23
uv{python_version >= '3.8'}==0.1.24
and the requested Python version (>=3.7) does not satisfy Python>=3.8, we can conclude that all versions of uv{python_version >= '3.8'} are incompatible.
And because you require uv{python_version >= '3.8'}, we can conclude that your requirements are unsatisfiable.
Because only the following versions of uv{python_full_version >= '3.8'} are available:
uv{python_full_version >= '3.8'}==0.0.5
uv{python_full_version >= '3.8'}==0.1.0
uv{python_full_version >= '3.8'}==0.1.1
uv{python_full_version >= '3.8'}==0.1.2
uv{python_full_version >= '3.8'}==0.1.3
uv{python_full_version >= '3.8'}==0.1.4
uv{python_full_version >= '3.8'}==0.1.5
uv{python_full_version >= '3.8'}==0.1.6
uv{python_full_version >= '3.8'}==0.1.7
uv{python_full_version >= '3.8'}==0.1.8
uv{python_full_version >= '3.8'}==0.1.9
uv{python_full_version >= '3.8'}==0.1.10
uv{python_full_version >= '3.8'}==0.1.11
uv{python_full_version >= '3.8'}==0.1.12
uv{python_full_version >= '3.8'}==0.1.13
uv{python_full_version >= '3.8'}==0.1.14
uv{python_full_version >= '3.8'}==0.1.15
uv{python_full_version >= '3.8'}==0.1.16
uv{python_full_version >= '3.8'}==0.1.17
uv{python_full_version >= '3.8'}==0.1.18
uv{python_full_version >= '3.8'}==0.1.19
uv{python_full_version >= '3.8'}==0.1.20
uv{python_full_version >= '3.8'}==0.1.21
uv{python_full_version >= '3.8'}==0.1.22
uv{python_full_version >= '3.8'}==0.1.23
uv{python_full_version >= '3.8'}==0.1.24
and the requested Python version (>=3.7) does not satisfy Python>=3.8, we can conclude that all versions of uv{python_full_version >= '3.8'} are incompatible.
And because you require uv{python_full_version >= '3.8'}, we can conclude that your requirements are unsatisfiable.
"###
);
@ -8075,7 +8075,7 @@ fn universal_no_repeated_unconditional_distributions_1() -> Result<()> {
# via requests
imagesize==1.4.1
# via sphinx
importlib-metadata==7.1.0 ; python_version < '3.10'
importlib-metadata==7.1.0 ; python_full_version < '3.10'
# via sphinx
isort==5.13.2
# via pylint
@ -8093,7 +8093,7 @@ fn universal_no_repeated_unconditional_distributions_1() -> Result<()> {
# via sphinx
pylint==3.1.0
# via -r requirements.in
pytz==2024.1 ; python_version < '3.9'
pytz==2024.1 ; python_full_version < '3.9'
# via babel
requests==2.31.0
# via sphinx
@ -8113,17 +8113,17 @@ fn universal_no_repeated_unconditional_distributions_1() -> Result<()> {
# via sphinx
sphinxcontrib-serializinghtml==1.1.5
# via sphinx
tomli==2.0.1 ; python_version < '3.11'
tomli==2.0.1 ; python_full_version < '3.11'
# via pylint
tomlkit==0.12.4
# via pylint
typing-extensions==4.10.0 ; python_version < '3.11'
typing-extensions==4.10.0 ; python_full_version < '3.11'
# via
# astroid
# pylint
urllib3==2.2.1
# via requests
zipp==3.18.1 ; python_version < '3.10'
zipp==3.18.1 ; python_full_version < '3.10'
# via importlib-metadata
----- stderr -----
@ -8226,11 +8226,11 @@ fn universal_prefer_upper_bounds() -> Result<()> {
# via pylint
pylint==2.17.7
# via -r requirements.in
tomli==2.0.1 ; python_version < '3.11'
tomli==2.0.1 ; python_full_version < '3.11'
# via pylint
tomlkit==0.12.4
# via pylint
typing-extensions==4.10.0 ; python_version < '3.11'
typing-extensions==4.10.0 ; python_full_version < '3.11'
# via
# astroid
# pylint
@ -10030,7 +10030,7 @@ fn emit_marker_expression_exciting_linux() -> Result<()> {
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] requirements.in --emit-marker-expression
# Pinned dependencies known to be valid for:
# python_version == '3.12' and platform_python_implementation == 'CPython' and platform_system == 'Linux'
# python_full_version == '3.12.1' and platform_python_implementation == 'CPython' and platform_system == 'Linux'
anyio==4.3.0
# via -r requirements.in
idna==3.6
@ -10067,7 +10067,7 @@ fn emit_marker_expression_direct() -> Result<()> {
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] requirements.in --emit-marker-expression
# Pinned dependencies known to be valid for:
# python_version == '3.12' and platform_python_implementation == 'CPython' and platform_system == 'Linux' and sys_platform == 'linux'
# python_full_version == '3.12.1' and platform_python_implementation == 'CPython' and platform_system == 'Linux' and sys_platform == 'linux'
anyio==4.3.0
# via -r requirements.in
idna==3.6
@ -10147,7 +10147,7 @@ fn emit_marker_expression_pypy() -> Result<()> {
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] requirements.in --emit-marker-expression
# Pinned dependencies known to be valid for:
# python_version == '3.12' and implementation_name == 'cpython'
# python_full_version == '3.12.1' and implementation_name == 'cpython'
pendulum==3.0.0
# via -r requirements.in
python-dateutil==2.9.0.post0

View file

@ -28,7 +28,7 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohappyeyeballs" },
{ name = "aiosignal" },
{ name = "async-timeout", marker = "python_version < '3.11'" },
{ name = "async-timeout", marker = "python_full_version < '3.11'" },
{ name = "attrs" },
{ name = "frozenlist" },
{ name = "multidict" },
@ -183,8 +183,8 @@ dependencies = [
{ name = "packaging" },
{ name = "pathspec" },
{ name = "platformdirs" },
{ name = "tomli", marker = "python_version < '3.11'" },
{ name = "typing-extensions", marker = "python_version < '3.11'" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
[package.optional-dependencies]
@ -214,8 +214,8 @@ requires-dist = [
{ name = "pathspec", specifier = ">=0.9.0" },
{ name = "platformdirs", specifier = ">=2" },
{ name = "tokenize-rt", marker = "extra == 'jupyter'", specifier = ">=3.2.0" },
{ name = "tomli", marker = "python_version < '3.11'", specifier = ">=1.1.0" },
{ name = "typing-extensions", marker = "python_version < '3.11'", specifier = ">=4.0.1" },
{ name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=1.1.0" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'", specifier = ">=4.0.1" },
{ name = "uvloop", marker = "extra == 'uvloop'", specifier = ">=0.15.2" },
]
@ -368,7 +368,7 @@ dependencies = [
{ name = "pygments" },
{ name = "stack-data" },
{ name = "traitlets" },
{ name = "typing-extensions", marker = "python_version < '3.10'" },
{ name = "typing-extensions", marker = "python_full_version < '3.10'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9e/6a/44ef299b1762f5a73841e87fae8a73a8cc8aee538d6dc8c77a5afe1fd2ce/ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363", size = 5470171 }
wheels = [

View file

@ -5,8 +5,8 @@ expression: lock
version = 1
requires-python = ">=3.12"
environment-markers = [
"python_version < '3.13'",
"python_version >= '3.13'",
"python_full_version < '3.13'",
"python_full_version >= '3.13'",
]
[options]

View file

@ -1196,7 +1196,7 @@ name = "sqlalchemy"
version = "2.0.31"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "greenlet", marker = "(python_version < '3.13' and platform_machine == 'AMD64') or (python_version < '3.13' and platform_machine == 'WIN32') or (python_version < '3.13' and platform_machine == 'aarch64') or (python_version < '3.13' and platform_machine == 'amd64') or (python_version < '3.13' and platform_machine == 'ppc64le') or (python_version < '3.13' and platform_machine == 'win32') or (python_version < '3.13' and platform_machine == 'x86_64')" },
{ name = "greenlet", marker = "(python_full_version < '3.13' and platform_machine == 'AMD64') or (python_full_version < '3.13' and platform_machine == 'WIN32') or (python_full_version < '3.13' and platform_machine == 'aarch64') or (python_full_version < '3.13' and platform_machine == 'amd64') or (python_full_version < '3.13' and platform_machine == 'ppc64le') or (python_full_version < '3.13' and platform_machine == 'win32') or (python_full_version < '3.13' and platform_machine == 'x86_64')" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ba/7d/e3312ae374fe7a4af7e1494735125a714a0907317c829ab8d8a31d89ded4/SQLAlchemy-2.0.31.tar.gz", hash = "sha256:b607489dd4a54de56984a0c7656247504bd5523d9d0ba799aef59d4add009484", size = 9524110 }

View file

@ -3,11 +3,12 @@ source: crates/uv/tests/ecosystem.rs
expression: lock
---
version = 1
requires-python = ">=3.11, <3.12"
requires-python = "==3.11.*"
environment-markers = [
"python_version < '3.12'",
"python_version < '3.13'",
"python_version >= '3.13'",
"python_full_version < '3.12'",
"python_full_version < '3.13'",
"python_full_version >= '3.13' and python_full_version < '4'",
"python_full_version >= '4'",
]
[options]
@ -194,7 +195,7 @@ dependencies = [
{ name = "annotated-types" },
{ name = "logfury" },
{ name = "requests" },
{ name = "typing-extensions", marker = "python_version < '3.12'" },
{ name = "typing-extensions", marker = "python_full_version < '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ce/53/2b4ac6ef318444bde570c36219ff0c8cd2207fb2fd8183d88fdf4f0445ab/b2sdk-2.5.0.tar.gz", hash = "sha256:d7c20125e64508a730e56307d75284790079cdb88e63851fff820a09b24fb1d9", size = 203212 }
wheels = [
@ -321,7 +322,7 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "botocore-stubs" },
{ name = "types-s3transfer" },
{ name = "typing-extensions", marker = "python_version < '3.12'" },
{ name = "typing-extensions", marker = "python_full_version < '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/6e/d6/da4a554a6436944cb47cfb84754e8a2e37b04e3ff6dfea3c4958e2eb93ed/boto3_stubs-1.34.156.tar.gz", hash = "sha256:c34220a00b6bd00a3cd326bc5d7e3296b7d1dab1a15660df6f8fba4ae307ff39", size = 88866 }
wheels = [
@ -3143,7 +3144,7 @@ name = "redis"
version = "5.0.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "async-timeout", marker = "python_full_version < '3.11.[X]' and python_version < '3.12'" },
{ name = "async-timeout", marker = "python_full_version < '3.11.[X]'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/48/10/defc227d65ea9c2ff5244645870859865cba34da7373477c8376629746ec/redis-5.0.8.tar.gz", hash = "sha256:0c5b10d387568dfe0698c6fad6615750c24170e548ca2deac10c649d463e9870", size = 4595651 }
wheels = [
@ -3643,7 +3644,7 @@ name = "sqlalchemy"
version = "2.0.32"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "greenlet", marker = "(python_version < '3.13' and platform_machine == 'AMD64') or (python_version < '3.13' and platform_machine == 'WIN32') or (python_version < '3.13' and platform_machine == 'aarch64') or (python_version < '3.13' and platform_machine == 'amd64') or (python_version < '3.13' and platform_machine == 'ppc64le') or (python_version < '3.13' and platform_machine == 'win32') or (python_version < '3.13' and platform_machine == 'x86_64')" },
{ name = "greenlet", marker = "(python_full_version < '3.13' and platform_machine == 'AMD64') or (python_full_version < '3.13' and platform_machine == 'WIN32') or (python_full_version < '3.13' and platform_machine == 'aarch64') or (python_full_version < '3.13' and platform_machine == 'amd64') or (python_full_version < '3.13' and platform_machine == 'ppc64le') or (python_full_version < '3.13' and platform_machine == 'win32') or (python_full_version < '3.13' and platform_machine == 'x86_64')" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/af/6f/967e987683908af816aa3072c1a6997ac9933cf38d66b0474fb03f253323/SQLAlchemy-2.0.32.tar.gz", hash = "sha256:c1b88cc8b02b6a5f0efb0345a03672d4c897dc7d92585176f88c67346f565ea8", size = 9546691 }