From 4633ce20954ab5c71f774ec8dd0c221ab897cc5d Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Tue, 23 Jun 2026 13:28:33 -0500 Subject: [PATCH] Make project environments relocatable under preview --- crates/uv/src/commands/project/mod.rs | 6 +-- crates/uv/tests/sync/sync.rs | 64 +++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index 11b20e79e8ee6..4507dcd7b0ad7 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -28,7 +28,7 @@ use uv_installer::{InstallationStrategy, SatisfiesResult, SitePackages}; use uv_normalize::{DEV_DEPENDENCIES, DefaultGroups, ExtraName, GroupName, PackageName}; use uv_pep440::{TildeVersionSpecifier, Version, VersionSpecifiers}; use uv_pep508::MarkerTreeContents; -use uv_preview::Preview; +use uv_preview::{Preview, PreviewFeature}; use uv_pypi_types::{ConflictItem, ConflictKind, ConflictSet, Conflicts}; use uv_python::{ BrokenLink, EnvironmentPreference, Interpreter, InvalidEnvironmentKind, PythonDownloads, @@ -1551,7 +1551,7 @@ impl ProjectEnvironment { uv_virtualenv::OnExisting::Remove( uv_virtualenv::RemovalReason::ManagedEnvironment, ), - false, + uv_preview::is_enabled(PreviewFeature::RelocatableEnvsDefault), false, upgradeable, )?; @@ -1591,7 +1591,7 @@ impl ProjectEnvironment { uv_virtualenv::OnExisting::Remove( uv_virtualenv::RemovalReason::ManagedEnvironment, ), - false, + uv_preview::is_enabled(PreviewFeature::RelocatableEnvsDefault), false, upgradeable, )?; diff --git a/crates/uv/tests/sync/sync.rs b/crates/uv/tests/sync/sync.rs index 6095a5b9a2cfe..b7ccd3b71e17b 100644 --- a/crates/uv/tests/sync/sync.rs +++ b/crates/uv/tests/sync/sync.rs @@ -50,6 +50,70 @@ fn sync() -> Result<()> { Ok(()) } +/// With `relocatable-envs-default`, project environments are relocatable by default. +#[test] +fn sync_relocatable_envs_default() -> Result<()> { + let context = uv_test::test_context!("3.12").with_filtered_counts(); + let project_dir = context.temp_dir.child("project"); + project_dir.create_dir_all()?; + project_dir.child("pyproject.toml").write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["black"] + "#, + )?; + + uv_snapshot!(context.filters(), context.sync() + .current_dir(project_dir.path()) + .arg("--preview-features") + .arg("relocatable-envs-default"), @" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Using CPython 3.12.[X] interpreter at: [PYTHON-3.12] + Creating virtual environment at: .venv + Resolved [N] packages in [TIME] + Prepared [N] packages in [TIME] + Installed [N] packages in [TIME] + + black==24.3.0 + + click==8.1.7 + + mypy-extensions==1.0.0 + + packaging==24.0 + + pathspec==0.12.1 + + platformdirs==4.2.0 + "); + + project_dir + .child(".venv") + .child("pyvenv.cfg") + .assert(predicate::str::contains("relocatable = true")); + + let relocated_dir = context.temp_dir.child("relocated"); + fs_err::rename(project_dir.path(), relocated_dir.path())?; + + uv_snapshot!(context.filters(), context.run() + .current_dir(relocated_dir.path()) + .env_remove(EnvVars::VIRTUAL_ENV) + .arg("--no-sync") + .arg("black") + .arg("--version"), @" + success: true + exit_code: 0 + ----- stdout ----- + black, 24.3.0 (compiled: yes) + Python (CPython) 3.12.[X] + + ----- stderr ----- + "); + + Ok(()) +} + /// Ensure that `uv sync` reuses remote wheels cached by `uv pip install`. #[test] fn sync_reuses_pip_install_wheel_cache() -> Result<()> {