mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-01 12:24:15 +00:00
Respect the user's upper-bound in requires-python (#6824)
## Summary We now respect the user-provided upper-bound in for `requires-python`. So, if the user has `requires-python = "==3.11.*"`, we won't explore forks that have `python_version >= '3.12'`, for example. However, we continue to _only_ compare the lower bounds when assessing whether a dependency is compatible with a given Python range. Closes https://github.com/astral-sh/uv/issues/6150.
This commit is contained in:
parent
a17c1e8e40
commit
34d74501ac
10 changed files with 308 additions and 77 deletions
|
|
@ -17,9 +17,15 @@ pub enum PubGrubSpecifierError {
|
|||
pub struct PubGrubSpecifier(Range<Version>);
|
||||
|
||||
impl PubGrubSpecifier {
|
||||
/// Returns an iterator over the bounds of the [`PubGrubSpecifier`].
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Bound<Version>, &Bound<Version>)> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Return the bounding [`Range`] of the [`PubGrubSpecifier`].
|
||||
pub fn bounding_range(&self) -> Option<(Bound<&Version>, Bound<&Version>)> {
|
||||
self.0.bounding_range()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Range<Version>> for PubGrubSpecifier {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ pub use preferences::{Preference, PreferenceError, Preferences};
|
|||
pub use prerelease::PrereleaseMode;
|
||||
pub use pubgrub::{PubGrubSpecifier, PubGrubSpecifierError};
|
||||
pub use python_requirement::PythonRequirement;
|
||||
pub use requires_python::{RequiresPython, RequiresPythonBound, RequiresPythonError};
|
||||
pub use requires_python::{
|
||||
RequiresPython, RequiresPythonBound, RequiresPythonError, RequiresPythonRange,
|
||||
};
|
||||
pub use resolution::{AnnotationStyle, DisplayResolutionGraph, ResolutionGraph};
|
||||
pub use resolution_mode::ResolutionMode;
|
||||
pub use resolver::{
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use pep508_rs::{MarkerTree, MarkerTreeKind, MarkerValueVersion};
|
||||
|
||||
use crate::requires_python::RequiresPythonRange;
|
||||
use crate::RequiresPythonBound;
|
||||
use pep440_rs::Version;
|
||||
use pep508_rs::{MarkerTree, MarkerTreeKind, MarkerValueVersion};
|
||||
use pubgrub::Range;
|
||||
|
||||
/// Returns the minimum Python version that can satisfy the [`MarkerTree`], if it's constrained.
|
||||
pub(crate) fn requires_python(tree: &MarkerTree) -> Option<RequiresPythonBound> {
|
||||
fn collect_python_markers(tree: &MarkerTree, markers: &mut Vec<RequiresPythonBound>) {
|
||||
/// Returns the bounding Python versions that can satisfy the [`MarkerTree`], if it's constrained.
|
||||
pub(crate) fn requires_python(tree: &MarkerTree) -> Option<RequiresPythonRange> {
|
||||
fn collect_python_markers(tree: &MarkerTree, markers: &mut Vec<Range<Version>>) {
|
||||
match tree.kind() {
|
||||
MarkerTreeKind::True | MarkerTreeKind::False => {}
|
||||
MarkerTreeKind::Version(marker) => match marker.key() {
|
||||
MarkerValueVersion::PythonVersion | MarkerValueVersion::PythonFullVersion => {
|
||||
for (range, tree) in marker.edges() {
|
||||
if !tree.is_false() {
|
||||
// Extract the lower bound.
|
||||
let (lower, _) = range.iter().next().unwrap();
|
||||
markers.push(RequiresPythonBound::new(lower.clone()));
|
||||
markers.push(range.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -48,5 +48,21 @@ pub(crate) fn requires_python(tree: &MarkerTree) -> Option<RequiresPythonBound>
|
|||
|
||||
let mut markers = Vec::new();
|
||||
collect_python_markers(tree, &mut markers);
|
||||
markers.into_iter().min()
|
||||
|
||||
// Take the union of all Python version markers.
|
||||
let range = markers
|
||||
.into_iter()
|
||||
.fold(None, |acc: Option<Range<Version>>, range| {
|
||||
Some(match acc {
|
||||
Some(acc) => acc.union(&range),
|
||||
None => range.clone(),
|
||||
})
|
||||
})?;
|
||||
|
||||
let (lower, upper) = range.bounding_range()?;
|
||||
|
||||
Some(RequiresPythonRange::new(
|
||||
RequiresPythonBound::new(lower.cloned()),
|
||||
RequiresPythonBound::new(upper.cloned()),
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use pep440_rs::{Version, VersionSpecifiers};
|
||||
use uv_python::{Interpreter, PythonVersion};
|
||||
|
||||
use crate::{RequiresPython, RequiresPythonBound};
|
||||
use crate::{RequiresPython, RequiresPythonRange};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct PythonRequirement {
|
||||
|
|
@ -49,7 +49,7 @@ impl PythonRequirement {
|
|||
|
||||
/// Narrow the [`PythonRequirement`] to the given version, if it's stricter (i.e., greater)
|
||||
/// than the current `Requires-Python` minimum.
|
||||
pub fn narrow(&self, target: &RequiresPythonBound) -> Option<Self> {
|
||||
pub fn narrow(&self, target: &RequiresPythonRange) -> Option<Self> {
|
||||
let Some(PythonTarget::RequiresPython(requires_python)) = self.target.as_ref() else {
|
||||
return None;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,9 +31,8 @@ pub struct RequiresPython {
|
|||
/// For a workspace, it's the union of all `requires-python` values in the workspace. If no
|
||||
/// bound was provided by the user, it's greater equal the current Python version.
|
||||
specifiers: VersionSpecifiers,
|
||||
/// The lower bound from the `specifiers` field, i.e. greater or greater equal the lowest
|
||||
/// version allowed by `specifiers`.
|
||||
bound: RequiresPythonBound,
|
||||
/// The lower and upper bounds of `specifiers`.
|
||||
range: RequiresPythonRange,
|
||||
}
|
||||
|
||||
impl RequiresPython {
|
||||
|
|
@ -44,22 +43,26 @@ impl RequiresPython {
|
|||
specifiers: VersionSpecifiers::from(VersionSpecifier::greater_than_equal_version(
|
||||
version.clone(),
|
||||
)),
|
||||
bound: RequiresPythonBound(Bound::Included(version)),
|
||||
range: RequiresPythonRange(
|
||||
RequiresPythonBound::new(Bound::Included(version.clone())),
|
||||
RequiresPythonBound::new(Bound::Unbounded),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a [`RequiresPython`] from a version specifier.
|
||||
pub fn from_specifiers(specifiers: &VersionSpecifiers) -> Result<Self, RequiresPythonError> {
|
||||
let bound = RequiresPythonBound(
|
||||
let (lower_bound, upper_bound) =
|
||||
crate::pubgrub::PubGrubSpecifier::from_release_specifiers(specifiers)?
|
||||
.iter()
|
||||
.next()
|
||||
.map(|(lower, _)| lower.clone())
|
||||
.unwrap_or(Bound::Unbounded),
|
||||
);
|
||||
.bounding_range()
|
||||
.map(|(lower_bound, upper_bound)| (lower_bound.cloned(), upper_bound.cloned()))
|
||||
.unwrap_or((Bound::Unbounded, Bound::Unbounded));
|
||||
Ok(Self {
|
||||
specifiers: specifiers.clone(),
|
||||
bound,
|
||||
range: RequiresPythonRange(
|
||||
RequiresPythonBound(lower_bound),
|
||||
RequiresPythonBound(upper_bound),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -85,14 +88,11 @@ impl RequiresPython {
|
|||
return Ok(None);
|
||||
};
|
||||
|
||||
// Extract the lower bound.
|
||||
let bound = RequiresPythonBound(
|
||||
range
|
||||
.iter()
|
||||
.next()
|
||||
.map(|(lower, _)| lower.clone())
|
||||
.unwrap_or(Bound::Unbounded),
|
||||
);
|
||||
// Extract the bounds.
|
||||
let (lower_bound, upper_bound) = range
|
||||
.bounding_range()
|
||||
.map(|(lower_bound, upper_bound)| (lower_bound.cloned(), upper_bound.cloned()))
|
||||
.unwrap_or((Bound::Unbounded, Bound::Unbounded));
|
||||
|
||||
// Convert back to PEP 440 specifiers.
|
||||
let specifiers = range
|
||||
|
|
@ -100,16 +100,24 @@ impl RequiresPython {
|
|||
.flat_map(VersionSpecifier::from_release_only_bounds)
|
||||
.collect();
|
||||
|
||||
Ok(Some(Self { specifiers, bound }))
|
||||
Ok(Some(Self {
|
||||
specifiers,
|
||||
range: RequiresPythonRange(
|
||||
RequiresPythonBound(lower_bound),
|
||||
RequiresPythonBound(upper_bound),
|
||||
),
|
||||
}))
|
||||
}
|
||||
|
||||
/// Narrow the [`RequiresPython`] to the given version, if it's stricter (i.e., greater) than
|
||||
/// the current target.
|
||||
pub fn narrow(&self, target: &RequiresPythonBound) -> Option<Self> {
|
||||
if target > &self.bound {
|
||||
pub fn narrow(&self, range: &RequiresPythonRange) -> Option<Self> {
|
||||
if *range == self.range {
|
||||
None
|
||||
} else if range.0 >= self.range.0 && range.1 <= self.range.1 {
|
||||
Some(Self {
|
||||
specifiers: VersionSpecifiers::from(VersionSpecifier::from_lower_bound(target)?),
|
||||
bound: target.clone(),
|
||||
specifiers: self.specifiers.clone(),
|
||||
range: range.clone(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
|
@ -142,7 +150,7 @@ impl RequiresPython {
|
|||
// `>=3.7`.
|
||||
//
|
||||
// That is: `version_lower` should be less than or equal to `requires_python_lower`.
|
||||
match (target, self.bound.as_ref()) {
|
||||
match (target, self.range.lower().as_ref()) {
|
||||
(Bound::Included(target_lower), Bound::Included(requires_python_lower)) => {
|
||||
target_lower <= requires_python_lower
|
||||
}
|
||||
|
|
@ -170,17 +178,12 @@ impl RequiresPython {
|
|||
|
||||
/// Returns `true` if the `Requires-Python` specifier is unbounded.
|
||||
pub fn is_unbounded(&self) -> bool {
|
||||
self.bound.as_ref() == Bound::Unbounded
|
||||
}
|
||||
|
||||
/// Returns the [`RequiresPythonBound`] for the `Requires-Python` specifier.
|
||||
pub fn bound(&self) -> &RequiresPythonBound {
|
||||
&self.bound
|
||||
self.range.lower().as_ref() == Bound::Unbounded
|
||||
}
|
||||
|
||||
/// Returns the [`RequiresPythonBound`] truncated to the major and minor version.
|
||||
pub fn bound_major_minor(&self) -> RequiresPythonBound {
|
||||
match self.bound.as_ref() {
|
||||
match self.range.lower().as_ref() {
|
||||
// Ex) `>=3.10.1` -> `>=3.10`
|
||||
Bound::Included(version) => RequiresPythonBound(Bound::Included(Version::new(
|
||||
version.release().iter().take(2),
|
||||
|
|
@ -195,6 +198,11 @@ impl RequiresPython {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the [`Range`] bounding the `Requires-Python` specifier.
|
||||
pub fn range(&self) -> &RequiresPythonRange {
|
||||
&self.range
|
||||
}
|
||||
|
||||
/// Returns `false` if the wheel's tags state it can't be used in the given Python version
|
||||
/// range.
|
||||
///
|
||||
|
|
@ -279,15 +287,76 @@ impl serde::Serialize for RequiresPython {
|
|||
impl<'de> serde::Deserialize<'de> for RequiresPython {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let specifiers = VersionSpecifiers::deserialize(deserializer)?;
|
||||
let bound = RequiresPythonBound(
|
||||
let (lower_bound, upper_bound) =
|
||||
crate::pubgrub::PubGrubSpecifier::from_release_specifiers(&specifiers)
|
||||
.map_err(serde::de::Error::custom)?
|
||||
.iter()
|
||||
.next()
|
||||
.map(|(lower, _)| lower.clone())
|
||||
.unwrap_or(Bound::Unbounded),
|
||||
);
|
||||
Ok(Self { specifiers, bound })
|
||||
.bounding_range()
|
||||
.map(|(lower_bound, upper_bound)| (lower_bound.cloned(), upper_bound.cloned()))
|
||||
.unwrap_or((Bound::Unbounded, Bound::Unbounded));
|
||||
Ok(Self {
|
||||
specifiers,
|
||||
range: RequiresPythonRange(
|
||||
RequiresPythonBound(lower_bound),
|
||||
RequiresPythonBound(upper_bound),
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct RequiresPythonRange(RequiresPythonBound, RequiresPythonBound);
|
||||
|
||||
impl RequiresPythonRange {
|
||||
/// Initialize a [`RequiresPythonRange`] with the given bounds.
|
||||
pub fn new(lower: RequiresPythonBound, upper: RequiresPythonBound) -> Self {
|
||||
Self(lower, upper)
|
||||
}
|
||||
|
||||
/// Returns the lower bound.
|
||||
pub fn lower(&self) -> &RequiresPythonBound {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns the upper bound.
|
||||
pub fn upper(&self) -> &RequiresPythonBound {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RequiresPythonRange {
|
||||
fn default() -> Self {
|
||||
Self(
|
||||
RequiresPythonBound(Bound::Unbounded),
|
||||
RequiresPythonBound(Bound::Unbounded),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RequiresPythonRange> for Range<Version> {
|
||||
fn from(value: RequiresPythonRange) -> Self {
|
||||
match (value.0.as_ref(), value.1.as_ref()) {
|
||||
(Bound::Included(lower), Bound::Included(upper)) => {
|
||||
Range::from_range_bounds(lower.clone()..=upper.clone())
|
||||
}
|
||||
(Bound::Included(lower), Bound::Excluded(upper)) => {
|
||||
Range::from_range_bounds(lower.clone()..upper.clone())
|
||||
}
|
||||
(Bound::Excluded(lower), Bound::Included(upper)) => {
|
||||
Range::strictly_higher_than(lower.clone())
|
||||
.intersection(&Range::lower_than(upper.clone()))
|
||||
}
|
||||
(Bound::Excluded(lower), Bound::Excluded(upper)) => {
|
||||
Range::strictly_higher_than(lower.clone())
|
||||
.intersection(&Range::strictly_lower_than(upper.clone()))
|
||||
}
|
||||
(Bound::Unbounded, Bound::Unbounded) => Range::full(),
|
||||
(Bound::Unbounded, Bound::Included(upper)) => Range::lower_than(upper.clone()),
|
||||
(Bound::Unbounded, Bound::Excluded(upper)) => Range::strictly_lower_than(upper.clone()),
|
||||
(Bound::Included(lower), Bound::Unbounded) => Range::higher_than(lower.clone()),
|
||||
(Bound::Excluded(lower), Bound::Unbounded) => {
|
||||
Range::strictly_higher_than(lower.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -301,6 +370,9 @@ impl Default for RequiresPythonBound {
|
|||
}
|
||||
|
||||
impl RequiresPythonBound {
|
||||
/// Initialize a [`RequiresPythonBound`] with the given bound.
|
||||
///
|
||||
/// These bounds use release-only semantics when comparing versions.
|
||||
pub fn new(bound: Bound<Version>) -> Self {
|
||||
Self(match bound {
|
||||
Bound::Included(version) => Bound::Included(version.only_release()),
|
||||
|
|
@ -310,16 +382,6 @@ impl RequiresPythonBound {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<RequiresPythonBound> for Range<Version> {
|
||||
fn from(value: RequiresPythonBound) -> Self {
|
||||
match value.0 {
|
||||
Bound::Included(version) => Range::higher_than(version),
|
||||
Bound::Excluded(version) => Range::strictly_higher_than(version),
|
||||
Bound::Unbounded => Range::full(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for RequiresPythonBound {
|
||||
type Target = Bound<Version>;
|
||||
|
||||
|
|
|
|||
|
|
@ -1486,7 +1486,7 @@ 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().clone()),
|
||||
Range::from(requires_python.range().clone()),
|
||||
);
|
||||
|
||||
if marker.is_false() {
|
||||
|
|
@ -1552,7 +1552,7 @@ 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().clone()),
|
||||
Range::from(requires_python.range().clone()),
|
||||
);
|
||||
marker.and(requirement.marker.clone());
|
||||
|
||||
|
|
@ -2813,7 +2813,7 @@ impl Ord for Fork {
|
|||
let self_bound = marker::requires_python(&self.markers).unwrap_or_default();
|
||||
let other_bound = marker::requires_python(&other.markers).unwrap_or_default();
|
||||
|
||||
other_bound.cmp(&self_bound).then_with(|| {
|
||||
other_bound.lower().cmp(self_bound.lower()).then_with(|| {
|
||||
// If there's no difference, prioritize forks with upper bounds. We'd prefer to solve
|
||||
// `numpy <= 2` before solving `numpy >= 1`, since the resolution produced by the former
|
||||
// might work for the latter, but the inverse is unlikely to be true due to maximum
|
||||
|
|
@ -2855,7 +2855,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().clone()))
|
||||
marker.simplify_python_versions(Range::from(requires_python.range().clone()))
|
||||
} else {
|
||||
marker
|
||||
}
|
||||
|
|
|
|||
|
|
@ -647,7 +647,7 @@ impl ValidatedLock {
|
|||
// Requires-Python bound in the workspace, we should have the necessary wheels to perform
|
||||
// a locked resolution.
|
||||
if let Some(locked) = lock.requires_python() {
|
||||
if locked.bound() != requires_python.bound() {
|
||||
if locked.range() != requires_python.range() {
|
||||
// On the other hand, if the bound in the lockfile is stricter, meaning the
|
||||
// bound has since been weakened, we have to perform a clean resolution to ensure
|
||||
// we fetch the necessary wheels.
|
||||
|
|
|
|||
|
|
@ -3292,6 +3292,158 @@ fn lock_requires_python() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Lock a requirement from PyPI, ignoring any dependencies that exceed the `requires-python`
|
||||
/// upper-bound.
|
||||
#[test]
|
||||
fn lock_requires_python_upper() -> Result<()> {
|
||||
let context = TestContext::new("3.11");
|
||||
|
||||
let lockfile = context.temp_dir.join("uv.lock");
|
||||
|
||||
// Require `==3.11.*`.
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "warehouse"
|
||||
version = "1.0.0"
|
||||
requires-python = "==3.11.*"
|
||||
dependencies = ["pydantic"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=42", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock().env("UV_EXCLUDE_NEWER", "2024-08-29T00:00:00Z"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 5 packages in [TIME]
|
||||
"###);
|
||||
|
||||
let lock = fs_err::read_to_string(&lockfile).unwrap();
|
||||
|
||||
insta::with_settings!({
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
assert_snapshot!(
|
||||
lock, @r###"
|
||||
version = 1
|
||||
requires-python = "==3.11.*"
|
||||
|
||||
[options]
|
||||
exclude-newer = "2024-08-29T00:00:00Z"
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.8.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
{ name = "pydantic-core" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8c/99/d0a5dca411e0a017762258013ba9905cd6e7baa9a3fd1fe8b6529472902e/pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a", size = 739834 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/fa/b7f815b8c9ad021c07f88875b601222ef5e70619391ade4a49234d12d278/pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8", size = 423875 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.20.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/12/e3/0d5ad91211dba310f7ded335f4dad871172b9cc9ce204f5a56d76ccd6247/pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4", size = 388371 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/61/db/f6a724db226d990a329910727cfac43539ff6969edc217286dd05cda3ef6/pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312", size = 1834507 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/83/6f2bfe75209d557ae1c3550c1252684fc1827b8b12fbed84c3b4439e135d/pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88", size = 1773527 },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/ef/513ea76d7ca81f2354bb9c8d7839fc1157673e652613f7e1aff17d8ce05d/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc", size = 1787879 },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/0a/ac294caecf235f0cc651de6232f1642bb793af448d1cfc541b0dc1fd72b8/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43", size = 1774694 },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/a4/08f12b5512f095963550a7cb49ae010e3f8f3f22b45e508c2cb4d7744fce/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6", size = 1976369 },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/59/b2495be4410462aedb399071c71884042a2c6443319cbf62d00b4a7ed7a5/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121", size = 2691250 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/ae/fc99ce1ba791c9e9d1dee04ce80eef1dae5b25b27e3fc8e19f4e3f1348bf/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1", size = 2061462 },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/bb/eb07cbe47cfd638603ce3cb8c220f1a054b821e666509e535f27ba07ca5f/pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b", size = 1893923 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/ef/5a52400553b8faa0e7f11fd7a2ba11e8d2feb50b540f9e7973c49b97eac0/pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27", size = 1966779 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/5b/fb37fe341344d9651f5c5f579639cd97d50a457dc53901aa8f7e9f28beb9/pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b", size = 2109044 },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/1a/6f7278802dbc66716661618807ab0dfa4fc32b09d1235923bbbe8b3a5757/pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a", size = 1708265 },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/7f/58758c42c61b0bdd585158586fecea295523d49933cb33664ea888162daf/pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2", size = 1901750 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/47/ef0d60ae23c41aced42921728650460dc831a0adf604bfa66b76028cb4d0/pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231", size = 1839225 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/23/430f2878c9cd977a61bb39f71751d9310ec55cee36b3d5bf1752c6341fd0/pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9", size = 1768604 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/2b/ec4e7225dee79e0dc80ccc3c35ab33cc2c4bbb8a1a7ecf060e5e453651ec/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f", size = 1789767 },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/b0/38b24a1fa6d2f96af3148362e10737ec073768cd44d3ec21dca3be40a519/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52", size = 1772061 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/da/bb73274c42cb60decfa61e9eb0c9029da78b3b9af0a9de0309dbc8ff87b6/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237", size = 1974573 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/65/41693110fb3552556180460daffdb8bbeefb87fc026fd9aa4b849374015c/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe", size = 2625596 },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/b3/a5a54b47cccd1ab661ed5775235c5e06924753c2d4817737c5667bfa19a8/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e", size = 2099064 },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/fa/443a7a6ea54beaba45ff3a59f3d3e6e3004b7460bcfb0be77bcf98719d3b/pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24", size = 1900345 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/e6/9aca9ffae60f9cdf0183069de3e271889b628d0fb175913fcb3db5618fb1/pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1", size = 1968252 },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/5e/6c716810ea20a6419188992973a73c2fb4eb99cd382368d0637ddb6d3c99/pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd", size = 2119191 },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/fc/6123b00a9240fbb9ae0babad7a005d51103d9a5d39c957a986f5cdd0c271/pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688", size = 1717788 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/36/e61ad5a46607a469e2786f398cd671ebafcd9fb17f09a2359985c7228df5/pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d", size = 1898188 },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/75/40b0e98b658fdba02a693b3bacb4c875a28bba87796c7b13975976597d8c/pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686", size = 1838688 },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/02/d8ba2d4a266591a6a623c68b331b96523d4b62ab82a951794e3ed8907390/pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a", size = 1768409 },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/ae/25ecd9bc4ce4993e99a1a3c9ab111c082630c914260e129572fafed4ecc2/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b", size = 1789317 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/80/72057580681cdbe55699c367963d9c661b569a1d39338b4f6239faf36cdc/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19", size = 1771949 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/be/d9bbabc55b05019013180f141fcaf3b14dbe15ca7da550e95b60c321009a/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac", size = 1974392 },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/2d/7bcd938c6afb0f40293283f5f09988b61fb0a4f1d180abe7c23a2f665f8e/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703", size = 2625565 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/88/ca758e979457096008a4b16a064509028e3e092a1e85a5ed6c18ced8da88/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c", size = 2098784 },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/de/2fad6d63c3c42e472e985acb12ec45b7f56e42e6f4cd6dfbc5e87ee8678c/pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83", size = 1900198 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/50/077c7f35b6488dc369a6d22993af3a37901e198630f38ac43391ca730f5b/pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203", size = 1968005 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/1f/f378631574ead46d636b9a04a80ff878b9365d4b361b1905ef1667d4182a/pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0", size = 2118920 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/ea/e4943f17df7a3031d709481fe4363d4624ae875a6409aec34c28c9e6cf59/pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e", size = 1717397 },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/63/b95781763e8d84207025071c0cec16d921c0163c7a9033ae4b9a0e020dc7/pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20", size = 1898013 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.12.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "warehouse"
|
||||
version = "1.0.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "pydantic" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "pydantic" }]
|
||||
"###
|
||||
);
|
||||
});
|
||||
|
||||
// Re-run with `--locked`.
|
||||
uv_snapshot!(context.filters(), context.lock().arg("--locked").env("UV_EXCLUDE_NEWER", "2024-08-29T00:00:00Z"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 5 packages in [TIME]
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Lock a requirement from PyPI, respecting the `Requires-Python` metadata
|
||||
#[test]
|
||||
fn lock_requires_python_wheels() -> Result<()> {
|
||||
|
|
|
|||
|
|
@ -919,7 +919,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_full_version < '3.11'" },
|
||||
{ name = "package-c", marker = "python_full_version == '3.10.*'" },
|
||||
]
|
||||
sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_b-1.0.0.tar.gz", hash = "sha256:c4deba44768923473d077bdc0e177033fcb6e6fd406d56830d7ee6f4ffad68c1" }
|
||||
wheels = [
|
||||
|
|
|
|||
|
|
@ -4,12 +4,6 @@ expression: lock
|
|||
---
|
||||
version = 1
|
||||
requires-python = "==3.11.*"
|
||||
resolution-markers = [
|
||||
"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]
|
||||
exclude-newer = "2024-08-08T00:00:00Z"
|
||||
|
|
@ -195,7 +189,7 @@ dependencies = [
|
|||
{ name = "annotated-types" },
|
||||
{ name = "logfury" },
|
||||
{ name = "requests" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.12'" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ce/53/2b4ac6ef318444bde570c36219ff0c8cd2207fb2fd8183d88fdf4f0445ab/b2sdk-2.5.0.tar.gz", hash = "sha256:d7c20125e64508a730e56307d75284790079cdb88e63851fff820a09b24fb1d9", size = 203212 }
|
||||
wheels = [
|
||||
|
|
@ -322,7 +316,7 @@ source = { registry = "https://pypi.org/simple" }
|
|||
dependencies = [
|
||||
{ name = "botocore-stubs" },
|
||||
{ name = "types-s3transfer" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.12'" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6e/d6/da4a554a6436944cb47cfb84754e8a2e37b04e3ff6dfea3c4958e2eb93ed/boto3_stubs-1.34.156.tar.gz", hash = "sha256:c34220a00b6bd00a3cd326bc5d7e3296b7d1dab1a15660df6f8fba4ae307ff39", size = 88866 }
|
||||
wheels = [
|
||||
|
|
@ -877,7 +871,6 @@ dependencies = [
|
|||
{ name = "envier" },
|
||||
{ name = "opentelemetry-api" },
|
||||
{ name = "protobuf" },
|
||||
{ name = "setuptools" },
|
||||
{ name = "six" },
|
||||
{ name = "sqlparse" },
|
||||
{ name = "typing-extensions" },
|
||||
|
|
@ -3643,7 +3636,7 @@ name = "sqlalchemy"
|
|||
version = "2.0.32"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ 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 = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or 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 }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue