Skip to content

Commit 81d41f8

Browse files
committed
feat: enhance conda path resolution with caching and improved search logic
1 parent c655ced commit 81d41f8

1 file changed

Lines changed: 93 additions & 23 deletions

File tree

src/managers/conda/condaUtils.ts

Lines changed: 93 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ async function getNamedCondaPythonInfo(
292292
const shellDeactivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
293293

294294
if (conda.includes('/') || conda.includes('\\')) {
295-
const shActivate = path.join(path.dirname(path.dirname(conda)), 'etc', 'profile.d', 'conda.sh');
295+
const shActivate = await getCondaShPath(conda);
296296

297297
if (isWindows()) {
298298
shellActivation.set(ShellConstants.GITBASH, [
@@ -385,7 +385,7 @@ async function getPrefixesCondaPythonInfo(
385385
const shellDeactivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
386386

387387
if (conda.includes('/') || conda.includes('\\')) {
388-
const shActivate = path.join(path.dirname(path.dirname(conda)), 'etc', 'profile.d', 'conda.sh');
388+
const shActivate = await getCondaShPath(conda);
389389

390390
if (isWindows()) {
391391
shellActivation.set(ShellConstants.GITBASH, [
@@ -1055,6 +1055,88 @@ export async function checkForNoPythonCondaEnvironment(
10551055

10561056
// Cache for conda hook paths to avoid redundant filesystem checks
10571057
const condaHookPathCache = new Map<string, Promise<string>>();
1058+
const condaShPathCache = new Map<string, Promise<string>>();
1059+
1060+
/**
1061+
* Helper function that checks for a file in a list of locations.
1062+
* Returns the first location where the file exists, or undefined if not found.
1063+
*/
1064+
async function findFileInLocations(
1065+
locations: string[],
1066+
description: string,
1067+
condaPath: string,
1068+
): Promise<string | undefined> {
1069+
for (const location of locations) {
1070+
if (await fse.pathExists(location)) {
1071+
traceInfo(`${description} found in ${location}`);
1072+
return location;
1073+
}
1074+
}
1075+
traceError(
1076+
`${description} not found in any of the locations: ${locations.join(', ')}, ` +
1077+
`given conda path: ${condaPath}`,
1078+
);
1079+
return undefined;
1080+
}
1081+
1082+
/**
1083+
* Returns the path to conda.sh given a conda executable path.
1084+
*
1085+
* Searches for conda.sh in these locations (relative to the conda root):
1086+
* - etc/profile.d/conda.sh
1087+
* - shell/etc/profile.d/conda.sh
1088+
* - Library/etc/profile.d/conda.sh
1089+
* - lib/pythonX.Y/site-packages/conda/shell/etc/profile.d/conda.sh
1090+
* - site-packages/conda/shell/etc/profile.d/conda.sh
1091+
* Also checks some system-level locations
1092+
*/
1093+
async function getCondaShPath(condaPath: string): Promise<string> {
1094+
// Check cache first
1095+
const cachedPath = condaShPathCache.get(condaPath);
1096+
if (cachedPath) {
1097+
return cachedPath;
1098+
}
1099+
1100+
// Create the promise for finding the conda.sh path
1101+
const shPathPromise = (async () => {
1102+
const condaRoot = path.dirname(path.dirname(condaPath));
1103+
1104+
// First try standard conda installation locations
1105+
const standardLocations = [
1106+
path.join(condaRoot, 'etc', 'profile.d', 'conda.sh'),
1107+
path.join(condaRoot, 'shell', 'etc', 'profile.d', 'conda.sh'),
1108+
path.join(condaRoot, 'Library', 'etc', 'profile.d', 'conda.sh'),
1109+
];
1110+
1111+
// Check standard locations first
1112+
const standardLocation = await findFileInLocations(standardLocations, 'conda.sh', condaPath);
1113+
if (standardLocation) {
1114+
return standardLocation;
1115+
}
1116+
1117+
// If not found in standard locations, try pip install locations
1118+
const possiblePythonVersions = ['python3.9', 'python3.10', 'python3.11', 'python3.12', 'python3.8'];
1119+
const pipInstallLocations = [
1120+
...possiblePythonVersions.map((ver) =>
1121+
path.join(condaRoot, 'lib', ver, 'site-packages', 'conda', 'shell', 'etc', 'profile.d', 'conda.sh'),
1122+
),
1123+
path.join(condaRoot, 'site-packages', 'conda', 'shell', 'etc', 'profile.d', 'conda.sh'),
1124+
];
1125+
1126+
// Check pip install locations
1127+
const pipLocation = await findFileInLocations(pipInstallLocations, 'conda.sh', condaPath);
1128+
if (pipLocation) {
1129+
return pipLocation;
1130+
}
1131+
1132+
// Fall back to the traditional location as a last resort
1133+
return path.join(condaRoot, 'etc', 'profile.d', 'conda.sh');
1134+
})();
1135+
1136+
// Store in cache and return
1137+
condaShPathCache.set(condaPath, shPathPromise);
1138+
return shPathPromise;
1139+
}
10581140

10591141
/**
10601142
* Returns the best guess path to conda-hook.ps1 given a conda executable path.
@@ -1076,31 +1158,19 @@ async function getCondaHookPs1Path(condaPath: string): Promise<string> {
10761158
const hookPathPromise = (async () => {
10771159
const condaRoot = path.dirname(path.dirname(condaPath));
10781160

1079-
const condaRootCandidates: string[] = [
1080-
path.join(condaRoot, 'shell', 'condabin'),
1081-
path.join(condaRoot, 'Library', 'shell', 'condabin'),
1082-
path.join(condaRoot, 'condabin'),
1083-
path.join(condaRoot, 'etc', 'profile.d'),
1161+
const hookLocations = [
1162+
path.join(condaRoot, 'shell', 'condabin', 'conda-hook.ps1'),
1163+
path.join(condaRoot, 'Library', 'shell', 'condabin', 'conda-hook.ps1'),
1164+
path.join(condaRoot, 'condabin', 'conda-hook.ps1'),
1165+
path.join(condaRoot, 'etc', 'profile.d', 'conda-hook.ps1'),
10841166
];
10851167

1086-
const checks = condaRootCandidates.map(async (hookSearchDir) => {
1087-
const candidate = path.join(hookSearchDir, 'conda-hook.ps1');
1088-
if (await fse.pathExists(candidate)) {
1089-
traceInfo(`Conda hook found at: ${candidate}`);
1090-
return candidate;
1091-
}
1092-
return undefined;
1093-
});
1094-
const results = await Promise.all(checks);
1095-
const found = results.find(Boolean);
1168+
const found = await findFileInLocations(hookLocations, 'conda-hook.ps1', condaPath);
10961169
if (found) {
1097-
return found as string;
1170+
return found;
10981171
}
1099-
traceError(
1100-
`Conda hook not found in any of the expected locations: ${condaRootCandidates.join(
1101-
', ',
1102-
)}, given conda path: ${condaPath}`,
1103-
);
1172+
1173+
// Fall back to the traditional location as a last resort
11041174
return path.join(condaRoot, 'shell', 'condabin', 'conda-hook.ps1');
11051175
})();
11061176

0 commit comments

Comments
 (0)