diff --git a/crates/uv-python/Cargo.toml b/crates/uv-python/Cargo.toml index 0a57d46ab..5f795f511 100644 --- a/crates/uv-python/Cargo.toml +++ b/crates/uv-python/Cargo.toml @@ -82,4 +82,6 @@ tempfile = { workspace = true } test-log = { workspace = true } [build-dependencies] +uv-static = { workspace = true } + serde_json = { workspace = true } diff --git a/crates/uv-python/build.rs b/crates/uv-python/build.rs index fa606036f..c3efba02b 100644 --- a/crates/uv-python/build.rs +++ b/crates/uv-python/build.rs @@ -4,6 +4,8 @@ use std::io::Write; use std::path::PathBuf; use std::{env, fs}; +use uv_static::EnvVars; + fn process_json(data: &serde_json::Value) -> serde_json::Value { let mut out_data = serde_json::Map::new(); @@ -18,12 +20,12 @@ fn process_json(data: &serde_json::Value) -> serde_json::Value { fn main() { let version_metadata = PathBuf::from_iter([ - env::var("CARGO_MANIFEST_DIR").unwrap(), + env::var(EnvVars::CARGO_MANIFEST_DIR).unwrap(), "download-metadata.json".into(), ]); let version_metadata_minified = PathBuf::from_iter([ - env::var("OUT_DIR").unwrap(), + env::var(EnvVars::OUT_DIR).unwrap(), "download-metadata-minified.json".into(), ]); diff --git a/crates/uv-python/src/lib.rs b/crates/uv-python/src/lib.rs index 6c313e3de..5dbdd3dc2 100644 --- a/crates/uv-python/src/lib.rs +++ b/crates/uv-python/src/lib.rs @@ -224,7 +224,7 @@ mod tests { Some(self.installations.root().as_os_str()), ), // Set a working directory - ("PWD", Some(self.workdir.path().as_os_str())), + (EnvVars::PWD, Some(self.workdir.path().as_os_str())), ]; for (key, value) in vars { run_vars.push((key, *value)); diff --git a/crates/uv-shell/src/lib.rs b/crates/uv-shell/src/lib.rs index 757e262ae..aa2a2febd 100644 --- a/crates/uv-shell/src/lib.rs +++ b/crates/uv-shell/src/lib.rs @@ -348,7 +348,11 @@ mod tests { use tempfile::tempdir; // First option used by std::env::home_dir. - const HOME_DIR_ENV_VAR: &str = if cfg!(windows) { "USERPROFILE" } else { "HOME" }; + const HOME_DIR_ENV_VAR: &str = if cfg!(windows) { + EnvVars::USERPROFILE + } else { + EnvVars::HOME + }; #[test] fn configuration_files_zsh_no_existing_zshenv() { @@ -357,7 +361,7 @@ mod tests { with_vars( [ - ("ZDOTDIR", None), + (EnvVars::ZDOTDIR, None), (HOME_DIR_ENV_VAR, tmp_home_dir.path().to_str()), ], || { @@ -370,7 +374,7 @@ mod tests { with_vars( [ - ("ZDOTDIR", tmp_zdotdir.path().to_str()), + (EnvVars::ZDOTDIR, tmp_zdotdir.path().to_str()), (HOME_DIR_ENV_VAR, tmp_home_dir.path().to_str()), ], || { @@ -391,7 +395,7 @@ mod tests { with_vars( [ - ("ZDOTDIR", None), + (EnvVars::ZDOTDIR, None), (HOME_DIR_ENV_VAR, tmp_home_dir.path().to_str()), ], || { @@ -404,7 +408,7 @@ mod tests { with_vars( [ - ("ZDOTDIR", tmp_zdotdir.path().to_str()), + (EnvVars::ZDOTDIR, tmp_zdotdir.path().to_str()), (HOME_DIR_ENV_VAR, tmp_home_dir.path().to_str()), ], || { @@ -425,7 +429,7 @@ mod tests { with_vars( [ - ("ZDOTDIR", tmp_zdotdir.path().to_str()), + (EnvVars::ZDOTDIR, tmp_zdotdir.path().to_str()), (HOME_DIR_ENV_VAR, tmp_home_dir.path().to_str()), ], || { diff --git a/crates/uv-static/src/env_vars.rs b/crates/uv-static/src/env_vars.rs index 354c3ec3a..ffec9956c 100644 --- a/crates/uv-static/src/env_vars.rs +++ b/crates/uv-static/src/env_vars.rs @@ -850,6 +850,16 @@ impl EnvVars { #[attr_added_in("0.8.18")] pub const GITLAB_CI: &'static str = "GITLAB_CI"; + /// Used for testing GitLab CI trusted publishing. + #[attr_hidden] + #[attr_added_in("0.8.18")] + pub const PYPI_ID_TOKEN: &'static str = "PYPI_ID_TOKEN"; + + /// Used for testing GitLab CI trusted publishing. + #[attr_hidden] + #[attr_added_in("0.8.18")] + pub const TESTPYPI_ID_TOKEN: &'static str = "TESTPYPI_ID_TOKEN"; + /// Sets the encoding for standard I/O streams (e.g., PYTHONIOENCODING=utf-8). #[attr_hidden] #[attr_added_in("0.4.18")] @@ -869,6 +879,16 @@ impl EnvVars { #[attr_added_in("0.1.22")] pub const PYTHONPATH: &'static str = "PYTHONPATH"; + /// Used to set the location of Python stdlib when using trampolines. + #[attr_hidden] + #[attr_added_in("0.7.13")] + pub const PYTHONHOME: &'static str = "PYTHONHOME"; + + /// Used to correctly detect virtual environments when using trampolines. + #[attr_hidden] + #[attr_added_in("0.7.13")] + pub const PYVENV_LAUNCHER: &'static str = "__PYVENV_LAUNCHER__"; + /// Used in tests to enforce a consistent locale setting. #[attr_hidden] #[attr_added_in("0.4.28")] @@ -989,6 +1009,16 @@ impl EnvVars { #[attr_added_in("0.0.5")] pub const CARGO_TARGET_DIR: &'static str = "CARGO_TARGET_DIR"; + /// Set by cargo when compiling for Windows-like platforms. + #[attr_hidden] + #[attr_added_in("0.0.5")] + pub const CARGO_CFG_WINDOWS: &'static str = "CARGO_CFG_WINDOWS"; + + /// Specifies the directory where Cargo stores intermediate build artifacts. + #[attr_hidden] + #[attr_added_in("0.8.25")] + pub const OUT_DIR: &'static str = "OUT_DIR"; + /// Used in tests for environment substitution testing in `requirements.in`. #[attr_hidden] #[attr_added_in("0.1.18")] @@ -1239,6 +1269,6 @@ impl EnvVars { /// Suppress output from the build backend when building source distributions, even in the event /// of build failures. - #[attr_added_in("0.9.14")] + #[attr_added_in("0.9.15")] pub const UV_HIDE_BUILD_OUTPUT: &'static str = "UV_HIDE_BUILD_OUTPUT"; } diff --git a/crates/uv-trampoline/Cargo.lock b/crates/uv-trampoline/Cargo.lock index 37edf9ede..bf77d64a7 100644 --- a/crates/uv-trampoline/Cargo.lock +++ b/crates/uv-trampoline/Cargo.lock @@ -25,13 +25,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "syn" version = "1.0.109" @@ -54,6 +60,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + [[package]] name = "ufmt" version = "0.2.0" @@ -87,6 +104,35 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "uv-macros" +version = "0.0.5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "textwrap", +] + +[[package]] +name = "uv-static" +version = "0.0.5" +dependencies = [ + "uv-macros", +] + [[package]] name = "uv-trampoline" version = "0.1.0" @@ -95,6 +141,7 @@ dependencies = [ "embed-manifest", "ufmt", "ufmt-write", + "uv-static", "windows", ] diff --git a/crates/uv-trampoline/Cargo.toml b/crates/uv-trampoline/Cargo.toml index b5b5e2ca8..6a16fa8f9 100644 --- a/crates/uv-trampoline/Cargo.toml +++ b/crates/uv-trampoline/Cargo.toml @@ -35,6 +35,8 @@ debug = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +uv-static = { path = "../uv-static" } + windows = { version = "0.61.0", features = [ "std", "Win32_Foundation", @@ -51,4 +53,6 @@ ufmt = { version = "0.2.0", features = ["std"] } dunce = { version = "1.0.5" } [build-dependencies] +uv-static = { path = "../uv-static" } + embed-manifest = "1.4.0" diff --git a/crates/uv-trampoline/build.rs b/crates/uv-trampoline/build.rs index aa7d20fa2..9294450f7 100644 --- a/crates/uv-trampoline/build.rs +++ b/crates/uv-trampoline/build.rs @@ -4,8 +4,10 @@ // don't have to mess with utf-16. use embed_manifest::{embed_manifest, new_manifest}; +use uv_static::EnvVars; + fn main() { - if std::env::var_os("CARGO_CFG_WINDOWS").is_some() { + if std::env::var_os(EnvVars::CARGO_CFG_WINDOWS).is_some() { let manifest = new_manifest("uv.Trampoline").remove_dependency("Microsoft.Windows.Common-Controls"); embed_manifest(manifest).expect("unable to embed manifest"); diff --git a/crates/uv-trampoline/src/bounce.rs b/crates/uv-trampoline/src/bounce.rs index 0046e174b..c1fb236eb 100644 --- a/crates/uv-trampoline/src/bounce.rs +++ b/crates/uv-trampoline/src/bounce.rs @@ -30,6 +30,8 @@ use windows::Win32::{ }; use windows::core::{BOOL, PSTR, s}; +use uv_static::EnvVars; + use crate::{error, format, warn}; // https://learn.microsoft.com/en-us/windows/win32/menurc/resource-types @@ -146,7 +148,7 @@ fn make_child_cmdline() -> CString { // the approach taken by CPython for Python Launchers // (in `launcher.c`). This allows virtual environments to // be correctly detected when using trampolines. - std::env::set_var("__PYVENV_LAUNCHER__", &executable_name); + std::env::set_var(EnvVars::PYVENV_LAUNCHER, &executable_name); // If this is not a virtual environment and `PYTHONHOME` has // not been set, then set `PYTHONHOME` to the parent directory of @@ -154,10 +156,10 @@ fn make_child_cmdline() -> CString { // directories are added to `sys.path` when running with a junction // trampoline. let python_home_set = - std::env::var("PYTHONHOME").is_ok_and(|home| !home.is_empty()); + std::env::var(EnvVars::PYTHONHOME).is_ok_and(|home| !home.is_empty()); if !is_virtualenv(python_exe.as_path()) && !python_home_set { std::env::set_var( - "PYTHONHOME", + EnvVars::PYTHONHOME, python_exe .parent() .expect("Python executable should have a parent directory"), diff --git a/crates/uv/Cargo.toml b/crates/uv/Cargo.toml index 6cdb81bcc..c043217d0 100644 --- a/crates/uv/Cargo.toml +++ b/crates/uv/Cargo.toml @@ -122,6 +122,7 @@ self-replace = { workspace = true } windows = { workspace = true } [build-dependencies] +uv-static = { workspace = true } uv-version = { workspace = true } embed-manifest = { workspace = true } diff --git a/crates/uv/build.rs b/crates/uv/build.rs index 8a7fb8843..5290530f5 100644 --- a/crates/uv/build.rs +++ b/crates/uv/build.rs @@ -16,8 +16,10 @@ use embed_manifest::manifest::{ActiveCodePage, ExecutionLevel, Setting, SupportedOS}; use embed_manifest::{embed_manifest, empty_manifest}; +use uv_static::EnvVars; + fn main() { - if std::env::var_os("CARGO_CFG_WINDOWS").is_some() { + if std::env::var_os(EnvVars::CARGO_CFG_WINDOWS).is_some() { let [major, minor, patch] = uv_version::version() .splitn(3, '.') .map(str::parse) diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index d9bdeae71..89be281b8 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -17834,8 +17834,8 @@ fn credentials_from_subdirectory() -> Result<()> { uv_snapshot!(context.filters(), context .pip_compile() .arg("foo/pyproject.toml") - .env("UV_INDEX_INTERNAL_USERNAME", "public") - .env("UV_INDEX_INTERNAL_PASSWORD", "heron"), @r" + .env(EnvVars::index_username("INTERNAL"), "public") + .env(EnvVars::index_password("INTERNAL"), "heron"), @r" success: true exit_code: 0 ----- stdout ----- diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs index ce8d1e580..a1711c96f 100644 --- a/crates/uv/tests/it/pip_install.rs +++ b/crates/uv/tests/it/pip_install.rs @@ -10710,7 +10710,7 @@ fn no_sources_workspace_discovery() -> Result<()> { uv_snapshot!(context.filters(), context.pip_install() .arg("--upgrade") .arg(".") - .env("UV_NO_SOURCES", "true"), @r###" + .env(EnvVars::UV_NO_SOURCES, "true"), @r###" success: true exit_code: 0 ----- stdout ----- @@ -10730,7 +10730,7 @@ fn no_sources_workspace_discovery() -> Result<()> { uv_snapshot!(context.filters(), context.pip_install() .arg("--upgrade") .arg(".") - .env("UV_NO_SOURCES", "false"), @r###" + .env(EnvVars::UV_NO_SOURCES, "false"), @r###" success: true exit_code: 0 ----- stdout ----- @@ -10751,7 +10751,7 @@ fn no_sources_workspace_discovery() -> Result<()> { .arg("--upgrade") .arg("--no-sources") .arg(".") - .env("UV_NO_SOURCES", "False"), @r" + .env(EnvVars::UV_NO_SOURCES, "False"), @r" success: true exit_code: 0 ----- stdout ----- diff --git a/crates/uv/tests/it/publish.rs b/crates/uv/tests/it/publish.rs index 3fb4da79e..9864fb9e2 100644 --- a/crates/uv/tests/it/publish.rs +++ b/crates/uv/tests/it/publish.rs @@ -483,8 +483,8 @@ async fn read_index_credential_env_vars_for_check_url() { .arg(&wheel) .arg("--index") .arg("private-index") - .env("UV_INDEX_PRIVATE_INDEX_USERNAME", "username") - .env("UV_INDEX_PRIVATE_INDEX_PASSWORD", "secret") + .env(EnvVars::index_username("PRIVATE_INDEX"), "username") + .env(EnvVars::index_password("PRIVATE_INDEX"), "secret") .arg("--trusted-publishing") .arg("never"), @r" @@ -540,7 +540,7 @@ async fn gitlab_trusted_publishing_pypi_id_token() { .arg("../../scripts/links/ok-1.0.0-py3-none-any.whl") .env(EnvVars::GITLAB_CI, "true") .env_remove(EnvVars::GITHUB_ACTIONS) - .env("PYPI_ID_TOKEN", "gitlab-oidc-jwt"), @r" + .env(EnvVars::PYPI_ID_TOKEN, "gitlab-oidc-jwt"), @r" success: true exit_code: 0 ----- stdout ----- @@ -595,7 +595,7 @@ async fn gitlab_trusted_publishing_testpypi_id_token() { // Emulate GitLab CI with TESTPYPI_ID_TOKEN present .env(EnvVars::GITLAB_CI, "true") .env_remove(EnvVars::GITHUB_ACTIONS) - .env("TESTPYPI_ID_TOKEN", "gitlab-oidc-jwt"), @r" + .env(EnvVars::TESTPYPI_ID_TOKEN, "gitlab-oidc-jwt"), @r" success: true exit_code: 0 ----- stdout ----- diff --git a/crates/uv/tests/it/python_install.rs b/crates/uv/tests/it/python_install.rs index e12113b4a..fd945d761 100644 --- a/crates/uv/tests/it/python_install.rs +++ b/crates/uv/tests/it/python_install.rs @@ -2750,7 +2750,7 @@ fn python_install_emulated_macos() { // Rosetta is not available to run the x86_64 interpreter // fail the test in CI, otherwise skip it #[allow(clippy::manual_assert)] - if env::var("CI").is_ok() { + if env::var(EnvVars::CI).is_ok() { panic!("x86_64 emulation is not available on this CI runner"); } debug!("Skipping test because x86_64 emulation is not available"); diff --git a/crates/uv/tests/it/run.rs b/crates/uv/tests/it/run.rs index 6be503bdd..69dd2ed3b 100644 --- a/crates/uv/tests/it/run.rs +++ b/crates/uv/tests/it/run.rs @@ -4395,7 +4395,7 @@ fn run_remote_pep723_script() { r"(?m)^Downloaded remote script to:.*\.py$", "Downloaded remote script to: [TEMP_PATH].py", )); - uv_snapshot!(filters, context.run().arg("https://raw.githubusercontent.com/astral-sh/uv/df45b9ac2584824309ff29a6a09421055ad730f6/scripts/uv-run-remote-script-test.py").arg("CI"), @r###" + uv_snapshot!(filters, context.run().arg("https://raw.githubusercontent.com/astral-sh/uv/df45b9ac2584824309ff29a6a09421055ad730f6/scripts/uv-run-remote-script-test.py").arg(EnvVars::CI), @r###" success: true exit_code: 0 ----- stdout ----- diff --git a/crates/uv/tests/it/sync.rs b/crates/uv/tests/it/sync.rs index c62facd83..d73d99062 100644 --- a/crates/uv/tests/it/sync.rs +++ b/crates/uv/tests/it/sync.rs @@ -3549,7 +3549,7 @@ fn sync_exclude_group_with_environment_variable() -> Result<()> { uv_snapshot!(context.filters(), context.sync() .arg("--group").arg("foo") .arg("--group").arg("bar") - .env("UV_NO_GROUP", "bar"), @r" + .env(EnvVars::UV_NO_GROUP, "bar"), @r" success: true exit_code: 0 ----- stdout ----- @@ -3569,7 +3569,7 @@ fn sync_exclude_group_with_environment_variable() -> Result<()> { .arg("--group").arg("foo") .arg("--group").arg("bar") .arg("--group").arg("baz") - .env("UV_NO_GROUP", "bar baz"), @r" + .env(EnvVars::UV_NO_GROUP, "bar baz"), @r" success: true exit_code: 0 ----- stdout ----- @@ -3586,7 +3586,7 @@ fn sync_exclude_group_with_environment_variable() -> Result<()> { .arg("--group").arg("bar") .arg("--group").arg("baz") .arg("--no-group").arg("bar") - .env("UV_NO_GROUP", "baz"), @r" + .env(EnvVars::UV_NO_GROUP, "baz"), @r" success: true exit_code: 0 ----- stdout ----- diff --git a/docs/reference/environment.md b/docs/reference/environment.md index 7f911b077..b0cd16e6e 100644 --- a/docs/reference/environment.md +++ b/docs/reference/environment.md @@ -151,7 +151,7 @@ Equivalent to the `--token` argument for self update. A GitHub token for authent Enables fetching files stored in Git LFS when installing a package from a Git repository. ### `UV_HIDE_BUILD_OUTPUT` -added in `0.9.14` +added in `0.9.15` Suppress output from the build backend when building source distributions, even in the event of build failures.