diff --git a/src/client/api.ts b/src/client/api.ts index b4168856ad9c..58e20bfe2f37 100644 --- a/src/client/api.ts +++ b/src/client/api.ts @@ -8,6 +8,7 @@ import { NotebookCell } from 'vscode-proposed'; import { isTestExecution } from './common/constants'; import { traceError } from './common/logger'; import { IConfigurationService, Resource } from './common/types'; +import { JupyterExtensionIntegration } from './datascience/api/jupyterIntegration'; import { IDataViewerDataProvider, IDataViewerFactory } from './datascience/data-viewing/types'; import { IJupyterUriProvider, IJupyterUriProviderRegistration, INotebookExtensibility } from './datascience/types'; import { getDebugpyLauncherArgs, getDebugpyPackagePath } from './debugger/extension/adapter/remoteLaunchers'; @@ -26,6 +27,9 @@ export interface IExtensionApi { * @memberof IExtensionApi */ ready: Promise; + jupyter: { + registerHooks(): void; + }; debug: { /** * Generate an array of strings for commands to pass to the Python executable to launch the debugger for remote debugging. @@ -103,12 +107,17 @@ export function buildApi( const configurationService = serviceContainer.get(IConfigurationService); const interpreterService = serviceContainer.get(IInterpreterService); const notebookExtensibility = serviceContainer.get(INotebookExtensibility); + serviceManager.addSingleton(JupyterExtensionIntegration, JupyterExtensionIntegration); + const jupyterIntegration = serviceContainer.get(JupyterExtensionIntegration); const api: IExtensionApi = { // 'ready' will propagate the exception, but we must log it here first. ready: ready.catch((ex) => { traceError('Failure during activation.', ex); return Promise.reject(ex); }), + jupyter: { + registerHooks: () => jupyterIntegration.integrateWithJupyterExtension() + }, debug: { async getRemoteLauncherCommand( host: string, diff --git a/src/client/datascience/api/jupyterIntegration.ts b/src/client/datascience/api/jupyterIntegration.ts new file mode 100644 index 000000000000..4df5a8a7cd11 --- /dev/null +++ b/src/client/datascience/api/jupyterIntegration.ts @@ -0,0 +1,103 @@ +// tslint:disable-next-line: no-single-line-block-comment +/* eslint-disable comma-dangle */ +// tslint:disable-next-line: no-single-line-block-comment +/* eslint-disable implicit-arrow-linebreak */ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { inject, injectable } from 'inversify'; +import { CancellationToken, Event, Uri } from 'vscode'; +import { InterpreterUri } from '../../common/installer/types'; +import { IExtensions, IInstaller, InstallerResponse, Product, Resource } from '../../common/types'; +import { IEnvironmentActivationService } from '../../interpreter/activation/types'; +import { IInterpreterQuickPickItem, IInterpreterSelector } from '../../interpreter/configuration/types'; +import { IInterpreterService } from '../../interpreter/contracts'; +import { IWindowsStoreInterpreter } from '../../interpreter/locators/types'; +import { WindowsStoreInterpreter } from '../../pythonEnvironments/discovery/locators/services/windowsStoreInterpreter'; +import { PythonEnvironment } from '../../pythonEnvironments/info'; + +type PythonApiForJupyterExtension = { + /** + * IInterpreterService + */ + onDidChangeInterpreter: Event; + /** + * IInterpreterService + */ + getInterpreters(resource?: Uri): Promise; + /** + * IInterpreterService + */ + getActiveInterpreter(resource?: Uri): Promise; + /** + * IInterpreterService + */ + getInterpreterDetails(pythonPath: string, resource?: Uri): Promise; + + /** + * IEnvironmentActivationService + */ + getActivatedEnvironmentVariables( + resource: Resource, + interpreter?: PythonEnvironment, + allowExceptions?: boolean + ): Promise; + isWindowsStoreInterpreter(pythonPath: string): Promise; + /** + * IWindowsStoreInterpreter + */ + getSuggestions(resource: Resource): Promise; + /** + * IInstaller + */ + install(product: Product, resource?: InterpreterUri, cancel?: CancellationToken): Promise; +}; + +type JupyterExtensionApi = { + registerPythonApi(interpreterService: PythonApiForJupyterExtension): void; +}; + +@injectable() +export class JupyterExtensionIntegration { + constructor( + @inject(IExtensions) private readonly extensions: IExtensions, + @inject(IInterpreterService) private readonly interpreterService: IInterpreterService, + @inject(IInterpreterSelector) private readonly interpreterSelector: IInterpreterSelector, + @inject(WindowsStoreInterpreter) private readonly windowsStoreInterpreter: IWindowsStoreInterpreter, + @inject(IInstaller) private readonly installer: IInstaller, + @inject(IEnvironmentActivationService) private readonly envActivation: IEnvironmentActivationService + ) {} + + public async integrateWithJupyterExtension(): Promise { + const jupyterExtension = this.extensions.getExtension('ms-ai-tools.jupyter'); + if (!jupyterExtension) { + return; + } + await jupyterExtension.activate(); + if (!jupyterExtension.isActive) { + return; + } + const jupyterExtensionApi = jupyterExtension.exports; + jupyterExtensionApi.registerPythonApi({ + onDidChangeInterpreter: this.interpreterService.onDidChangeInterpreter, + getActiveInterpreter: async (resource?: Uri) => this.interpreterService.getActiveInterpreter(resource), + getInterpreterDetails: async (pythonPath: string) => + this.interpreterService.getInterpreterDetails(pythonPath), + getInterpreters: async (resource: Uri | undefined) => this.interpreterService.getInterpreters(resource), + getActivatedEnvironmentVariables: async ( + resource: Resource, + interpreter?: PythonEnvironment, + allowExceptions?: boolean + ) => this.envActivation.getActivatedEnvironmentVariables(resource, interpreter, allowExceptions), + isWindowsStoreInterpreter: async (pythonPath: string): Promise => + this.windowsStoreInterpreter.isWindowsStoreInterpreter(pythonPath), + getSuggestions: async (resource: Resource): Promise => + this.interpreterSelector.getSuggestions(resource), + install: async ( + product: Product, + resource?: InterpreterUri, + cancel?: CancellationToken + ): Promise => this.installer.install(product, resource, cancel) + }); + } +}