Skip to content

Commit 5894555

Browse files
author
Kartik Raj
authored
Ensure conda info command isn't run multiple times during startup when large number of conda interpreters are present (#18808)
1 parent 67398a6 commit 5894555

File tree

4 files changed

+24
-17
lines changed

4 files changed

+24
-17
lines changed

news/2 Fixes/18200.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Ensure `conda info` command isn't run multiple times during startup when large number of conda interpreters are present.

src/client/pythonEnvironments/base/locators/composite/envsResolver.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export class PythonEnvsResolver implements IResolvingLocator {
7878
);
7979
} else if (seen[event.index] !== undefined) {
8080
const old = seen[event.index];
81-
seen[event.index] = await resolveBasicEnv(event.update);
81+
seen[event.index] = await resolveBasicEnv(event.update, true);
8282
didUpdate.fire({ old, index: event.index, update: seen[event.index] });
8383
this.resolveInBackground(event.index, state, didUpdate, seen).ignoreErrors();
8484
} else {
@@ -92,7 +92,8 @@ export class PythonEnvsResolver implements IResolvingLocator {
9292

9393
let result = await iterator.next();
9494
while (!result.done) {
95-
const currEnv = await resolveBasicEnv(result.value);
95+
// Use cache from the current refresh where possible.
96+
const currEnv = await resolveBasicEnv(result.value, true);
9697
seen.push(currEnv);
9798
yield currEnv;
9899
this.resolveInBackground(seen.indexOf(currEnv), state, didUpdate, seen).ignoreErrors();

src/client/pythonEnvironments/base/locators/composite/resolverUtils.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import { BasicEnvInfo } from '../../locator';
2727
import { parseVersionFromExecutable } from '../../info/executable';
2828
import { traceError, traceWarn } from '../../../../logging';
2929

30-
function getResolvers(): Map<PythonEnvKind, (env: BasicEnvInfo) => Promise<PythonEnvInfo>> {
31-
const resolvers = new Map<PythonEnvKind, (_: BasicEnvInfo) => Promise<PythonEnvInfo>>();
30+
function getResolvers(): Map<PythonEnvKind, (env: BasicEnvInfo, useCache?: boolean) => Promise<PythonEnvInfo>> {
31+
const resolvers = new Map<PythonEnvKind, (_: BasicEnvInfo, useCache?: boolean) => Promise<PythonEnvInfo>>();
3232
Object.values(PythonEnvKind).forEach((k) => {
3333
resolvers.set(k, resolveGloballyInstalledEnv);
3434
});
@@ -46,11 +46,11 @@ function getResolvers(): Map<PythonEnvKind, (env: BasicEnvInfo) => Promise<Pytho
4646
* executable and returns it. Notice `undefined` is never returned, so environment
4747
* returned could still be invalid.
4848
*/
49-
export async function resolveBasicEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
49+
export async function resolveBasicEnv(env: BasicEnvInfo, useCache = false): Promise<PythonEnvInfo> {
5050
const { kind, source } = env;
5151
const resolvers = getResolvers();
5252
const resolverForKind = resolvers.get(kind)!;
53-
const resolvedEnv = await resolverForKind(env);
53+
const resolvedEnv = await resolverForKind(env, useCache);
5454
resolvedEnv.searchLocation = getSearchLocation(resolvedEnv);
5555
resolvedEnv.source = uniq(resolvedEnv.source.concat(source ?? []));
5656
if (getOSType() === OSType.Windows && resolvedEnv.source?.includes(PythonEnvSource.WindowsRegistry)) {
@@ -137,13 +137,13 @@ async function resolveSimpleEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
137137
return envInfo;
138138
}
139139

140-
async function resolveCondaEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
140+
async function resolveCondaEnv(env: BasicEnvInfo, useCache?: boolean): Promise<PythonEnvInfo> {
141141
const { executablePath } = env;
142142
const conda = await Conda.getConda();
143143
if (conda === undefined) {
144144
traceWarn(`${executablePath} identified as Conda environment even though Conda is not installed`);
145145
}
146-
const envs = (await conda?.getEnvList()) ?? [];
146+
const envs = (await conda?.getEnvList(useCache)) ?? [];
147147
for (const { name, prefix } of envs) {
148148
let executable = await getInterpreterPathFromDir(prefix);
149149
const currEnv: BasicEnvInfo = { executablePath: executable ?? '', kind: PythonEnvKind.Conda, envPath: prefix };

src/client/pythonEnvironments/common/environmentManagers/conda.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ export class Conda {
241241
*/
242242
private static condaPromise: Promise<Conda | undefined> | undefined;
243243

244+
private condaInfoCached: Promise<CondaInfo> | undefined;
245+
244246
/**
245247
* Creates a Conda service corresponding to the corresponding "conda" command.
246248
*
@@ -402,16 +404,19 @@ export class Conda {
402404
* Retrieves global information about this conda.
403405
* Corresponds to "conda info --json".
404406
*/
405-
public async getInfo(): Promise<CondaInfo> {
406-
return this.getInfoCached(this.command);
407+
public async getInfo(useCache?: boolean): Promise<CondaInfo> {
408+
if (!useCache || !this.condaInfoCached) {
409+
this.condaInfoCached = this.getInfoImpl(this.command);
410+
}
411+
return this.condaInfoCached;
407412
}
408413

409414
/**
410-
* Cache result for this particular command.
415+
* Temporarily cache result for this particular command.
411416
*/
412-
@cache(30_000, true, 10_000)
417+
// @cache(30_000, true, 10_000)
413418
// eslint-disable-next-line class-methods-use-this
414-
private async getInfoCached(command: string): Promise<CondaInfo> {
419+
private async getInfoImpl(command: string): Promise<CondaInfo> {
415420
const quoted = [command.toCommandArgument(), 'info', '--json'].join(' ');
416421
// Execute in a shell as `conda` on windows refers to `conda.bat`, which requires a shell to work.
417422
const result = await shellExecute(quoted, { timeout: CONDA_GENERAL_TIMEOUT });
@@ -423,9 +428,9 @@ export class Conda {
423428
* Retrieves list of Python environments known to this conda.
424429
* Corresponds to "conda env list --json", but also computes environment names.
425430
*/
426-
@cache(30_000, true, 10_000)
427-
public async getEnvList(): Promise<CondaEnvInfo[]> {
428-
const info = await this.getInfo();
431+
// @cache(30_000, true, 10_000)
432+
public async getEnvList(useCache?: boolean): Promise<CondaEnvInfo[]> {
433+
const info = await this.getInfo(useCache);
429434
const { envs } = info;
430435
if (envs === undefined) {
431436
return [];
@@ -510,7 +515,7 @@ export class Conda {
510515
*/
511516
@cache(-1, true)
512517
public async getCondaVersion(): Promise<SemVer | undefined> {
513-
const info = await this.getInfo().catch<CondaInfo | undefined>(() => undefined);
518+
const info = await this.getInfo(true).catch<CondaInfo | undefined>(() => undefined);
514519
let versionString: string | undefined;
515520
if (info && info.conda_version) {
516521
versionString = info.conda_version;

0 commit comments

Comments
 (0)