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
13 changes: 9 additions & 4 deletions crates/uv-resolver/src/resolver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1472,9 +1472,9 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
requests: &'index MetadataRequests,
) -> Result<Option<ResolverVersion>, ResolveError> {
// This only applies to universal resolutions.
if env.marker_environment().is_some() {
let Some(fork_markers) = env.fork_markers() else {
return Ok(None);
}
};

// If the package is already compatible with all environments (as is the case for
// packages that include a source distribution), we don't need to fork.
Expand All @@ -1491,8 +1491,13 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
if !env.included_by_marker(dist.implied_markers().and(marker))
&& env.included_by_marker(find_environments(id, pubgrub).and(marker))
{
// Then we need to fork.
let Some((left, right)) = fork_version_by_marker(env, marker) else {
// Separate the required environment from the candidate's wheel coverage in
// this fork, allowing environments in neither set to fall on either side.
// For example, Darwin == 24 becomes Darwin < 25 when the wheels require
// Darwin >= 25. This avoids introducing forks for unrelated wheel tags.
let coverage = dist.implied_markers().and(fork_markers);
let split = marker.restrict(marker.or(coverage));
let Some((left, right)) = fork_version_by_marker(env, split) else {
return Ok(Some(ResolverVersion::Unavailable(
candidate.version().clone(),
UnavailableVersion::IncompatibleDist(IncompatibleDist::Wheel(
Expand Down
164 changes: 142 additions & 22 deletions crates/uv/tests/lock/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38121,9 +38121,9 @@ fn lock_intel_mac() -> Result<()> {
revision = 3
requires-python = ">=3.11"
resolution-markers = [
"(python_full_version >= '3.12' and platform_machine != 'x86_64') or (python_full_version >= '3.12' and sys_platform != 'darwin')",
"(python_full_version < '3.12' and platform_machine != 'x86_64') or (python_full_version < '3.12' and sys_platform != 'darwin')",
"platform_machine == 'x86_64' and sys_platform == 'darwin'",
"(python_full_version >= '3.12' and platform_machine != 'x86_64') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')",
"(python_full_version < '3.12' and platform_machine != 'x86_64') or (python_full_version < '3.12' and sys_platform == 'linux') or (python_full_version < '3.12' and sys_platform == 'win32')",
"platform_machine == 'x86_64' and sys_platform != 'linux' and sys_platform != 'win32'",
]
required-markers = [
"platform_machine == 'x86_64' and sys_platform == 'darwin'",
Expand Down Expand Up @@ -38343,8 +38343,8 @@ fn lock_intel_mac() -> Result<()> {
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "torch", version = "2.2.2", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine == 'x86_64' and sys_platform == 'darwin'" },
{ name = "torch", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" },
{ name = "torch", version = "2.2.2", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine == 'x86_64' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "torch", version = "2.5.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'x86_64' or sys_platform == 'linux' or sys_platform == 'win32'" },
]

[package.metadata]
Expand Down Expand Up @@ -38376,7 +38376,7 @@ fn lock_intel_mac() -> Result<()> {
version = "2.2.2"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"platform_machine == 'x86_64' and sys_platform == 'darwin'",
"platform_machine == 'x86_64' and sys_platform != 'linux' and sys_platform != 'win32'",
]
dependencies = [
{ name = "filelock" },
Expand All @@ -38396,8 +38396,8 @@ fn lock_intel_mac() -> Result<()> {
version = "2.5.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"(python_full_version >= '3.12' and platform_machine != 'x86_64') or (python_full_version >= '3.12' and sys_platform != 'darwin')",
"(python_full_version < '3.12' and platform_machine != 'x86_64') or (python_full_version < '3.12' and sys_platform != 'darwin')",
"(python_full_version >= '3.12' and platform_machine != 'x86_64') or (python_full_version >= '3.12' and sys_platform == 'linux') or (python_full_version >= '3.12' and sys_platform == 'win32')",
"(python_full_version < '3.12' and platform_machine != 'x86_64') or (python_full_version < '3.12' and sys_platform == 'linux') or (python_full_version < '3.12' and sys_platform == 'win32')",
]
dependencies = [
{ name = "filelock" },
Expand Down Expand Up @@ -42457,8 +42457,8 @@ fn lock_required_environment_python_fork() -> Result<()> {
revision = 3
requires-python = ">=3.12, <3.14"
resolution-markers = [
"(python_full_version >= '3.13' and platform_machine != 'x86_64') or (python_full_version >= '3.13' and sys_platform != 'linux')",
"python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
"python_full_version >= '3.13' and sys_platform == 'win32'",
"python_full_version >= '3.13' and sys_platform != 'win32'",
"python_full_version < '3.13'",
]
supported-markers = [
Expand All @@ -42477,7 +42477,7 @@ fn lock_required_environment_python_fork() -> Result<()> {
version = "1.0.0"
source = { registry = "http://[LOCALHOST]/simple/" }
resolution-markers = [
"python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'",
"python_full_version >= '3.13' and sys_platform != 'win32'",
]
wheels = [
{ url = "http://[LOCALHOST]/files/a-1.0.0-cp312-abi3-manylinux_2_17_x86_64.whl", hash = "sha256:[SHA256:a-1.0.0-cp312-abi3-manylinux_2_17_x86_64.whl]", upload-time = "2024-03-24T00:00:00Z" },
Expand All @@ -42488,7 +42488,7 @@ fn lock_required_environment_python_fork() -> Result<()> {
version = "2.0.0"
source = { registry = "http://[LOCALHOST]/simple/" }
resolution-markers = [
"(python_full_version >= '3.13' and platform_machine != 'x86_64') or (python_full_version >= '3.13' and sys_platform != 'linux')",
"python_full_version >= '3.13' and sys_platform == 'win32'",
"python_full_version < '3.13'",
]
wheels = [
Expand All @@ -42501,8 +42501,8 @@ fn lock_required_environment_python_fork() -> Result<()> {
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "python_full_version < '3.13' or platform_machine != 'x86_64' or sys_platform != 'linux'" },
{ name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "python_full_version >= '3.13' and sys_platform != 'win32'" },
{ name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "python_full_version < '3.13' or sys_platform == 'win32'" },
]

[package.metadata]
Expand All @@ -42514,6 +42514,7 @@ fn lock_required_environment_python_fork() -> Result<()> {
}

/// A required Darwin release must have a compatible wheel, without removing newer wheels.
/// The newer version must not be selected between the baseline and its minimum Darwin release.
#[cfg(feature = "test-universal")]
#[test]
fn lock_required_environment_macos_release() -> Result<()> {
Expand Down Expand Up @@ -42567,8 +42568,8 @@ fn lock_required_environment_macos_release() -> Result<()> {
revision = 3
requires-python = ">=3.12"
resolution-markers = [
"(platform_machine == 'arm64' and platform_release < '24' and sys_platform == 'darwin') or (platform_machine == 'arm64' and platform_release > '24' and sys_platform == 'darwin')",
"platform_machine == 'arm64' and platform_release == '24' and sys_platform == 'darwin'",
"platform_machine == 'arm64' and platform_release >= '25' and sys_platform == 'darwin'",
"platform_machine == 'arm64' and platform_release < '25' and sys_platform == 'darwin'",
]
supported-markers = [
"platform_machine == 'arm64' and sys_platform == 'darwin'",
Expand All @@ -42585,7 +42586,7 @@ fn lock_required_environment_macos_release() -> Result<()> {
version = "1.0.0"
source = { registry = "http://[LOCALHOST]/simple/" }
resolution-markers = [
"platform_machine == 'arm64' and platform_release == '24' and sys_platform == 'darwin'",
"platform_machine == 'arm64' and platform_release < '25' and sys_platform == 'darwin'",
]
wheels = [
{ url = "http://[LOCALHOST]/files/a-1.0.0-py3-none-macosx_14_0_arm64.whl", hash = "sha256:[SHA256:a-1.0.0-py3-none-macosx_14_0_arm64.whl]", upload-time = "2024-03-24T00:00:00Z" },
Expand All @@ -42597,7 +42598,7 @@ fn lock_required_environment_macos_release() -> Result<()> {
version = "2.0.0"
source = { registry = "http://[LOCALHOST]/simple/" }
resolution-markers = [
"(platform_machine == 'arm64' and platform_release < '24' and sys_platform == 'darwin') or (platform_machine == 'arm64' and platform_release > '24' and sys_platform == 'darwin')",
"platform_machine == 'arm64' and platform_release >= '25' and sys_platform == 'darwin'",
]
wheels = [
{ url = "http://[LOCALHOST]/files/a-2.0.0-py3-none-macosx_26_0_arm64.whl", hash = "sha256:[SHA256:a-2.0.0-py3-none-macosx_26_0_arm64.whl]", upload-time = "2024-03-24T00:00:00Z" },
Expand All @@ -42608,8 +42609,8 @@ fn lock_required_environment_macos_release() -> Result<()> {
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "platform_release == '24'" },
{ name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "platform_release < '24' or platform_release > '24'" },
{ name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "platform_release < '25'" },
{ name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "platform_release >= '25'" },
]

[package.metadata]
Expand Down Expand Up @@ -42645,13 +42646,132 @@ fn lock_required_environment_macos_release() -> Result<()> {
uv_snapshot!(filters, context.lock().arg("--index-url").arg(server.index_url()), @"
exit_code: 1 (failure)
----- stderr -----
error: No solution found when resolving dependencies for split (markers: python_full_version >= '3.12' and platform_machine == 'arm64' and platform_release == '24' and sys_platform == 'darwin')
error: No solution found when resolving dependencies for split (markers: python_full_version >= '3.12' and platform_machine == 'arm64' and platform_release < '25' and sys_platform == 'darwin')
cause: Because only a==2.0.0 is available and a==2.0.0 has no `platform_machine == 'arm64' and sys_platform == 'darwin'`-compatible wheels, we can conclude that all versions of a cannot be used.
And because your project depends on a, we can conclude that your project's requirements are unsatisfiable.
");
Ok(())
}

/// Wheels for another Python version must not determine the Darwin release split in this fork.
#[cfg(feature = "test-universal")]
#[test]
fn lock_required_environment_macos_release_python_fork() -> Result<()> {
let scenario = toml::from_str::<Scenario>(indoc! {r#"
name = "required-environment-macos-release-python-fork"

[root]

[expected]
satisfiable = true

[packages.a.versions."1.0.0"]
sdist = false
wheel_tags = ["cp312-cp312-macosx_14_0_arm64", "cp313-cp313-macosx_14_0_arm64"]

[packages.a.versions."2.0.0"]
sdist = false
wheel_tags = ["cp312-cp312-macosx_14_0_arm64", "cp313-cp313-macosx_26_0_arm64"]
"#})?;
let server = PackseServer::from_scenario(&scenario);
let context = uv_test::test_context!("3.12").with_filters(
server
.files()
.map(|(filename, hash)| (hash.to_owned(), format!("[SHA256:{filename}]"))),
);
context
.temp_dir
.child("pyproject.toml")
.write_str(indoc! {r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12,<3.14"
dependencies = ["a"]

[tool.uv]
environments = [
"sys_platform == 'darwin' and platform_machine == 'arm64' and python_version < '3.13'",
"sys_platform == 'darwin' and platform_machine == 'arm64' and python_version >= '3.13'",
]
required-environments = ["sys_platform == 'darwin' and platform_machine == 'arm64' and platform_release == '24.0.0'"]
"#})?;

uv_snapshot!(context.filters(), context.lock().arg("--index-url").arg(server.index_url()), @"
exit_code: 0 (success)
----- stderr -----
Resolved 3 packages in [TIME]
");

insta::with_settings!({ filters => context.filters() }, {
assert_snapshot!(context.read("uv.lock"), @r#"
version = 1
revision = 3
requires-python = ">=3.12, <3.14"
resolution-markers = [
"python_full_version >= '3.13' and platform_machine == 'arm64' and platform_release >= '25' and sys_platform == 'darwin'",
"python_full_version >= '3.13' and platform_machine == 'arm64' and platform_release < '25' and sys_platform == 'darwin'",
"python_full_version < '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin'",
]
supported-markers = [
"python_full_version < '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin'",
"python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin'",
]
required-markers = [
"platform_machine == 'arm64' and platform_release == '24' and sys_platform == 'darwin'",
]

[options]
exclude-newer = "2024-03-25T00:00:00Z"

[[package]]
name = "a"
version = "1.0.0"
source = { registry = "http://[LOCALHOST]/simple/" }
resolution-markers = [
"python_full_version >= '3.13' and platform_machine == 'arm64' and platform_release < '25' and sys_platform == 'darwin'",
]
wheels = [
{ url = "http://[LOCALHOST]/files/a-1.0.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:[SHA256:a-1.0.0-cp312-cp312-macosx_14_0_arm64.whl]", upload-time = "2024-03-24T00:00:00Z" },
{ url = "http://[LOCALHOST]/files/a-1.0.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:[SHA256:a-1.0.0-cp313-cp313-macosx_14_0_arm64.whl]", upload-time = "2024-03-24T00:00:00Z" },
]

[[package]]
name = "a"
version = "2.0.0"
source = { registry = "http://[LOCALHOST]/simple/" }
resolution-markers = [
"python_full_version >= '3.13' and platform_machine == 'arm64' and platform_release >= '25' and sys_platform == 'darwin'",
"python_full_version < '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin'",
]
wheels = [
{ url = "http://[LOCALHOST]/files/a-2.0.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:[SHA256:a-2.0.0-cp312-cp312-macosx_14_0_arm64.whl]", upload-time = "2024-03-24T00:00:00Z" },
{ url = "http://[LOCALHOST]/files/a-2.0.0-cp313-cp313-macosx_26_0_arm64.whl", hash = "sha256:[SHA256:a-2.0.0-cp313-cp313-macosx_26_0_arm64.whl]", upload-time = "2024-03-24T00:00:00Z" },
]

[[package]]
name = "project"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "a", version = "1.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "python_full_version >= '3.13' and platform_release < '25'" },
{ name = "a", version = "2.0.0", source = { registry = "http://[LOCALHOST]/simple/" }, marker = "python_full_version < '3.13' or platform_release >= '25'" },
]

[package.metadata]
requires-dist = [{ name = "a" }]
"#);
});

uv_snapshot!(context.filters(), context.lock().arg("--locked").arg("--index-url").arg(server.index_url()), @"
exit_code: 0 (success)
----- stderr -----
Resolved 3 packages in [TIME]
");

Ok(())
}

/// A direct wheel must support the required platform within the current architecture fork.
#[cfg(feature = "test-universal")]
#[test]
Expand Down Expand Up @@ -42783,7 +42903,7 @@ fn lock_required_environment_cycle_reports_resolution_error() -> Result<()> {
@"
exit_code: 1 (failure)
----- stderr -----
error: No solution found when resolving dependencies for split (markers: platform_machine == 'arm64')
error: No solution found when resolving dependencies for split (markers: platform_machine != 'ppc64')
cause: Because a==1.0.0 has no `platform_machine == 'arm64'`-compatible wheels and only a==1.0.0 is available, we can conclude that all versions of a cannot be used.
And because pkg-a depends on a and your workspace requires pkg-a, we can conclude that your workspace's requirements are unsatisfiable.
"
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/tests/pip_compile/pip_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15492,7 +15492,7 @@ fn universal_required_environment() -> Result<()> {
.env_remove(EnvVars::UV_EXCLUDE_NEWER), @"
exit_code: 1 (failure)
----- stderr -----
error: No solution found when resolving dependencies for split (markers: platform_machine == 'arm64')
error: No solution found when resolving dependencies for split (markers: platform_machine != 'ppc64')
cause: Because a==1.0.0 has no `platform_machine == 'arm64'`-compatible wheels and only a==1.0.0 is available, we can conclude that all versions of a cannot be used.
And because project depends on a, we can conclude that your requirements are unsatisfiable.
");
Expand Down
Loading