Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Investigate lazy locating of windows reg #22507

Closed
wants to merge 41 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/client/interpreter/autoSelection/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,19 @@ export class InterpreterAutoSelectionService implements IInterpreterAutoSelectio
private async autoselectInterpreterWithLocators(resource: Resource): Promise<void> {
// Do not perform a full interpreter search if we already have cached interpreters for this workspace.
const queriedState = this.getAutoSelectionInterpretersQueryState(resource);
const workspaceUri = this.interpreterHelper.getActiveWorkspaceUri(resource);
if (queriedState.value !== true && resource) {
await this.interpreterService.triggerRefresh({
searchLocations: { roots: [resource], doNotIncludeNonRooted: true },
});
const interpreters = this.interpreterService.getInterpreters(resource);
const workspaceRecommendedInterpreter = this.envTypeComparer.getRecommended(
interpreters,
workspaceUri?.folderUri,
);
if (!workspaceRecommendedInterpreter) {
return;
}
}

const globalQueriedState = this.getAutoSelectionQueriedOnceState();
Expand All @@ -212,7 +221,6 @@ export class InterpreterAutoSelectionService implements IInterpreterAutoSelectio
await this.interpreterService.refreshPromise;
}
const interpreters = this.interpreterService.getInterpreters(resource);
const workspaceUri = this.interpreterHelper.getActiveWorkspaceUri(resource);

const recommendedInterpreter = this.envTypeComparer.getRecommended(interpreters, workspaceUri?.folderUri);
if (!recommendedInterpreter) {
Expand Down
3 changes: 3 additions & 0 deletions src/client/logging/outputChannelLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export class OutputChannelLogger implements ILogging {
}

public traceVerbose(...data: Arguments): void {
if(util.format(...data).startsWith('Searching for')) {
console.debug(util.format(...data));
}
this.channel.debug(util.format(...data));
}
}
2 changes: 1 addition & 1 deletion src/client/pythonEnvironments/base/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type PythonEnvUpdatedEvent<I = PythonEnvInfo> = {
/**
* The iteration index of The env info that was previously provided.
*/
index: number;
index?: number;
/**
* The env info that was previously provided.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/client/pythonEnvironments/base/locatorUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export async function getEnvs<I = PythonEnvInfo>(iterator: IPythonEnvsIterator<I
}
updatesDone.resolve();
listener.dispose();
} else {
} else if (event.index) {
const { index, update } = event;
if (envs[index] === undefined) {
const json = JSON.stringify(update);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* eslint-disable class-methods-use-this */
/* eslint-disable require-yield */
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

Expand Down Expand Up @@ -43,9 +45,18 @@ export abstract class LazyResourceBasedLocator extends Locator<BasicEnvInfo> imp
await this.disposables.dispose();
}

public async *iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator<BasicEnvInfo> {
await this.activate();
public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator<BasicEnvInfo> {
const iterator = this.doIterEnvs(query);
const it = this._iterEnvs(iterator, query);
it.onUpdated = iterator.onUpdated;
return it;
}

private async *_iterEnvs(
iterator: IPythonEnvsIterator<BasicEnvInfo>,
query?: PythonLocatorQuery,
): IPythonEnvsIterator<BasicEnvInfo> {
await this.activate();
if (query?.envPath) {
let result = await iterator.next();
while (!result.done) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
switch (event.stage) {
case ProgressReportStage.discoveryFinished:
state.done = true;
listener.dispose();
break;
case ProgressReportStage.allPathsDiscovered:
if (!query) {
Expand All @@ -157,13 +156,16 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
default:
this.progress.fire(event);
}
} else {
} else if (event.index) {
state.pending += 1;
this.cache.updateEnv(seen[event.index], event.update);
if (event.update) {
seen[event.index] = event.update;
}
state.pending -= 1;
} else if (event.update) {
// New env, add it to cache.
this.cache.addEnv(event.update);
}
if (state.done && state.pending === 0) {
updatesDone.resolve();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,33 +49,33 @@ async function* iterEnvsIterator(
};
const seen: BasicEnvInfo[] = [];

didUpdate.fire({ stage: ProgressReportStage.discoveryStarted });
if (iterator.onUpdated !== undefined) {
const listener = iterator.onUpdated((event) => {
state.pending += 1;
let notifyIfFinished = true;
if (isProgressEvent(event)) {
if (event.stage === ProgressReportStage.discoveryFinished) {
state.done = true;
listener.dispose();
// listener.dispose();
} else {
didUpdate.fire(event);
}
} else if (event.update === undefined) {
throw new Error(
'Unsupported behavior: `undefined` environment updates are not supported from downstream locators in reducer',
);
} else if (seen[event.index] !== undefined) {
} else if (event.index && seen[event.index] !== undefined) {
const oldEnv = seen[event.index];
seen[event.index] = event.update;
didUpdate.fire({ index: event.index, old: oldEnv, update: event.update });
} else {
// This implies a problem in a downstream locator
traceVerbose(`Expected already iterated env, got ${event.old} (#${event.index})`);
didUpdate.fire({ update: event.update });
notifyIfFinished = false;
}
if (notifyIfFinished) {
checkIfFinishedAndNotify(state, didUpdate);
}
state.pending -= 1;
checkIfFinishedAndNotify(state, didUpdate);
});
} else {
didUpdate.fire({ stage: ProgressReportStage.discoveryStarted });
}

let result = await iterator.next();
Expand All @@ -91,10 +91,8 @@ async function* iterEnvsIterator(
}
result = await iterator.next();
}
if (iterator.onUpdated === undefined) {
state.done = true;
checkIfFinishedAndNotify(state, didUpdate);
}
state.done = true;
checkIfFinishedAndNotify(state, didUpdate);
}

async function resolveDifferencesInBackground(
Expand Down Expand Up @@ -128,7 +126,7 @@ function checkIfFinishedAndNotify(
) {
if (state.done && state.pending === 0) {
didUpdate.fire({ stage: ProgressReportStage.discoveryFinished });
didUpdate.dispose();
// didUpdate.dispose();
traceVerbose(`Finished with environment reducer`);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,31 +82,34 @@ export class PythonEnvsResolver implements IResolvingLocator {

if (iterator.onUpdated !== undefined) {
const listener = iterator.onUpdated(async (event) => {
let notifyIfFinished = true;
state.pending += 1;
if (isProgressEvent(event)) {
if (event.stage === ProgressReportStage.discoveryFinished) {
didUpdate.fire({ stage: ProgressReportStage.allPathsDiscovered });
state.done = true;
listener.dispose();
// listener.dispose();
} else {
didUpdate.fire(event);
}
} else if (event.update === undefined) {
throw new Error(
'Unsupported behavior: `undefined` environment updates are not supported from downstream locators in resolver',
);
} else if (seen[event.index] !== undefined) {
} else if (event.index && seen[event.index] !== undefined) {
const old = seen[event.index];
await setKind(event.update, environmentKinds);
seen[event.index] = await resolveBasicEnv(event.update);
didUpdate.fire({ old, index: event.index, update: seen[event.index] });
this.resolveInBackground(event.index, state, didUpdate, seen).ignoreErrors();
} else {
// This implies a problem in a downstream locator
traceVerbose(`Expected already iterated env, got ${event.old} (#${event.index})`);
} else if (event.update) {
didUpdate.fire({ update: await this.resolveEnv(event.update.executablePath) });
notifyIfFinished = false;
}
state.pending -= 1;
checkIfFinishedAndNotify(state, didUpdate);
if (notifyIfFinished) {
checkIfFinishedAndNotify(state, didUpdate);
}
});
} else {
didUpdate.fire({ stage: ProgressReportStage.discoveryStarted });
Expand Down Expand Up @@ -137,16 +140,16 @@ export class PythonEnvsResolver implements IResolvingLocator {
state.pending += 1;
// It's essential we increment the pending call count before any asynchronus calls in this method.
// We want this to be run even when `resolveInBackground` is called in background.
const info = await this.environmentInfoService.getEnvironmentInfo(seen[envIndex]);
const old = seen[envIndex];
if (info) {
const resolvedEnv = getResolvedEnv(info, seen[envIndex]);
seen[envIndex] = resolvedEnv;
didUpdate.fire({ old, index: envIndex, update: resolvedEnv });
} else {
// Send update that the environment is not valid.
didUpdate.fire({ old, index: envIndex, update: undefined });
}
// const info = await this.environmentInfoService.getEnvironmentInfo(seen[envIndex]);
// const old = seen[envIndex];
// if (info) {
// const resolvedEnv = getResolvedEnv(info, seen[envIndex]);
// seen[envIndex] = resolvedEnv;
// didUpdate.fire({ old, index: envIndex, update: resolvedEnv });
// } else {
// // Send update that the environment is not valid.
// didUpdate.fire({ old, index: envIndex, update: undefined });
// }
state.pending -= 1;
checkIfFinishedAndNotify(state, didUpdate);
}
Expand All @@ -173,7 +176,7 @@ function checkIfFinishedAndNotify(
) {
if (state.done && state.pending === 0) {
didUpdate.fire({ stage: ProgressReportStage.discoveryFinished });
didUpdate.dispose();
// didUpdate.dispose();
traceVerbose(`Finished with environment resolver`);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/* eslint-disable require-yield */
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import '../../../../common/extensions';
import { EventEmitter } from 'vscode';
import { PythonEnvKind } from '../../info';
import { BasicEnvInfo, IPythonEnvsIterator } from '../../locator';
import { BasicEnvInfo, IPythonEnvsIterator, ProgressNotificationEvent, PythonEnvUpdatedEvent } from '../../locator';
import { Conda, getCondaEnvironmentsTxt } from '../../../common/environmentManagers/conda';
import { traceError, traceVerbose } from '../../../../logging';
import { FSWatchingLocator } from './fsWatchingLocator';
Expand All @@ -21,28 +23,48 @@ export class CondaEnvironmentLocator extends FSWatchingLocator {
}

// eslint-disable-next-line class-methods-use-this
public async *doIterEnvs(
public doIterEnvs(
_: unknown,
useWorkerThreads = inExperiment(DiscoveryUsingWorkers.experiment),
): IPythonEnvsIterator<BasicEnvInfo> {
const conda = await Conda.getConda(undefined, useWorkerThreads);
if (conda === undefined) {
traceVerbose(`Couldn't locate the conda binary.`);
return;
}
traceVerbose(`Searching for conda environments using ${conda.command}`);
const didUpdate = new EventEmitter<PythonEnvUpdatedEvent<BasicEnvInfo> | ProgressNotificationEvent>();
const iterator = iterEnvsIterator(useWorkerThreads, didUpdate);
iterator.onUpdated = didUpdate.event;
return iterator;
}
}

async function* iterEnvsIterator(
useWorkerThreads: boolean,
didUpdate: EventEmitter<PythonEnvUpdatedEvent<BasicEnvInfo> | ProgressNotificationEvent>,
): IPythonEnvsIterator<BasicEnvInfo> {
updateLazily(useWorkerThreads, didUpdate).ignoreErrors();
}
async function updateLazily(
useWorkerThreads: boolean,
didUpdate: EventEmitter<PythonEnvUpdatedEvent<BasicEnvInfo> | ProgressNotificationEvent>,
) {
console.time('Time taken for conda');
const conda = await Conda.getConda(undefined, useWorkerThreads);
if (conda === undefined) {
traceVerbose(`Couldn't locate the conda binary.`);
return;
}
console.timeLog('Time taken for conda');
traceVerbose(`Searching for conda environments using ${conda.command}`);

const envs = await conda.getEnvList();
for (const env of envs) {
try {
traceVerbose(`Looking into conda env for executable: ${JSON.stringify(env)}`);
const executablePath = await conda.getInterpreterPathForEnvironment(env);
traceVerbose(`Found conda executable: ${executablePath}`);
yield { kind: PythonEnvKind.Conda, executablePath, envPath: env.prefix };
} catch (ex) {
traceError(`Failed to process conda env: ${JSON.stringify(env)}`, ex);
}
const envs = await conda.getEnvList();
for (const env of envs) {
try {
traceVerbose(`Looking into conda env for executable: ${JSON.stringify(env)}`);
const executablePath = await conda.getInterpreterPathForEnvironment(env);
traceVerbose(`Found conda executable: ${executablePath}`);
const e = { kind: PythonEnvKind.Conda, executablePath, envPath: env.prefix };
didUpdate.fire({ update: e });
} catch (ex) {
traceError(`Failed to process conda env: ${JSON.stringify(env)}`, ex);
}
traceVerbose(`Finished searching for conda environments`);
}
traceVerbose(`Finished searching for conda environments`);
console.timeEnd('Time taken for conda');
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export class GlobalVirtualEnvironmentLocator extends FSWatchingLocator {
const searchDepth = this.searchDepth ?? DEFAULT_SEARCH_DEPTH;

async function* iterator() {
console.time(`Searching for global virtual envs`);
const envRootDirs = await getGlobalVirtualEnvDirs();
const envGenerators = envRootDirs.map((envRootDir) => {
async function* generator() {
Expand Down Expand Up @@ -139,6 +140,7 @@ export class GlobalVirtualEnvironmentLocator extends FSWatchingLocator {

yield* iterable(chain(envGenerators));
traceVerbose(`Finished searching for global virtual envs`);
console.timeEnd(`Searching for global virtual envs`);
}

return iterator();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,14 @@ export class MicrosoftStoreLocator extends FSWatchingLocator {
protected doIterEnvs(): IPythonEnvsIterator<BasicEnvInfo> {
const iterator = async function* (kind: PythonEnvKind) {
traceVerbose('Searching for windows store envs');
console.time('Time taken for windows store');
const exes = await getMicrosoftStorePythonExes();
yield* exes.map(async (executablePath: string) => ({
kind,
executablePath,
}));
traceVerbose(`Finished searching for windows store envs`);
console.timeEnd('Time taken for windows store');
};
return iterator(this.kind);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export class PoetryLocator extends LazyResourceBasedLocator {

protected doIterEnvs(): IPythonEnvsIterator<BasicEnvInfo> {
async function* iterator(root: string) {
console.time('Time taken for poetry');
const envDirs = await getVirtualEnvDirs(root);
const envGenerators = envDirs.map((envDir) => {
async function* generator() {
Expand All @@ -71,6 +72,7 @@ export class PoetryLocator extends LazyResourceBasedLocator {

yield* iterable(chain(envGenerators));
traceVerbose(`Finished searching for poetry envs`);
console.timeEnd('Time taken for poetry');
}

return iterator(this.root);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { traceError, traceVerbose } from '../../../../logging';
*/
async function* getPyenvEnvironments(): AsyncIterableIterator<BasicEnvInfo> {
traceVerbose('Searching for pyenv environments');
console.time('Time taken for pyenv');
const pyenvVersionDir = getPyenvVersionsDir();

const subDirs = getSubDirs(pyenvVersionDir, { resolveSymlinks: true });
Expand All @@ -35,6 +36,7 @@ async function* getPyenvEnvironments(): AsyncIterableIterator<BasicEnvInfo> {
}
}
traceVerbose('Finished searching for pyenv environments');
console.timeEnd('Time taken for pyenv');
}

export class PyenvLocator extends FSWatchingLocator {
Expand Down
Loading
Loading