Avoid excluding valid wheels for exact requires-python bounds (#8140)

## Summary

Closes https://github.com/astral-sh/uv/issues/8136.
This commit is contained in:
Charlie Marsh 2024-10-12 06:17:36 +02:00 committed by GitHub
parent 7362f84d6f
commit b91bd29970
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 109 additions and 7 deletions

View file

@ -350,8 +350,8 @@ impl RequiresPython {
return true;
};
// Ex) If the wheel bound is `3.12`, then it doesn't match `==3.10.*`.
let wheel_bound = UpperBound(Bound::Excluded(Version::new([3, minor + 1])));
// Ex) If the wheel bound is `3.12`, then it doesn't match `<=3.10.`.
let wheel_bound = UpperBound(Bound::Included(Version::new([3, minor])));
if wheel_bound > self.range.upper().major_minor() {
return false;
}
@ -375,7 +375,8 @@ impl RequiresPython {
return false;
}
let wheel_bound = UpperBound(Bound::Excluded(Version::new([3, minor + 1])));
// Ex) If the wheel bound is `3.12`, then it doesn't match `<=3.10.`.
let wheel_bound = UpperBound(Bound::Included(Version::new([3, minor])));
if wheel_bound > self.range.upper().major_minor() {
return false;
}
@ -403,8 +404,8 @@ impl RequiresPython {
return false;
}
// Ex) If the wheel bound is `3.12`, then it doesn't match `==3.10.*`.
let wheel_bound = UpperBound(Bound::Excluded(Version::new([3, minor + 1])));
// Ex) If the wheel bound is `3.12`, then it doesn't match `<=3.10.`.
let wheel_bound = UpperBound(Bound::Included(Version::new([3, minor])));
if wheel_bound > self.range.upper().major_minor() {
return false;
}
@ -428,8 +429,8 @@ impl RequiresPython {
return false;
}
// Ex) If the wheel bound is `3.12`, then it doesn't match `==3.10.*`.
let wheel_bound = UpperBound(Bound::Excluded(Version::new([3, minor + 1])));
// Ex) If the wheel bound is `3.12`, then it doesn't match `<=3.10.`.
let wheel_bound = UpperBound(Bound::Included(Version::new([3, minor])));
if wheel_bound > self.range.upper().major_minor() {
return false;
}

View file

@ -38,6 +38,16 @@ fn requires_python_included() {
"{wheel_name}"
);
}
let version_specifiers = VersionSpecifiers::from_str("==3.12").unwrap();
let requires_python = RequiresPython::from_specifiers(&version_specifiers).unwrap();
let wheel_names = &["lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl"];
for wheel_name in wheel_names {
assert!(
requires_python.matches_wheel_tag(&WheelFilename::from_str(wheel_name).unwrap()),
"{wheel_name}"
);
}
}
#[test]

View file

@ -3499,6 +3499,97 @@ fn lock_requires_python_upper() -> Result<()> {
Ok(())
}
/// Lock a requirement from PyPI with an exact Python bound.
#[test]
fn lock_requires_python_exact() -> Result<()> {
let context = TestContext::new("3.12");
let lockfile = context.temp_dir.join("uv.lock");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "warehouse"
version = "1.0.0"
requires-python = "==3.12"
dependencies = ["lxml"]
[build-system]
requires = ["setuptools>=42"]
build-backend = "setuptools.build_meta"
"#,
)?;
uv_snapshot!(context.filters(), context.lock(), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using CPython 3.12.[X]
Resolved 2 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.12"
[options]
exclude-newer = "2024-03-25T00:00:00Z"
[[package]]
name = "lxml"
version = "5.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/2b/b4/bbccb250adbee490553b6a52712c46c20ea1ba533a643f1424b27ffc6845/lxml-5.1.0.tar.gz", hash = "sha256:3eea6ed6e6c918e468e693c41ef07f3c3acc310b70ddd9cc72d9ef84bc9564ca", size = 3839638 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f9/8c/faab484c22459bd777cccc36905e3e98d2a128f0f66ef447a621e8858b92/lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114", size = 8777117 },
{ url = "https://files.pythonhosted.org/packages/53/c0/bac6130e2edfe32211e6c99280dc0e1b1eabf0591948c1aa8a8e7c7172c6/lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8", size = 4761459 },
{ url = "https://files.pythonhosted.org/packages/56/75/aa934ccbc158d181dcdc32e087a0c37d437f39466489ee395a7aa44e4884/lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e", size = 4526167 },
{ url = "https://files.pythonhosted.org/packages/5e/75/6646eba09a0660715ae1b10ae59e9c72518fbd3921fbadeada081a6ba1fc/lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a", size = 8299397 },
{ url = "https://files.pythonhosted.org/packages/b3/0c/57298a0a8b47c62d9722d99ae1f6f27a976ccc1fea34ed54bdaea7d7a983/lxml-5.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16dd953fb719f0ffc5bc067428fc9e88f599e15723a85618c45847c96f11f431", size = 7812489 },
{ url = "https://files.pythonhosted.org/packages/07/a2/fdb226e6aca18b7448caa5b6551b80df14460c8df38aaad87bab58fbbb17/lxml-5.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16018f7099245157564d7148165132c70adb272fb5a17c048ba70d9cc542a1a1", size = 8111967 },
{ url = "https://files.pythonhosted.org/packages/c3/19/b7644b40df5d04e76de550a50d0251ee5efebd18f7cf65675ecfc020a8f5/lxml-5.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82cd34f1081ae4ea2ede3d52f71b7be313756e99b4b5f829f89b12da552d3aa3", size = 7680287 },
{ url = "https://files.pythonhosted.org/packages/e8/6e/0a6fb288b02e3f10e7090958d99e43cc2a373aceb401362ec083e4298029/lxml-5.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:19a1bc898ae9f06bccb7c3e1dfd73897ecbbd2c96afe9095a6026016e5ca97b8", size = 7950438 },
{ url = "https://files.pythonhosted.org/packages/e7/a9/44cbee712b0e371dff08eb3f98d3a6415848bea5ab40ce0e5ff059d88c44/lxml-5.1.0-cp312-cp312-win32.whl", hash = "sha256:13521a321a25c641b9ea127ef478b580b5ec82aa2e9fc076c86169d161798b01", size = 3570322 },
{ url = "https://files.pythonhosted.org/packages/02/59/e1fbe2514d8ab39977b72e77f98d0fa49772f61e938049baf151b307a4f0/lxml-5.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:1ad17c20e3666c035db502c78b86e58ff6b5991906e55bdbef94977700c72623", size = 3911611 },
]
[[package]]
name = "warehouse"
version = "1.0.0"
source = { editable = "." }
dependencies = [
{ name = "lxml" },
]
[package.metadata]
requires-dist = [{ name = "lxml" }]
"###
);
});
// Re-run with `--locked`.
uv_snapshot!(context.filters(), context.lock().arg("--locked"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using CPython 3.12.[X]
Resolved 2 packages in [TIME]
"###);
Ok(())
}
/// Lock a requirement from PyPI, respecting the `Requires-Python` metadata
#[test]
fn lock_requires_python_wheels() -> Result<()> {