From 629e7f45efcdaafe3c40a7134f12626b71cc77bd Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Fri, 11 Aug 2023 13:08:45 -0700 Subject: [PATCH 1/6] Apply API recommendations for Create Env API --- .../creation/createEnvironment.ts | 17 ++-- .../creation/proposed.createEnvApis.ts | 91 ++++++++++++++----- .../provider/condaCreationProvider.ts | 2 +- 3 files changed, 77 insertions(+), 33 deletions(-) diff --git a/src/client/pythonEnvironments/creation/createEnvironment.ts b/src/client/pythonEnvironments/creation/createEnvironment.ts index 4593ff1abf92..f026a1a82ccd 100644 --- a/src/client/pythonEnvironments/creation/createEnvironment.ts +++ b/src/client/pythonEnvironments/creation/createEnvironment.ts @@ -33,14 +33,12 @@ function fireStartedEvent(options?: CreateEnvironmentOptions): void { } function fireExitedEvent(result?: CreateEnvironmentResult, options?: CreateEnvironmentOptions, error?: Error): void { - onCreateEnvironmentExitedEvent.fire({ - options, - workspaceFolder: result?.workspaceFolder, - path: result?.path, - action: result?.action, - error: error || result?.error, - }); startedEventCount -= 1; + if (result) { + onCreateEnvironmentExitedEvent.fire({ options, ...result }); + } else if (error) { + onCreateEnvironmentExitedEvent.fire({ options, error }); + } } export function getCreationEvents(): { @@ -195,5 +193,8 @@ export async function handleCreateEnvironmentCommand( } } - return result; + if (result) { + return Object.freeze(result); + } + return undefined; } diff --git a/src/client/pythonEnvironments/creation/proposed.createEnvApis.ts b/src/client/pythonEnvironments/creation/proposed.createEnvApis.ts index 0120b2a6e8d7..c1c4591dc11c 100644 --- a/src/client/pythonEnvironments/creation/proposed.createEnvApis.ts +++ b/src/client/pythonEnvironments/creation/proposed.createEnvApis.ts @@ -43,37 +43,80 @@ export interface EnvironmentWillCreateEvent { options: CreateEnvironmentOptions | undefined; } +export type CreateEnvironmentResult = + | { + /** + * Workspace folder associated with the environment. + */ + readonly workspaceFolder?: WorkspaceFolder; + + /** + * Path to the executable python in the environment + */ + readonly path: string; + + /** + * User action that resulted in exit from the create environment flow. + */ + readonly action?: CreateEnvironmentUserActions; + + /** + * Error if any occurred during environment creation. + */ + readonly error?: Error; + } + | { + /** + * Workspace folder associated with the environment. + */ + readonly workspaceFolder?: WorkspaceFolder; + + /** + * Path to the executable python in the environment + */ + readonly path?: string; + + /** + * User action that resulted in exit from the create environment flow. + */ + readonly action: CreateEnvironmentUserActions; + + /** + * Error if any occurred during environment creation. + */ + readonly error?: Error; + } + | { + /** + * Workspace folder associated with the environment. + */ + readonly workspaceFolder?: WorkspaceFolder; + + /** + * Path to the executable python in the environment + */ + readonly path?: string; + + /** + * User action that resulted in exit from the create environment flow. + */ + readonly action?: CreateEnvironmentUserActions; + + /** + * Error if any occurred during environment creation. + */ + readonly error: Error | undefined; + }; + /** * Params passed on `onDidCreateEnvironment` event handler. */ -export interface EnvironmentDidCreateEvent extends CreateEnvironmentResult { +export type EnvironmentDidCreateEvent = CreateEnvironmentResult & { /** * Options used to create the Python environment. */ options: CreateEnvironmentOptions | undefined; -} - -export interface CreateEnvironmentResult { - /** - * Workspace folder associated with the environment. - */ - workspaceFolder: WorkspaceFolder | undefined; - - /** - * Path to the executable python in the environment - */ - path: string | undefined; - - /** - * User action that resulted in exit from the create environment flow. - */ - action: CreateEnvironmentUserActions | undefined; - - /** - * Error if any occurred during environment creation. - */ - error: Error | undefined; -} +}; /** * Extensions that want to contribute their own environment creation can do that by registering an object diff --git a/src/client/pythonEnvironments/creation/provider/condaCreationProvider.ts b/src/client/pythonEnvironments/creation/provider/condaCreationProvider.ts index 39cd40afd41a..975ac5a439e4 100644 --- a/src/client/pythonEnvironments/creation/provider/condaCreationProvider.ts +++ b/src/client/pythonEnvironments/creation/provider/condaCreationProvider.ts @@ -247,7 +247,7 @@ async function createEnvironment(options?: CreateEnvironmentOptions): Promise Date: Fri, 11 Aug 2023 15:16:09 -0700 Subject: [PATCH 2/6] Fix test --- .../creation/provider/condaCreationProvider.unit.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/pythonEnvironments/creation/provider/condaCreationProvider.unit.test.ts b/src/test/pythonEnvironments/creation/provider/condaCreationProvider.unit.test.ts index cb4df95c8c1f..d7a344fce26b 100644 --- a/src/test/pythonEnvironments/creation/provider/condaCreationProvider.unit.test.ts +++ b/src/test/pythonEnvironments/creation/provider/condaCreationProvider.unit.test.ts @@ -134,7 +134,6 @@ suite('Conda Creation provider tests', () => { assert.deepStrictEqual(await promise, { path: 'new_environment', workspaceFolder: workspace1, - action: undefined, error: undefined, }); assert.isTrue(showErrorMessageWithLogsStub.notCalled); From e5c984ef7110cd851f92a181960c468eb9156c7f Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Mon, 14 Aug 2023 12:36:54 -0700 Subject: [PATCH 3/6] Fix typing --- .../creation/proposed.createEnvApis.ts | 2 +- .../provider/condaCreationProvider.ts | 38 +++++++++++-------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/client/pythonEnvironments/creation/proposed.createEnvApis.ts b/src/client/pythonEnvironments/creation/proposed.createEnvApis.ts index c1c4591dc11c..b71ac96cd390 100644 --- a/src/client/pythonEnvironments/creation/proposed.createEnvApis.ts +++ b/src/client/pythonEnvironments/creation/proposed.createEnvApis.ts @@ -105,7 +105,7 @@ export type CreateEnvironmentResult = /** * Error if any occurred during environment creation. */ - readonly error: Error | undefined; + readonly error: Error; }; /** diff --git a/src/client/pythonEnvironments/creation/provider/condaCreationProvider.ts b/src/client/pythonEnvironments/creation/provider/condaCreationProvider.ts index 975ac5a439e4..ad0e87cce088 100644 --- a/src/client/pythonEnvironments/creation/provider/condaCreationProvider.ts +++ b/src/client/pythonEnvironments/creation/provider/condaCreationProvider.ts @@ -77,7 +77,7 @@ async function createCondaEnv( args: string[], progress: CreateEnvironmentProgress, token?: CancellationToken, -): Promise { +): Promise { progress.report({ message: CreateEnv.Conda.creating, }); @@ -223,13 +223,14 @@ async function createEnvironment(options?: CreateEnvironmentOptions): Promise Python for more info.') }; } - return { path: envPath, workspaceFolder: workspace, error: undefined }; + return { error: new Error('A workspace is needed to create conda environment') }; }, ); } From b8ff61f0596b0ee2654e26ef6a654f1e3c382c7b Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Mon, 14 Aug 2023 13:03:40 -0700 Subject: [PATCH 4/6] Fix errors --- .../provider/condaCreationProvider.ts | 45 +++++++++---------- .../creation/provider/venvCreationProvider.ts | 21 ++++----- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/client/pythonEnvironments/creation/provider/condaCreationProvider.ts b/src/client/pythonEnvironments/creation/provider/condaCreationProvider.ts index ad0e87cce088..74a42808e609 100644 --- a/src/client/pythonEnvironments/creation/provider/condaCreationProvider.ts +++ b/src/client/pythonEnvironments/creation/provider/condaCreationProvider.ts @@ -4,7 +4,7 @@ import { CancellationToken, ProgressLocation, WorkspaceFolder } from 'vscode'; import * as path from 'path'; import { Commands, PVSC_EXTENSION_ID } from '../../../common/constants'; -import { traceError, traceLog } from '../../../logging'; +import { traceError, traceInfo, traceLog } from '../../../logging'; import { CreateEnvironmentProgress } from '../types'; import { pickWorkspaceFolder } from '../common/workspaceSelection'; import { execObservable } from '../../../common/process/rawProcessApis'; @@ -174,6 +174,7 @@ async function createEnvironment(options?: CreateEnvironmentOptions): Promise => { - let hasError = false; - progress.report({ message: CreateEnv.statusStarting, }); - if (workspace) { - let envPath: string | undefined; - try { - sendTelemetryEvent(EventName.ENVIRONMENT_CREATING, undefined, { - environmentType: 'conda', - pythonVersion: version, - }); - + let envPath: string | undefined; + try { + sendTelemetryEvent(EventName.ENVIRONMENT_CREATING, undefined, { + environmentType: 'conda', + pythonVersion: version, + }); + if (workspace) { envPath = await createCondaEnv( workspace, getExecutableCommand(conda), @@ -238,22 +237,20 @@ async function createEnvironment(options?: CreateEnvironmentOptions): Promise Python for more info.') }; + throw new Error('Failed to create conda environment. See Output > Python for more info.'); + } else { + throw new Error('A workspace is needed to create conda environment'); + } + } catch (ex) { + traceError(ex); + showErrorMessageWithLogs(CreateEnv.Conda.errorCreatingEnvironment); + throw ex; } - return { error: new Error('A workspace is needed to create conda environment') }; }, ); } diff --git a/src/client/pythonEnvironments/creation/provider/venvCreationProvider.ts b/src/client/pythonEnvironments/creation/provider/venvCreationProvider.ts index b00682c3cb5d..6c310fd68331 100644 --- a/src/client/pythonEnvironments/creation/provider/venvCreationProvider.ts +++ b/src/client/pythonEnvironments/creation/provider/venvCreationProvider.ts @@ -8,7 +8,7 @@ import { createVenvScript } from '../../../common/process/internal/scripts'; import { execObservable } from '../../../common/process/rawProcessApis'; import { createDeferred } from '../../../common/utils/async'; import { Common, CreateEnv } from '../../../common/utils/localize'; -import { traceError, traceLog, traceVerbose } from '../../../logging'; +import { traceError, traceInfo, traceLog, traceVerbose } from '../../../logging'; import { CreateEnvironmentProgress } from '../types'; import { pickWorkspaceFolder } from '../common/workspaceSelection'; import { IInterpreterQuickPick } from '../../../interpreter/configuration/types'; @@ -144,6 +144,7 @@ export class VenvCreationProvider implements CreateEnvironmentProvider { traceError('Workspace was not selected or found for creating virtual environment.'); return MultiStepAction.Cancel; } + traceInfo(`Selected workspace ${workspace.uri.fsPath} for creating virtual environment.`); return MultiStepAction.Continue; }, undefined, @@ -183,6 +184,7 @@ export class VenvCreationProvider implements CreateEnvironmentProvider { traceError('Virtual env creation requires an interpreter.'); return MultiStepAction.Cancel; } + traceInfo(`Selected interpreter ${interpreter} for creating virtual environment.`); return MultiStepAction.Continue; }, undefined, @@ -237,8 +239,6 @@ export class VenvCreationProvider implements CreateEnvironmentProvider { progress: CreateEnvironmentProgress, token: CancellationToken, ): Promise => { - let hasError = false; - progress.report({ message: CreateEnv.statusStarting, }); @@ -247,18 +247,19 @@ export class VenvCreationProvider implements CreateEnvironmentProvider { try { if (interpreter && workspace) { envPath = await createVenv(workspace, interpreter, args, progress, token); + if (envPath) { + return { path: envPath, workspaceFolder: workspace }; + } + throw new Error('Failed to create virtual environment. See Output > Python for more info.'); } + throw new Error( + 'Failed to create virtual environment. Either interpreter or workspace is undefined.', + ); } catch (ex) { traceError(ex); - hasError = true; + showErrorMessageWithLogs(CreateEnv.Venv.errorCreatingEnvironment); throw ex; - } finally { - if (hasError) { - showErrorMessageWithLogs(CreateEnv.Venv.errorCreatingEnvironment); - } } - - return { path: envPath, workspaceFolder: workspace, action: undefined, error: undefined }; }, ); } From 1653e1b8789c47c0053bea2900c33245613339e5 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Mon, 14 Aug 2023 13:30:33 -0700 Subject: [PATCH 5/6] Fix tests --- .../creation/provider/condaCreationProvider.unit.test.ts | 1 - .../creation/provider/venvCreationProvider.unit.test.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/test/pythonEnvironments/creation/provider/condaCreationProvider.unit.test.ts b/src/test/pythonEnvironments/creation/provider/condaCreationProvider.unit.test.ts index d7a344fce26b..3fcadc42ba09 100644 --- a/src/test/pythonEnvironments/creation/provider/condaCreationProvider.unit.test.ts +++ b/src/test/pythonEnvironments/creation/provider/condaCreationProvider.unit.test.ts @@ -134,7 +134,6 @@ suite('Conda Creation provider tests', () => { assert.deepStrictEqual(await promise, { path: 'new_environment', workspaceFolder: workspace1, - error: undefined, }); assert.isTrue(showErrorMessageWithLogsStub.notCalled); }); diff --git a/src/test/pythonEnvironments/creation/provider/venvCreationProvider.unit.test.ts b/src/test/pythonEnvironments/creation/provider/venvCreationProvider.unit.test.ts index 1c22264f2ada..5bd325c51a0f 100644 --- a/src/test/pythonEnvironments/creation/provider/venvCreationProvider.unit.test.ts +++ b/src/test/pythonEnvironments/creation/provider/venvCreationProvider.unit.test.ts @@ -158,8 +158,6 @@ suite('venv Creation provider tests', () => { assert.deepStrictEqual(actual, { path: 'new_environment', workspaceFolder: workspace1, - action: undefined, - error: undefined, }); interpreterQuickPick.verifyAll(); progressMock.verifyAll(); From dad994326a9903d52e0c283db741e6b795dba644 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Mon, 14 Aug 2023 18:52:04 -0700 Subject: [PATCH 6/6] Mark APIs as readonly --- .../pythonEnvironments/creation/proposed.createEnvApis.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/pythonEnvironments/creation/proposed.createEnvApis.ts b/src/client/pythonEnvironments/creation/proposed.createEnvApis.ts index b71ac96cd390..ea520fdd27e2 100644 --- a/src/client/pythonEnvironments/creation/proposed.createEnvApis.ts +++ b/src/client/pythonEnvironments/creation/proposed.createEnvApis.ts @@ -40,7 +40,7 @@ export interface EnvironmentWillCreateEvent { /** * Options used to create a Python environment. */ - options: CreateEnvironmentOptions | undefined; + readonly options: CreateEnvironmentOptions | undefined; } export type CreateEnvironmentResult = @@ -115,7 +115,7 @@ export type EnvironmentDidCreateEvent = CreateEnvironmentResult & { /** * Options used to create the Python environment. */ - options: CreateEnvironmentOptions | undefined; + readonly options: CreateEnvironmentOptions | undefined; }; /** @@ -163,14 +163,14 @@ export interface ProposedCreateEnvironmentAPI { * provider (including internal providers). This will also receive any options passed in * or defaults used to create environment. */ - onWillCreateEnvironment: Event; + readonly onWillCreateEnvironment: Event; /** * This API can be used to detect when the environment provider exits for any registered * provider (including internal providers). This will also receive created environment path, * any errors, or user actions taken from the provider. */ - onDidCreateEnvironment: Event; + readonly onDidCreateEnvironment: Event; /** * This API will show a QuickPick to select an environment provider from available list of