Skip to content

Commit 2e096fb

Browse files
committed
Configure Python Env tool
1 parent 7430a9a commit 2e096fb

File tree

10 files changed

+233
-250
lines changed

10 files changed

+233
-250
lines changed

src/client/chat/configurePythonEnvTool.ts

Lines changed: 140 additions & 223 deletions
Large diffs are not rendered by default.

src/client/chat/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export function registerTools(
4343
),
4444
);
4545
ourTools.add(
46-
lm.registerTool(ConfigurePythonEnvTool.toolName, new ConfigurePythonEnvTool(environmentsApi, discoverApi, serviceContainer)),
46+
lm.registerTool(ConfigurePythonEnvTool.toolName, new ConfigurePythonEnvTool(environmentsApi, serviceContainer)),
4747
);
4848
ourTools.add(
4949
extensions.onDidChange(() => {

src/client/extensionActivation.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import { DebugService } from './common/application/debugService';
4545
import { DebugSessionEventDispatcher } from './debugger/extension/hooks/eventHandlerDispatcher';
4646
import { IDebugSessionEventHandlers } from './debugger/extension/hooks/types';
4747
import { WorkspaceService } from './common/application/workspace';
48-
import { IInterpreterQuickPick } from './interpreter/configuration/types';
48+
import { IInterpreterQuickPick, IRecommendedEnvironmentService } from './interpreter/configuration/types';
4949
import { registerAllCreateEnvironmentFeatures } from './pythonEnvironments/creation/registrations';
5050
import { registerCreateEnvironmentTriggers } from './pythonEnvironments/creation/createEnvironmentTrigger';
5151
import { initializePersistentStateForTriggers } from './common/persistentState';
@@ -111,6 +111,7 @@ export function activateFeatures(ext: ExtensionState, _components: Components):
111111
interpreterPathService,
112112
interpreterService,
113113
pathUtils,
114+
ext.legacyIOC.serviceContainer.get<IRecommendedEnvironmentService>(IRecommendedEnvironmentService),
114115
);
115116
const executionHelper = ext.legacyIOC.serviceContainer.get<ICodeExecutionHelper>(ICodeExecutionHelper);
116117
const commandManager = ext.legacyIOC.serviceContainer.get<ICommandManager>(ICommandManager);

src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,10 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand implem
569569
* @returns true when an interpreter was set, undefined if the user cancelled the quickpick.
570570
*/
571571
@captureTelemetry(EventName.SELECT_INTERPRETER)
572-
public async setInterpreter(): Promise<true | undefined> {
572+
public async setInterpreter(options?: {
573+
hideCreateVenv?: boolean;
574+
showBackButton?: boolean;
575+
}): Promise<SelectEnvironmentResult | undefined> {
573576
const targetConfig = await this.getConfigTargets();
574577
if (!targetConfig) {
575578
return;
@@ -578,11 +581,25 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand implem
578581
const wkspace = targetConfig[0].folderUri;
579582
const interpreterState: InterpreterStateArgs = { path: undefined, workspace: wkspace };
580583
const multiStep = this.multiStepFactory.create<InterpreterStateArgs>();
581-
await multiStep.run(
582-
(input, s) => this._pickInterpreter(input, s, undefined, { showCreateEnvironment: true }),
583-
interpreterState,
584-
);
585-
584+
try {
585+
await multiStep.run(
586+
(input, s) =>
587+
this._pickInterpreter(input, s, undefined, {
588+
showCreateEnvironment: !options?.hideCreateVenv,
589+
showBackButton: options?.showBackButton,
590+
}),
591+
interpreterState,
592+
);
593+
} catch (ex) {
594+
if (ex === InputFlowAction.back) {
595+
// User clicked back button, so we need to return this action.
596+
return { action: 'Back' };
597+
}
598+
if (ex === InputFlowAction.cancel) {
599+
// User clicked cancel button, so we need to return this action.
600+
return { action: 'Cancel' };
601+
}
602+
}
586603
if (interpreterState.path !== undefined) {
587604
// User may choose to have an empty string stored, so variable `interpreterState.path` may be
588605
// an empty string, in which case we should update.
@@ -591,7 +608,7 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand implem
591608
if (useEnvExtension()) {
592609
await setInterpreterLegacy(interpreterState.path, wkspace);
593610
}
594-
return true;
611+
return { path: interpreterState.path };
595612
}
596613
}
597614

@@ -692,3 +709,14 @@ function getGroup(item: IInterpreterQuickPickItem, workspacePath?: string) {
692709
return EnvGroups[item.interpreter.envType];
693710
}
694711
}
712+
713+
export type SelectEnvironmentResult = {
714+
/**
715+
* Path to the executable python in the environment
716+
*/
717+
readonly path?: string;
718+
/*
719+
* User action that resulted in exit from the create environment flow.
720+
*/
721+
readonly action?: 'Back' | 'Cancel';
722+
};

src/client/interpreter/configuration/recommededEnvironmentService.ts

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

44
import { inject, injectable } from 'inversify';
55
import { IRecommendedEnvironmentService } from './types';
6-
import { PythonExtension } from '../../api/types';
6+
import { PythonExtension, ResolvedEnvironment } from '../../api/types';
77
import { IExtensionContext, Resource } from '../../common/types';
88
import { Uri, workspace } from 'vscode';
99
import { getWorkspaceStateValue, updateWorkspaceStateValue } from '../../common/persistentState';
@@ -32,7 +32,38 @@ export class RecommendedEnvironmentService implements IRecommendedEnvironmentSer
3232
}
3333
}
3434

35-
getRecommededEnvironment(
35+
async getRecommededEnvironment(
36+
resource: Resource,
37+
): Promise<
38+
| {
39+
environment: ResolvedEnvironment;
40+
reason: 'globalUserSelected' | 'workspaceUserSelected' | 'defaultRecommended';
41+
}
42+
| undefined
43+
> {
44+
if (!workspace.isTrusted || !this.api) {
45+
return undefined;
46+
}
47+
const preferred = await this.getRecommededInternal(resource);
48+
if (!preferred) {
49+
return undefined;
50+
}
51+
const activeEnv = await this.api.resolveEnvironment(this.api.getActiveEnvironmentPath(resource));
52+
const recommendedEnv = await this.api.resolveEnvironment(preferred.environmentPath);
53+
if (activeEnv && recommendedEnv && activeEnv.id !== recommendedEnv.id) {
54+
traceError(
55+
`Active environment ${activeEnv.id} is different from recommended environment ${
56+
recommendedEnv.id
57+
} for resource ${resource?.toString()}`,
58+
);
59+
return undefined;
60+
}
61+
if (recommendedEnv) {
62+
return { environment: recommendedEnv, reason: preferred.reason };
63+
}
64+
return undefined;
65+
}
66+
getRecommededInternal(
3667
resource: Resource,
3768
):
3869
| { environmentPath: string; reason: 'globalUserSelected' | 'workspaceUserSelected' | 'defaultRecommended' }

src/client/interpreter/configuration/types.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ConfigurationTarget, Disposable, QuickPickItem, Uri } from 'vscode';
22
import { Resource } from '../../common/types';
33
import { PythonEnvironment } from '../../pythonEnvironments/info';
4-
import { PythonExtension } from '../../api/types';
4+
import { PythonExtension, ResolvedEnvironment } from '../../api/types';
55

66
export interface IPythonPathUpdaterService {
77
updatePythonPath(pythonPath: string | undefined): Promise<void>;
@@ -104,7 +104,11 @@ export interface IRecommendedEnvironmentService {
104104
trackUserSelectedEnvironment(environmentPath: string | undefined, uri: Uri | undefined): void;
105105
getRecommededEnvironment(
106106
resource: Resource,
107-
):
108-
| { environmentPath: string; reason: 'globalUserSelected' | 'workspaceUserSelected' | 'defaultRecommended' }
109-
| undefined;
107+
): Promise<
108+
| {
109+
environment: ResolvedEnvironment;
110+
reason: 'globalUserSelected' | 'workspaceUserSelected' | 'defaultRecommended';
111+
}
112+
| undefined
113+
>;
110114
}

src/client/jupyter/jupyterIntegration.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ type PythonApiForJupyterExtension = {
7272
* Returns the preferred environment for the given URI.
7373
*/
7474
getRecommededEnvironment(
75-
uri: Uri,
75+
uri: Uri | undefined,
7676
): Promise<
7777
| {
7878
environment: EnvironmentPath;
@@ -147,14 +147,7 @@ export class JupyterExtensionIntegration {
147147
if (!this.environmentApi) {
148148
return undefined;
149149
}
150-
const preferred = this.preferredEnvironmentService.getRecommededEnvironment(uri);
151-
if (!preferred) {
152-
return undefined;
153-
}
154-
const environment = workspace.isTrusted
155-
? await this.environmentApi.resolveEnvironment(preferred.environmentPath)
156-
: undefined;
157-
return environment ? { environment, reason: preferred.reason } : undefined;
150+
return this.preferredEnvironmentService.getRecommededEnvironment(uri);
158151
},
159152
});
160153
return undefined;

src/client/pythonEnvironments/creation/createEnvApi.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ConfigurationTarget, Disposable, QuickInputButtons } from 'vscode';
55
import { Commands } from '../../common/constants';
66
import { IDisposableRegistry, IInterpreterPathService, IPathUtils } from '../../common/types';
77
import { executeCommand, registerCommand } from '../../common/vscodeApis/commandApis';
8-
import { IInterpreterQuickPick } from '../../interpreter/configuration/types';
8+
import { IInterpreterQuickPick, IRecommendedEnvironmentService } from '../../interpreter/configuration/types';
99
import { getCreationEvents, handleCreateEnvironmentCommand } from './createEnvironment';
1010
import { condaCreationProvider } from './provider/condaCreationProvider';
1111
import { VenvCreationProvider } from './provider/venvCreationProvider';
@@ -63,6 +63,7 @@ export function registerCreateEnvironmentFeatures(
6363
interpreterQuickPick: IInterpreterQuickPick,
6464
interpreterPathService: IInterpreterPathService,
6565
pathUtils: IPathUtils,
66+
recommededEnvService: IRecommendedEnvironmentService,
6667
): void {
6768
disposables.push(
6869
registerCommand(
@@ -108,6 +109,7 @@ export function registerCreateEnvironmentFeatures(
108109
ConfigurationTarget.WorkspaceFolder,
109110
e.path,
110111
);
112+
recommededEnvService.trackUserSelectedEnvironment(e.path, e.workspaceFolder?.uri);
111113
showInformationMessage(`${CreateEnv.informEnvCreation} ${pathUtils.getDisplayName(e.path)}`);
112114
}
113115
}),

src/client/pythonEnvironments/creation/registrations.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the MIT License.
33

44
import { IDisposableRegistry, IInterpreterPathService, IPathUtils } from '../../common/types';
5-
import { IInterpreterQuickPick } from '../../interpreter/configuration/types';
5+
import { IInterpreterQuickPick, IRecommendedEnvironmentService } from '../../interpreter/configuration/types';
66
import { IInterpreterService } from '../../interpreter/contracts';
77
import { registerCreateEnvironmentFeatures } from './createEnvApi';
88
import { registerCreateEnvironmentButtonFeatures } from './createEnvButtonContext';
@@ -16,8 +16,9 @@ export function registerAllCreateEnvironmentFeatures(
1616
interpreterPathService: IInterpreterPathService,
1717
interpreterService: IInterpreterService,
1818
pathUtils: IPathUtils,
19+
recommededEnvService: IRecommendedEnvironmentService
1920
): void {
20-
registerCreateEnvironmentFeatures(disposables, interpreterQuickPick, interpreterPathService, pathUtils);
21+
registerCreateEnvironmentFeatures(disposables, interpreterQuickPick, interpreterPathService, pathUtils, recommededEnvService);
2122
registerCreateEnvironmentButtonFeatures(disposables);
2223
registerPyProjectTomlFeatures(disposables);
2324
registerInstalledPackagesDiagnosticsProvider(disposables, interpreterService);

src/test/pythonEnvironments/creation/createEnvApi.unit.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { registerCreateEnvironmentFeatures } from '../../../client/pythonEnviron
1414
import * as windowApis from '../../../client/common/vscodeApis/windowApis';
1515
import { handleCreateEnvironmentCommand } from '../../../client/pythonEnvironments/creation/createEnvironment';
1616
import { CreateEnvironmentProvider } from '../../../client/pythonEnvironments/creation/proposed.createEnvApis';
17+
import { noop } from '../../core';
1718

1819
chaiUse(chaiAsPromised.default);
1920

@@ -48,6 +49,11 @@ suite('Create Environment APIs', () => {
4849
interpreterQuickPick.object,
4950
interpreterPathService.object,
5051
pathUtils.object,
52+
{
53+
getRecommededEnvironment: () => Promise.resolve(undefined),
54+
registerEnvApi: noop,
55+
trackUserSelectedEnvironment: noop,
56+
},
5157
);
5258
});
5359
teardown(() => {

0 commit comments

Comments
 (0)