From fd5131cb7c61628b3ae9fbc4dd7af83eabb59bc7 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Thu, 23 Jan 2025 15:24:40 -0600 Subject: [PATCH] Require tests to opt-in to managed Python installation (#10912) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First of all, I want to test automatic managed installs (see #10913) and need to set that up. Second of all, some tests were _implicitly_ downloading interpreters instead of using the one from their context — which is unexpected and naughty and very slow. --- crates/uv/tests/it/common/mod.rs | 48 ++++++++++++++------ crates/uv/tests/it/lock_conflict.rs | 27 +++-------- crates/uv/tests/it/python_install.rs | 27 +++++++---- scripts/packages/anyio_local/.python-version | 1 + scripts/packages/built-by-uv/.python-version | 1 + 5 files changed, 60 insertions(+), 44 deletions(-) create mode 100644 scripts/packages/anyio_local/.python-version create mode 100644 scripts/packages/built-by-uv/.python-version diff --git a/crates/uv/tests/it/common/mod.rs b/crates/uv/tests/it/common/mod.rs index e71c28182..24c0fb61e 100644 --- a/crates/uv/tests/it/common/mod.rs +++ b/crates/uv/tests/it/common/mod.rs @@ -102,6 +102,9 @@ pub struct TestContext { /// Standard filters for this test context. filters: Vec<(String, String)>, + /// Extra environment variables to apply to all commands. + extra_env: Vec<(OsString, OsString)>, + #[allow(dead_code)] _root: tempfile::TempDir, } @@ -233,6 +236,29 @@ impl TestContext { self } + /// Add extra directories and configuration for managed Python installations. + #[must_use] + pub fn with_managed_python_dirs(mut self) -> Self { + let managed = self.temp_dir.join("managed"); + let bin = self.temp_dir.join("bin"); + + self.extra_env.push(( + EnvVars::PATH.into(), + env::join_paths(std::iter::once(bin.clone()).chain(env::split_paths( + &env::var(EnvVars::PATH).unwrap_or_default(), + ))) + .unwrap(), + )); + self.extra_env + .push((EnvVars::UV_PYTHON_BIN_DIR.into(), bin.into())); + self.extra_env + .push((EnvVars::UV_PYTHON_INSTALL_DIR.into(), managed.into())); + self.extra_env + .push((EnvVars::UV_PYTHON_DOWNLOADS.into(), "automatic".into())); + + self + } + /// Discover the path to the XDG state directory. We use this, rather than the OS-specific /// temporary directory, because on macOS (and Windows on GitHub Actions), they involve /// symlinks. (On macOS, the temporary directory is, like `/var/...`, which resolves to @@ -456,6 +482,7 @@ impl TestContext { python_version, python_versions, filters, + extra_env: vec![], _root: root, } } @@ -510,12 +537,18 @@ impl TestContext { .env(EnvVars::PATH, path) .env(EnvVars::HOME, self.home_dir.as_os_str()) .env(EnvVars::UV_PYTHON_INSTALL_DIR, "") + // Installations are not allowed by default; see `Self::with_managed_python_dirs` + .env(EnvVars::UV_PYTHON_DOWNLOADS, "never") .env(EnvVars::UV_TEST_PYTHON_PATH, self.python_path()) .env(EnvVars::UV_EXCLUDE_NEWER, EXCLUDE_NEWER) .env_remove(EnvVars::UV_CACHE_DIR) .env_remove(EnvVars::UV_TOOL_BIN_DIR) .current_dir(self.temp_dir.path()); + for (key, value) in &self.extra_env { + command.env(key, value); + } + if activate_venv { command.env(EnvVars::VIRTUAL_ENV, self.venv.as_os_str()); } @@ -677,21 +710,10 @@ impl TestContext { /// Create a `uv python install` command with options shared across scenarios. pub fn python_install(&self) -> Command { let mut command = self.new_command(); - let managed = self.temp_dir.join("managed"); - let bin = self.temp_dir.join("bin"); self.add_shared_args(&mut command, true); command .arg("python") .arg("install") - .env(EnvVars::UV_PYTHON_INSTALL_DIR, managed) - .env(EnvVars::UV_PYTHON_BIN_DIR, bin.as_os_str()) - .env( - EnvVars::PATH, - env::join_paths(std::iter::once(bin).chain(env::split_paths( - &env::var(EnvVars::PATH).unwrap_or_default(), - ))) - .unwrap(), - ) .current_dir(&self.temp_dir); command } @@ -699,14 +721,10 @@ impl TestContext { /// Create a `uv python uninstall` command with options shared across scenarios. pub fn python_uninstall(&self) -> Command { let mut command = self.new_command(); - let managed = self.temp_dir.join("managed"); - let bin = self.temp_dir.join("bin"); self.add_shared_args(&mut command, true); command .arg("python") .arg("uninstall") - .env(EnvVars::UV_PYTHON_INSTALL_DIR, managed) - .env(EnvVars::UV_PYTHON_BIN_DIR, bin) .current_dir(&self.temp_dir); command } diff --git a/crates/uv/tests/it/lock_conflict.rs b/crates/uv/tests/it/lock_conflict.rs index e2bc49751..bcda1cc6b 100644 --- a/crates/uv/tests/it/lock_conflict.rs +++ b/crates/uv/tests/it/lock_conflict.rs @@ -3394,7 +3394,7 @@ fn shared_optional_dependency_mixed1() -> Result<()> { /// Regression test for: #[test] fn shared_optional_dependency_extra2() -> Result<()> { - let context = TestContext::new("3.12"); + let context = TestContext::new("3.11"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -3432,9 +3432,6 @@ fn shared_optional_dependency_extra2() -> Result<()> { ----- stdout ----- ----- stderr ----- - Using CPython 3.11.11 - Removed virtual environment at: .venv - Creating virtual environment at: .venv Resolved 5 packages in [TIME] Prepared 3 packages in [TIME] Installed 3 packages in [TIME] @@ -3536,7 +3533,7 @@ fn shared_optional_dependency_extra2() -> Result<()> { /// Regression test for: #[test] fn shared_optional_dependency_group2() -> Result<()> { - let context = TestContext::new("3.12"); + let context = TestContext::new("3.11"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -3574,9 +3571,6 @@ fn shared_optional_dependency_group2() -> Result<()> { ----- stdout ----- ----- stderr ----- - Using CPython 3.11.11 - Removed virtual environment at: .venv - Creating virtual environment at: .venv Resolved 5 packages in [TIME] Prepared 3 packages in [TIME] Installed 3 packages in [TIME] @@ -3682,7 +3676,7 @@ fn shared_optional_dependency_group2() -> Result<()> { /// Regression test for: #[test] fn shared_optional_dependency_mixed2() -> Result<()> { - let context = TestContext::new("3.12"); + let context = TestContext::new("3.11"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -3722,9 +3716,6 @@ fn shared_optional_dependency_mixed2() -> Result<()> { ----- stdout ----- ----- stderr ----- - Using CPython 3.11.11 - Removed virtual environment at: .venv - Creating virtual environment at: .venv Resolved 5 packages in [TIME] Prepared 3 packages in [TIME] Installed 3 packages in [TIME] @@ -4364,7 +4355,7 @@ fn shared_dependency_mixed() -> Result<()> { /// Ref #[test] fn extras_are_namespaced() -> Result<()> { - let context = TestContext::new("3.12"); + let context = TestContext::new("3.11"); let root_pyproject_toml = context.temp_dir.child("pyproject.toml"); root_pyproject_toml.write_str( @@ -4423,9 +4414,6 @@ conflicts = [ ----- stdout ----- ----- stderr ----- - Using CPython 3.11.11 - Removed virtual environment at: .venv - Creating virtual environment at: .venv Resolved 7 packages in [TIME] Prepared 3 packages in [TIME] Installed 3 packages in [TIME] @@ -7297,7 +7285,7 @@ fn deduplicate_resolution_markers() -> Result<()> { #[test] fn overlapping_resolution_markers() -> Result<()> { - let context = TestContext::new("3.12"); + let context = TestContext::new("3.10"); let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( @@ -7305,7 +7293,7 @@ fn overlapping_resolution_markers() -> Result<()> { [project] name = "ads-mega-model" version = "0.1.0" - requires-python = "==3.10.12" + requires-python = "==3.10.*" dependencies = [ "wandb==0.17.6", ] @@ -7344,7 +7332,6 @@ fn overlapping_resolution_markers() -> Result<()> { ----- stdout ----- ----- stderr ----- - Using CPython 3.10.12 Resolved 45 packages in [TIME] "###); @@ -7356,7 +7343,7 @@ fn overlapping_resolution_markers() -> Result<()> { lock, @r###" version = 1 - requires-python = "==3.10.12" + requires-python = "==3.10.*" resolution-markers = [ "sys_platform == 'linux' and extra != 'extra-14-ads-mega-model-cpu' and extra == 'extra-14-ads-mega-model-cu118'", "sys_platform != 'linux' and extra != 'extra-14-ads-mega-model-cpu' and extra == 'extra-14-ads-mega-model-cu118'", diff --git a/crates/uv/tests/it/python_install.rs b/crates/uv/tests/it/python_install.rs index 1e132cc06..ec2e8ed8f 100644 --- a/crates/uv/tests/it/python_install.rs +++ b/crates/uv/tests/it/python_install.rs @@ -13,7 +13,8 @@ use crate::common::{uv_snapshot, TestContext}; fn python_install() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() - .with_filtered_exe_suffix(); + .with_filtered_exe_suffix() + .with_managed_python_dirs(); // Install the latest version uv_snapshot!(context.filters(), context.python_install(), @r###" @@ -95,7 +96,8 @@ fn python_install() { fn python_install_preview() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() - .with_filtered_exe_suffix(); + .with_filtered_exe_suffix() + .with_managed_python_dirs(); // Install the latest version uv_snapshot!(context.filters(), context.python_install().arg("--preview"), @r###" @@ -269,7 +271,8 @@ fn python_install_preview() { fn python_install_preview_upgrade() { let context = TestContext::new_with_versions(&[]) .with_filtered_python_keys() - .with_filtered_exe_suffix(); + .with_filtered_exe_suffix() + .with_managed_python_dirs(); let bin_python = context .temp_dir @@ -408,7 +411,8 @@ fn python_install_preview_upgrade() { fn python_install_freethreaded() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() - .with_filtered_exe_suffix(); + .with_filtered_exe_suffix() + .with_managed_python_dirs(); // Install the latest version uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.13t"), @r###" @@ -482,7 +486,8 @@ fn python_install_freethreaded() { fn python_install_invalid_request() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() - .with_filtered_exe_suffix(); + .with_filtered_exe_suffix() + .with_managed_python_dirs(); // Request something that is not a Python version uv_snapshot!(context.filters(), context.python_install().arg("foobar"), @r###" @@ -519,7 +524,8 @@ fn python_install_invalid_request() { fn python_install_default() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() - .with_filtered_exe_suffix(); + .with_filtered_exe_suffix() + .with_managed_python_dirs(); let bin_python_minor_13 = context .temp_dir @@ -815,7 +821,7 @@ fn read_link_path(path: &Path) -> String { #[test] fn python_install_unknown() { - let context: TestContext = TestContext::new_with_versions(&[]); + let context: TestContext = TestContext::new_with_versions(&[]).with_managed_python_dirs(); // An unknown request uv_snapshot!(context.filters(), context.python_install().arg("foobar"), @r###" @@ -848,7 +854,8 @@ fn python_install_preview_broken_link() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() - .with_filtered_exe_suffix(); + .with_filtered_exe_suffix() + .with_managed_python_dirs(); let bin_python = context.temp_dir.child("bin").child("python3.13"); @@ -883,7 +890,9 @@ fn python_dylib_install_name_is_patched_on_install() { use assert_cmd::assert::OutputAssertExt; use uv_python::managed::platform_key_from_env; - let context: TestContext = TestContext::new_with_versions(&[]).with_filtered_python_keys(); + let context: TestContext = TestContext::new_with_versions(&[]) + .with_filtered_python_keys() + .with_managed_python_dirs(); // Install the latest version context diff --git a/scripts/packages/anyio_local/.python-version b/scripts/packages/anyio_local/.python-version new file mode 100644 index 000000000..e4fba2183 --- /dev/null +++ b/scripts/packages/anyio_local/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/scripts/packages/built-by-uv/.python-version b/scripts/packages/built-by-uv/.python-version new file mode 100644 index 000000000..e4fba2183 --- /dev/null +++ b/scripts/packages/built-by-uv/.python-version @@ -0,0 +1 @@ +3.12