diff --git a/crates/uv-resolver/src/exclusions.rs b/crates/uv-resolver/src/exclusions.rs index 658517db3f97..7b691d27e2b3 100644 --- a/crates/uv-resolver/src/exclusions.rs +++ b/crates/uv-resolver/src/exclusions.rs @@ -3,7 +3,7 @@ use rustc_hash::FxHashSet; use uv_types::{Reinstall, Upgrade}; /// Tracks locally installed packages that should not be selected during resolution. -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub enum Exclusions { #[default] None, diff --git a/crates/uv-resolver/src/preferences.rs b/crates/uv-resolver/src/preferences.rs index 6b507d748f9a..d456d5daf0b7 100644 --- a/crates/uv-resolver/src/preferences.rs +++ b/crates/uv-resolver/src/preferences.rs @@ -8,11 +8,9 @@ use pep508_rs::{ }; use pypi_types::{HashError, Hashes}; use requirements_txt::RequirementEntry; -use tracing::{trace}; +use tracing::trace; use uv_normalize::PackageName; - - #[derive(thiserror::Error, Debug)] pub enum PreferenceError { #[error("direct URL requirements without package names are not supported: {0}")] diff --git a/crates/uv/tests/pip_compile.rs b/crates/uv/tests/pip_compile.rs index 8cc33861055e..70d2088ca294 100644 --- a/crates/uv/tests/pip_compile.rs +++ b/crates/uv/tests/pip_compile.rs @@ -6294,3 +6294,103 @@ fn emit_marker_expression_pypy() -> Result<()> { Ok(()) } + +/// A local version of a package shadowing a remote package is installed. +#[test] +fn local_version_of_remote_package() -> Result<()> { + let context = TestContext::new("3.12"); + let root_path = context.workspace_root.join("scripts/packages"); + + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("anyio")?; + + uv_snapshot!(context.filters(), context.compile() + .arg(requirements_in.path()), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z [TEMP_DIR]/requirements.in + anyio==4.3.0 + idna==3.6 + # via anyio + sniffio==1.3.1 + # via anyio + + ----- stderr ----- + Resolved 3 packages in [TIME] + "###); + + // Actually install the local dependency + uv_snapshot!( + context.filters(), + Command::new(get_bin()) + .arg("pip") + .arg("install") + .arg(root_path.join("anyio_local")) + .arg("--cache-dir") + .arg(context.cache_dir.path()) + .arg("--exclude-newer") + .arg(EXCLUDE_NEWER) + .env("VIRTUAL_ENV", context.venv.as_os_str()) + .current_dir(context.temp_dir.path()), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Downloaded 1 package in [TIME] + Installed 1 package in [TIME] + + anyio==4.3.0+foo (from file://[WORKSPACE]/scripts/packages/anyio_local) + "### + ); + + // The local version should _not_ be included in the resolution + uv_snapshot!(context.filters(), context.compile() + .arg(requirements_in.path()), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z [TEMP_DIR]/requirements.in + anyio==4.3.0 + idna==3.6 + # via anyio + sniffio==1.3.1 + # via anyio + + ----- stderr ----- + Resolved 3 packages in [TIME] + "###); + + // Write a lock file with the local version + let requirements_txt = context.temp_dir.child("requirements.txt"); + requirements_txt.write_str(&indoc::formatdoc! {r" + anyio @ {workspace_root}/scripts/packages/anyio_local + ", + workspace_root = context.workspace_root.simplified_display(), + })?; + + // The local version is _still_ excluded from the resolution + uv_snapshot!(context.filters(), context.compile() + .arg(requirements_in.path()) + .arg("--output-file") + .arg(requirements_txt.path()), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z [TEMP_DIR]/requirements.in --output-file [TEMP_DIR]/requirements.txt + anyio==4.3.0 + idna==3.6 + # via anyio + sniffio==1.3.1 + # via anyio + + ----- stderr ----- + Resolved 3 packages in [TIME] + "###); + + Ok(()) +}