Skip to content

Commit 8c44490

Browse files
IanMatthewHuffIan Huff
and
Ian Huff
authored
Port ipykernel install fix to release (#13975)
* Fix installing ipykernel into interpreters for raw kernel (#13959) * update news Co-authored-by: Ian Huff <[email protected]>
1 parent 411aae9 commit 8c44490

File tree

7 files changed

+45
-44
lines changed

7 files changed

+45
-44
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
([#13612](https://github.com/Microsoft/vscode-python/issues/13612))
5858
1. Fix the behavior of the 'python.showStartPage' setting.
5959
([#13706](https://github.com/Microsoft/vscode-python/issues/13706))
60+
1. Correctly install ipykernel when launching from an interpreter.
61+
([#13956](https://github.com/Microsoft/vscode-python/issues/13956))
6062

6163
### Code Health
6264

package.nls.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,5 +595,6 @@
595595
"DataScience.interactiveWindowModeBannerTitle": "Do you want to open a new Python Interactive window for this file? [More Information](command:workbench.action.openSettings?%5B%22python.dataScience.interactiveWindowMode%22%5D).",
596596
"DataScience.interactiveWindowModeBannerSwitchYes": "Yes",
597597
"DataScience.interactiveWindowModeBannerSwitchAlways": "Always",
598-
"DataScience.interactiveWindowModeBannerSwitchNo": "No"
598+
"DataScience.interactiveWindowModeBannerSwitchNo": "No",
599+
"DataScience.ipykernelNotInstalled": "IPyKernel not installed into interpreter {0}"
599600
}

src/client/common/utils/localize.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,10 @@ export namespace DataScience {
11161116
);
11171117
export const connected = localize('DataScience.connected', 'Connected');
11181118
export const disconnected = localize('DataScience.disconnected', 'Disconnected');
1119+
export const ipykernelNotInstalled = localize(
1120+
'DataScience.ipykernelNotInstalled',
1121+
'IPyKernel not installed into interpreter {0}'
1122+
);
11191123
}
11201124

11211125
export namespace StartPage {

src/client/datascience/jupyter/kernels/kernelSelector.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import {
2727
IJupyterSessionManagerFactory,
2828
IKernelDependencyService,
2929
INotebookMetadataLive,
30-
INotebookProviderConnection
30+
INotebookProviderConnection,
31+
KernelInterpreterDependencyResponse
3132
} from '../../types';
3233
import { createDefaultKernelSpec, getDisplayNameOrNameOfKernelConnection } from './helpers';
3334
import { KernelSelectionProvider } from './kernelSelections';
@@ -504,6 +505,8 @@ export class KernelSelector implements IKernelSelectionUsage {
504505
if (!kernelSpec && !activeInterpreter) {
505506
return;
506507
} else if (!kernelSpec && activeInterpreter) {
508+
await this.installDependenciesIntoInterpreter(activeInterpreter, ignoreDependencyCheck, cancelToken);
509+
507510
// Return current interpreter.
508511
return {
509512
kind: 'startUsingPythonInterpreter',
@@ -512,6 +515,11 @@ export class KernelSelector implements IKernelSelectionUsage {
512515
} else if (kernelSpec) {
513516
// Locate the interpreter that matches our kernelspec
514517
const interpreter = await this.kernelService.findMatchingInterpreter(kernelSpec, cancelToken);
518+
519+
if (interpreter) {
520+
await this.installDependenciesIntoInterpreter(interpreter, ignoreDependencyCheck, cancelToken);
521+
}
522+
515523
return { kind: 'startUsingKernelSpec', kernelSpec, interpreter };
516524
}
517525
}
@@ -545,6 +553,25 @@ export class KernelSelector implements IKernelSelectionUsage {
545553
return { kernelSpec, interpreter, kind: 'startUsingPythonInterpreter' };
546554
}
547555

556+
// If we need to install our dependencies now (for non-native scenarios)
557+
// then install ipykernel into the interpreter or throw error
558+
private async installDependenciesIntoInterpreter(
559+
interpreter: PythonEnvironment,
560+
ignoreDependencyCheck?: boolean,
561+
cancelToken?: CancellationToken
562+
) {
563+
if (!ignoreDependencyCheck) {
564+
if (
565+
(await this.kernelDependencyService.installMissingDependencies(interpreter, cancelToken)) !==
566+
KernelInterpreterDependencyResponse.ok
567+
) {
568+
throw new Error(
569+
localize.DataScience.ipykernelNotInstalled().format(interpreter.displayName || interpreter.path)
570+
);
571+
}
572+
}
573+
}
574+
548575
/**
549576
* Use the provided interpreter as a kernel.
550577
* If `displayNameOfKernelNotFound` is provided, then display a message indicating we're using the `current interpreter`.

src/client/datascience/kernel-launcher/kernelFinder.ts

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@
55
import type { nbformat } from '@jupyterlab/coreutils';
66
import { inject, injectable, named } from 'inversify';
77
import * as path from 'path';
8-
import { CancellationToken, CancellationTokenSource } from 'vscode';
8+
import { CancellationToken } from 'vscode';
99
import { IWorkspaceService } from '../../common/application/types';
10-
import { wrapCancellationTokens } from '../../common/cancellation';
1110
import { traceError, traceInfo } from '../../common/logger';
1211
import { IPlatformService } from '../../common/platform/types';
1312
import { IPythonExecutionFactory } from '../../common/process/types';
14-
import { IExtensionContext, IInstaller, InstallerResponse, IPathUtils, Product, Resource } from '../../common/types';
13+
import { IExtensionContext, IPathUtils, Resource } from '../../common/types';
1514
import { IEnvironmentVariablesProvider } from '../../common/variables/types';
1615
import { IInterpreterLocatorService, IInterpreterService, KNOWN_PATH_SERVICE } from '../../interpreter/contracts';
1716
import { captureTelemetry } from '../../telemetry';
@@ -20,7 +19,6 @@ import { Telemetry } from '../constants';
2019
import { defaultKernelSpecName } from '../jupyter/kernels/helpers';
2120
import { JupyterKernelSpec } from '../jupyter/kernels/jupyterKernelSpec';
2221
import { IDataScienceFileSystem, IJupyterKernelSpec } from '../types';
23-
import { getKernelInterpreter } from './helpers';
2422
import { IKernelFinder } from './types';
2523
// tslint:disable-next-line:no-require-imports no-var-requires
2624
const flatten = require('lodash/flatten') as typeof import('lodash/flatten');
@@ -56,7 +54,6 @@ export class KernelFinder implements IKernelFinder {
5654
@inject(IPlatformService) private platformService: IPlatformService,
5755
@inject(IDataScienceFileSystem) private fs: IDataScienceFileSystem,
5856
@inject(IPathUtils) private readonly pathUtils: IPathUtils,
59-
@inject(IInstaller) private installer: IInstaller,
6057
@inject(IExtensionContext) private readonly context: IExtensionContext,
6158
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService,
6259
@inject(IPythonExecutionFactory) private readonly exeFactory: IPythonExecutionFactory,
@@ -65,9 +62,7 @@ export class KernelFinder implements IKernelFinder {
6562
@captureTelemetry(Telemetry.KernelFinderPerf)
6663
public async findKernelSpec(
6764
resource: Resource,
68-
kernelSpecMetadata?: nbformat.IKernelspecMetadata,
69-
cancelToken?: CancellationToken,
70-
ignoreDependencyCheck?: boolean
65+
kernelSpecMetadata?: nbformat.IKernelspecMetadata
7166
): Promise<IJupyterKernelSpec | undefined> {
7267
await this.readCache();
7368
let foundKernel: IJupyterKernelSpec | undefined;
@@ -108,8 +103,7 @@ export class KernelFinder implements IKernelFinder {
108103

109104
this.writeCache().ignoreErrors();
110105

111-
// Verify that ipykernel is installed into the given kernelspec interpreter
112-
return ignoreDependencyCheck || !foundKernel ? foundKernel : this.verifyIpyKernel(foundKernel, cancelToken);
106+
return foundKernel;
113107
}
114108

115109
// Search all our local file system locations for installed kernel specs and return them
@@ -318,30 +312,6 @@ export class KernelFinder implements IKernelFinder {
318312
return flatten(fullPathResults);
319313
}
320314

321-
// For the given kernelspec return back the kernelspec with ipykernel installed into it or error
322-
private async verifyIpyKernel(
323-
kernelSpec: IJupyterKernelSpec,
324-
cancelToken?: CancellationToken
325-
): Promise<IJupyterKernelSpec> {
326-
const interpreter = await getKernelInterpreter(kernelSpec, this.interpreterService);
327-
328-
if (await this.installer.isInstalled(Product.ipykernel, interpreter)) {
329-
return kernelSpec;
330-
} else {
331-
const token = new CancellationTokenSource();
332-
const response = await this.installer.promptToInstall(
333-
Product.ipykernel,
334-
interpreter,
335-
wrapCancellationTokens(cancelToken, token.token)
336-
);
337-
if (response === InstallerResponse.Installed) {
338-
return kernelSpec;
339-
}
340-
}
341-
342-
throw new Error(`IPyKernel not installed into interpreter ${interpreter.displayName}`);
343-
}
344-
345315
private async getKernelSpecFromActiveInterpreter(
346316
kernelName: string,
347317
resource: Resource

src/test/datascience/jupyter/kernels/kernelSelector.unit.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {
3131
LiveKernelModel
3232
} from '../../../../client/datascience/jupyter/kernels/types';
3333
import { IKernelFinder } from '../../../../client/datascience/kernel-launcher/types';
34-
import { IJupyterSessionManager } from '../../../../client/datascience/types';
34+
import { IJupyterSessionManager, KernelInterpreterDependencyResponse } from '../../../../client/datascience/types';
3535
import { IInterpreterService } from '../../../../client/interpreter/contracts';
3636
import { InterpreterService } from '../../../../client/interpreter/interpreterService';
3737
import { EnvironmentType, PythonEnvironment } from '../../../../client/pythonEnvironments/info';
@@ -72,6 +72,9 @@ suite('DataScience - KernelSelector', () => {
7272
kernelSelectionProvider = mock(KernelSelectionProvider);
7373
appShell = mock(ApplicationShell);
7474
dependencyService = mock(KernelDependencyService);
75+
when(dependencyService.installMissingDependencies(anything(), anything())).thenResolve(
76+
KernelInterpreterDependencyResponse.ok
77+
);
7578
interpreterService = mock(InterpreterService);
7679
kernelFinder = mock<IKernelFinder>();
7780
const jupyterSessionManagerFactory = mock(JupyterSessionManagerFactory);

src/test/datascience/kernelFinder.unit.test.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Uri } from 'vscode';
1111
import { IWorkspaceService } from '../../client/common/application/types';
1212
import { IPlatformService } from '../../client/common/platform/types';
1313
import { PythonExecutionFactory } from '../../client/common/process/pythonExecutionFactory';
14-
import { IExtensionContext, IInstaller, IPathUtils, Resource } from '../../client/common/types';
14+
import { IExtensionContext, IPathUtils, Resource } from '../../client/common/types';
1515
import { Architecture } from '../../client/common/utils/platform';
1616
import { IEnvironmentVariablesProvider } from '../../client/common/variables/types';
1717
import { defaultKernelSpecName } from '../../client/datascience/jupyter/kernels/helpers';
@@ -30,7 +30,6 @@ suite('Kernel Finder', () => {
3030
let pathUtils: typemoq.IMock<IPathUtils>;
3131
let context: typemoq.IMock<IExtensionContext>;
3232
let envVarsProvider: typemoq.IMock<IEnvironmentVariablesProvider>;
33-
let installer: IInstaller;
3433
let workspaceService: IWorkspaceService;
3534
let kernelFinder: IKernelFinder;
3635
let activeInterpreter: PythonEnvironment;
@@ -83,9 +82,6 @@ suite('Kernel Finder', () => {
8382
context.setup((c) => c.globalStoragePath).returns(() => './');
8483
fileSystem = typemoq.Mock.ofType<IDataScienceFileSystem>();
8584

86-
installer = mock<IInstaller>();
87-
when(installer.isInstalled(anything(), anything())).thenResolve(true);
88-
8985
platformService = typemoq.Mock.ofType<IPlatformService>();
9086
platformService.setup((ps) => ps.isWindows).returns(() => true);
9187
platformService.setup((ps) => ps.isMac).returns(() => true);
@@ -325,7 +321,6 @@ suite('Kernel Finder', () => {
325321
platformService.object,
326322
fileSystem.object,
327323
pathUtils.object,
328-
instance(installer),
329324
context.object,
330325
instance(workspaceService),
331326
instance(executionFactory),
@@ -408,7 +403,6 @@ suite('Kernel Finder', () => {
408403
platformService.object,
409404
fileSystem.object,
410405
pathUtils.object,
411-
instance(installer),
412406
context.object,
413407
instance(workspaceService),
414408
instance(executionFactory),

0 commit comments

Comments
 (0)