Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions crates/uv-python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,7 @@ mod tests {
&[
("CONDA_PREFIX", Some(baseenv.as_os_str())),
("CONDA_DEFAULT_ENV", Some(&OsString::from("base"))),
(EnvVars::CONDA_ROOT, None),
],
|| {
find_python_installation(
Expand All @@ -1231,6 +1232,7 @@ mod tests {
&[
("CONDA_PREFIX", Some(baseenv.as_os_str())),
("CONDA_DEFAULT_ENV", Some(&OsString::from("base"))),
(EnvVars::CONDA_ROOT, None),
],
|| {
find_python_installation(
Expand Down Expand Up @@ -1275,6 +1277,66 @@ mod tests {
"We should find the conda environment"
);

// Test _CONDA_ROOT detection of base environment
let conda_root_env = context.tempdir.child("conda-root");
TestContext::mock_conda_prefix(&conda_root_env, "3.12.2")?;

// When _CONDA_ROOT matches CONDA_PREFIX, it should be treated as a base environment
let result = context.run_with_vars(
&[
(EnvVars::CONDA_PREFIX, Some(conda_root_env.as_os_str())),
(EnvVars::CONDA_ROOT, Some(conda_root_env.as_os_str())),
(
EnvVars::CONDA_DEFAULT_ENV,
Some(&OsString::from("custom-name")),
),
],
|| {
find_python_installation(
&PythonRequest::Default,
EnvironmentPreference::OnlyVirtual,
PythonPreference::OnlySystem,
&context.cache,
Preview::default(),
)
},
)?;

assert!(
matches!(result, Err(PythonNotFound { .. })),
"Base environment detected via _CONDA_ROOT should be excluded from virtual environments; got {result:?}"
);

// When _CONDA_ROOT doesn't match CONDA_PREFIX, it should be treated as a regular conda environment
let other_conda_env = context.tempdir.child("other-conda");
TestContext::mock_conda_prefix(&other_conda_env, "3.12.3")?;

let python = context.run_with_vars(
&[
(EnvVars::CONDA_PREFIX, Some(other_conda_env.as_os_str())),
(EnvVars::CONDA_ROOT, Some(conda_root_env.as_os_str())),
(
EnvVars::CONDA_DEFAULT_ENV,
Some(&OsString::from("custom-env")),
),
],
|| {
find_python_installation(
&PythonRequest::Default,
EnvironmentPreference::OnlyVirtual,
PythonPreference::OnlySystem,
&context.cache,
Preview::default(),
)
},
)??;

assert_eq!(
python.interpreter().python_full_version().to_string(),
"3.12.3",
"Non-base conda environment should be available for virtual environment preference"
);

Ok(())
}

Expand Down
22 changes: 15 additions & 7 deletions crates/uv-python/src/virtualenv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,27 @@ pub(crate) enum CondaEnvironmentKind {
impl CondaEnvironmentKind {
/// Whether the given `CONDA_PREFIX` path is the base Conda environment.
///
/// When the base environment is used, `CONDA_DEFAULT_ENV` will be set to a name, i.e., `base` or
/// `root` which does not match the prefix, e.g. `/usr/local` instead of
/// `/usr/local/conda/envs/<name>`.
/// The base environment is typically stored in a location matching the `_CONDA_ROOT` path.
///
/// Note the name `CONDA_DEFAULT_ENV` is misleading, it's the current environment, not the base
/// environment name.
/// Additionally, when the base environment is active, `CONDA_DEFAULT_ENV` will be set to a
/// name, e.g., `base`, which does not match the `CONDA_PREFIX`, e.g., `/usr/local` instead of
/// `/usr/local/conda/envs/<name>`. Note the name `CONDA_DEFAULT_ENV` is misleading, it's the
/// active environment name, not a constant base environment name.
fn from_prefix_path(path: &Path) -> Self {
// If we cannot read `CONDA_DEFAULT_ENV`, there's no way to know if the base environment
// If `_CONDA_ROOT` is set and matches `CONDA_PREFIX`, it's the base environment.
if let Ok(conda_root) = env::var(EnvVars::CONDA_ROOT) {
if path == Path::new(&conda_root) {
return Self::Base;
}
}

// Next, we'll use a heuristic based on `CONDA_DEFAULT_ENV`
let Ok(current_env) = env::var(EnvVars::CONDA_DEFAULT_ENV) else {
return Self::Child;
};

// These are the expected names for the base environment
// These are the expected names for the base environment; we may want to remove this
// restriction in the future as it's not strictly necessary.
if current_env != "base" && current_env != "root" {
return Self::Child;
}
Expand Down
7 changes: 5 additions & 2 deletions crates/uv-static/src/env_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,12 +497,15 @@ impl EnvVars {
/// Used to detect an activated virtual environment.
pub const VIRTUAL_ENV: &'static str = "VIRTUAL_ENV";

/// Used to detect an activated Conda environment.
/// Used to detect the path of an active Conda environment.
pub const CONDA_PREFIX: &'static str = "CONDA_PREFIX";

/// Used to determine if an active Conda environment is the base environment or not.
/// Used to determine the name of the active Conda environment.
pub const CONDA_DEFAULT_ENV: &'static str = "CONDA_DEFAULT_ENV";

/// Used to determine the root install path of Conda.
pub const CONDA_ROOT: &'static str = "_CONDA_ROOT";

/// If set to `1` before a virtual environment is activated, then the
/// virtual environment name will not be prepended to the terminal prompt.
pub const VIRTUAL_ENV_DISABLE_PROMPT: &'static str = "VIRTUAL_ENV_DISABLE_PROMPT";
Expand Down
8 changes: 6 additions & 2 deletions docs/reference/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -584,11 +584,15 @@ This is a quasi-standard variable, described, e.g., in `ncurses(3x)`.

### `CONDA_DEFAULT_ENV`

Used to determine if an active Conda environment is the base environment or not.
Used to determine the name of the active Conda environment.

### `CONDA_PREFIX`

Used to detect an activated Conda environment.
Used to detect the path of an active Conda environment.

### `CONDA_ROOT`

Used to determine the root install path of Conda.

### `FISH_VERSION`

Expand Down
Loading