Skip to content

Better messaging on notebook fail #10056

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
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
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 @@ -292,6 +292,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'
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,7 +56,7 @@ function sortProductsInOrderForInstallation(products: Product[]) {
* @param {Product[]} products
* @returns {string}
*/
export function getMessageForLibrariesNotInstalled(products: Product[]): string {
export function getMessageForLibrariesNotInstalled(products: Product[], interpreterName?: string): string {
const names = products
// Ignore kernelspec as it not something that can be installed.
.filter(product => product !== Product.kernelspec)
Expand All @@ -68,12 +68,19 @@ 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 @@ -132,15 +139,14 @@ export class JupyterInterpreterDependencyService {
return JupyterInterpreterDependencyResponse.ok;
}

const message = getMessageForLibrariesNotInstalled(productsToInstall);
const message = getMessageForLibrariesNotInstalled(productsToInstall, 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 @@ -181,6 +187,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 @@ -94,7 +94,7 @@ export class JupyterInterpreterSubCommandExecutionService
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
7 changes: 4 additions & 3 deletions src/datascience-ui/history-react/interactivePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,12 @@ export class InteractivePanel extends React.Component<IInteractivePanelProps> {
>
{this.renderVariablePanel(this.props.baseTheme)}
</section>
<main id="main-panel-content" onClick={this.contentPanelClick} onScroll={this.handleScroll}>
<main id="main-panel-content" onScroll={this.handleScroll}>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small change related to @DonJayamanne feedback on this PR:
#10029

I'm worried catching all clicks on the main panel is too aggressive. Don already noted that you now can't handle selecting sys info text to copy out. So instead moved it over to the footer. This had the nice benefit of greatly expanding the click area for the input box there. Previously you had to click just on the editor, which is close to the footer BG color. Now the entire footer can be clicked to focus the input.

{this.renderContentPanel(this.props.baseTheme)}
</main>
<section
id="main-panel-footer"
onClick={this.footerPanelClick}
aria-label={getLocString('DataScience.editSection', 'Input new cells here')}
>
{this.renderFooterPanel(this.props.baseTheme)}
Expand All @@ -87,8 +88,8 @@ export class InteractivePanel extends React.Component<IInteractivePanelProps> {
);
}

// If click is not handled by something else, focus our input box
private contentPanelClick = (_event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
// 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.props.focusInput();
};

Expand Down
4 changes: 1 addition & 3 deletions src/test/datascience/interactiveWindow.functional.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,8 @@ for i in range(10):
domDiv.tabIndex = -1;
domDiv.focus();

// Click in content-panel-div, since this doesn't click on any valid click handlers this
// should set input back to the input box
wrapper
.find('div#content-panel-div')
.find('section#main-panel-footer')
.first()
.simulate('click');

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,
Expand Down Expand Up @@ -75,7 +75,7 @@ suite('Data Science - Jupyter Interpreter Configuration', () => {
anything(),
DataScience.jupyterInstall(),
DataScience.selectDifferentJupyterInterpreter(),
Common.cancel()
DataScience.pythonInteractiveHelpLink()
)
).once();
assert.equal(response, JupyterInterpreterDependencyResponse.cancel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ 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(
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
activePythonInterpreter.displayName!,
ProductNames.get(Product.jupyter)!
);
when(jupyterDependencyService.getDependenciesNotInstalled(activePythonInterpreter, undefined)).thenResolve([
Expand All @@ -128,7 +129,8 @@ suite('Data Science - Jupyter InterpreterSubCommandExecutionService', () => {
assert.equal(reason, expectedReason);
});
test('Jupyter cannot be started because notebook is not installed', async () => {
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
activePythonInterpreter.displayName!,
ProductNames.get(Product.notebook)!
);
when(jupyterDependencyService.getDependenciesNotInstalled(activePythonInterpreter, undefined)).thenResolve([
Expand All @@ -146,7 +148,10 @@ suite('Data Science - Jupyter InterpreterSubCommandExecutionService', () => {
]);

await expect(promise).to.eventually.be.rejectedWith(
DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!)
DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
activePythonInterpreter.displayName!,
ProductNames.get(Product.notebook)!
)
);
});
test('Cannot launch notebook file in jupyter notebook', async () => {
Expand All @@ -156,7 +161,10 @@ suite('Data Science - Jupyter InterpreterSubCommandExecutionService', () => {
]);

await expect(promise).to.eventually.be.rejectedWith(
DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!)
DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
activePythonInterpreter.displayName!,
ProductNames.get(Product.notebook)!
)
);
});
test('Cannot export notebook to python', async () => {
Expand All @@ -166,7 +174,10 @@ suite('Data Science - Jupyter InterpreterSubCommandExecutionService', () => {
]);

await expect(promise).to.eventually.be.rejectedWith(
DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!)
DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
activePythonInterpreter.displayName!,
ProductNames.get(Product.notebook)!
)
);
});
test('Cannot get a list of running jupyter servers', async () => {
Expand All @@ -176,7 +187,10 @@ suite('Data Science - Jupyter InterpreterSubCommandExecutionService', () => {
]);

await expect(promise).to.eventually.be.rejectedWith(
DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!)
DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
activePythonInterpreter.displayName!,
ProductNames.get(Product.notebook)!
)
);
});
test('Cannot get kernelspecs', async () => {
Expand All @@ -186,7 +200,10 @@ suite('Data Science - Jupyter InterpreterSubCommandExecutionService', () => {
]);

await expect(promise).to.eventually.be.rejectedWith(
DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(ProductNames.get(Product.notebook)!)
DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
activePythonInterpreter.displayName!,
ProductNames.get(Product.notebook)!
)
);
});
});
Expand Down Expand Up @@ -226,7 +243,8 @@ 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(
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
selectedJupyterInterpreter.displayName!,
ProductNames.get(Product.jupyter)!
);
when(
Expand All @@ -240,7 +258,8 @@ suite('Data Science - Jupyter InterpreterSubCommandExecutionService', () => {
assert.equal(reason, expectedReason);
});
test('Jupyter cannot be started because notebook is not installed', async () => {
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalled().format(
const expectedReason = DataScience.libraryRequiredToLaunchJupyterNotInstalledInterpreter().format(
selectedJupyterInterpreter.displayName!,
ProductNames.get(Product.notebook)!
);
when(
Expand Down
5 changes: 4 additions & 1 deletion src/test/datascience/notebook.functional.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1441,7 +1441,10 @@ plt.show()`,
threw = true;
// When using old command finder, the error is `Not Supported` (directly from stdout). - can be deprecated when jupyterCommandFinder.ts is deleted.
// When using new approach, we inform user that some packages are not installed.
const expectedErrorMsg = getMessageForLibrariesNotInstalled([Product.jupyter, Product.notebook]);
const expectedErrorMsg = getMessageForLibrariesNotInstalled(
[Product.jupyter, Product.notebook],
'Python'
);

assert.ok(
e.message.includes('Not supported') || e.message.includes(expectedErrorMsg),
Expand Down