Skip to content

Commit 8e57b74

Browse files
committed
Merged PR posit-dev/positron-python#117: prompt to install ipykernel on runtime start instead of registration
Merge pull request #117 from posit-dev/install-ipykernel-on-start prompt to install ipykernel on runtime start instead of registration -------------------- Commit message for posit-dev/positron-python@0c1d9c0: prompt to install ipykernel on runtime start instead of registration This required moving that functionality to the language runtime. It also lets us throw an error if the install failed, which currently gets rendered in the console. We can replace that with a pop-up in future. Addresses #436. Authored-by: Wasim Lorgat <[email protected]> Signed-off-by: Wasim Lorgat <[email protected]>
1 parent 3251371 commit 8e57b74

File tree

2 files changed

+37
-20
lines changed

2 files changed

+37
-20
lines changed

extensions/positron-python/src/client/activation/jedi/positronLanguageRuntimes.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ import { Disposable, DocumentFilter, LanguageClientOptions } from 'vscode-langua
1414

1515
import * as semver from 'semver';
1616
import { EXTENSION_ROOT_DIR, PYTHON_LANGUAGE } from '../../common/constants';
17-
import { IConfigurationService, IDisposableRegistry, IInstaller, InstallerResponse, Product, Resource } from '../../common/types';
18-
import { InstallOptions } from '../../common/installer/types';
17+
import { IConfigurationService, IDisposableRegistry, IInstaller, Product, Resource } from '../../common/types';
1918
import { IInterpreterService } from '../../interpreter/contracts';
2019
import { IServiceContainer } from '../../ioc/types';
2120
import { traceVerbose } from '../../logging';
@@ -43,10 +42,6 @@ export class PositronJediLanguageServerProxy implements ILanguageServerProxy {
4342

4443
private readonly installer: IInstaller;
4544

46-
// Using a process to install modules avoids using the terminal service,
47-
// which has issues waiting for the outcome of the install.
48-
private readonly installOptions: InstallOptions = { installAsProcess: true };
49-
5045
private registered = false;
5146

5247
private readonly minimumSupportedVersion = '3.8.0';
@@ -65,7 +60,7 @@ export class PositronJediLanguageServerProxy implements ILanguageServerProxy {
6560
traceVerbose("Unable to read package.json to determine our extension version", e);
6661
}
6762

68-
this.disposables = serviceContainer.get<IDisposableRegistry>(IDisposableRegistry);
63+
this.disposables = this.serviceContainer.get<IDisposableRegistry>(IDisposableRegistry);
6964
this.installer = this.serviceContainer.get<IInstaller>(IInstaller);
7065
}
7166

@@ -99,16 +94,6 @@ export class PositronJediLanguageServerProxy implements ILanguageServerProxy {
9994
// Extend LSP support to include unsaved editors
10095
options.documentSelector = this.initDocumentSelector(options.documentSelector as DocumentFilter[]);
10196

102-
// Offer to install the ipykernel module for the preferred interpreter, if it is missing
103-
const hasKernel = await this.installer.isInstalled(Product.ipykernel, interpreter);
104-
if (!hasKernel) {
105-
const response = await this.installer.promptToInstall(Product.ipykernel,
106-
interpreter, undefined, undefined, this.installOptions);
107-
if (response === InstallerResponse.Installed) {
108-
traceVerbose(`Successfully installed ipykernel for ${interpreter?.displayName}`);
109-
}
110-
}
111-
11297
// Register available python interpreters as language runtimes with our Jupyter Adapter
11398
this.withActiveExtention(ext, async () => {
11499
// Create typesafe reference to the Jupyter Adapter's API
@@ -245,7 +230,7 @@ export class PositronJediLanguageServerProxy implements ILanguageServerProxy {
245230

246231
// Create an adapter for the kernel as our language runtime
247232
const runtime = new PythonLanguageRuntime(
248-
kernelSpec, metadata, jupyterAdapterApi, options);
233+
kernelSpec, metadata, jupyterAdapterApi, options, interpreter, this.installer);
249234

250235
// Register our language runtime provider
251236
return positron.runtime.registerLanguageRuntime(runtime);

extensions/positron-python/src/client/activation/jedi/pythonLanguageRuntime.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@ import * as positron from 'positron';
1010
import * as vscode from 'vscode';
1111
import { Disposable, LanguageClient, LanguageClientOptions, ServerOptions, StreamInfo } from 'vscode-languageclient/node';
1212
import { PYTHON_LANGUAGE } from '../../common/constants';
13+
import { InstallOptions } from '../../common/installer/types';
14+
import { IInstaller, Product, InstallerResponse } from '../../common/types';
1315
import { JupyterAdapterApi, JupyterKernelSpec, JupyterLanguageRuntime } from '../../jupyter-adapter.d';
16+
import { traceVerbose } from '../../logging';
1417
import { ProgressReporting } from '../progress';
18+
import { PythonEnvironment } from '../../pythonEnvironments/info';
1519

1620
/**
1721
* A Positron Python language runtime that wraps a Jupyter kernel runtime.
@@ -26,6 +30,10 @@ export class PythonLanguageRuntime implements JupyterLanguageRuntime, Disposable
2630

2731
private readonly disposables: Disposable[] = [];
2832

33+
// Using a process to install modules avoids using the terminal service,
34+
// which has issues waiting for the outcome of the install.
35+
private readonly installOptions: InstallOptions = { installAsProcess: true };
36+
2937
/**
3038
* Create a new PythonLanguageRuntime object to wrap a Jupyter kernel.
3139
*
@@ -37,7 +45,10 @@ export class PythonLanguageRuntime implements JupyterLanguageRuntime, Disposable
3745
readonly kernelSpec: JupyterKernelSpec,
3846
readonly metadata: positron.LanguageRuntimeMetadata,
3947
readonly adapterApi: JupyterAdapterApi,
40-
readonly languageClientOptions: LanguageClientOptions) {
48+
readonly languageClientOptions: LanguageClientOptions,
49+
private readonly interpreter: PythonEnvironment | undefined,
50+
private readonly installer: IInstaller,
51+
) {
4152

4253
this._kernel = adapterApi.adaptKernel(kernelSpec, metadata);
4354

@@ -91,10 +102,31 @@ export class PythonLanguageRuntime implements JupyterLanguageRuntime, Disposable
91102
this._kernel.replyToPrompt(id, reply);
92103
}
93104

94-
start(): Thenable<positron.LanguageRuntimeInfo> {
105+
async start(): Promise<positron.LanguageRuntimeInfo> {
106+
await this._installIpykernel();
95107
return this._kernel.start();
96108
}
97109

110+
private async _installIpykernel(): Promise<void> {
111+
// Offer to install the ipykernel module for the preferred interpreter, if it is missing.
112+
// Thow an error if it could not be installed.
113+
const hasKernel = await this.installer.isInstalled(Product.ipykernel, this.interpreter);
114+
if (!hasKernel) {
115+
const response = await this.installer.promptToInstall(Product.ipykernel,
116+
this.interpreter, undefined, undefined, this.installOptions);
117+
switch (response) {
118+
case InstallerResponse.Installed:
119+
traceVerbose(`Successfully installed ipykernel for ${this.interpreter?.displayName}`);
120+
break;
121+
case InstallerResponse.Ignore:
122+
case InstallerResponse.Disabled:
123+
throw new Error(`Could not start runtime: failed to install ipykernel for ${this.interpreter?.displayName}.`);
124+
default:
125+
throw new Error(`Unknown installer response type: ${response}`);
126+
}
127+
}
128+
}
129+
98130
async interrupt(): Promise<void> {
99131
return this._kernel.interrupt();
100132
}

0 commit comments

Comments
 (0)