Skip to content
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
1 change: 1 addition & 0 deletions news/1 Enhancements/3668.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the Jupyter Server URI to the Interactive Window info cell
7 changes: 4 additions & 3 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,9 @@
"Common.canceled": "Canceled",
"DataScience.importChangeDirectoryComment": "#%% Change working directory from the workspace root to the ipynb file location. Turn this addition off with the DataSciece.changeDirOnImportExport setting",
"DataScience.exportChangeDirectoryComment": "# Change directory to VSCode workspace root so that relative path loads work correctly. Turn this addition off with the DataSciece.changeDirOnImportExport setting",
"DataScience.interruptKernelStatus": "Interrupting iPython Kernel",
"DataScience.restartKernelAfterInterruptMessage": "Interrupting the kernel timed out. Do you want to restart the kernel instead? All variables will be lost.",
"DataScience.pythonInterruptFailedHeader": "Keyboard interrupt crashed the kernel. Kernel restarted.",
"DataScience.interruptKernelStatus" : "Interrupting iPython Kernel",
"DataScience.restartKernelAfterInterruptMessage" : "Interrupting the kernel timed out. Do you want to restart the kernel instead? All variables will be lost.",
"DataScience.pythonInterruptFailedHeader" : "Keyboard interrupt crashed the kernel. Kernel restarted.",
"DataScience.sysInfoURILabel" : "Jupyter Server URI: ",
"Common.loadingPythonExtension": "Python extension loading..."
}
1 change: 1 addition & 0 deletions src/client/common/utils/localize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export namespace DataScience {
export const exportCancel = localize('DataScience.exportCancel', 'Cancel');
export const restartKernelAfterInterruptMessage = localize('DataScience.restartKernelAfterInterruptMessage', 'Interrupting the kernel timed out. Do you want to restart the kernel instead? All variables will be lost.');
export const pythonInterruptFailedHeader = localize('DataScience.pythonInterruptFailedHeader', 'Keyboard interrupt crashed the kernel. Kernel restarted.');
export const sysInfoURILabel = localize('DataScience.sysInfoURILabel', 'Jupyter Server URI: ');
}

// Skip using vscode-nls and instead just compute our strings based on key values. Key values
Expand Down
72 changes: 50 additions & 22 deletions src/client/datascience/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
CellState,
ICell,
ICodeCssGenerator,
IConnection,
IHistory,
IHistoryInfo,
IJupyterExecution,
Expand All @@ -43,6 +44,12 @@ import {
IStatusProvider
} from './types';

export enum SysInfoReason {
Start,
Restart,
Interrupt
}

@injectable()
export class History implements IWebPanelMessageListener, IHistory {
private disposed : boolean = false;
Expand Down Expand Up @@ -119,7 +126,7 @@ export class History implements IWebPanelMessageListener, IHistory {
await this.show();

// Add our sys info if necessary
await this.addInitialSysInfo();
await this.addSysInfo(SysInfoReason.Start);

if (this.jupyterServer) {
// Before we try to execute code make sure that we have an initial directory set
Expand Down Expand Up @@ -308,7 +315,7 @@ export class History implements IWebPanelMessageListener, IHistory {
});
} else if (result === InterruptResult.Restarted) {
// Uh-oh, keyboard interrupt crashed the kernel.
this.addInterruptFailedInfo().ignoreErrors();
this.addSysInfo(SysInfoReason.Interrupt).ignoreErrors();
}
})
.catch(err => {
Expand Down Expand Up @@ -339,7 +346,7 @@ export class History implements IWebPanelMessageListener, IHistory {
try {
if (this.jupyterServer) {
await this.jupyterServer.restartKernel();
await this.addRestartSysInfo();
await this.addSysInfo(SysInfoReason.Restart);
}
} finally {
status.dispose();
Expand Down Expand Up @@ -554,7 +561,7 @@ export class History implements IWebPanelMessageListener, IHistory {

// If this is a restart, show our restart info
if (restart) {
await this.addRestartSysInfo();
await this.addSysInfo(SysInfoReason.Restart);
}
} finally {
if (status) {
Expand Down Expand Up @@ -619,10 +626,11 @@ export class History implements IWebPanelMessageListener, IHistory {
return result;
}

private generateSysInfoCell = async (message: string) : Promise<ICell | undefined> => {
private generateSysInfoCell = async (reason: SysInfoReason) : Promise<ICell | undefined> => {
// Execute the code 'import sys\r\nsys.version' and 'import sys\r\nsys.executable' to get our
// version and executable
if (this.jupyterServer) {
const message = await this.generateSysInfoMessage(reason);
// tslint:disable-next-line:no-multiline-string
const versionCells = await this.jupyterServer.execute(`import sys\r\nsys.version`, 'foo.py', 0);
// tslint:disable-next-line:no-multiline-string
Expand All @@ -638,6 +646,12 @@ export class History implements IWebPanelMessageListener, IHistory {
// Both should influence our ignore count. We don't want them to count against execution
this.ignoreCount = this.ignoreCount + 3;

// Connection string only for our initial start, not restart or interrupt
let connectionString: string = '';
if (reason === SysInfoReason.Start) {
connectionString = this.generateConnectionInfoString(this.jupyterServer.getConnectionInfo());
}

// Combine this data together to make our sys info
return {
data: {
Expand All @@ -646,6 +660,7 @@ export class History implements IWebPanelMessageListener, IHistory {
version: version,
notebook_version: localize.DataScience.notebookVersionFormat().format(notebookVersion),
path: pythonPath,
connection: connectionString,
metadata: {},
source: []
},
Expand All @@ -657,33 +672,46 @@ export class History implements IWebPanelMessageListener, IHistory {
}
}

private addInitialSysInfo = async () : Promise<void> => {
// Message depends upon if ipykernel is supported or not.
if (!(await this.jupyterExecution.isKernelCreateSupported())) {
return this.addSysInfo(localize.DataScience.pythonVersionHeaderNoPyKernel());
private async generateSysInfoMessage(reason: SysInfoReason): Promise<string> {
switch (reason) {
case SysInfoReason.Start:
// Message depends upon if ipykernel is supported or not.
if (!(await this.jupyterExecution.isKernelCreateSupported())) {
return localize.DataScience.pythonVersionHeaderNoPyKernel();
}
return localize.DataScience.pythonVersionHeader();
break;
case SysInfoReason.Restart:
return localize.DataScience.pythonRestartHeader();
break;
case SysInfoReason.Interrupt:
return localize.DataScience.pythonInterruptFailedHeader();
break;
default:
this.logger.logError('Invalid SysInfoReason');
return '';
break;
}

return this.addSysInfo(localize.DataScience.pythonVersionHeader());
}

private addRestartSysInfo = () : Promise<void> => {
this.addedSysInfo = false;
return this.addSysInfo(localize.DataScience.pythonRestartHeader());
}
private generateConnectionInfoString(connInfo: IConnection | undefined): string {
if (!connInfo) {
return '';
}

const tokenString = connInfo.token.length > 0 ? `?token=${connInfo.token}` : '';
const urlString = `${connInfo.baseUrl}${tokenString}`;

private addInterruptFailedInfo = () : Promise<void> => {
this.addedSysInfo = false;
return this.addSysInfo(localize.DataScience.pythonInterruptFailedHeader());
return `${localize.DataScience.sysInfoURILabel()}${urlString}`;
}

private addSysInfo = async (message: string) : Promise<void> => {
// Add our sys info if necessary
if (!this.addedSysInfo) {
private addSysInfo = async (reason: SysInfoReason) : Promise<void> => {
if (!this.addedSysInfo || reason === SysInfoReason.Interrupt || reason === SysInfoReason.Restart) {
this.addedSysInfo = true;
this.ignoreCount = 0;

// Generate a new sys info cell and send it to the web panel.
const sysInfo = await this.generateSysInfoCell(message);
const sysInfo = await this.generateSysInfoCell(reason);
if (sysInfo) {
this.onAddCodeEvent([sysInfo]);
}
Expand Down
14 changes: 13 additions & 1 deletion src/client/datascience/jupyterServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ export class JupyterServer implements INotebookServer, IDisposable {
return InterruptResult.TimedOut;
} catch (exc) {
// Something failed. See if we restarted or not.
if (interruptBeginTime < this.sessionStartTime) {
if (this.sessionStartTime && (interruptBeginTime < this.sessionStartTime)) {
return InterruptResult.Restarted;
}

Expand All @@ -419,6 +419,18 @@ export class JupyterServer implements INotebookServer, IDisposable {
throw new Error(localize.DataScience.sessionDisposed());
}

// Return a copy of the connection information that this server used to connect with
public getConnectionInfo(): IConnection | undefined {
if (!this.connInfo) {
return undefined;
}

// Return a copy with a no-op for dispose
return {
...this.connInfo,
dispose: noop };
}

private shutdownSessionAndConnection = async () => {
if (this.contentsManager) {
this.contentsManager.dispose();
Expand Down
2 changes: 2 additions & 0 deletions src/client/datascience/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface INotebookServer extends Disposable {
shutdown() : Promise<void>;
interruptKernel(timeoutInMs: number) : Promise<InterruptResult>;
setInitialDirectory(directory: string): Promise<void>;
getConnectionInfo(): IConnection | undefined;
}

export const IJupyterExecution = Symbol('IJupyterExecution');
Expand Down Expand Up @@ -156,6 +157,7 @@ export interface ISysInfo extends nbformat.IBaseCell {
notebook_version: string;
path: string;
message: string;
connection: string;
}

export const ICodeCssGenerator = Symbol('ICodeCssGenerator');
Expand Down
2 changes: 1 addition & 1 deletion src/datascience-ui/history-react/cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class Cell extends React.Component<ICellProps> {

public render() {
if (this.props.cellVM.cell.data.cell_type === 'sys_info') {
return <SysInfo theme={this.props.theme} path={this.props.cellVM.cell.data.path} message={this.props.cellVM.cell.data.message} version={this.props.cellVM.cell.data.version} notebook_version={this.props.cellVM.cell.data.notebook_version}/>;
return <SysInfo theme={this.props.theme} connection={this.props.cellVM.cell.data.connection} path={this.props.cellVM.cell.data.path} message={this.props.cellVM.cell.data.message} version={this.props.cellVM.cell.data.version} notebook_version={this.props.cellVM.cell.data.notebook_version}/>;
} else {
return this.renderNormalCell();
}
Expand Down
3 changes: 2 additions & 1 deletion src/datascience-ui/history-react/mainPanelState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ function generateCellData() : (nbformat.ICodeCell | nbformat.IMarkdownCell | nbf
notebook_version: '(5, 9, 9)',
source: [],
metadata: {},
message: 'You have this python data:'
message: 'You have this python data:',
connection: 'https:\\localhost'
},
{
cell_type: 'code',
Expand Down
4 changes: 3 additions & 1 deletion src/datascience-ui/history-react/sysInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface ISysInfoProps
notebook_version: string;
version: string;
theme: string;
connection: string;
}

export class SysInfo extends React.Component<ISysInfoProps> {
Expand All @@ -21,7 +22,8 @@ export class SysInfo extends React.Component<ISysInfoProps> {
}

public render() {
const output = `${this.props.message}\r\n${this.props.version}\r\n${this.props.path}\r\n${this.props.notebook_version}`;
const connectionString = this.props.connection.length > 0 ? `${this.props.connection}\r\n` : '';
const output = `${connectionString}${this.props.message}\r\n${this.props.version}\r\n${this.props.path}\r\n${this.props.notebook_version}`;

return (
<div className='sysinfo-wrapper'>
Expand Down
5 changes: 4 additions & 1 deletion src/test/datascience/execution.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class MockJupyterServer implements INotebookServer {
this.kernelSpec = kernelSpec;

// Validate connection info and kernel spec
if (conninfo.baseUrl && /[a-z,A-Z,0-9,-,.,_]+/.test(kernelSpec.name)) {
if (conninfo.baseUrl && kernelSpec.name && /[a-z,A-Z,0-9,-,.,_]+/.test(kernelSpec.name)) {
return Promise.resolve();
}
return Promise.reject('invalid server startup');
Expand Down Expand Up @@ -81,6 +81,9 @@ class MockJupyterServer implements INotebookServer {
public setInitialDirectory(directory: string): Promise<void> {
throw new Error('Method not implemented');
}
public getConnectionInfo(): IConnection | undefined {
throw new Error('Method not implemented');
}
public async shutdown() {
return Promise.resolve();
}
Expand Down