diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 3481b3e093e6..52e437c37c41 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -618,9 +618,6 @@ pub enum ProjectCommand { /// arguments to uv. All options to uv must be provided before the command, /// e.g., `uv run --verbose foo`. A `--` can be used to separate the command /// from uv options for clarity, e.g., `uv run --python 3.12 -- python`. - /// - /// Respects `.env` files in the current directory unless `--no-env-file` is - /// provided. #[command( after_help = "Use `uv help run` for more details.", after_long_help = "" @@ -2663,13 +2660,11 @@ pub struct RunArgs { /// /// Can be provided multiple times, with subsequent files overriding values defined in /// previous files. - /// - /// Defaults to reading `.env` in the current working directory. #[arg(long, env = EnvVars::UV_ENV_FILE)] pub env_file: Vec<PathBuf>, /// Avoid reading environment variables from a `.env` file. - #[arg(long, conflicts_with = "env_file", value_parser = clap::builder::BoolishValueParser::new(), env = EnvVars::UV_NO_ENV_FILE)] + #[arg(long, value_parser = clap::builder::BoolishValueParser::new(), env = EnvVars::UV_NO_ENV_FILE)] pub no_env_file: bool, /// The command to run. diff --git a/crates/uv-static/src/env_vars.rs b/crates/uv-static/src/env_vars.rs index 6f26cd4fe28e..124e9d416a40 100644 --- a/crates/uv-static/src/env_vars.rs +++ b/crates/uv-static/src/env_vars.rs @@ -520,9 +520,9 @@ impl EnvVars { #[attr_hidden] pub const KEYRING_TEST_CREDENTIALS: &'static str = "KEYRING_TEST_CREDENTIALS"; - /// Used to overwrite path for loading `.env` files when executing `uv run` commands. + /// `.env` files from which to load environment variables when executing `uv run` commands. pub const UV_ENV_FILE: &'static str = "UV_ENV_FILE"; - /// Used to ignore `.env` files when executing `uv run` commands. + /// Ignore `.env` files when executing `uv run` commands. pub const UV_NO_ENV_FILE: &'static str = "UV_NO_ENV_FILE"; } diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index 6926b2a375a9..e308610463ed 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -8,7 +8,7 @@ use std::path::{Path, PathBuf}; use anstream::eprint; use anyhow::{anyhow, bail, Context}; use futures::StreamExt; -use itertools::{Either, Itertools}; +use itertools::Itertools; use owo_colors::OwoColorize; use tokio::process::Command; use tracing::{debug, warn}; @@ -111,38 +111,19 @@ pub(crate) async fn run( // Read from the `.env` file, if necessary. if !no_env_file { - let env_file_paths = if env_file.is_empty() { - Either::Left(std::iter::once(Path::new(".env"))) - } else { - Either::Right(env_file.iter().rev().map(PathBuf::as_path)) - }; - for env_file_path in env_file_paths { + for env_file_path in env_file.iter().rev().map(PathBuf::as_path) { match dotenvy::from_path(env_file_path) { Err(dotenvy::Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => { - if env_file.is_empty() { - debug!( - "No environment file found at: `{}`", - env_file_path.simplified_display() - ); - } else { - bail!( - "No environment file found at: `{}`", - env_file_path.simplified_display() - ); - } + bail!( + "No environment file found at: `{}`", + env_file_path.simplified_display() + ); } Err(dotenvy::Error::Io(err)) => { - if env_file.is_empty() { - debug!( - "Failed to read environment file `{}`: {err}", - env_file_path.simplified_display() - ); - } else { - bail!( - "Failed to read environment file `{}`: {err}", - env_file_path.simplified_display() - ); - } + bail!( + "Failed to read environment file `{}`: {err}", + env_file_path.simplified_display() + ); } Err(dotenvy::Error::LineParse(content, position)) => { warn_user!( diff --git a/crates/uv/tests/it/run.rs b/crates/uv/tests/it/run.rs index d5adf94bfd40..36056fec2ef3 100644 --- a/crates/uv/tests/it/run.rs +++ b/crates/uv/tests/it/run.rs @@ -2855,6 +2855,18 @@ fn run_with_env() -> Result<()> { success: true exit_code: 0 ----- stdout ----- + None + None + None + None + + ----- stderr ----- + "###); + + uv_snapshot!(context.filters(), context.run().arg("--env-file").arg(".env").arg("test.py"), @r###" + success: true + exit_code: 0 + ----- stdout ----- palpatine leia_organa obi_wan_kenobi @@ -2965,51 +2977,11 @@ fn run_with_env_omitted() -> Result<()> { " })?; - uv_snapshot!(context.filters(), context.run().arg("--no-env-file").arg("test.py"), @r###" - success: true - exit_code: 0 - ----- stdout ----- - None - - ----- stderr ----- - "###); - - Ok(()) -} - -#[test] -fn run_with_parent_env() -> Result<()> { - let context = TestContext::new("3.12"); - - context - .temp_dir - .child("test") - .child("test.py") - .write_str(indoc! { " - import os - print(os.environ.get('THE_EMPIRE_VARIABLE')) - print(os.environ.get('REBEL_1')) - print(os.environ.get('REBEL_2')) - print(os.environ.get('REBEL_3')) - " - })?; - - context.temp_dir.child(".env").write_str(indoc! { " - THE_EMPIRE_VARIABLE=palpatine - REBEL_1=leia_organa - REBEL_2=obi_wan_kenobi - REBEL_3=C3PO - " - })?; - - uv_snapshot!(context.filters(), context.run().arg("test.py").current_dir(context.temp_dir.child("test")), @r###" + uv_snapshot!(context.filters(), context.run().arg("--env-file").arg(".env").arg("--no-env-file").arg("test.py"), @r###" success: true exit_code: 0 ----- stdout ----- None - None - None - None ----- stderr ----- "###); @@ -3032,7 +3004,7 @@ fn run_with_malformed_env() -> Result<()> { " })?; - uv_snapshot!(context.filters(), context.run().arg("test.py"), @r###" + uv_snapshot!(context.filters(), context.run().arg("--env-file").arg(".env").arg("test.py"), @r###" success: true exit_code: 0 ----- stdout ----- diff --git a/docs/configuration/environment.md b/docs/configuration/environment.md index a1cecf3504a5..06002d38780b 100644 --- a/docs/configuration/environment.md +++ b/docs/configuration/environment.md @@ -174,5 +174,5 @@ uv respects the following environment variables: For example, `RUST_LOG=trace` will enable trace-level logging. See the [tracing documentation](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax) for more. -- <a id="UV_ENV_FILE"></a> [`UV_ENV_FILE`](#UV_ENV_FILE): Used to overwrite path for loading `.env` files when executing `uv run` commands. -- <a id="UV_NO_ENV_FILE"></a> [`UV_NO_ENV_FILE`](#UV_NO_ENV_FILE): Used to ignore `.env` files when executing `uv run` commands. +- <a id="UV_ENV_FILE"></a> [`UV_ENV_FILE`](#UV_ENV_FILE): `.env` files from which to load environment variables when executing `uv run` commands. +- <a id="UV_NO_ENV_FILE"></a> [`UV_NO_ENV_FILE`](#UV_NO_ENV_FILE): Ignore `.env` files when executing `uv run` commands. diff --git a/docs/configuration/files.md b/docs/configuration/files.md index 86152bffd278..3ad6d3bc72b4 100644 --- a/docs/configuration/files.md +++ b/docs/configuration/files.md @@ -74,19 +74,25 @@ See the [settings reference](../reference/settings.md) for an enumeration of the ## `.env` -By default, `uv run` will load environment variables from a `.env` file in the current working -directory, following the discovery and parsing rules of the -[`dotenvy`](https://github.com/allan2/dotenvy) crate. +`uv run` can load environment variables from dotenv files (e.g., `.env`, `.env.local`, +`.env.development`), powered by the [`dotenvy`](https://github.com/allan2/dotenvy) crate. To load a `.env` file from a dedicated location, set the `UV_ENV_FILE` environment variable, or pass the `--env-file` flag to `uv run`. +For example, to load environment variables from a `.env` file in the current working directory: + +```console +$ uv run --env-file .env -- echo $MY_ENV_VAR +``` + The `--env-file` flag can be provided multiple times, with subsequent files overriding values defined in previous files. To provide multiple files via the `UV_ENV_FILE` environment variable, separate the paths with a space (e.g., `UV_ENV_FILE="/path/to/file1 /path/to/file2"`). -To disable this behavior, set the `UV_NO_ENV_FILE` environment variable to `1`, or pass the -`--no-env-file` flag to `uv run`. +To disable dotenv loading (e.g., to override `UV_ENV_FILE` or the `--env-file` command-line +argument), set the `UV_NO_ENV_FILE` environment variable to `1`, or pass the`--no-env-file` flag to +`uv run`. If the same variable is defined in the environment and in a `.env` file, the value from the environment will take precedence. diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 7ce2ac4d44ec..cac93c92030f 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -64,8 +64,6 @@ When used outside a project, if a virtual environment can be found in the curren Arguments following the command (or script) are not interpreted as arguments to uv. All options to uv must be provided before the command, e.g., `uv run --verbose foo`. A `--` can be used to separate the command from uv options for clarity, e.g., `uv run --python 3.12 -- python`. -Respects `.env` files in the current directory unless `--no-env-file` is provided. - <h3 class="cli-reference">Usage</h3> ``` @@ -145,8 +143,6 @@ uv run [OPTIONS] [COMMAND] <p>Can be provided multiple times, with subsequent files overriding values defined in previous files.</p> -<p>Defaults to reading <code>.env</code> in the current working directory.</p> - <p>May also be set with the <code>UV_ENV_FILE</code> environment variable.</p> </dd><dt><code>--exclude-newer</code> <i>exclude-newer</i></dt><dd><p>Limit candidate packages to those that were uploaded prior to the given date.</p>