Skip to content

Commit 7580a50

Browse files
committed
Move into algebra
1 parent 801b71c commit 7580a50

13 files changed

+482
-354
lines changed

crates/uv-pep508/src/marker/algebra.rs

+333-12
Large diffs are not rendered by default.

crates/uv-pep508/src/marker/lowering.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,23 @@ impl From<CanonicalMarkerValueVersion> for MarkerValueVersion {
3535
/// Those environment markers with an arbitrary string as value such as `sys_platform`
3636
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
3737
pub enum CanonicalMarkerValueString {
38-
/// `implementation_name`
39-
ImplementationName,
4038
/// `os_name`
4139
OsName,
40+
/// `sys_platform`
41+
SysPlatform,
42+
/// `platform_system`
43+
PlatformSystem,
4244
/// `platform_machine`
4345
PlatformMachine,
4446
/// Deprecated `platform.machine` from <https://peps.python.org/pep-0345/#environment-markers>
4547
/// `platform_python_implementation`
4648
PlatformPythonImplementation,
4749
/// `platform_release`
4850
PlatformRelease,
49-
/// `platform_system`
50-
PlatformSystem,
5151
/// `platform_version`
5252
PlatformVersion,
53-
/// `sys_platform`
54-
SysPlatform,
53+
/// `implementation_name`
54+
ImplementationName,
5555
}
5656

5757
impl From<MarkerValueString> for CanonicalMarkerValueString {

crates/uv-pep508/src/marker/tree.rs

+11-204
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use std::cmp::Ordering;
22
use std::fmt::{self, Display, Formatter};
33
use std::ops::{Bound, Deref};
44
use std::str::FromStr;
5-
use std::sync::LazyLock;
65

76
use itertools::Itertools;
87
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
@@ -697,14 +696,12 @@ impl MarkerTree {
697696
#[allow(clippy::needless_pass_by_value)]
698697
pub fn and(&mut self, tree: MarkerTree) {
699698
self.0 = INTERNER.lock().and(self.0, tree.0);
700-
self.simplify();
701699
}
702700

703701
/// Combine this marker tree with the one given via a disjunction.
704702
#[allow(clippy::needless_pass_by_value)]
705703
pub fn or(&mut self, tree: MarkerTree) {
706704
self.0 = INTERNER.lock().or(self.0, tree.0);
707-
self.simplify();
708705
}
709706

710707
/// Sets this to a marker equivalent to the implication of this one and the
@@ -720,23 +717,6 @@ impl MarkerTree {
720717
self.or(consequent);
721718
}
722719

723-
/// Simplify the marker, namely, by detecting known impossible conditions.
724-
///
725-
/// For example, while the marker specification and grammar do not _forbid_ it, we know that
726-
/// both `sys_platform == 'win32'` and `platform_system == 'Darwin'` will never true at the
727-
/// same time.
728-
///
729-
/// This method thus encodes assumptions about the environment that are not guaranteed by the
730-
/// PEP 508 specification alone.
731-
fn simplify(&mut self) {
732-
if !self.0.is_false() {
733-
let mutex = &*MUTUAL_EXCLUSIONS;
734-
if INTERNER.lock().is_disjoint(self.0, mutex.0) {
735-
*self = MarkerTree::FALSE;
736-
}
737-
}
738-
}
739-
740720
/// Returns `true` if there is no environment in which both marker trees can apply,
741721
/// i.e. their conjunction is always `false`.
742722
///
@@ -745,9 +725,7 @@ impl MarkerTree {
745725
/// false negatives, i.e. it may not be able to detect that two markers are disjoint for
746726
/// complex expressions.
747727
pub fn is_disjoint(&self, other: &MarkerTree) -> bool {
748-
let mutex = &*MUTUAL_EXCLUSIONS;
749-
let node = INTERNER.lock().and(self.0, other.0);
750-
node.is_false() || INTERNER.lock().is_disjoint(node, mutex.0)
728+
INTERNER.lock().is_disjoint(self.0, other.0)
751729
}
752730

753731
/// Returns the contents of this marker tree, if it contains at least one expression.
@@ -1595,175 +1573,6 @@ impl schemars::JsonSchema for MarkerTree {
15951573
}
15961574
}
15971575

1598-
static MUTUAL_EXCLUSIONS: LazyLock<MarkerTree> = LazyLock::new(|| {
1599-
let mut tree = NodeId::FALSE;
1600-
for (a, b) in [
1601-
// sys_platform == 'darwin' and platform_system == 'Windows'
1602-
(
1603-
MarkerExpression::String {
1604-
key: MarkerValueString::SysPlatform,
1605-
operator: MarkerOperator::Equal,
1606-
value: "darwin".to_string(),
1607-
},
1608-
MarkerExpression::String {
1609-
key: MarkerValueString::PlatformSystem,
1610-
operator: MarkerOperator::Equal,
1611-
value: "Windows".to_string(),
1612-
},
1613-
),
1614-
// sys_platform == 'darwin' and platform_system == 'Linux'
1615-
(
1616-
MarkerExpression::String {
1617-
key: MarkerValueString::SysPlatform,
1618-
operator: MarkerOperator::Equal,
1619-
value: "darwin".to_string(),
1620-
},
1621-
MarkerExpression::String {
1622-
key: MarkerValueString::PlatformSystem,
1623-
operator: MarkerOperator::Equal,
1624-
value: "Linux".to_string(),
1625-
},
1626-
),
1627-
// sys_platform == 'win32' and platform_system == 'Darwin'
1628-
(
1629-
MarkerExpression::String {
1630-
key: MarkerValueString::SysPlatform,
1631-
operator: MarkerOperator::Equal,
1632-
value: "win32".to_string(),
1633-
},
1634-
MarkerExpression::String {
1635-
key: MarkerValueString::PlatformSystem,
1636-
operator: MarkerOperator::Equal,
1637-
value: "Darwin".to_string(),
1638-
},
1639-
),
1640-
// sys_platform == 'win32' and platform_system == 'Linux'
1641-
(
1642-
MarkerExpression::String {
1643-
key: MarkerValueString::SysPlatform,
1644-
operator: MarkerOperator::Equal,
1645-
value: "win32".to_string(),
1646-
},
1647-
MarkerExpression::String {
1648-
key: MarkerValueString::PlatformSystem,
1649-
operator: MarkerOperator::Equal,
1650-
value: "Linux".to_string(),
1651-
},
1652-
),
1653-
// sys_platform == 'linux' and platform_system == 'Darwin'
1654-
(
1655-
MarkerExpression::String {
1656-
key: MarkerValueString::SysPlatform,
1657-
operator: MarkerOperator::Equal,
1658-
value: "linux".to_string(),
1659-
},
1660-
MarkerExpression::String {
1661-
key: MarkerValueString::PlatformSystem,
1662-
operator: MarkerOperator::Equal,
1663-
value: "Darwin".to_string(),
1664-
},
1665-
),
1666-
// sys_platform == 'linux' and platform_system == 'Windows'
1667-
(
1668-
MarkerExpression::String {
1669-
key: MarkerValueString::SysPlatform,
1670-
operator: MarkerOperator::Equal,
1671-
value: "linux".to_string(),
1672-
},
1673-
MarkerExpression::String {
1674-
key: MarkerValueString::PlatformSystem,
1675-
operator: MarkerOperator::Equal,
1676-
value: "Windows".to_string(),
1677-
},
1678-
),
1679-
// os_name == 'nt' and sys_platform == 'darwin'
1680-
(
1681-
MarkerExpression::String {
1682-
key: MarkerValueString::OsName,
1683-
operator: MarkerOperator::Equal,
1684-
value: "nt".to_string(),
1685-
},
1686-
MarkerExpression::String {
1687-
key: MarkerValueString::SysPlatform,
1688-
operator: MarkerOperator::Equal,
1689-
value: "darwin".to_string(),
1690-
},
1691-
),
1692-
// os_name == 'nt' and sys_platform == 'linux'
1693-
(
1694-
MarkerExpression::String {
1695-
key: MarkerValueString::OsName,
1696-
operator: MarkerOperator::Equal,
1697-
value: "nt".to_string(),
1698-
},
1699-
MarkerExpression::String {
1700-
key: MarkerValueString::SysPlatform,
1701-
operator: MarkerOperator::Equal,
1702-
value: "linux".to_string(),
1703-
},
1704-
),
1705-
// os_name == 'posix' and sys_platform == 'win32'
1706-
(
1707-
MarkerExpression::String {
1708-
key: MarkerValueString::OsName,
1709-
operator: MarkerOperator::Equal,
1710-
value: "posix".to_string(),
1711-
},
1712-
MarkerExpression::String {
1713-
key: MarkerValueString::SysPlatform,
1714-
operator: MarkerOperator::Equal,
1715-
value: "win32".to_string(),
1716-
},
1717-
),
1718-
// os_name == 'nt' and platform_system == 'Darwin'
1719-
(
1720-
MarkerExpression::String {
1721-
key: MarkerValueString::OsName,
1722-
operator: MarkerOperator::Equal,
1723-
value: "nt".to_string(),
1724-
},
1725-
MarkerExpression::String {
1726-
key: MarkerValueString::PlatformSystem,
1727-
operator: MarkerOperator::Equal,
1728-
value: "Darwin".to_string(),
1729-
},
1730-
),
1731-
// os_name == 'nt' and platform_system == 'Linux'
1732-
(
1733-
MarkerExpression::String {
1734-
key: MarkerValueString::OsName,
1735-
operator: MarkerOperator::Equal,
1736-
value: "nt".to_string(),
1737-
},
1738-
MarkerExpression::String {
1739-
key: MarkerValueString::PlatformSystem,
1740-
operator: MarkerOperator::Equal,
1741-
value: "Linux".to_string(),
1742-
},
1743-
),
1744-
// os_name == 'posix' and platform_system == 'Windows'
1745-
(
1746-
MarkerExpression::String {
1747-
key: MarkerValueString::OsName,
1748-
operator: MarkerOperator::Equal,
1749-
value: "posix".to_string(),
1750-
},
1751-
MarkerExpression::String {
1752-
key: MarkerValueString::PlatformSystem,
1753-
operator: MarkerOperator::Equal,
1754-
value: "Windows".to_string(),
1755-
},
1756-
),
1757-
] {
1758-
let mut interner = INTERNER.lock();
1759-
let a = interner.expression(a);
1760-
let b = interner.expression(b);
1761-
let a_and_b = interner.and(a, b);
1762-
tree = interner.or(tree, a_and_b);
1763-
}
1764-
MarkerTree(tree).negate()
1765-
});
1766-
17671576
#[cfg(test)]
17681577
mod test {
17691578
use std::ops::Bound;
@@ -2661,13 +2470,13 @@ mod test {
26612470
or (implementation_name != 'pypy' and sys_platform == 'win32')
26622471
or (sys_platform == 'win32' and os_name != 'nt')
26632472
or (sys_platform != 'win32' and os_name == 'nt')",
2664-
"(os_name != 'nt' and sys_platform == 'win32') \
2665-
or (implementation_name != 'pypy' and os_name == 'nt') \
2666-
or (implementation_name == 'pypy' and os_name != 'nt') \
2667-
or (os_name == 'nt' and sys_platform != 'win32')",
2473+
"(sys_platform != 'win32' and implementation_name == 'pypy') \
2474+
or (os_name != 'nt' and sys_platform == 'win32') \
2475+
or (os_name == 'nt' and sys_platform != 'win32') \
2476+
or (sys_platform == 'win32' and implementation_name != 'pypy')",
26682477
);
26692478

2670-
// This is another case we cannot simplify fully, depending on the variable order.
2479+
// This is a case we can simplify fully, but it's dependent on the variable order.
26712480
// The expression is equivalent to `sys_platform == 'x' or (os_name == 'Linux' and platform_system == 'win32')`.
26722481
assert_simplifies(
26732482
"(os_name == 'Linux' and platform_system == 'win32')
@@ -2676,14 +2485,14 @@ mod test {
26762485
or (os_name != 'Linux' and platform_system == 'win32' and sys_platform == 'x')
26772486
or (os_name == 'Linux' and platform_system != 'win32' and sys_platform == 'x')
26782487
or (os_name != 'Linux' and platform_system != 'win32' and sys_platform == 'x')",
2679-
"(os_name != 'Linux' and sys_platform == 'x') or (platform_system != 'win32' and sys_platform == 'x') or (os_name == 'Linux' and platform_system == 'win32')",
2488+
"(os_name == 'Linux' and platform_system == 'win32') or sys_platform == 'x'",
26802489
);
26812490

26822491
assert_simplifies("python_version > '3.7'", "python_full_version >= '3.8'");
26832492

26842493
assert_simplifies(
26852494
"(python_version <= '3.7' and os_name == 'Linux') or python_version > '3.7'",
2686-
"os_name == 'Linux' or python_full_version >= '3.8'",
2495+
"python_full_version >= '3.8' or os_name == 'Linux'",
26872496
);
26882497

26892498
// Again, the extra `<3.7` and `>=3.9` expressions cannot be seen as redundant due to them being interdependent.
@@ -2692,9 +2501,7 @@ mod test {
26922501
"(os_name == 'Linux' and sys_platform == 'win32') \
26932502
or (os_name != 'Linux' and sys_platform == 'win32' and python_version == '3.7') \
26942503
or (os_name != 'Linux' and sys_platform == 'win32' and python_version == '3.8')",
2695-
"(python_full_version < '3.7' and os_name == 'Linux' and sys_platform == 'win32') \
2696-
or (python_full_version >= '3.9' and os_name == 'Linux' and sys_platform == 'win32') \
2697-
or (python_full_version >= '3.7' and python_full_version < '3.9' and sys_platform == 'win32')",
2504+
"(sys_platform == 'win32' and python_full_version >= '3.7' and python_full_version < '3.9') or (os_name == 'Linux' and sys_platform == 'win32')",
26982505
);
26992506

27002507
assert_simplifies(
@@ -2716,8 +2523,8 @@ mod test {
27162523
assert_simplifies(
27172524
"(os_name == 'nt' and sys_platform == 'win32') \
27182525
or (os_name != 'nt' and platform_version == '1' and (sys_platform == 'win32' or sys_platform == 'win64'))",
2719-
"(platform_version == '1' and sys_platform == 'win32') \
2720-
or (os_name != 'nt' and platform_version == '1' and sys_platform == 'win64') \
2526+
"(sys_platform == 'win32' and platform_version == '1') \
2527+
or (os_name != 'nt' and sys_platform == 'win64' and platform_version == '1') \
27212528
or (os_name == 'nt' and sys_platform == 'win32')",
27222529
);
27232530

crates/uv-resolver/src/resolution/output.rs

+1
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ impl ResolverOutput {
151151
// Insert each node only once.
152152
continue;
153153
}
154+
154155
Self::add_edge(&mut graph, &mut inverse, root_index, edge, marker.clone());
155156
}
156157
}

crates/uv/tests/it/export.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -439,36 +439,36 @@ fn dependency_multiple_markers() -> Result<()> {
439439
# This file was autogenerated by uv via the following command:
440440
# uv export --cache-dir [CACHE_DIR]
441441
-e .
442-
attrs==23.2.0 ; sys_platform == 'win32' or python_full_version >= '3.12' \
442+
attrs==23.2.0 ; python_full_version >= '3.12' or sys_platform == 'win32' \
443443
--hash=sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30 \
444444
--hash=sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1
445-
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') \
445+
cffi==1.16.0 ; (os_name == 'nt' and implementation_name != 'pypy' and python_full_version >= '3.12') or (os_name == 'nt' and sys_platform == 'win32' and implementation_name != 'pypy') \
446446
--hash=sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab \
447447
--hash=sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235 \
448448
--hash=sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a \
449449
--hash=sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b \
450450
--hash=sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0 \
451451
--hash=sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba \
452452
--hash=sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1
453-
exceptiongroup==1.2.0 ; python_full_version < '3.11' and sys_platform == 'win32' \
453+
exceptiongroup==1.2.0 ; sys_platform == 'win32' and python_full_version < '3.11' \
454454
--hash=sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14 \
455455
--hash=sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68
456-
idna==3.6 ; sys_platform == 'win32' or python_full_version >= '3.12' \
456+
idna==3.6 ; python_full_version >= '3.12' or sys_platform == 'win32' \
457457
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
458458
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
459-
outcome==1.3.0.post0 ; sys_platform == 'win32' or python_full_version >= '3.12' \
459+
outcome==1.3.0.post0 ; python_full_version >= '3.12' or sys_platform == 'win32' \
460460
--hash=sha256:9dcf02e65f2971b80047b377468e72a268e15c0af3cf1238e6ff14f7f91143b8 \
461461
--hash=sha256:e771c5ce06d1415e356078d3bdd68523f284b4ce5419828922b6871e65eda82b
462-
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') \
462+
pycparser==2.21 ; (os_name == 'nt' and implementation_name != 'pypy' and python_full_version >= '3.12') or (os_name == 'nt' and sys_platform == 'win32' and implementation_name != 'pypy') \
463463
--hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \
464464
--hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206
465-
sniffio==1.3.1 ; sys_platform == 'win32' or python_full_version >= '3.12' \
465+
sniffio==1.3.1 ; python_full_version >= '3.12' or sys_platform == 'win32' \
466466
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
467467
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
468-
sortedcontainers==2.4.0 ; sys_platform == 'win32' or python_full_version >= '3.12' \
468+
sortedcontainers==2.4.0 ; python_full_version >= '3.12' or sys_platform == 'win32' \
469469
--hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \
470470
--hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0
471-
trio==0.25.0 ; sys_platform == 'win32' or python_full_version >= '3.12' \
471+
trio==0.25.0 ; python_full_version >= '3.12' or sys_platform == 'win32' \
472472
--hash=sha256:9b41f5993ad2c0e5f62d0acca320ec657fdb6b2a2c22b8c7aed6caf154475c4e \
473473
--hash=sha256:e6458efe29cc543e557a91e614e2b51710eba2961669329ce9c862d50c6e8e81
474474

0 commit comments

Comments
 (0)