Skip to content

Commit f7d7354

Browse files
author
Kartik Raj
authored
Do not prompt to select new virtual envrionment if it has already been selected (#18928)
* Do not prompt to select new virtual envrionment if it has already been selected * Add commnet
1 parent 56bba33 commit f7d7354

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed

news/2 Fixes/18915.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Do not prompt to select new virtual envrionment if it has already been selected.

src/client/interpreter/virtualEnvs/virtualEnvPrompt.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import { IApplicationShell } from '../../common/application/types';
88
import { IDisposableRegistry, IPersistentStateFactory } from '../../common/types';
99
import { sleep } from '../../common/utils/async';
1010
import { Common, Interpreters } from '../../common/utils/localize';
11-
import { traceDecoratorError } from '../../logging';
11+
import { traceDecoratorError, traceVerbose } from '../../logging';
1212
import { PythonEnvironment } from '../../pythonEnvironments/info';
1313
import { sendTelemetryEvent } from '../../telemetry';
1414
import { EventName } from '../../telemetry/constants';
1515
import { IPythonPathUpdaterServiceManager } from '../configuration/types';
16-
import { IComponentAdapter, IInterpreterHelper } from '../contracts';
16+
import { IComponentAdapter, IInterpreterHelper, IInterpreterService } from '../contracts';
1717

1818
const doNotDisplayPromptStateKey = 'MESSAGE_KEY_FOR_VIRTUAL_ENV';
1919
@injectable()
@@ -28,6 +28,7 @@ export class VirtualEnvironmentPrompt implements IExtensionActivationService {
2828
@inject(IDisposableRegistry) private readonly disposableRegistry: Disposable[],
2929
@inject(IApplicationShell) private readonly appShell: IApplicationShell,
3030
@inject(IComponentAdapter) private readonly pyenvs: IComponentAdapter,
31+
@inject(IInterpreterService) private readonly interpreterService: IInterpreterService,
3132
) {}
3233

3334
public async activate(resource: Uri): Promise<void> {
@@ -47,6 +48,11 @@ export class VirtualEnvironmentPrompt implements IExtensionActivationService {
4748
if (!interpreter) {
4849
return;
4950
}
51+
const currentInterpreter = await this.interpreterService.getActiveInterpreter(resource);
52+
if (currentInterpreter?.id === interpreter.id) {
53+
traceVerbose('New environment has already been selected');
54+
return;
55+
}
5056
await this.notifyUser(interpreter, resource);
5157
}
5258

src/test/interpreters/virtualEnvs/virtualEnvPrompt.unit.test.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
'use strict';
55

6-
import { anything, deepEqual, instance, mock, verify, when } from 'ts-mockito';
6+
import { anything, deepEqual, instance, mock, reset, verify, when } from 'ts-mockito';
77
import * as TypeMoq from 'typemoq';
88
import { ConfigurationTarget, Disposable, Uri } from 'vscode';
99
import { ApplicationShell } from '../../../client/common/application/applicationShell';
@@ -13,7 +13,7 @@ import { IPersistentState, IPersistentStateFactory } from '../../../client/commo
1313
import { Common } from '../../../client/common/utils/localize';
1414
import { PythonPathUpdaterService } from '../../../client/interpreter/configuration/pythonPathUpdaterService';
1515
import { IPythonPathUpdaterServiceManager } from '../../../client/interpreter/configuration/types';
16-
import { IComponentAdapter, IInterpreterHelper } from '../../../client/interpreter/contracts';
16+
import { IComponentAdapter, IInterpreterHelper, IInterpreterService } from '../../../client/interpreter/contracts';
1717
import { InterpreterHelper } from '../../../client/interpreter/helpers';
1818
import { VirtualEnvironmentPrompt } from '../../../client/interpreter/virtualEnvs/virtualEnvPrompt';
1919
import { PythonEnvironment } from '../../../client/pythonEnvironments/info';
@@ -34,12 +34,18 @@ suite('Virtual Environment Prompt', () => {
3434
let disposable: Disposable;
3535
let appShell: IApplicationShell;
3636
let componentAdapter: IComponentAdapter;
37+
let interpreterService: IInterpreterService;
3738
let environmentPrompt: VirtualEnvironmentPromptTest;
3839
setup(() => {
3940
persistentStateFactory = mock(PersistentStateFactory);
4041
helper = mock(InterpreterHelper);
4142
pythonPathUpdaterService = mock(PythonPathUpdaterService);
4243
componentAdapter = mock<IComponentAdapter>();
44+
interpreterService = mock<IInterpreterService>();
45+
when(interpreterService.getActiveInterpreter(anything())).thenResolve(({
46+
id: 'selected',
47+
path: 'path/to/selected',
48+
} as unknown) as PythonEnvironment);
4349
disposable = mock(Disposable);
4450
appShell = mock(ApplicationShell);
4551
environmentPrompt = new VirtualEnvironmentPromptTest(
@@ -49,6 +55,7 @@ suite('Virtual Environment Prompt', () => {
4955
[instance(disposable)],
5056
instance(appShell),
5157
instance(componentAdapter),
58+
instance(interpreterService),
5259
);
5360
});
5461

@@ -77,7 +84,36 @@ suite('Virtual Environment Prompt', () => {
7784
verify(appShell.showInformationMessage(anything(), ...prompts)).once();
7885
});
7986

80-
test('When in experiment, user is notified if interpreter exists and only python path to global interpreter is specified in settings', async () => {
87+
test('User is not notified if currently selected interpreter is the same as new interpreter', async () => {
88+
const resource = Uri.file('a');
89+
const interpreter1 = { path: 'path/to/interpreter1' };
90+
const interpreter2 = { path: 'path/to/interpreter2' };
91+
const prompts = [Common.bannerLabelYes(), Common.bannerLabelNo(), Common.doNotShowAgain()];
92+
const notificationPromptEnabled = TypeMoq.Mock.ofType<IPersistentState<boolean>>();
93+
94+
// Return interpreters using the component adapter instead
95+
when(componentAdapter.getWorkspaceVirtualEnvInterpreters(resource)).thenResolve([
96+
interpreter1,
97+
interpreter2,
98+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
99+
] as any);
100+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
101+
when(helper.getBestInterpreter(deepEqual([interpreter1, interpreter2] as any))).thenReturn(interpreter2 as any);
102+
reset(interpreterService);
103+
when(interpreterService.getActiveInterpreter(anything())).thenResolve(
104+
(interpreter2 as unknown) as PythonEnvironment,
105+
);
106+
when(persistentStateFactory.createWorkspacePersistentState(anything(), true)).thenReturn(
107+
notificationPromptEnabled.object,
108+
);
109+
notificationPromptEnabled.setup((n) => n.value).returns(() => true);
110+
when(appShell.showInformationMessage(anything(), ...prompts)).thenResolve();
111+
112+
await environmentPrompt.handleNewEnvironment(resource);
113+
114+
verify(appShell.showInformationMessage(anything(), ...prompts)).never();
115+
});
116+
test('User is notified if interpreter exists and only python path to global interpreter is specified in settings', async () => {
81117
const resource = Uri.file('a');
82118
const interpreter1 = { path: 'path/to/interpreter1' };
83119
const interpreter2 = { path: 'path/to/interpreter2' };

0 commit comments

Comments
 (0)