Skip to content

Commit f50b477

Browse files
IanMatthewHuffrchiodo
authored andcommitted
Better messaging on notebook fail (#10056)
1 parent 5066319 commit f50b477

12 files changed

+132
-27
lines changed

PYTHON_INTERACTIVE_TROUBLESHOOTING.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Trouble shooting the Python Interactive Window
1+
# Troubleshooting Jupyter issues in the Python Interactive Window or Notebook Editor
22

3-
This document is intended to help troubleshoot problems in the Python Interactive Window.
3+
This document is intended to help troubleshoot problems with starting Jupyter in the Python Interactive Window or Notebook Editor.
44

55
---
66
## Jupyter Not Starting
@@ -10,12 +10,21 @@ This error can happen when
1010
* Jupyter is not installed
1111
* You picked the wrong Python environment (one that doesn't have Jupyter installed).
1212

13-
### The first step is to verify you are running the Python environment you want.
13+
### The first step is to verify you are running the Python environment that you have Jupyter installed into.
1414

15-
The Python you're using is picked with the selection dropdown on the bottom left of the VS Code window:
15+
The first time that you start the Interactive Window or the Notebook Editor VS Code will attempt to locate a Python environment that has Jupyter installed in it and can start a notebook.
16+
17+
The first Python interpreter to check will be the one selected with the selection dropdown on the bottom left of the VS Code window:
1618

1719
![selector](resources/PythonSelector.png)
1820

21+
Once a suitable interpreter with Jupyter has been located, VS Code will continue to use that interpreter for starting up Jupyter servers.
22+
If no interpreters are found with Jupyter installed a popup message will ask if you would like to install Jupyter into the current interpreter.
23+
24+
![install Jupyter](resources/InstallJupyter.png)
25+
26+
If you would like to change from using the saved Python interpreter to a new interpreter for launching Jupyter just use the "Python: Select interpreter to start Jupyter server" VS Code command to change it.
27+
1928
### The second step is to check that jupyter isn't giving any errors on startup.
2029

2130
Run the following command from an environment that matches the Python you selected:

news/2 Fixes/9904.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve error messaging when the jupyter notebook cannot be started.

package.nls.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@
193193
"DataScience.libraryNotInstalled": "Data Science library {0} is not installed. Install?",
194194
"DataScience.libraryRequiredToLaunchJupyterNotInstalled": "Data Science library {0} is not installed.",
195195
"DataScience.librariesRequiredToLaunchJupyterNotInstalled": "Data Science libraries {0} are not installed.",
196+
"DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter": "Data Science library {1} is not installed in interpreter {0}.",
197+
"DataScience.librariesRequiredToLaunchJupyterNotInstalledInterpreter": "Data Science libraries {1} are not installed in interpreter {0}.",
196198
"DataScience.jupyterInstall": "Install",
197199
"DataScience.jupyterSelectURIPrompt": "Enter the URI of the running Jupyter server",
198200
"DataScience.jupyterSelectURIInvalidURI": "Invalid URI specified",

resources/InstallJupyter.png

14.4 KB
Loading

src/client/common/utils/localize.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,14 @@ export namespace DataScience {
211211
'DataScience.librariesRequiredToLaunchJupyterNotInstalled',
212212
'Data Science libraries {0} are not installed.'
213213
);
214+
export const libraryRequiredToLaunchJupyterNotInstalledInterpreter = localize(
215+
'DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter',
216+
'Data Science library {1} is not installed in interpreter {0}.'
217+
);
218+
export const librariesRequiredToLaunchJupyterNotInstalledInterpreter = localize(
219+
'DataScience.librariesRequiredToLaunchJupyterNotInstalledInterpreter',
220+
'Data Science libraries {1} are not installed in interpreter {0}.'
221+
);
214222
export const selectJupyterInterpreter = localize('DataScience.selectJupyterInterpreter', 'Select an Interpreter to start Jupyter');
215223
export const jupyterInstall = localize('DataScience.jupyterInstall', 'Install');
216224
export const currentlySelectedJupyterInterpreterForPlaceholder = localize('Datascience.currentlySelectedJupyterInterpreterForPlaceholder', 'current: {0}');

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { Common, DataScience } from '../../../common/utils/localize';
1414
import { noop } from '../../../common/utils/misc';
1515
import { PythonInterpreter } from '../../../interpreter/contracts';
1616
import { sendTelemetryEvent } from '../../../telemetry';
17-
import { Telemetry } from '../../constants';
17+
import { HelpLinks, Telemetry } from '../../constants';
1818
import { JupyterInstallError } from '../jupyterInstallError';
1919

2020
export enum JupyterInterpreterDependencyResponse {
@@ -56,8 +56,7 @@ function sortProductsInOrderForInstallation(products: Product[]) {
5656
* @param {Product[]} products
5757
* @returns {string}
5858
*/
59-
export function getMessageForLibrariesNotInstalled(products: Product[]): string {
60-
// Even though kernelspec cannot be installed, display it so user knows what is missing.
59+
export function getMessageForLibrariesNotInstalled(products: Product[], interpreterName?: string): string {
6160
const names = products
6261
.map(product => ProductNames.get(product))
6362
.filter(name => !!name)
@@ -67,10 +66,14 @@ export function getMessageForLibrariesNotInstalled(products: Product[]): string
6766
case 0:
6867
return '';
6968
case 1:
70-
return DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(names[0]);
69+
return interpreterName
70+
? DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(interpreterName, names[0])
71+
: DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(names[0]);
7172
default: {
7273
const lastItem = names.pop();
73-
return DataScience.librariesRequiredToLaunchJupyterNotInstalled().format(`${names.join(', ')} ${Common.and()} ${lastItem}`);
74+
return interpreterName
75+
? DataScience.librariesRequiredToLaunchJupyterNotInstalledInterpreter().format(interpreterName, `${names.join(', ')} ${Common.and()} ${lastItem}`)
76+
: DataScience.librariesRequiredToLaunchJupyterNotInstalled().format(`${names.join(', ')} ${Common.and()} ${lastItem}`);
7477
}
7578
}
7679
}
@@ -129,15 +132,14 @@ export class JupyterInterpreterDependencyService {
129132
return JupyterInterpreterDependencyResponse.ok;
130133
}
131134

132-
const message = getMessageForLibrariesNotInstalled(missingProducts);
135+
const message = getMessageForLibrariesNotInstalled(missingProducts, interpreter.displayName);
133136

134137
sendTelemetryEvent(Telemetry.JupyterNotInstalledErrorShown);
135138
const selection = await this.applicationShell.showErrorMessage(
136-
// tslint:disable-next-line: messages-must-be-localized
137-
`${message}\r\n${DataScience.markdownHelpInstallingMissingDependencies()}`,
139+
message,
138140
DataScience.jupyterInstall(),
139141
DataScience.selectDifferentJupyterInterpreter(),
140-
Common.cancel()
142+
DataScience.pythonInteractiveHelpLink()
141143
);
142144

143145
if (Cancellation.isCanceled(token)) {
@@ -178,6 +180,12 @@ export class JupyterInterpreterDependencyService {
178180
return JupyterInterpreterDependencyResponse.selectAnotherInterpreter;
179181
}
180182

183+
case DataScience.pythonInteractiveHelpLink(): {
184+
this.applicationShell.openUrl(HelpLinks.PythonInteractiveHelpLink);
185+
sendTelemetryEvent(Telemetry.UserDidNotInstallJupyter);
186+
return JupyterInterpreterDependencyResponse.cancel;
187+
}
188+
181189
default:
182190
sendTelemetryEvent(Telemetry.UserDidNotInstallJupyter);
183191
return JupyterInterpreterDependencyResponse.cancel;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export class JupyterInterpreterSubCommandExecutionService implements IJupyterSub
8585
return DataScience.jupyterKernelSpecModuleNotFound();
8686
}
8787

88-
return getMessageForLibrariesNotInstalled(productsNotInstalled);
88+
return getMessageForLibrariesNotInstalled(productsNotInstalled, interpreter.displayName);
8989
}
9090
public async getSelectedInterpreter(token?: CancellationToken): Promise<PythonInterpreter | undefined> {
9191
return this.jupyterInterpreter.getSelectedInterpreter(token);

src/datascience-ui/history-react/interactivePanel.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
'use strict';
44
import * as React from 'react';
55
import { connect } from 'react-redux';
6+
import { noop } from '../../client/common/utils/misc';
67
import { Identifiers } from '../../client/datascience/constants';
78
import { ContentPanel, IContentPanelProps } from '../interactive-common/contentPanel';
89
import { handleLinkClick } from '../interactive-common/handlers';
@@ -75,13 +76,20 @@ export class InteractivePanel extends React.Component<IInteractivePanelProps> {
7576
<main id="main-panel-content" onScroll={this.handleScroll}>
7677
{this.renderContentPanel(this.props.baseTheme)}
7778
</main>
78-
<section id="main-panel-footer" aria-label={getLocString('DataScience.editSection', 'Input new cells here')}>
79+
<section id="main-panel-footer" onClick={this.footerPanelClick} aria-label={getLocString('DataScience.editSection', 'Input new cells here')}>
7980
{this.renderFooterPanel(this.props.baseTheme)}
8081
</section>
8182
</div>
8283
);
8384
}
8485

86+
// Make the entire footer focus our input, instead of having to click directly on the monaco editor
87+
private footerPanelClick = (_event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
88+
// This does nothing in release bits. Not necessary for point release.
89+
noop();
90+
};
91+
92+
// tslint:disable-next-line: max-func-body-length
8593
private renderToolbarPanel() {
8694
const variableExplorerTooltip = this.props.variableState.visible
8795
? getLocString('DataScience.collapseVariableExplorerTooltip', 'Hide variables active in jupyter kernel')

src/test/datascience/interactiveWindow.functional.test.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,48 @@ suite('DataScience Interactive Window output tests', () => {
187187
}
188188
);
189189

190+
runMountedTest(
191+
'Click outside cells sets focus to input box',
192+
async wrapper => {
193+
// Create an interactive window so that it listens to the results.
194+
const interactiveWindow = await getOrCreateInteractiveWindow(ioc);
195+
await interactiveWindow.show();
196+
197+
// Type in the input box
198+
const editor = getInteractiveEditor(wrapper);
199+
typeCode(editor, 'a=1\na');
200+
201+
// Give focus to a random div
202+
const reactDiv = wrapper
203+
.find('div')
204+
.first()
205+
.getDOMNode();
206+
207+
const domDiv = reactDiv.querySelector('div');
208+
209+
if (domDiv && ioc.postMessage) {
210+
domDiv.tabIndex = -1;
211+
domDiv.focus();
212+
213+
wrapper
214+
.find('section#main-panel-footer')
215+
.first()
216+
.simulate('click');
217+
218+
// Then enter press shift + enter on the active element
219+
const activeElement = document.activeElement;
220+
if (activeElement) {
221+
await submitInput(ioc, activeElement as HTMLTextAreaElement);
222+
}
223+
}
224+
225+
verifyHtmlOnCell(wrapper, 'InteractiveCell', '<span>1</span>', CellPosition.Last);
226+
},
227+
() => {
228+
return ioc;
229+
}
230+
);
231+
190232
runMountedTest(
191233
'Collapse / expand cell',
192234
async wrapper => {

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { PythonExecutionFactory } from '../../../../client/common/process/python
1212
import { PythonExecutionService } from '../../../../client/common/process/pythonProcess';
1313
import { IPythonExecutionService } from '../../../../client/common/process/types';
1414
import { IInstaller, InstallerResponse, Product } from '../../../../client/common/types';
15-
import { Common, DataScience } from '../../../../client/common/utils/localize';
15+
import { DataScience } from '../../../../client/common/utils/localize';
1616
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';
@@ -57,7 +57,9 @@ suite('Data Science - Jupyter Interpreter Configuration', () => {
5757

5858
const response = await configuration.installMissingDependencies(pythonInterpreter);
5959

60-
verify(appShell.showErrorMessage(anything(), DataScience.jupyterInstall(), DataScience.selectDifferentJupyterInterpreter(), Common.cancel())).once();
60+
verify(
61+
appShell.showErrorMessage(anything(), DataScience.jupyterInstall(), DataScience.selectDifferentJupyterInterpreter(), DataScience.pythonInteractiveHelpLink())
62+
).once();
6163
assert.equal(response, JupyterInterpreterDependencyResponse.cancel);
6264
}
6365
test('Prompt to install if Jupyter is not installed', async () => testPromptIfModuleNotInstalled(false, true));

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

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,19 @@ suite('Data Science - Jupyter InterpreterSubCommandExecutionService', () => {
107107
assert.equal(reason, DataScience.selectJupyterInterpreter());
108108
});
109109
test('Jupyter cannot be started because jupyter is not installed', async () => {
110-
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.jupyter)!);
110+
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
111+
activePythonInterpreter.displayName!,
112+
ProductNames.get(Product.jupyter)!
113+
);
111114
when(jupyterDependencyService.getDependenciesNotInstalled(activePythonInterpreter, undefined)).thenResolve([Product.jupyter]);
112115
const reason = await jupyterInterpreterExecutionService.getReasonForJupyterNotebookNotBeingSupported(undefined);
113116
assert.equal(reason, expectedReason);
114117
});
115118
test('Jupyter cannot be started because notebook is not installed', async () => {
116-
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!);
119+
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
120+
activePythonInterpreter.displayName!,
121+
ProductNames.get(Product.notebook)!
122+
);
117123
when(jupyterDependencyService.getDependenciesNotInstalled(activePythonInterpreter, undefined)).thenResolve([Product.notebook]);
118124
const reason = await jupyterInterpreterExecutionService.getReasonForJupyterNotebookNotBeingSupported(undefined);
119125
assert.equal(reason, expectedReason);
@@ -122,31 +128,41 @@ suite('Data Science - Jupyter InterpreterSubCommandExecutionService', () => {
122128
const promise = jupyterInterpreterExecutionService.startNotebook([], {});
123129
when(jupyterDependencyService.getDependenciesNotInstalled(activePythonInterpreter, undefined)).thenResolve([Product.notebook]);
124130

125-
await expect(promise).to.eventually.be.rejectedWith(DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!));
131+
await expect(promise).to.eventually.be.rejectedWith(
132+
DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(activePythonInterpreter.displayName!, ProductNames.get(Product.notebook)!)
133+
);
126134
});
127135
test('Cannot launch notebook file in jupyter notebook', async () => {
128136
const promise = jupyterInterpreterExecutionService.openNotebook('some.ipynb');
129137
when(jupyterDependencyService.getDependenciesNotInstalled(activePythonInterpreter, undefined)).thenResolve([Product.notebook]);
130138

131-
await expect(promise).to.eventually.be.rejectedWith(DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!));
139+
await expect(promise).to.eventually.be.rejectedWith(
140+
DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(activePythonInterpreter.displayName!, ProductNames.get(Product.notebook)!)
141+
);
132142
});
133143
test('Cannot export notebook to python', async () => {
134144
const promise = jupyterInterpreterExecutionService.exportNotebookToPython('somefile.ipynb');
135145
when(jupyterDependencyService.getDependenciesNotInstalled(activePythonInterpreter, undefined)).thenResolve([Product.notebook]);
136146

137-
await expect(promise).to.eventually.be.rejectedWith(DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!));
147+
await expect(promise).to.eventually.be.rejectedWith(
148+
DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(activePythonInterpreter.displayName!, ProductNames.get(Product.notebook)!)
149+
);
138150
});
139151
test('Cannot get a list of running jupyter servers', async () => {
140152
const promise = jupyterInterpreterExecutionService.getRunningJupyterServers(undefined);
141153
when(jupyterDependencyService.getDependenciesNotInstalled(activePythonInterpreter, undefined)).thenResolve([Product.notebook]);
142154

143-
await expect(promise).to.eventually.be.rejectedWith(DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!));
155+
await expect(promise).to.eventually.be.rejectedWith(
156+
DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(activePythonInterpreter.displayName!, ProductNames.get(Product.notebook)!)
157+
);
144158
});
145159
test('Cannot get kernelspecs', async () => {
146160
const promise = jupyterInterpreterExecutionService.getKernelSpecs(undefined);
147161
when(jupyterDependencyService.getDependenciesNotInstalled(activePythonInterpreter, undefined)).thenResolve([Product.notebook]);
148162

149-
await expect(promise).to.eventually.be.rejectedWith(DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!));
163+
await expect(promise).to.eventually.be.rejectedWith(
164+
DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(activePythonInterpreter.displayName!, ProductNames.get(Product.notebook)!)
165+
);
150166
});
151167
});
152168
// tslint:disable-next-line: max-func-body-length
@@ -181,15 +197,21 @@ suite('Data Science - Jupyter InterpreterSubCommandExecutionService', () => {
181197
verify(jupyterDependencyService.installMissingDependencies(selectedJupyterInterpreter, undefined)).once();
182198
});
183199
test('Jupyter cannot be started because jupyter is not installed', async () => {
184-
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.jupyter)!);
200+
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
201+
selectedJupyterInterpreter.displayName!,
202+
ProductNames.get(Product.jupyter)!
203+
);
185204
when(jupyterDependencyService.getDependenciesNotInstalled(selectedJupyterInterpreter, undefined)).thenResolve([Product.jupyter]);
186205

187206
const reason = await jupyterInterpreterExecutionService.getReasonForJupyterNotebookNotBeingSupported(undefined);
188207

189208
assert.equal(reason, expectedReason);
190209
});
191210
test('Jupyter cannot be started because notebook is not installed', async () => {
192-
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!);
211+
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
212+
selectedJupyterInterpreter.displayName!,
213+
ProductNames.get(Product.notebook)!
214+
);
193215
when(jupyterDependencyService.getDependenciesNotInstalled(selectedJupyterInterpreter, undefined)).thenResolve([Product.notebook]);
194216

195217
const reason = await jupyterInterpreterExecutionService.getReasonForJupyterNotebookNotBeingSupported(undefined);

0 commit comments

Comments
 (0)