Skip to content

Commit ffbe5e1

Browse files
authored
Install jupyter instead of installing kernel spec (#10080) (#10083)
* Install jupyter instead of installing kernel spec For #10071
1 parent f2d788c commit ffbe5e1

File tree

3 files changed

+31
-5
lines changed

3 files changed

+31
-5
lines changed

news/2 Fixes/10071.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Re-install `Jupyter` instead of installing `kernelspec` if `kernelspec` cannot be found in the python environment.

src/client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,8 @@ function sortProductsInOrderForInstallation(products: Product[]) {
5757
* @returns {string}
5858
*/
5959
export function getMessageForLibrariesNotInstalled(products: Product[]): string {
60+
// Even though kernelspec cannot be installed, display it so user knows what is missing.
6061
const names = products
61-
// Ignore kernelspec as it not something that can be installed.
62-
.filter(product => product !== Product.kernelspec)
6362
.map(product => ProductNames.get(product))
6463
.filter(name => !!name)
6564
.map(name => name as string);
@@ -122,15 +121,15 @@ export class JupyterInterpreterDependencyService {
122121
_error?: JupyterInstallError,
123122
token?: CancellationToken
124123
): Promise<JupyterInterpreterDependencyResponse> {
125-
const productsToInstall = await this.getDependenciesNotInstalled(interpreter, token);
124+
const missingProducts = await this.getDependenciesNotInstalled(interpreter, token);
126125
if (Cancellation.isCanceled(token)) {
127126
return JupyterInterpreterDependencyResponse.cancel;
128127
}
129-
if (productsToInstall.length === 0) {
128+
if (missingProducts.length === 0) {
130129
return JupyterInterpreterDependencyResponse.ok;
131130
}
132131

133-
const message = getMessageForLibrariesNotInstalled(productsToInstall);
132+
const message = getMessageForLibrariesNotInstalled(missingProducts);
134133

135134
sendTelemetryEvent(Telemetry.JupyterNotInstalledErrorShown);
136135
const selection = await this.applicationShell.showErrorMessage(
@@ -147,8 +146,15 @@ export class JupyterInterpreterDependencyService {
147146

148147
switch (selection) {
149148
case DataScience.jupyterInstall(): {
149+
// Ignore kernelspec as it not something that can be installed.
150+
// If kernelspec isn't available, then re-install `Jupyter`.
151+
if (missingProducts.includes(Product.kernelspec) && !missingProducts.includes(Product.jupyter)) {
152+
missingProducts.push(Product.jupyter);
153+
}
154+
const productsToInstall = missingProducts.filter(product => product !== Product.kernelspec);
150155
// Install jupyter, then notebook, then others in that order.
151156
sortProductsInOrderForInstallation(productsToInstall);
157+
152158
let productToInstall = productsToInstall.shift();
153159
const cancellatonPromise = createPromiseFromCancellation({ cancelAction: 'resolve', defaultValue: InstallerResponse.Ignore, token });
154160
while (productToInstall) {

src/test/datascience/jupyter/interpreter/jupyterInterpreterDependencyService.unit.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { Architecture } from '../../../../client/common/utils/platform';
1717
import { JupyterInterpreterDependencyResponse, JupyterInterpreterDependencyService } from '../../../../client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService';
1818
import { InterpreterType, PythonInterpreter } from '../../../../client/interpreter/contracts';
1919

20+
// tslint:disable-next-line: max-func-body-length
2021
suite('Data Science - Jupyter Interpreter Configuration', () => {
2122
let configuration: JupyterInterpreterDependencyService;
2223
let appShell: IApplicationShell;
@@ -62,6 +63,24 @@ suite('Data Science - Jupyter Interpreter Configuration', () => {
6263
test('Prompt to install if Jupyter is not installed', async () => testPromptIfModuleNotInstalled(false, true));
6364
test('Prompt to install if notebook is not installed', async () => testPromptIfModuleNotInstalled(true, false));
6465
test('Prompt to install if jupyter & notebook is not installed', async () => testPromptIfModuleNotInstalled(false, false));
66+
test('Reinstall Jupyter if jupyter and notebook are installed but kernelspec is not found', async () => {
67+
when(installer.isInstalled(Product.jupyter, pythonInterpreter)).thenResolve(true);
68+
when(installer.isInstalled(Product.notebook, pythonInterpreter)).thenResolve(true);
69+
when(appShell.showErrorMessage(anything(), anything(), anything(), anything())).thenResolve(
70+
// tslint:disable-next-line: no-any
71+
DataScience.jupyterInstall() as any
72+
);
73+
when(pythonExecService.execModule('jupyter', deepEqual(['kernelspec', '--version']), anything())).thenReject(new Error('Not found'));
74+
when(installer.install(anything(), anything(), anything())).thenResolve(InstallerResponse.Installed);
75+
76+
const response = await configuration.installMissingDependencies(pythonInterpreter);
77+
78+
// Jupyter must be installed & not kernelspec or anything else.
79+
verify(installer.install(Product.jupyter, anything(), anything())).once();
80+
verify(installer.install(anything(), anything(), anything())).once();
81+
verify(appShell.showErrorMessage(anything(), DataScience.jupyterInstall(), DataScience.selectDifferentJupyterInterpreter(), anything())).once();
82+
assert.equal(response, JupyterInterpreterDependencyResponse.cancel);
83+
});
6584

6685
async function testInstallationOfJupyter(installerResponse: InstallerResponse, expectedConfigurationReponse: JupyterInterpreterDependencyResponse): Promise<void> {
6786
when(installer.isInstalled(Product.jupyter, pythonInterpreter)).thenResolve(false);

0 commit comments

Comments
 (0)