Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions news/3 Code Health/16735.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Run auto-selection only once, and return the cached value for subsequent calls.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@
"light": "resources/walkthrough/open-folder-light.png",
"dark": "resources/walkthrough/open-folder-dark.png",
"hc": "resources/walkthrough/open-folder-hc.png"
},
},
"altText": "Open a folder"
},
"completionEvents": [
Expand All @@ -214,7 +214,7 @@
"light": "resources/walkthrough/open-folder-light.png",
"dark": "resources/walkthrough/open-folder-dark.png",
"hc": "resources/walkthrough/open-folder-hc.png"
},
},
"altText": "Open a folder"
},
"completionEvents": [
Expand Down
15 changes: 14 additions & 1 deletion src/client/interpreter/autoSelection/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,13 @@ export class InterpreterAutoSelectionService implements IInterpreterAutoSelectio
return undefined;
}

private getAutoSelectionInterpretersQueryState(resource: Resource): IPersistentState<boolean | undefined> {
const workspaceUri = this.interpreterHelper.getActiveWorkspaceUri(resource);
const key = `autoSelectionInterpretersQueried-${workspaceUri?.folderUri.fsPath || 'global'}`;

return this.stateFactory.createWorkspacePersistentState(key, undefined);
}

private async autoselectInterpreterWithRules(resource: Resource): Promise<void> {
await this.userDefinedInterpreter.autoSelectInterpreter(resource, this);

Expand All @@ -260,7 +267,11 @@ export class InterpreterAutoSelectionService implements IInterpreterAutoSelectio
* As such, we can sort interpreters based on what it returns.
*/
private async autoselectInterpreterWithLocators(resource: Resource): Promise<void> {
const interpreters = await this.interpreterService.getInterpreters(resource, { ignoreCache: true });
// Do not perform a full interpreter search if we already have cached interpreters for this workspace.
const queriedState = this.getAutoSelectionInterpretersQueryState(resource);
const interpreters = await this.interpreterService.getInterpreters(resource, {
ignoreCache: queriedState.value !== true,
});
const workspaceUri = this.interpreterHelper.getActiveWorkspaceUri(resource);

// When auto-selecting an intepreter for a workspace, we either want to return a local one
Expand All @@ -278,6 +289,8 @@ export class InterpreterAutoSelectionService implements IInterpreterAutoSelectio
this.setGlobalInterpreter(filteredInterpreters[0]);
}

queriedState.updateValue(true);

this.didAutoSelectedInterpreterEmitter.fire();
}
}
102 changes: 102 additions & 0 deletions src/test/interpreters/autoSelection/index.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,108 @@ suite('Interpreters - Auto Selection', () => {
verify(interpreterService.getInterpreters(resource, anything())).once();
verify(state.updateValue(systemEnv)).once();
});

test('getInterpreters is called with ignoreCache at true if there is no value set in the workspace persistent state', async () => {
const interpreterComparer = new EnvironmentTypeComparer(instance(helper));
const options: { ignoreCache: boolean }[] = [];
const queryState = mock(PersistentState) as PersistentState<boolean | undefined>;

when(queryState.value).thenReturn(undefined);
when(stateFactory.createWorkspacePersistentState<boolean | undefined>(anyString(), undefined)).thenReturn(
instance(queryState),
);
when(interpreterService.getInterpreters(resource, anything())).thenCall((_, opts) => {
options.push(opts);

return Promise.resolve([
{
envType: EnvironmentType.Conda,
envPath: path.join('some', 'conda', 'env'),
version: { major: 3, minor: 7, patch: 2 },
} as PythonEnvironment,
{
envType: EnvironmentType.Pipenv,
envPath: path.join('some', 'pipenv', 'env'),
version: { major: 3, minor: 10, patch: 0 },
} as PythonEnvironment,
]);
});

autoSelectionService = new InterpreterAutoSelectionServiceTest(
instance(workspaceService),
instance(stateFactory),
instance(fs),
instance(experiments),
instance(interpreterService),
interpreterComparer,
instance(systemInterpreter),
instance(currentPathInterpreter),
instance(winRegInterpreter),
instance(cachedPaths),
instance(userDefinedInterpreter),
instance(workspaceInterpreter),
instance(proxy),
instance(helper),
);

autoSelectionService.initializeStore = () => Promise.resolve();

await autoSelectionService.autoSelectInterpreter(resource);

verify(interpreterService.getInterpreters(resource, anything())).once();
expect(options).to.deep.equal([{ ignoreCache: true }], 'getInterpreters options are different');
});

test('getInterpreters is called with ignoreCache at false if there is a value set in the workspace persistent state', async () => {
const interpreterComparer = new EnvironmentTypeComparer(instance(helper));
const options: { ignoreCache: boolean }[] = [];
const queryState = mock(PersistentState) as PersistentState<boolean | undefined>;

when(queryState.value).thenReturn(true);
when(stateFactory.createWorkspacePersistentState<boolean | undefined>(anyString(), undefined)).thenReturn(
instance(queryState),
);
when(interpreterService.getInterpreters(resource, anything())).thenCall((_, opts) => {
options.push(opts);

return Promise.resolve([
{
envType: EnvironmentType.Conda,
envPath: path.join('some', 'conda', 'env'),
version: { major: 3, minor: 7, patch: 2 },
} as PythonEnvironment,
{
envType: EnvironmentType.Pipenv,
envPath: path.join('some', 'pipenv', 'env'),
version: { major: 3, minor: 10, patch: 0 },
} as PythonEnvironment,
]);
});

autoSelectionService = new InterpreterAutoSelectionServiceTest(
instance(workspaceService),
instance(stateFactory),
instance(fs),
instance(experiments),
instance(interpreterService),
interpreterComparer,
instance(systemInterpreter),
instance(currentPathInterpreter),
instance(winRegInterpreter),
instance(cachedPaths),
instance(userDefinedInterpreter),
instance(workspaceInterpreter),
instance(proxy),
instance(helper),
);

autoSelectionService.initializeStore = () => Promise.resolve();

await autoSelectionService.autoSelectInterpreter(resource);

verify(interpreterService.getInterpreters(resource, anything())).once();
expect(options).to.deep.equal([{ ignoreCache: false }], 'getInterpreters options are different');
});
});

test('Initialize the store', async () => {
Expand Down