in src/client/pythonEnvironments/common/environmentManagers/conda.ts [240:347]
private static async locate(): Promise<Conda | undefined> {
traceVerbose(`Searching for conda.`);
const home = getUserHomeDir();
const suffix = getOSType() === OSType.Windows ? 'Scripts\\conda.exe' : 'bin/conda';
// Produce a list of candidate binaries to be probed by exec'ing them.
async function* getCandidates() {
const customCondaPath = getPythonSetting<string>('condaPath');
if (customCondaPath && customCondaPath !== 'conda') {
// If user has specified a custom conda path, use it first.
yield customCondaPath;
}
// Check unqualified filename first, in case it's on PATH.
yield 'conda';
if (getOSType() === OSType.Windows) {
yield* getCandidatesFromRegistry();
}
yield* getCandidatesFromKnownPaths();
yield* getCandidatesFromEnvironmentsTxt();
}
async function* getCandidatesFromRegistry() {
const interps = await getRegistryInterpreters();
const candidates = interps
.filter((interp) => interp.interpreterPath && interp.distroOrgName === 'ContinuumAnalytics')
.map((interp) => path.join(path.win32.dirname(interp.interpreterPath), suffix));
yield* candidates;
}
async function* getCandidatesFromKnownPaths() {
// Check common locations. We want to look up "<prefix>/*conda*/<suffix>", where prefix and suffix
// depend on the platform, to account for both Anaconda and Miniconda, and all possible variations.
// The check cannot use globs, because on Windows, prefixes are absolute paths with a drive letter,
// and the glob module doesn't understand globs with drive letters in them, producing wrong results
// for "C:/*" etc.
const prefixes: string[] = [];
if (getOSType() === OSType.Windows) {
const programData = getEnvironmentVariable('PROGRAMDATA') || 'C:\\ProgramData';
prefixes.push(programData);
if (home) {
const localAppData = getEnvironmentVariable('LOCALAPPDATA') || path.join(home, 'AppData', 'Local');
prefixes.push(home, path.join(localAppData, 'Continuum'));
}
} else {
prefixes.push('/usr/share', '/usr/local/share', '/opt');
if (home) {
prefixes.push(home, path.join(home, 'opt'));
}
}
for (const prefix of prefixes) {
let items: string[] | undefined;
try {
items = await fsapi.readdir(prefix);
} catch (ex) {
// Directory doesn't exist or is not readable - not an error.
items = undefined;
}
if (items !== undefined) {
yield* items
.filter((fileName) => fileName.toLowerCase().includes('conda'))
.map((fileName) => path.join(prefix, fileName, suffix));
}
}
}
async function* getCandidatesFromEnvironmentsTxt() {
if (!home) {
return;
}
let contents: string;
try {
contents = await fsapi.readFile(path.join(home, '.conda', 'environments.txt'), 'utf8');
} catch (ex) {
// File doesn't exist or is not readable - not an error.
contents = '';
}
// Match conda behavior; see conda.gateways.disk.read.yield_lines().
// Note that this precludes otherwise legal paths with trailing spaces.
yield* contents
.split(/\r?\n/g)
.map((line) => line.trim())
.filter((line) => line !== '' && !line.startsWith('#'))
.map((line) => path.join(line, suffix));
}
// Probe the candidates, and pick the first one that exists and does what we need.
for await (const condaPath of getCandidates()) {
traceVerbose(`Probing conda binary: ${condaPath}`);
const conda = new Conda(condaPath);
try {
await conda.getInfo();
traceVerbose(`Found conda via filesystem probing: ${condaPath}`);
return conda;
} catch (ex) {
// Failed to spawn because the binary doesn't exist or isn't on PATH, or the current
// user doesn't have execute permissions for it, or this conda couldn't handle command
// line arguments that we passed (indicating an old version that we do not support).
traceVerbose('Failed to spawn conda binary', condaPath, ex);
}
}
// Didn't find anything.
traceVerbose("Couldn't locate the conda binary.");
return undefined;
}