Skip to content

Commit 0263703

Browse files
author
Kartik Raj
authored
Ensure we block on autoselection when no interpreter is explictly set by user (#16723)
* Ensure we block on autoselection when no interpreter is explictly set by user * Added tests * News entry
1 parent 2e5d9f7 commit 0263703

File tree

7 files changed

+120
-14
lines changed

7 files changed

+120
-14
lines changed

news/2 Fixes/16723.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Ensure we block on autoselection when no interpreter is explictly set by user.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
import { inject, injectable } from 'inversify';
7+
import { IWorkspaceService } from './application/types';
8+
import { DeprecatePythonPath } from './experiments/groups';
9+
import { IExperimentService, IInterpreterPathProxyService, IInterpreterPathService, Resource } from './types';
10+
import { SystemVariables } from './variables/systemVariables';
11+
12+
@injectable()
13+
export class InterpreterPathProxyService implements IInterpreterPathProxyService {
14+
constructor(
15+
@inject(IInterpreterPathService) private readonly interpreterPathService: IInterpreterPathService,
16+
@inject(IExperimentService) private readonly experiment: IExperimentService,
17+
@inject(IWorkspaceService) private readonly workspace: IWorkspaceService,
18+
) {}
19+
20+
public get(resource: Resource): string {
21+
const systemVariables = new SystemVariables(
22+
undefined,
23+
this.workspace.getWorkspaceFolder(resource)?.uri.fsPath,
24+
this.workspace,
25+
);
26+
const pythonSettings = this.workspace.getConfiguration('python', resource);
27+
return systemVariables.resolveAny(
28+
this.experiment.inExperimentSync(DeprecatePythonPath.experiment)
29+
? this.interpreterPathService.get(resource)
30+
: pythonSettings.get<string>('pythonPath'),
31+
)!;
32+
}
33+
}

src/client/common/serviceRegistry.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
IToolExecutionPath,
2121
IsWindows,
2222
ToolExecutionPath,
23+
IInterpreterPathProxyService,
2324
} from './types';
2425
import { IServiceManager } from '../ioc/types';
2526
import { JupyterExtensionDependencyManager } from '../jupyter/jupyterExtensionDependencyManager';
@@ -118,11 +119,16 @@ import { IMultiStepInputFactory, MultiStepInputFactory } from './utils/multiStep
118119
import { Random } from './utils/random';
119120
import { JupyterNotInstalledNotificationHelper } from '../jupyter/jupyterNotInstalledNotificationHelper';
120121
import { IJupyterNotInstalledNotificationHelper } from '../jupyter/types';
122+
import { InterpreterPathProxyService } from './interpreterPathProxyService';
121123

122124
export function registerTypes(serviceManager: IServiceManager): void {
123125
serviceManager.addSingletonInstance<boolean>(IsWindows, IS_WINDOWS);
124126

125127
serviceManager.addSingleton<IActiveResourceService>(IActiveResourceService, ActiveResourceService);
128+
serviceManager.addSingleton<IInterpreterPathProxyService>(
129+
IInterpreterPathProxyService,
130+
InterpreterPathProxyService,
131+
);
126132
serviceManager.addSingleton<IInterpreterPathService>(IInterpreterPathService, InterpreterPathService);
127133
serviceManager.addSingleton<IExtensions>(IExtensions, Extensions);
128134
serviceManager.addSingleton<IRandom>(IRandom, Random);

src/client/common/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,14 @@ export interface IInterpreterPathService {
557557
copyOldInterpreterStorageValuesToNew(resource: Uri | undefined): Promise<void>;
558558
}
559559

560+
/**
561+
* Interface used to access current Interpreter Path
562+
*/
563+
export const IInterpreterPathProxyService = Symbol('IInterpreterPathProxyService');
564+
export interface IInterpreterPathProxyService {
565+
get(resource: Resource): string;
566+
}
567+
560568
export type DefaultLSType = LanguageServerType.Jedi | LanguageServerType.JediLSP | LanguageServerType.Node;
561569

562570
/**

src/client/interpreter/display/index.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ import { Disposable, OutputChannel, StatusBarAlignment, StatusBarItem, Uri } fro
33
import { IApplicationShell, IWorkspaceService } from '../../common/application/types';
44
import { STANDARD_OUTPUT_CHANNEL } from '../../common/constants';
55
import '../../common/extensions';
6-
import { IConfigurationService, IDisposableRegistry, IOutputChannel, IPathUtils, Resource } from '../../common/types';
6+
import {
7+
IDisposableRegistry,
8+
IInterpreterPathProxyService,
9+
IOutputChannel,
10+
IPathUtils,
11+
Resource,
12+
} from '../../common/types';
713
import { Interpreters } from '../../common/utils/localize';
814
import { IServiceContainer } from '../../ioc/types';
915
import { PythonEnvironment } from '../../pythonEnvironments/info';
@@ -22,7 +28,7 @@ export class InterpreterDisplay implements IInterpreterDisplay {
2228
private readonly workspaceService: IWorkspaceService;
2329
private readonly pathUtils: IPathUtils;
2430
private readonly interpreterService: IInterpreterService;
25-
private readonly configService: IConfigurationService;
31+
private readonly interpreterPathExpHelper: IInterpreterPathProxyService;
2632
private currentlySelectedInterpreterPath?: string;
2733
private currentlySelectedWorkspaceFolder: Resource;
2834
private readonly autoSelection: IInterpreterAutoSelectionService;
@@ -39,7 +45,9 @@ export class InterpreterDisplay implements IInterpreterDisplay {
3945

4046
const application = serviceContainer.get<IApplicationShell>(IApplicationShell);
4147
const disposableRegistry = serviceContainer.get<Disposable[]>(IDisposableRegistry);
42-
this.configService = serviceContainer.get<IConfigurationService>(IConfigurationService);
48+
this.interpreterPathExpHelper = serviceContainer.get<IInterpreterPathProxyService>(
49+
IInterpreterPathProxyService,
50+
);
4351

4452
this.statusBar = application.createStatusBarItem(StatusBarAlignment.Left, 100);
4553
this.statusBar.command = 'python.setInterpreter';
@@ -75,7 +83,7 @@ export class InterpreterDisplay implements IInterpreterDisplay {
7583
}
7684
}
7785
private async updateDisplay(workspaceFolder?: Uri) {
78-
const interpreterPath = this.configService.getSettings(workspaceFolder)?.pythonPath;
86+
const interpreterPath = this.interpreterPathExpHelper.get(workspaceFolder);
7987
if (!interpreterPath || interpreterPath === 'python') {
8088
await this.autoSelection.autoSelectInterpreter(workspaceFolder); // Block on this only if no interpreter selected.
8189
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
import { Uri, WorkspaceConfiguration } from 'vscode';
7+
import * as TypeMoq from 'typemoq';
8+
import { expect } from 'chai';
9+
import { InterpreterPathProxyService } from '../../client/common/interpreterPathProxyService';
10+
import { IExperimentService, IInterpreterPathProxyService, IInterpreterPathService } from '../../client/common/types';
11+
import { IWorkspaceService } from '../../client/common/application/types';
12+
import { DeprecatePythonPath } from '../../client/common/experiments/groups';
13+
14+
suite('Interpreter Path Proxy Service', async () => {
15+
let interpreterPathProxyService: IInterpreterPathProxyService;
16+
let workspaceService: TypeMoq.IMock<IWorkspaceService>;
17+
let experiments: TypeMoq.IMock<IExperimentService>;
18+
let interpreterPathService: TypeMoq.IMock<IInterpreterPathService>;
19+
const resource = Uri.parse('a');
20+
const interpreterPath = 'path/to/interpreter';
21+
setup(() => {
22+
workspaceService = TypeMoq.Mock.ofType<IWorkspaceService>();
23+
experiments = TypeMoq.Mock.ofType<IExperimentService>();
24+
interpreterPathService = TypeMoq.Mock.ofType<IInterpreterPathService>();
25+
workspaceService
26+
.setup((w) => w.getWorkspaceFolder(resource))
27+
.returns(() => ({
28+
uri: resource,
29+
name: 'Workspacefolder',
30+
index: 0,
31+
}));
32+
interpreterPathProxyService = new InterpreterPathProxyService(
33+
interpreterPathService.object,
34+
experiments.object,
35+
workspaceService.object,
36+
);
37+
});
38+
39+
test('When in experiment, use interpreter path service to get setting value', () => {
40+
experiments.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true);
41+
interpreterPathService.setup((i) => i.get(resource)).returns(() => interpreterPath);
42+
const value = interpreterPathProxyService.get(resource);
43+
expect(value).to.equal(interpreterPath);
44+
});
45+
46+
test('When not in experiment, use workspace service to get setting value', () => {
47+
experiments.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false);
48+
const workspaceConfig = TypeMoq.Mock.ofType<WorkspaceConfiguration>();
49+
workspaceService.setup((i) => i.getConfiguration('python', resource)).returns(() => workspaceConfig.object);
50+
workspaceConfig.setup((w) => w.get('pythonPath')).returns(() => interpreterPath);
51+
const value = interpreterPathProxyService.get(resource);
52+
expect(value).to.equal(interpreterPath);
53+
});
54+
});

src/test/interpreters/display.unit.test.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,10 @@ import { IApplicationShell, IWorkspaceService } from '../../client/common/applic
1616
import { STANDARD_OUTPUT_CHANNEL } from '../../client/common/constants';
1717
import { IFileSystem } from '../../client/common/platform/types';
1818
import {
19-
IConfigurationService,
19+
IInterpreterPathProxyService,
2020
IDisposableRegistry,
2121
IOutputChannel,
2222
IPathUtils,
23-
IPythonSettings,
2423
ReadWrite,
2524
} from '../../client/common/types';
2625
import { Interpreters } from '../../client/common/utils/localize';
@@ -57,8 +56,7 @@ suite('Interpreters Display', () => {
5756
let fileSystem: TypeMoq.IMock<IFileSystem>;
5857
let disposableRegistry: Disposable[];
5958
let statusBar: TypeMoq.IMock<StatusBarItem>;
60-
let pythonSettings: TypeMoq.IMock<IPythonSettings>;
61-
let configurationService: TypeMoq.IMock<IConfigurationService>;
59+
let interpreterPathProxyService: TypeMoq.IMock<IInterpreterPathProxyService>;
6260
let interpreterDisplay: IInterpreterDisplay;
6361
let interpreterHelper: TypeMoq.IMock<IInterpreterHelper>;
6462
let pathUtils: TypeMoq.IMock<IPathUtils>;
@@ -73,8 +71,7 @@ suite('Interpreters Display', () => {
7371
interpreterHelper = TypeMoq.Mock.ofType<IInterpreterHelper>();
7472
disposableRegistry = [];
7573
statusBar = TypeMoq.Mock.ofType<StatusBarItem>();
76-
pythonSettings = TypeMoq.Mock.ofType<IPythonSettings>();
77-
configurationService = TypeMoq.Mock.ofType<IConfigurationService>();
74+
interpreterPathProxyService = TypeMoq.Mock.ofType<IInterpreterPathProxyService>();
7875
pathUtils = TypeMoq.Mock.ofType<IPathUtils>();
7976
output = TypeMoq.Mock.ofType<IOutputChannel>();
8077
autoSelection = mock(InterpreterAutoSelectionService);
@@ -94,8 +91,8 @@ suite('Interpreters Display', () => {
9491
serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object);
9592
serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry))).returns(() => disposableRegistry);
9693
serviceContainer
97-
.setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService)))
98-
.returns(() => configurationService.object);
94+
.setup((c) => c.get(TypeMoq.It.isValue(IInterpreterPathProxyService)))
95+
.returns(() => interpreterPathProxyService.object);
9996
serviceContainer
10097
.setup((c) => c.get(TypeMoq.It.isValue(IInterpreterHelper)))
10198
.returns(() => interpreterHelper.object);
@@ -215,8 +212,7 @@ suite('Interpreters Display', () => {
215212
interpreterService
216213
.setup((i) => i.getActiveInterpreter(TypeMoq.It.isValue(workspaceFolder)))
217214
.returns(() => Promise.resolve(undefined));
218-
configurationService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => pythonSettings.object);
219-
pythonSettings.setup((p) => p.pythonPath).returns(() => pythonPath);
215+
interpreterPathProxyService.setup((c) => c.get(TypeMoq.It.isAny())).returns(() => pythonPath);
220216
fileSystem.setup((f) => f.fileExists(TypeMoq.It.isValue(pythonPath))).returns(() => Promise.resolve(false));
221217
interpreterHelper
222218
.setup((v) => v.getInterpreterInformation(TypeMoq.It.isValue(pythonPath)))

0 commit comments

Comments
 (0)