Skip to content

Commit f7f363b

Browse files
committed
Prefer preferences with greater package versions
1 parent e8d6b33 commit f7f363b

File tree

5 files changed

+465
-372
lines changed

5 files changed

+465
-372
lines changed

crates/uv-resolver/src/candidate_selector.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,12 @@ impl CandidateSelector {
196196
type Entries<'a> = SmallVec<[&'a Entry; 3]>;
197197

198198
let mut preferences = preferences.iter().collect::<Entries>();
199+
199200
// Filter out preferences that map to a conflicting index.
200201
preferences.retain(|entry| index.is_none_or(|index| entry.index().matches(index)));
202+
203+
// Sort the preferences by priority.
204+
let highest = self.use_highest_version(package_name, env);
201205
preferences.sort_by_key(|entry| {
202206
let marker = entry.marker();
203207

@@ -207,8 +211,16 @@ impl CandidateSelector {
207211
// Prefer preferences that match the current index.
208212
let matches_index = index.is_none_or(|index| entry.index().matches(index));
209213

210-
std::cmp::Reverse((matches_env, matches_index))
214+
// Prefer the latest (or earliest) version.
215+
let version = if highest {
216+
Either::Left(entry.pin().version())
217+
} else {
218+
Either::Right(std::cmp::Reverse(entry.pin().version()))
219+
};
220+
221+
std::cmp::Reverse((matches_env, matches_index, version))
211222
});
223+
212224
Either::Right(
213225
preferences
214226
.into_iter()

crates/uv/tests/it/export.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -1243,11 +1243,10 @@ fn non_project_fork() -> Result<()> {
12431243
source = { registry = "https://pypi.org/simple" }
12441244
resolution-markers = [
12451245
"sys_platform == 'win32'",
1246-
"sys_platform != 'linux' and sys_platform != 'win32'",
12471246
]
12481247
dependencies = [
1249-
{ name = "idna", marker = "sys_platform != 'linux'" },
1250-
{ name = "sniffio", marker = "sys_platform != 'linux'" },
1248+
{ name = "idna", marker = "sys_platform == 'win32'" },
1249+
{ name = "sniffio", marker = "sys_platform == 'win32'" },
12511250
]
12521251
sdist = { url = "https://files.pythonhosted.org/packages/fe/dc/daeadb9b34093d3968afcc93946ee567cd6d2b402a96c608cb160f74d737/anyio-2.0.0.tar.gz", hash = "sha256:ceca4669ffa3f02bf20ef3d6c2a0c323b16cdc71d1ce0b0bc03c6f1f36054826", size = 91291 }
12531252
wheels = [
@@ -1260,10 +1259,11 @@ fn non_project_fork() -> Result<()> {
12601259
source = { registry = "https://pypi.org/simple" }
12611260
resolution-markers = [
12621261
"sys_platform == 'linux'",
1262+
"sys_platform != 'linux' and sys_platform != 'win32'",
12631263
]
12641264
dependencies = [
1265-
{ name = "idna", marker = "sys_platform == 'linux'" },
1266-
{ name = "sniffio", marker = "sys_platform == 'linux'" },
1265+
{ name = "idna", marker = "sys_platform != 'win32'" },
1266+
{ name = "sniffio", marker = "sys_platform != 'win32'" },
12671267
]
12681268
sdist = { url = "https://files.pythonhosted.org/packages/99/0d/65165f99e5f4f3b4c43a5ed9db0fb7aa655f5a58f290727a30528a87eb45/anyio-3.0.0.tar.gz", hash = "sha256:b553598332c050af19f7d41f73a7790142f5bc3d5eb8bd82f5e515ec22019bd9", size = 116952 }
12691269
wheels = [
@@ -1338,10 +1338,10 @@ fn non_project_fork() -> Result<()> {
13381338
# This file was autogenerated by uv via the following command:
13391339
# uv export --cache-dir [CACHE_DIR] --group async
13401340
-e ./child
1341-
anyio==2.0.0 ; sys_platform != 'linux' \
1341+
anyio==2.0.0 ; sys_platform == 'win32' \
13421342
--hash=sha256:0b8375c8fc665236cb4d143ea13e849eb9e074d727b1b5c27d88aba44ca8c547 \
13431343
--hash=sha256:ceca4669ffa3f02bf20ef3d6c2a0c323b16cdc71d1ce0b0bc03c6f1f36054826
1344-
anyio==3.0.0 ; sys_platform == 'linux' \
1344+
anyio==3.0.0 ; sys_platform != 'win32' \
13451345
--hash=sha256:b553598332c050af19f7d41f73a7790142f5bc3d5eb8bd82f5e515ec22019bd9 \
13461346
--hash=sha256:e71c3d9d72291d12056c0265d07c6bbedf92332f78573e278aeb116f24f30395
13471347
idna==3.6 \
@@ -1361,10 +1361,10 @@ fn non_project_fork() -> Result<()> {
13611361
----- stdout -----
13621362
# This file was autogenerated by uv via the following command:
13631363
# uv export --cache-dir [CACHE_DIR] --group async --prune child
1364-
anyio==2.0.0 ; sys_platform != 'linux' \
1364+
anyio==2.0.0 ; sys_platform == 'win32' \
13651365
--hash=sha256:0b8375c8fc665236cb4d143ea13e849eb9e074d727b1b5c27d88aba44ca8c547 \
13661366
--hash=sha256:ceca4669ffa3f02bf20ef3d6c2a0c323b16cdc71d1ce0b0bc03c6f1f36054826
1367-
anyio==3.0.0 ; sys_platform == 'linux' \
1367+
anyio==3.0.0 ; sys_platform != 'win32' \
13681368
--hash=sha256:b553598332c050af19f7d41f73a7790142f5bc3d5eb8bd82f5e515ec22019bd9 \
13691369
--hash=sha256:e71c3d9d72291d12056c0265d07c6bbedf92332f78573e278aeb116f24f30395
13701370
idna==3.6 \

crates/uv/tests/it/lock_conflict.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4418,7 +4418,7 @@ conflicts = [
44184418
Prepared 3 packages in [TIME]
44194419
Installed 3 packages in [TIME]
44204420
+ anyio==4.3.0
4421-
+ idna==3.5
4421+
+ idna==3.6
44224422
+ sniffio==1.3.1
44234423
"###);
44244424

crates/uv/tests/it/pip_compile.rs

+64-5
Original file line numberDiff line numberDiff line change
@@ -7906,7 +7906,7 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> {
79067906
----- stdout -----
79077907
# This file was autogenerated by uv via the following command:
79087908
# uv pip compile --cache-dir [CACHE_DIR] requirements.in --universal
7909-
cmake==3.28.4 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'
7909+
cmake==3.28.4 ; python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'
79107910
# via triton
79117911
.
79127912
# via -r requirements.in
@@ -7916,7 +7916,7 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> {
79167916
# triton
79177917
jinja2==3.1.3
79187918
# via torch
7919-
lit==18.1.2 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'
7919+
lit==18.1.2 ; python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'
79207920
# via triton
79217921
markupsafe==2.1.5
79227922
# via jinja2
@@ -7930,16 +7930,16 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> {
79307930
# via
79317931
# -r requirements.in
79327932
# example
7933-
torch==2.0.0+cpu ; python_full_version >= '3.13' or (python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')
7933+
torch==2.0.0+cpu ; python_full_version >= '3.13'
79347934
# via
79357935
# -r requirements.in
79367936
# example
7937-
torch==2.0.0+cu118 ; python_full_version >= '3.11' and python_full_version < '3.13'
7937+
torch==2.0.0+cu118 ; (python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')
79387938
# via
79397939
# -r requirements.in
79407940
# example
79417941
# triton
7942-
triton==2.0.0 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'
7942+
triton==2.0.0 ; python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'
79437943
# via torch
79447944
typing-extensions==4.10.0
79457945
# via torch
@@ -14430,3 +14430,62 @@ fn respect_index_preference() -> Result<()> {
1443014430

1443114431
Ok(())
1443214432
}
14433+
14434+
/// See: <https://github.com/astral-sh/uv/issues/10957>
14435+
#[test]
14436+
fn compile_preserve_requires_python_split() -> Result<()> {
14437+
static EXCLUDE_NEWER: &str = "2025-01-01T00:00:00Z";
14438+
14439+
let context = TestContext::new("3.8");
14440+
let requirements_in = context.temp_dir.child("requirements.in");
14441+
requirements_in.write_str("zipp")?;
14442+
14443+
uv_snapshot!(context
14444+
.pip_compile()
14445+
.env(EnvVars::UV_EXCLUDE_NEWER, EXCLUDE_NEWER)
14446+
.arg("--python-version")
14447+
.arg("3.8")
14448+
.arg("--universal")
14449+
.arg("requirements.in")
14450+
.arg("-o")
14451+
.arg("requirements.txt"), @r###"
14452+
success: true
14453+
exit_code: 0
14454+
----- stdout -----
14455+
# This file was autogenerated by uv via the following command:
14456+
# uv pip compile --cache-dir [CACHE_DIR] --python-version 3.8 --universal requirements.in -o requirements.txt
14457+
zipp==3.20.2 ; python_full_version < '3.9'
14458+
# via -r requirements.in
14459+
zipp==3.21.0 ; python_full_version >= '3.9'
14460+
# via -r requirements.in
14461+
14462+
----- stderr -----
14463+
Resolved 2 packages in [TIME]
14464+
"###);
14465+
14466+
// Re-running shouldn't change the output.
14467+
uv_snapshot!(context
14468+
.pip_compile()
14469+
.env(EnvVars::UV_EXCLUDE_NEWER, EXCLUDE_NEWER)
14470+
.arg("--python-version")
14471+
.arg("3.8")
14472+
.arg("--universal")
14473+
.arg("requirements.in")
14474+
.arg("-o")
14475+
.arg("requirements.txt"), @r###"
14476+
success: true
14477+
exit_code: 0
14478+
----- stdout -----
14479+
# This file was autogenerated by uv via the following command:
14480+
# uv pip compile --cache-dir [CACHE_DIR] --python-version 3.8 --universal requirements.in -o requirements.txt
14481+
zipp==3.20.2 ; python_full_version < '3.9'
14482+
# via -r requirements.in
14483+
zipp==3.21.0 ; python_full_version >= '3.9'
14484+
# via -r requirements.in
14485+
14486+
----- stderr -----
14487+
Resolved 2 packages in [TIME]
14488+
"###);
14489+
14490+
Ok(())
14491+
}

0 commit comments

Comments
 (0)