Skip to content

Commit 95761d9

Browse files
Conda hook search (#648)
fixes microsoft/vscode-python#25316 --------- Co-authored-by: Anthony Kim <[email protected]>
1 parent 0c07e51 commit 95761d9

File tree

1 file changed

+56
-16
lines changed

1 file changed

+56
-16
lines changed

src/managers/conda/condaUtils.ts

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
import { ENVS_EXTENSION_ID, EXTENSION_ROOT_DIR } from '../../common/constants';
2929
import { showErrorMessageWithLogs } from '../../common/errors/utils';
3030
import { Common, CondaStrings, PackageManagement, Pickers } from '../../common/localize';
31-
import { traceInfo } from '../../common/logging';
31+
import { traceError, traceInfo } from '../../common/logging';
3232
import { getWorkspacePersistentState } from '../../common/persistentState';
3333
import { pickProject } from '../../common/pickers/projects';
3434
import { createDeferred } from '../../common/utils/deferred';
@@ -280,13 +280,13 @@ function isPrefixOf(roots: string[], e: string): boolean {
280280
return false;
281281
}
282282

283-
function getNamedCondaPythonInfo(
283+
async function getNamedCondaPythonInfo(
284284
name: string,
285285
prefix: string,
286286
executable: string,
287287
version: string,
288288
conda: string,
289-
): PythonEnvironmentInfo {
289+
): Promise<PythonEnvironmentInfo> {
290290
const sv = shortVersion(version);
291291
const shellActivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
292292
const shellDeactivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
@@ -323,7 +323,7 @@ function getNamedCondaPythonInfo(
323323
]);
324324
shellDeactivation.set(ShellConstants.ZSH, [{ executable: 'conda', args: ['deactivate'] }]);
325325
}
326-
const psActivate = path.join(path.dirname(path.dirname(conda)), 'shell', 'condabin', 'conda-hook.ps1');
326+
const psActivate = await getCondaHookPs1Path(conda);
327327
shellActivation.set(ShellConstants.PWSH, [
328328
{ executable: '&', args: [psActivate] },
329329
{ executable: 'conda', args: ['activate', name] },
@@ -374,12 +374,12 @@ function getNamedCondaPythonInfo(
374374
};
375375
}
376376

377-
function getPrefixesCondaPythonInfo(
377+
async function getPrefixesCondaPythonInfo(
378378
prefix: string,
379379
executable: string,
380380
version: string,
381381
conda: string,
382-
): PythonEnvironmentInfo {
382+
): Promise<PythonEnvironmentInfo> {
383383
const sv = shortVersion(version);
384384
const shellActivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
385385
const shellDeactivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
@@ -416,7 +416,7 @@ function getPrefixesCondaPythonInfo(
416416
]);
417417
shellDeactivation.set(ShellConstants.ZSH, [{ executable: 'conda', args: ['deactivate'] }]);
418418
}
419-
const psActivate = path.join(path.dirname(path.dirname(conda)), 'shell', 'condabin', 'conda-hook.ps1');
419+
const psActivate = await getCondaHookPs1Path(conda);
420420
shellActivation.set(ShellConstants.PWSH, [
421421
{ executable: '&', args: [psActivate] },
422422
{ executable: 'conda', args: ['activate', prefix] },
@@ -487,14 +487,14 @@ function getCondaWithoutPython(name: string, prefix: string, conda: string): Pyt
487487
};
488488
}
489489

490-
function nativeToPythonEnv(
490+
async function nativeToPythonEnv(
491491
e: NativeEnvInfo,
492492
api: PythonEnvironmentApi,
493493
manager: EnvironmentManager,
494494
log: LogOutputChannel,
495495
conda: string,
496496
condaPrefixes: string[],
497-
): PythonEnvironment | undefined {
497+
): Promise<PythonEnvironment | undefined> {
498498
if (!(e.prefix && e.executable && e.version)) {
499499
let name = e.name;
500500
const environment = api.createPythonEnvironmentItem(
@@ -507,14 +507,14 @@ function nativeToPythonEnv(
507507

508508
if (e.name === 'base') {
509509
const environment = api.createPythonEnvironmentItem(
510-
getNamedCondaPythonInfo('base', e.prefix, e.executable, e.version, conda),
510+
await getNamedCondaPythonInfo('base', e.prefix, e.executable, e.version, conda),
511511
manager,
512512
);
513513
log.info(`Found base environment: ${e.prefix}`);
514514
return environment;
515515
} else if (!isPrefixOf(condaPrefixes, e.prefix)) {
516516
const environment = api.createPythonEnvironmentItem(
517-
getPrefixesCondaPythonInfo(e.prefix, e.executable, e.version, conda),
517+
await getPrefixesCondaPythonInfo(e.prefix, e.executable, e.version, conda),
518518
manager,
519519
);
520520
log.info(`Found prefix environment: ${e.prefix}`);
@@ -523,7 +523,7 @@ function nativeToPythonEnv(
523523
const basename = path.basename(e.prefix);
524524
const name = e.name ?? basename;
525525
const environment = api.createPythonEnvironmentItem(
526-
getNamedCondaPythonInfo(name, e.prefix, e.executable, e.version, conda),
526+
await getNamedCondaPythonInfo(name, e.prefix, e.executable, e.version, conda),
527527
manager,
528528
);
529529
log.info(`Found named environment: ${e.prefix}`);
@@ -586,8 +586,8 @@ export async function refreshCondaEnvs(
586586
.filter((e) => e.kind === NativePythonEnvironmentKind.conda);
587587
const collection: PythonEnvironment[] = [];
588588

589-
envs.forEach((e) => {
590-
const environment = nativeToPythonEnv(e, api, manager, log, condaPath, condaPrefixes);
589+
envs.forEach(async (e) => {
590+
const environment = await nativeToPythonEnv(e, api, manager, log, condaPath, condaPrefixes);
591591
if (environment) {
592592
collection.push(environment);
593593
}
@@ -699,7 +699,7 @@ async function createNamedCondaEnvironment(
699699
const version = await getVersion(envPath);
700700

701701
const environment = api.createPythonEnvironmentItem(
702-
getNamedCondaPythonInfo(envName, envPath, path.join(envPath, bin), version, await getConda()),
702+
await getNamedCondaPythonInfo(envName, envPath, path.join(envPath, bin), version, await getConda()),
703703
manager,
704704
);
705705
return environment;
@@ -757,7 +757,7 @@ async function createPrefixCondaEnvironment(
757757
const version = await getVersion(prefix);
758758

759759
const environment = api.createPythonEnvironmentItem(
760-
getPrefixesCondaPythonInfo(prefix, path.join(prefix, bin), version, await getConda()),
760+
await getPrefixesCondaPythonInfo(prefix, path.join(prefix, bin), version, await getConda()),
761761
manager,
762762
);
763763
return environment;
@@ -1052,3 +1052,43 @@ export async function checkForNoPythonCondaEnvironment(
10521052
}
10531053
return environment;
10541054
}
1055+
1056+
/**
1057+
* Returns the best guess path to conda-hook.ps1 given a conda executable path.
1058+
*
1059+
* Searches for conda-hook.ps1 in these locations (relative to the conda root):
1060+
* - shell/condabin/
1061+
* - Library/shell/condabin/
1062+
* - condabin/
1063+
* - etc/profile.d/
1064+
*/
1065+
async function getCondaHookPs1Path(condaPath: string): Promise<string> {
1066+
const condaRoot = path.dirname(path.dirname(condaPath));
1067+
1068+
const condaRootCandidates: string[] = [
1069+
path.join(condaRoot, 'shell', 'condabin'),
1070+
path.join(condaRoot, 'Library', 'shell', 'condabin'),
1071+
path.join(condaRoot, 'condabin'),
1072+
path.join(condaRoot, 'etc', 'profile.d'),
1073+
];
1074+
1075+
const checks = condaRootCandidates.map(async (hookSearchDir) => {
1076+
const candidate = path.join(hookSearchDir, 'conda-hook.ps1');
1077+
if (await fse.pathExists(candidate)) {
1078+
traceInfo(`Conda hook found at: ${candidate}`);
1079+
return candidate;
1080+
}
1081+
return undefined;
1082+
});
1083+
const results = await Promise.all(checks);
1084+
const found = results.find(Boolean);
1085+
if (found) {
1086+
return found as string;
1087+
}
1088+
traceError(
1089+
`Conda hook not found in any of the expected locations: ${condaRootCandidates.join(
1090+
', ',
1091+
)}, given conda path: ${condaPath}`,
1092+
);
1093+
return path.join(condaRoot, 'shell', 'condabin', 'conda-hook.ps1');
1094+
}

0 commit comments

Comments
 (0)