Avoid propagating top-level options to sub-resolutions (#1607)

## Summary

It's incorrect to pass the resolution and dependency mode down to the
`BuildDispatch`, since it means that we'll use `--no-deps` when building
source distributions. If you set resolution to `lowest`, it also means
we end up using (e.g.) the lowest version of `wheel`, which also doesn't
make sense.

It's fine to pass `--exclude-newer`.

Closes https://github.com/astral-sh/uv/issues/1355.

Closes https://github.com/astral-sh/uv/issues/1563.
This commit is contained in:
Charlie Marsh 2024-02-17 13:53:38 -05:00 committed by GitHub
parent ee2e7bb9ae
commit b5617198f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 79 additions and 18 deletions

View file

@ -210,13 +210,6 @@ pub(crate) async fn pip_compile(
// Track in-flight downloads, builds, etc., across resolutions.
let in_flight = InFlight::default();
let options = OptionsBuilder::new()
.resolution_mode(resolution_mode)
.prerelease_mode(prerelease_mode)
.dependency_mode(dependency_mode)
.exclude_newer(exclude_newer)
.build();
let build_dispatch = BuildDispatch::new(
&client,
&cache,
@ -230,7 +223,7 @@ pub(crate) async fn pip_compile(
no_build,
&NoBinary::None,
)
.with_options(options);
.with_options(OptionsBuilder::new().exclude_newer(exclude_newer).build());
// Build the editables and add their requirements
let editable_metadata = if editables.is_empty() {
@ -286,6 +279,13 @@ pub(crate) async fn pip_compile(
editable_metadata,
);
let options = OptionsBuilder::new()
.resolution_mode(resolution_mode)
.prerelease_mode(prerelease_mode)
.dependency_mode(dependency_mode)
.exclude_newer(exclude_newer)
.build();
// Resolve the dependencies.
let resolver = Resolver::new(
manifest,

View file

@ -163,13 +163,6 @@ pub(crate) async fn pip_install(
// Track in-flight downloads, builds, etc., across resolutions.
let in_flight = InFlight::default();
let options = OptionsBuilder::new()
.resolution_mode(resolution_mode)
.prerelease_mode(prerelease_mode)
.dependency_mode(dependency_mode)
.exclude_newer(exclude_newer)
.build();
let resolve_dispatch = BuildDispatch::new(
&client,
&cache,
@ -183,7 +176,7 @@ pub(crate) async fn pip_install(
no_build,
no_binary,
)
.with_options(options);
.with_options(OptionsBuilder::new().exclude_newer(exclude_newer).build());
// Build all editable distributions. The editables are shared between resolution and
// installation, and should live for the duration of the command. If an editable is already
@ -205,6 +198,13 @@ pub(crate) async fn pip_install(
.await?
};
let options = OptionsBuilder::new()
.resolution_mode(resolution_mode)
.prerelease_mode(prerelease_mode)
.dependency_mode(dependency_mode)
.exclude_newer(exclude_newer)
.build();
// Resolve the requirements.
let resolution = match resolve(
requirements,
@ -258,6 +258,7 @@ pub(crate) async fn pip_install(
no_build,
no_binary,
)
.with_options(OptionsBuilder::new().exclude_newer(exclude_newer).build())
};
// Sync the environment.

View file

@ -144,7 +144,6 @@ async fn venv_impl(
let in_flight = InFlight::default();
// Prep the build context.
let options = OptionsBuilder::new().exclude_newer(exclude_newer).build();
let build_dispatch = BuildDispatch::new(
&client,
cache,
@ -158,7 +157,7 @@ async fn venv_impl(
&NoBuild::All,
&NoBinary::None,
)
.with_options(options);
.with_options(OptionsBuilder::new().exclude_newer(exclude_newer).build());
// Resolve the seed packages.
let resolution = build_dispatch

View file

@ -575,6 +575,38 @@ fn compile_python_37() -> Result<()> {
Ok(())
}
/// Resolve a source distribution with `--resolution=lowest-direct`, to ensure that the build
/// requirements aren't resolved at their lowest compatible version.
#[test]
fn compile_sdist_resolution_lowest() -> Result<()> {
let context = TestContext::new("3.12");
let requirements_in = context.temp_dir.child("requirements.in");
requirements_in.write_str("anyio @ https://files.pythonhosted.org/packages/2d/b8/7333d87d5f03247215d86a86362fd3e324111788c6cdd8d2e6196a6ba833/anyio-4.2.0.tar.gz")?;
uv_snapshot!(context.compile()
.arg("requirements.in")
.arg("--resolution=lowest-direct")
.arg("--python-version")
.arg("3.12"), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv v[VERSION] via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z requirements.in --resolution=lowest-direct --python-version 3.12
anyio @ https://files.pythonhosted.org/packages/2d/b8/7333d87d5f03247215d86a86362fd3e324111788c6cdd8d2e6196a6ba833/anyio-4.2.0.tar.gz
idna==3.4
# via anyio
sniffio==1.3.0
# via anyio
----- stderr -----
Resolved 3 packages in [TIME]
"###
);
Ok(())
}
/// Resolve a specific version of Black against an invalid Python version.
#[test]
fn compile_python_invalid_version() -> Result<()> {

View file

@ -1129,3 +1129,32 @@ fn install_pinned_polars_invalid_metadata() {
context.assert_command("import polars").success();
}
/// Install a source distribution with `--resolution=lowest-direct`, to ensure that the build
/// requirements aren't resolved at their lowest compatible version.
#[test]
fn install_sdist_resolution_lowest() -> Result<()> {
let context = TestContext::new("3.12");
let requirements_in = context.temp_dir.child("requirements.in");
requirements_in.write_str("anyio @ https://files.pythonhosted.org/packages/2d/b8/7333d87d5f03247215d86a86362fd3e324111788c6cdd8d2e6196a6ba833/anyio-4.2.0.tar.gz")?;
uv_snapshot!(command(&context)
.arg("-r")
.arg("requirements.in")
.arg("--resolution=lowest-direct"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 3 packages in [TIME]
Downloaded 3 packages in [TIME]
Installed 3 packages in [TIME]
+ anyio==4.2.0 (from https://files.pythonhosted.org/packages/2d/b8/7333d87d5f03247215d86a86362fd3e324111788c6cdd8d2e6196a6ba833/anyio-4.2.0.tar.gz)
+ idna==3.4
+ sniffio==1.3.0
"###
);
Ok(())
}