Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Better messaging on notebook fail (#10056)
  • Loading branch information
IanMatthewHuff authored and rchiodo committed Feb 15, 2020
commit f50b47788827894ad20d56882edbf1b12651f6dc
17 changes: 13 additions & 4 deletions PYTHON_INTERACTIVE_TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Trouble shooting the Python Interactive Window
# Troubleshooting Jupyter issues in the Python Interactive Window or Notebook Editor

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

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

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

The Python you're using is picked with the selection dropdown on the bottom left of the VS Code window:
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.

The first Python interpreter to check will be the one selected with the selection dropdown on the bottom left of the VS Code window:

![selector](resources/PythonSelector.png)

Once a suitable interpreter with Jupyter has been located, VS Code will continue to use that interpreter for starting up Jupyter servers.
If no interpreters are found with Jupyter installed a popup message will ask if you would like to install Jupyter into the current interpreter.

![install Jupyter](resources/InstallJupyter.png)

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.

### The second step is to check that jupyter isn't giving any errors on startup.

Run the following command from an environment that matches the Python you selected:
Expand Down
1 change: 1 addition & 0 deletions news/2 Fixes/9904.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improve error messaging when the jupyter notebook cannot be started.
2 changes: 2 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@
"DataScience.libraryNotInstalled": "Data Science library {0} is not installed. Install?",
"DataScience.libraryRequiredToLaunchJupyterNotInstalled": "Data Science library {0} is not installed.",
"DataScience.librariesRequiredToLaunchJupyterNotInstalled": "Data Science libraries {0} are not installed.",
"DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter": "Data Science library {1} is not installed in interpreter {0}.",
"DataScience.librariesRequiredToLaunchJupyterNotInstalledInterpreter": "Data Science libraries {1} are not installed in interpreter {0}.",
"DataScience.jupyterInstall": "Install",
"DataScience.jupyterSelectURIPrompt": "Enter the URI of the running Jupyter server",
"DataScience.jupyterSelectURIInvalidURI": "Invalid URI specified",
Expand Down
Binary file added resources/InstallJupyter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/client/common/utils/localize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,14 @@ export namespace DataScience {
'DataScience.librariesRequiredToLaunchJupyterNotInstalled',
'Data Science libraries {0} are not installed.'
);
export const libraryRequiredToLaunchJupyterNotInstalledInterpreter = localize(
'DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter',
'Data Science library {1} is not installed in interpreter {0}.'
);
export const librariesRequiredToLaunchJupyterNotInstalledInterpreter = localize(
'DataScience.librariesRequiredToLaunchJupyterNotInstalledInterpreter',
'Data Science libraries {1} are not installed in interpreter {0}.'
);
export const selectJupyterInterpreter = localize('DataScience.selectJupyterInterpreter', 'Select an Interpreter to start Jupyter');
export const jupyterInstall = localize('DataScience.jupyterInstall', 'Install');
export const currentlySelectedJupyterInterpreterForPlaceholder = localize('Datascience.currentlySelectedJupyterInterpreterForPlaceholder', 'current: {0}');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Common, DataScience } from '../../../common/utils/localize';
import { noop } from '../../../common/utils/misc';
import { PythonInterpreter } from '../../../interpreter/contracts';
import { sendTelemetryEvent } from '../../../telemetry';
import { Telemetry } from '../../constants';
import { HelpLinks, Telemetry } from '../../constants';
import { JupyterInstallError } from '../jupyterInstallError';

export enum JupyterInterpreterDependencyResponse {
Expand Down Expand Up @@ -56,8 +56,7 @@ function sortProductsInOrderForInstallation(products: Product[]) {
* @param {Product[]} products
* @returns {string}
*/
export function getMessageForLibrariesNotInstalled(products: Product[]): string {
// Even though kernelspec cannot be installed, display it so user knows what is missing.
export function getMessageForLibrariesNotInstalled(products: Product[], interpreterName?: string): string {
const names = products
.map(product => ProductNames.get(product))
.filter(name => !!name)
Expand All @@ -67,10 +66,14 @@ export function getMessageForLibrariesNotInstalled(products: Product[]): string
case 0:
return '';
case 1:
return DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(names[0]);
return interpreterName
? DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(interpreterName, names[0])
: DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(names[0]);
default: {
const lastItem = names.pop();
return DataScience.librariesRequiredToLaunchJupyterNotInstalled().format(`${names.join(', ')} ${Common.and()} ${lastItem}`);
return interpreterName
? DataScience.librariesRequiredToLaunchJupyterNotInstalledInterpreter().format(interpreterName, `${names.join(', ')} ${Common.and()} ${lastItem}`)
: DataScience.librariesRequiredToLaunchJupyterNotInstalled().format(`${names.join(', ')} ${Common.and()} ${lastItem}`);
}
}
}
Expand Down Expand Up @@ -129,15 +132,14 @@ export class JupyterInterpreterDependencyService {
return JupyterInterpreterDependencyResponse.ok;
}

const message = getMessageForLibrariesNotInstalled(missingProducts);
const message = getMessageForLibrariesNotInstalled(missingProducts, interpreter.displayName);

sendTelemetryEvent(Telemetry.JupyterNotInstalledErrorShown);
const selection = await this.applicationShell.showErrorMessage(
// tslint:disable-next-line: messages-must-be-localized
`${message}\r\n${DataScience.markdownHelpInstallingMissingDependencies()}`,
message,
DataScience.jupyterInstall(),
DataScience.selectDifferentJupyterInterpreter(),
Common.cancel()
DataScience.pythonInteractiveHelpLink()
);

if (Cancellation.isCanceled(token)) {
Expand Down Expand Up @@ -178,6 +180,12 @@ export class JupyterInterpreterDependencyService {
return JupyterInterpreterDependencyResponse.selectAnotherInterpreter;
}

case DataScience.pythonInteractiveHelpLink(): {
this.applicationShell.openUrl(HelpLinks.PythonInteractiveHelpLink);
sendTelemetryEvent(Telemetry.UserDidNotInstallJupyter);
return JupyterInterpreterDependencyResponse.cancel;
}

default:
sendTelemetryEvent(Telemetry.UserDidNotInstallJupyter);
return JupyterInterpreterDependencyResponse.cancel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export class JupyterInterpreterSubCommandExecutionService implements IJupyterSub
return DataScience.jupyterKernelSpecModuleNotFound();
}

return getMessageForLibrariesNotInstalled(productsNotInstalled);
return getMessageForLibrariesNotInstalled(productsNotInstalled, interpreter.displayName);
}
public async getSelectedInterpreter(token?: CancellationToken): Promise<PythonInterpreter | undefined> {
return this.jupyterInterpreter.getSelectedInterpreter(token);
Expand Down
10 changes: 9 additions & 1 deletion src/datascience-ui/history-react/interactivePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
'use strict';
import * as React from 'react';
import { connect } from 'react-redux';
import { noop } from '../../client/common/utils/misc';
import { Identifiers } from '../../client/datascience/constants';
import { ContentPanel, IContentPanelProps } from '../interactive-common/contentPanel';
import { handleLinkClick } from '../interactive-common/handlers';
Expand Down Expand Up @@ -75,13 +76,20 @@ export class InteractivePanel extends React.Component<IInteractivePanelProps> {
<main id="main-panel-content" onScroll={this.handleScroll}>
{this.renderContentPanel(this.props.baseTheme)}
</main>
<section id="main-panel-footer" aria-label={getLocString('DataScience.editSection', 'Input new cells here')}>
<section id="main-panel-footer" onClick={this.footerPanelClick} aria-label={getLocString('DataScience.editSection', 'Input new cells here')}>
{this.renderFooterPanel(this.props.baseTheme)}
</section>
</div>
);
}

// Make the entire footer focus our input, instead of having to click directly on the monaco editor
private footerPanelClick = (_event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
// This does nothing in release bits. Not necessary for point release.
noop();
};

// tslint:disable-next-line: max-func-body-length
private renderToolbarPanel() {
const variableExplorerTooltip = this.props.variableState.visible
? getLocString('DataScience.collapseVariableExplorerTooltip', 'Hide variables active in jupyter kernel')
Expand Down
42 changes: 42 additions & 0 deletions src/test/datascience/interactiveWindow.functional.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,48 @@ suite('DataScience Interactive Window output tests', () => {
}
);

runMountedTest(
'Click outside cells sets focus to input box',
async wrapper => {
// Create an interactive window so that it listens to the results.
const interactiveWindow = await getOrCreateInteractiveWindow(ioc);
await interactiveWindow.show();

// Type in the input box
const editor = getInteractiveEditor(wrapper);
typeCode(editor, 'a=1\na');

// Give focus to a random div
const reactDiv = wrapper
.find('div')
.first()
.getDOMNode();

const domDiv = reactDiv.querySelector('div');

if (domDiv && ioc.postMessage) {
domDiv.tabIndex = -1;
domDiv.focus();

wrapper
.find('section#main-panel-footer')
.first()
.simulate('click');

// Then enter press shift + enter on the active element
const activeElement = document.activeElement;
if (activeElement) {
await submitInput(ioc, activeElement as HTMLTextAreaElement);
}
}

verifyHtmlOnCell(wrapper, 'InteractiveCell', '<span>1</span>', CellPosition.Last);
},
() => {
return ioc;
}
);

runMountedTest(
'Collapse / expand cell',
async wrapper => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { PythonExecutionFactory } from '../../../../client/common/process/python
import { PythonExecutionService } from '../../../../client/common/process/pythonProcess';
import { IPythonExecutionService } from '../../../../client/common/process/types';
import { IInstaller, InstallerResponse, Product } from '../../../../client/common/types';
import { Common, DataScience } from '../../../../client/common/utils/localize';
import { DataScience } from '../../../../client/common/utils/localize';
import { Architecture } from '../../../../client/common/utils/platform';
import { JupyterInterpreterDependencyResponse, JupyterInterpreterDependencyService } from '../../../../client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService';
import { InterpreterType, PythonInterpreter } from '../../../../client/interpreter/contracts';
Expand Down Expand Up @@ -57,7 +57,9 @@ suite('Data Science - Jupyter Interpreter Configuration', () => {

const response = await configuration.installMissingDependencies(pythonInterpreter);

verify(appShell.showErrorMessage(anything(), DataScience.jupyterInstall(), DataScience.selectDifferentJupyterInterpreter(), Common.cancel())).once();
verify(
appShell.showErrorMessage(anything(), DataScience.jupyterInstall(), DataScience.selectDifferentJupyterInterpreter(), DataScience.pythonInteractiveHelpLink())
).once();
assert.equal(response, JupyterInterpreterDependencyResponse.cancel);
}
test('Prompt to install if Jupyter is not installed', async () => testPromptIfModuleNotInstalled(false, true));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,19 @@ suite('Data Science - Jupyter InterpreterSubCommandExecutionService', () => {
assert.equal(reason, DataScience.selectJupyterInterpreter());
});
test('Jupyter cannot be started because jupyter is not installed', async () => {
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.jupyter)!);
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
activePythonInterpreter.displayName!,
ProductNames.get(Product.jupyter)!
);
when(jupyterDependencyService.getDependenciesNotInstalled(activePythonInterpreter, undefined)).thenResolve([Product.jupyter]);
const reason = await jupyterInterpreterExecutionService.getReasonForJupyterNotebookNotBeingSupported(undefined);
assert.equal(reason, expectedReason);
});
test('Jupyter cannot be started because notebook is not installed', async () => {
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!);
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
activePythonInterpreter.displayName!,
ProductNames.get(Product.notebook)!
);
when(jupyterDependencyService.getDependenciesNotInstalled(activePythonInterpreter, undefined)).thenResolve([Product.notebook]);
const reason = await jupyterInterpreterExecutionService.getReasonForJupyterNotebookNotBeingSupported(undefined);
assert.equal(reason, expectedReason);
Expand All @@ -122,31 +128,41 @@ suite('Data Science - Jupyter InterpreterSubCommandExecutionService', () => {
const promise = jupyterInterpreterExecutionService.startNotebook([], {});
when(jupyterDependencyService.getDependenciesNotInstalled(activePythonInterpreter, undefined)).thenResolve([Product.notebook]);

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

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

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

await expect(promise).to.eventually.be.rejectedWith(DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!));
await expect(promise).to.eventually.be.rejectedWith(
DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(activePythonInterpreter.displayName!, ProductNames.get(Product.notebook)!)
);
});
test('Cannot get kernelspecs', async () => {
const promise = jupyterInterpreterExecutionService.getKernelSpecs(undefined);
when(jupyterDependencyService.getDependenciesNotInstalled(activePythonInterpreter, undefined)).thenResolve([Product.notebook]);

await expect(promise).to.eventually.be.rejectedWith(DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!));
await expect(promise).to.eventually.be.rejectedWith(
DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(activePythonInterpreter.displayName!, ProductNames.get(Product.notebook)!)
);
});
});
// tslint:disable-next-line: max-func-body-length
Expand Down Expand Up @@ -181,15 +197,21 @@ suite('Data Science - Jupyter InterpreterSubCommandExecutionService', () => {
verify(jupyterDependencyService.installMissingDependencies(selectedJupyterInterpreter, undefined)).once();
});
test('Jupyter cannot be started because jupyter is not installed', async () => {
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.jupyter)!);
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
selectedJupyterInterpreter.displayName!,
ProductNames.get(Product.jupyter)!
);
when(jupyterDependencyService.getDependenciesNotInstalled(selectedJupyterInterpreter, undefined)).thenResolve([Product.jupyter]);

const reason = await jupyterInterpreterExecutionService.getReasonForJupyterNotebookNotBeingSupported(undefined);

assert.equal(reason, expectedReason);
});
test('Jupyter cannot be started because notebook is not installed', async () => {
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!);
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
selectedJupyterInterpreter.displayName!,
ProductNames.get(Product.notebook)!
);
when(jupyterDependencyService.getDependenciesNotInstalled(selectedJupyterInterpreter, undefined)).thenResolve([Product.notebook]);

const reason = await jupyterInterpreterExecutionService.getReasonForJupyterNotebookNotBeingSupported(undefined);
Expand Down
Loading