Skip to content

Commit d805d4a

Browse files
authored
Use _CONDA_ROOT to detect conda base environments (#15680)
While investigating #15679, I noticed this variable was available in the environment and seems like a nice additional heuristic.
1 parent ee5f155 commit d805d4a

File tree

4 files changed

+88
-11
lines changed

4 files changed

+88
-11
lines changed

crates/uv-python/src/lib.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,7 @@ mod tests {
12081208
&[
12091209
("CONDA_PREFIX", Some(baseenv.as_os_str())),
12101210
("CONDA_DEFAULT_ENV", Some(&OsString::from("base"))),
1211+
(EnvVars::CONDA_ROOT, None),
12111212
],
12121213
|| {
12131214
find_python_installation(
@@ -1231,6 +1232,7 @@ mod tests {
12311232
&[
12321233
("CONDA_PREFIX", Some(baseenv.as_os_str())),
12331234
("CONDA_DEFAULT_ENV", Some(&OsString::from("base"))),
1235+
(EnvVars::CONDA_ROOT, None),
12341236
],
12351237
|| {
12361238
find_python_installation(
@@ -1275,6 +1277,66 @@ mod tests {
12751277
"We should find the conda environment"
12761278
);
12771279

1280+
// Test _CONDA_ROOT detection of base environment
1281+
let conda_root_env = context.tempdir.child("conda-root");
1282+
TestContext::mock_conda_prefix(&conda_root_env, "3.12.2")?;
1283+
1284+
// When _CONDA_ROOT matches CONDA_PREFIX, it should be treated as a base environment
1285+
let result = context.run_with_vars(
1286+
&[
1287+
(EnvVars::CONDA_PREFIX, Some(conda_root_env.as_os_str())),
1288+
(EnvVars::CONDA_ROOT, Some(conda_root_env.as_os_str())),
1289+
(
1290+
EnvVars::CONDA_DEFAULT_ENV,
1291+
Some(&OsString::from("custom-name")),
1292+
),
1293+
],
1294+
|| {
1295+
find_python_installation(
1296+
&PythonRequest::Default,
1297+
EnvironmentPreference::OnlyVirtual,
1298+
PythonPreference::OnlySystem,
1299+
&context.cache,
1300+
Preview::default(),
1301+
)
1302+
},
1303+
)?;
1304+
1305+
assert!(
1306+
matches!(result, Err(PythonNotFound { .. })),
1307+
"Base environment detected via _CONDA_ROOT should be excluded from virtual environments; got {result:?}"
1308+
);
1309+
1310+
// When _CONDA_ROOT doesn't match CONDA_PREFIX, it should be treated as a regular conda environment
1311+
let other_conda_env = context.tempdir.child("other-conda");
1312+
TestContext::mock_conda_prefix(&other_conda_env, "3.12.3")?;
1313+
1314+
let python = context.run_with_vars(
1315+
&[
1316+
(EnvVars::CONDA_PREFIX, Some(other_conda_env.as_os_str())),
1317+
(EnvVars::CONDA_ROOT, Some(conda_root_env.as_os_str())),
1318+
(
1319+
EnvVars::CONDA_DEFAULT_ENV,
1320+
Some(&OsString::from("custom-env")),
1321+
),
1322+
],
1323+
|| {
1324+
find_python_installation(
1325+
&PythonRequest::Default,
1326+
EnvironmentPreference::OnlyVirtual,
1327+
PythonPreference::OnlySystem,
1328+
&context.cache,
1329+
Preview::default(),
1330+
)
1331+
},
1332+
)??;
1333+
1334+
assert_eq!(
1335+
python.interpreter().python_full_version().to_string(),
1336+
"3.12.3",
1337+
"Non-base conda environment should be available for virtual environment preference"
1338+
);
1339+
12781340
Ok(())
12791341
}
12801342

crates/uv-python/src/virtualenv.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,19 +79,27 @@ pub(crate) enum CondaEnvironmentKind {
7979
impl CondaEnvironmentKind {
8080
/// Whether the given `CONDA_PREFIX` path is the base Conda environment.
8181
///
82-
/// When the base environment is used, `CONDA_DEFAULT_ENV` will be set to a name, i.e., `base` or
83-
/// `root` which does not match the prefix, e.g. `/usr/local` instead of
84-
/// `/usr/local/conda/envs/<name>`.
82+
/// The base environment is typically stored in a location matching the `_CONDA_ROOT` path.
8583
///
86-
/// Note the name `CONDA_DEFAULT_ENV` is misleading, it's the current environment, not the base
87-
/// environment name.
84+
/// Additionally, when the base environment is active, `CONDA_DEFAULT_ENV` will be set to a
85+
/// name, e.g., `base`, which does not match the `CONDA_PREFIX`, e.g., `/usr/local` instead of
86+
/// `/usr/local/conda/envs/<name>`. Note the name `CONDA_DEFAULT_ENV` is misleading, it's the
87+
/// active environment name, not a constant base environment name.
8888
fn from_prefix_path(path: &Path) -> Self {
89-
// If we cannot read `CONDA_DEFAULT_ENV`, there's no way to know if the base environment
89+
// If `_CONDA_ROOT` is set and matches `CONDA_PREFIX`, it's the base environment.
90+
if let Ok(conda_root) = env::var(EnvVars::CONDA_ROOT) {
91+
if path == Path::new(&conda_root) {
92+
return Self::Base;
93+
}
94+
}
95+
96+
// Next, we'll use a heuristic based on `CONDA_DEFAULT_ENV`
9097
let Ok(current_env) = env::var(EnvVars::CONDA_DEFAULT_ENV) else {
9198
return Self::Child;
9299
};
93100

94-
// These are the expected names for the base environment
101+
// These are the expected names for the base environment; we may want to remove this
102+
// restriction in the future as it's not strictly necessary.
95103
if current_env != "base" && current_env != "root" {
96104
return Self::Child;
97105
}

crates/uv-static/src/env_vars.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,12 +497,15 @@ impl EnvVars {
497497
/// Used to detect an activated virtual environment.
498498
pub const VIRTUAL_ENV: &'static str = "VIRTUAL_ENV";
499499

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

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

506+
/// Used to determine the root install path of Conda.
507+
pub const CONDA_ROOT: &'static str = "_CONDA_ROOT";
508+
506509
/// If set to `1` before a virtual environment is activated, then the
507510
/// virtual environment name will not be prepended to the terminal prompt.
508511
pub const VIRTUAL_ENV_DISABLE_PROMPT: &'static str = "VIRTUAL_ENV_DISABLE_PROMPT";

docs/reference/environment.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,11 +584,15 @@ This is a quasi-standard variable, described, e.g., in `ncurses(3x)`.
584584

585585
### `CONDA_DEFAULT_ENV`
586586

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

589589
### `CONDA_PREFIX`
590590

591-
Used to detect an activated Conda environment.
591+
Used to detect the path of an active Conda environment.
592+
593+
### `CONDA_ROOT`
594+
595+
Used to determine the root install path of Conda.
592596

593597
### `FISH_VERSION`
594598

0 commit comments

Comments
 (0)