Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion crates/uv-resolver/src/candidate_selector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,12 @@ impl CandidateSelector {
type Entries<'a> = SmallVec<[&'a Entry; 3]>;

let mut preferences = preferences.iter().collect::<Entries>();

// Filter out preferences that map to a conflicting index.
preferences.retain(|entry| index.is_none_or(|index| entry.index().matches(index)));

// Sort the preferences by priority.
let highest = self.use_highest_version(package_name, env);
preferences.sort_by_key(|entry| {
let marker = entry.marker();

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

std::cmp::Reverse((matches_env, matches_index))
// Prefer the latest (or earliest) version.
let version = if highest {
Either::Left(entry.pin().version())
} else {
Either::Right(std::cmp::Reverse(entry.pin().version()))
};

std::cmp::Reverse((matches_env, matches_index, version))
});

Either::Right(
preferences
.into_iter()
Expand Down
18 changes: 9 additions & 9 deletions crates/uv/tests/it/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1243,11 +1243,10 @@ fn non_project_fork() -> Result<()> {
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"sys_platform == 'win32'",
"sys_platform != 'linux' and sys_platform != 'win32'",
]
dependencies = [
{ name = "idna", marker = "sys_platform != 'linux'" },
{ name = "sniffio", marker = "sys_platform != 'linux'" },
{ name = "idna", marker = "sys_platform == 'win32'" },
{ name = "sniffio", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fe/dc/daeadb9b34093d3968afcc93946ee567cd6d2b402a96c608cb160f74d737/anyio-2.0.0.tar.gz", hash = "sha256:ceca4669ffa3f02bf20ef3d6c2a0c323b16cdc71d1ce0b0bc03c6f1f36054826", size = 91291 }
wheels = [
Expand All @@ -1260,10 +1259,11 @@ fn non_project_fork() -> Result<()> {
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"sys_platform == 'linux'",
"sys_platform != 'linux' and sys_platform != 'win32'",
]
dependencies = [
{ name = "idna", marker = "sys_platform == 'linux'" },
{ name = "sniffio", marker = "sys_platform == 'linux'" },
{ name = "idna", marker = "sys_platform != 'win32'" },
{ name = "sniffio", marker = "sys_platform != 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/99/0d/65165f99e5f4f3b4c43a5ed9db0fb7aa655f5a58f290727a30528a87eb45/anyio-3.0.0.tar.gz", hash = "sha256:b553598332c050af19f7d41f73a7790142f5bc3d5eb8bd82f5e515ec22019bd9", size = 116952 }
wheels = [
Expand Down Expand Up @@ -1338,10 +1338,10 @@ fn non_project_fork() -> Result<()> {
# This file was autogenerated by uv via the following command:
# uv export --cache-dir [CACHE_DIR] --group async
-e ./child
anyio==2.0.0 ; sys_platform != 'linux' \
anyio==2.0.0 ; sys_platform == 'win32' \
--hash=sha256:0b8375c8fc665236cb4d143ea13e849eb9e074d727b1b5c27d88aba44ca8c547 \
--hash=sha256:ceca4669ffa3f02bf20ef3d6c2a0c323b16cdc71d1ce0b0bc03c6f1f36054826
anyio==3.0.0 ; sys_platform == 'linux' \
anyio==3.0.0 ; sys_platform != 'win32' \
--hash=sha256:b553598332c050af19f7d41f73a7790142f5bc3d5eb8bd82f5e515ec22019bd9 \
--hash=sha256:e71c3d9d72291d12056c0265d07c6bbedf92332f78573e278aeb116f24f30395
idna==3.6 \
Expand All @@ -1361,10 +1361,10 @@ fn non_project_fork() -> Result<()> {
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv export --cache-dir [CACHE_DIR] --group async --prune child
anyio==2.0.0 ; sys_platform != 'linux' \
anyio==2.0.0 ; sys_platform == 'win32' \
--hash=sha256:0b8375c8fc665236cb4d143ea13e849eb9e074d727b1b5c27d88aba44ca8c547 \
--hash=sha256:ceca4669ffa3f02bf20ef3d6c2a0c323b16cdc71d1ce0b0bc03c6f1f36054826
anyio==3.0.0 ; sys_platform == 'linux' \
anyio==3.0.0 ; sys_platform != 'win32' \
--hash=sha256:b553598332c050af19f7d41f73a7790142f5bc3d5eb8bd82f5e515ec22019bd9 \
--hash=sha256:e71c3d9d72291d12056c0265d07c6bbedf92332f78573e278aeb116f24f30395
idna==3.6 \
Expand Down
6 changes: 3 additions & 3 deletions crates/uv/tests/it/lock_conflict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4418,7 +4418,7 @@ conflicts = [
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
+ anyio==4.3.0
+ idna==3.5
+ idna==3.6
+ sniffio==1.3.1
"###);

Expand Down Expand Up @@ -4451,8 +4451,8 @@ conflicts = [
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna", version = "3.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-6-proxy1-x2' or (extra == 'extra-6-proxy1-x3' and extra == 'extra-7-project-x1')" },
{ name = "idna", version = "3.5", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-6-proxy1-x3' or (extra == 'extra-6-proxy1-x2' and extra == 'extra-7-project-x1') or (extra != 'extra-6-proxy1-x2' and extra != 'extra-7-project-x1')" },
{ name = "idna", version = "3.6", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-6-proxy1-x2' and extra == 'extra-6-proxy1-x3') or (extra != 'extra-6-proxy1-x3' and extra == 'extra-7-project-x1') or (extra != 'extra-6-proxy1-x2' and extra == 'extra-7-project-x1')" },
{ name = "idna", version = "3.5", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-6-proxy1-x3' or (extra == 'extra-6-proxy1-x2' and extra == 'extra-7-project-x1')" },
{ name = "idna", version = "3.6", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-7-project-x1' or (extra == 'extra-6-proxy1-x2' and extra == 'extra-6-proxy1-x3') or (extra != 'extra-6-proxy1-x2' and extra != 'extra-6-proxy1-x3')" },
{ name = "sniffio" },
]
sdist = { url = "https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6", size = 159642 }
Expand Down
69 changes: 64 additions & 5 deletions crates/uv/tests/it/pip_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7906,7 +7906,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_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'
cmake==3.28.4 ; python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'
# via triton
.
# via -r requirements.in
Expand All @@ -7916,7 +7916,7 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> {
# triton
jinja2==3.1.3
# via torch
lit==18.1.2 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'
lit==18.1.2 ; python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'
# via triton
markupsafe==2.1.5
# via jinja2
Expand All @@ -7930,16 +7930,16 @@ fn universal_disjoint_base_or_local_requirement() -> Result<()> {
# via
# -r requirements.in
# example
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')
torch==2.0.0+cpu ; python_full_version >= '3.13'
# via
# -r requirements.in
# example
torch==2.0.0+cu118 ; python_full_version >= '3.11' and python_full_version < '3.13'
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')
# via
# -r requirements.in
# example
# triton
triton==2.0.0 ; python_full_version >= '3.11' and python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'
triton==2.0.0 ; python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'
# via torch
typing-extensions==4.10.0
# via torch
Expand Down Expand Up @@ -14430,3 +14430,62 @@ fn respect_index_preference() -> Result<()> {

Ok(())
}

/// See: <https://github.com/astral-sh/uv/issues/10957>
#[test]
fn compile_preserve_requires_python_split() -> Result<()> {
static EXCLUDE_NEWER: &str = "2025-01-01T00:00:00Z";

let context = TestContext::new("3.8");
let requirements_in = context.temp_dir.child("requirements.in");
requirements_in.write_str("zipp")?;

uv_snapshot!(context
.pip_compile()
.env(EnvVars::UV_EXCLUDE_NEWER, EXCLUDE_NEWER)
.arg("--python-version")
.arg("3.8")
.arg("--universal")
.arg("requirements.in")
.arg("-o")
.arg("requirements.txt"), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --python-version 3.8 --universal requirements.in -o requirements.txt
zipp==3.20.2 ; python_full_version < '3.9'
# via -r requirements.in
zipp==3.21.0 ; python_full_version >= '3.9'
# via -r requirements.in

----- stderr -----
Resolved 2 packages in [TIME]
"###);

// Re-running shouldn't change the output.
uv_snapshot!(context
.pip_compile()
.env(EnvVars::UV_EXCLUDE_NEWER, EXCLUDE_NEWER)
.arg("--python-version")
.arg("3.8")
.arg("--universal")
.arg("requirements.in")
.arg("-o")
.arg("requirements.txt"), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --python-version 3.8 --universal requirements.in -o requirements.txt
zipp==3.20.2 ; python_full_version < '3.9'
# via -r requirements.in
zipp==3.21.0 ; python_full_version >= '3.9'
# via -r requirements.in

----- stderr -----
Resolved 2 packages in [TIME]
"###);

Ok(())
}
Loading