Skip to content

Commit fca6cee

Browse files
Add Server URI to sys info (#3670) (#3681)
Merge master PR into release branch #3670
1 parent 03fd2d4 commit fca6cee

File tree

10 files changed

+81
-30
lines changed

10 files changed

+81
-30
lines changed

news/1 Enhancements/3668.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add the Jupyter Server URI to the Interactive Window info cell

package.nls.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,9 @@
135135
"Common.canceled": "Canceled",
136136
"DataScience.importChangeDirectoryComment": "#%% Change working directory from the workspace root to the ipynb file location. Turn this addition off with the DataSciece.changeDirOnImportExport setting",
137137
"DataScience.exportChangeDirectoryComment": "# Change directory to VSCode workspace root so that relative path loads work correctly. Turn this addition off with the DataSciece.changeDirOnImportExport setting",
138-
"DataScience.interruptKernelStatus": "Interrupting iPython Kernel",
139-
"DataScience.restartKernelAfterInterruptMessage": "Interrupting the kernel timed out. Do you want to restart the kernel instead? All variables will be lost.",
140-
"DataScience.pythonInterruptFailedHeader": "Keyboard interrupt crashed the kernel. Kernel restarted.",
138+
"DataScience.interruptKernelStatus" : "Interrupting iPython Kernel",
139+
"DataScience.restartKernelAfterInterruptMessage" : "Interrupting the kernel timed out. Do you want to restart the kernel instead? All variables will be lost.",
140+
"DataScience.pythonInterruptFailedHeader" : "Keyboard interrupt crashed the kernel. Kernel restarted.",
141+
"DataScience.sysInfoURILabel" : "Jupyter Server URI: ",
141142
"Common.loadingPythonExtension": "Python extension loading..."
142143
}

src/client/common/utils/localize.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export namespace DataScience {
102102
export const exportCancel = localize('DataScience.exportCancel', 'Cancel');
103103
export const restartKernelAfterInterruptMessage = localize('DataScience.restartKernelAfterInterruptMessage', 'Interrupting the kernel timed out. Do you want to restart the kernel instead? All variables will be lost.');
104104
export const pythonInterruptFailedHeader = localize('DataScience.pythonInterruptFailedHeader', 'Keyboard interrupt crashed the kernel. Kernel restarted.');
105+
export const sysInfoURILabel = localize('DataScience.sysInfoURILabel', 'Jupyter Server URI: ');
105106
}
106107

107108
// Skip using vscode-nls and instead just compute our strings based on key values. Key values

src/client/datascience/history.ts

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
CellState,
3535
ICell,
3636
ICodeCssGenerator,
37+
IConnection,
3738
IHistory,
3839
IHistoryInfo,
3940
IJupyterExecution,
@@ -43,6 +44,12 @@ import {
4344
IStatusProvider
4445
} from './types';
4546

47+
export enum SysInfoReason {
48+
Start,
49+
Restart,
50+
Interrupt
51+
}
52+
4653
@injectable()
4754
export class History implements IWebPanelMessageListener, IHistory {
4855
private disposed : boolean = false;
@@ -119,7 +126,7 @@ export class History implements IWebPanelMessageListener, IHistory {
119126
await this.show();
120127

121128
// Add our sys info if necessary
122-
await this.addInitialSysInfo();
129+
await this.addSysInfo(SysInfoReason.Start);
123130

124131
if (this.jupyterServer) {
125132
// Before we try to execute code make sure that we have an initial directory set
@@ -308,7 +315,7 @@ export class History implements IWebPanelMessageListener, IHistory {
308315
});
309316
} else if (result === InterruptResult.Restarted) {
310317
// Uh-oh, keyboard interrupt crashed the kernel.
311-
this.addInterruptFailedInfo().ignoreErrors();
318+
this.addSysInfo(SysInfoReason.Interrupt).ignoreErrors();
312319
}
313320
})
314321
.catch(err => {
@@ -339,7 +346,7 @@ export class History implements IWebPanelMessageListener, IHistory {
339346
try {
340347
if (this.jupyterServer) {
341348
await this.jupyterServer.restartKernel();
342-
await this.addRestartSysInfo();
349+
await this.addSysInfo(SysInfoReason.Restart);
343350
}
344351
} finally {
345352
status.dispose();
@@ -554,7 +561,7 @@ export class History implements IWebPanelMessageListener, IHistory {
554561

555562
// If this is a restart, show our restart info
556563
if (restart) {
557-
await this.addRestartSysInfo();
564+
await this.addSysInfo(SysInfoReason.Restart);
558565
}
559566
} finally {
560567
if (status) {
@@ -619,10 +626,11 @@ export class History implements IWebPanelMessageListener, IHistory {
619626
return result;
620627
}
621628

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

649+
// Connection string only for our initial start, not restart or interrupt
650+
let connectionString: string = '';
651+
if (reason === SysInfoReason.Start) {
652+
connectionString = this.generateConnectionInfoString(this.jupyterServer.getConnectionInfo());
653+
}
654+
641655
// Combine this data together to make our sys info
642656
return {
643657
data: {
@@ -646,6 +660,7 @@ export class History implements IWebPanelMessageListener, IHistory {
646660
version: version,
647661
notebook_version: localize.DataScience.notebookVersionFormat().format(notebookVersion),
648662
path: pythonPath,
663+
connection: connectionString,
649664
metadata: {},
650665
source: []
651666
},
@@ -657,33 +672,46 @@ export class History implements IWebPanelMessageListener, IHistory {
657672
}
658673
}
659674

660-
private addInitialSysInfo = async () : Promise<void> => {
661-
// Message depends upon if ipykernel is supported or not.
662-
if (!(await this.jupyterExecution.isKernelCreateSupported())) {
663-
return this.addSysInfo(localize.DataScience.pythonVersionHeaderNoPyKernel());
675+
private async generateSysInfoMessage(reason: SysInfoReason): Promise<string> {
676+
switch (reason) {
677+
case SysInfoReason.Start:
678+
// Message depends upon if ipykernel is supported or not.
679+
if (!(await this.jupyterExecution.isKernelCreateSupported())) {
680+
return localize.DataScience.pythonVersionHeaderNoPyKernel();
681+
}
682+
return localize.DataScience.pythonVersionHeader();
683+
break;
684+
case SysInfoReason.Restart:
685+
return localize.DataScience.pythonRestartHeader();
686+
break;
687+
case SysInfoReason.Interrupt:
688+
return localize.DataScience.pythonInterruptFailedHeader();
689+
break;
690+
default:
691+
this.logger.logError('Invalid SysInfoReason');
692+
return '';
693+
break;
664694
}
665-
666-
return this.addSysInfo(localize.DataScience.pythonVersionHeader());
667695
}
668696

669-
private addRestartSysInfo = () : Promise<void> => {
670-
this.addedSysInfo = false;
671-
return this.addSysInfo(localize.DataScience.pythonRestartHeader());
672-
}
697+
private generateConnectionInfoString(connInfo: IConnection | undefined): string {
698+
if (!connInfo) {
699+
return '';
700+
}
701+
702+
const tokenString = connInfo.token.length > 0 ? `?token=${connInfo.token}` : '';
703+
const urlString = `${connInfo.baseUrl}${tokenString}`;
673704

674-
private addInterruptFailedInfo = () : Promise<void> => {
675-
this.addedSysInfo = false;
676-
return this.addSysInfo(localize.DataScience.pythonInterruptFailedHeader());
705+
return `${localize.DataScience.sysInfoURILabel()}${urlString}`;
677706
}
678707

679-
private addSysInfo = async (message: string) : Promise<void> => {
680-
// Add our sys info if necessary
681-
if (!this.addedSysInfo) {
708+
private addSysInfo = async (reason: SysInfoReason) : Promise<void> => {
709+
if (!this.addedSysInfo || reason === SysInfoReason.Interrupt || reason === SysInfoReason.Restart) {
682710
this.addedSysInfo = true;
683711
this.ignoreCount = 0;
684712

685713
// Generate a new sys info cell and send it to the web panel.
686-
const sysInfo = await this.generateSysInfoCell(message);
714+
const sysInfo = await this.generateSysInfoCell(reason);
687715
if (sysInfo) {
688716
this.onAddCodeEvent([sysInfo]);
689717
}

src/client/datascience/jupyterServer.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ export class JupyterServer implements INotebookServer, IDisposable {
405405
return InterruptResult.TimedOut;
406406
} catch (exc) {
407407
// Something failed. See if we restarted or not.
408-
if (interruptBeginTime < this.sessionStartTime) {
408+
if (this.sessionStartTime && (interruptBeginTime < this.sessionStartTime)) {
409409
return InterruptResult.Restarted;
410410
}
411411

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

422+
// Return a copy of the connection information that this server used to connect with
423+
public getConnectionInfo(): IConnection | undefined {
424+
if (!this.connInfo) {
425+
return undefined;
426+
}
427+
428+
// Return a copy with a no-op for dispose
429+
return {
430+
...this.connInfo,
431+
dispose: noop };
432+
}
433+
422434
private shutdownSessionAndConnection = async () => {
423435
if (this.contentsManager) {
424436
this.contentsManager.dispose();

src/client/datascience/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export interface INotebookServer extends Disposable {
4747
shutdown() : Promise<void>;
4848
interruptKernel(timeoutInMs: number) : Promise<InterruptResult>;
4949
setInitialDirectory(directory: string): Promise<void>;
50+
getConnectionInfo(): IConnection | undefined;
5051
}
5152

5253
export const IJupyterExecution = Symbol('IJupyterExecution');
@@ -156,6 +157,7 @@ export interface ISysInfo extends nbformat.IBaseCell {
156157
notebook_version: string;
157158
path: string;
158159
message: string;
160+
connection: string;
159161
}
160162

161163
export const ICodeCssGenerator = Symbol('ICodeCssGenerator');

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export class Cell extends React.Component<ICellProps> {
4444

4545
public render() {
4646
if (this.props.cellVM.cell.data.cell_type === 'sys_info') {
47-
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}/>;
47+
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}/>;
4848
} else {
4949
return this.renderNormalCell();
5050
}

src/datascience-ui/history-react/mainPanelState.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ function generateCellData() : (nbformat.ICodeCell | nbformat.IMarkdownCell | nbf
8383
notebook_version: '(5, 9, 9)',
8484
source: [],
8585
metadata: {},
86-
message: 'You have this python data:'
86+
message: 'You have this python data:',
87+
connection: 'https:\\localhost'
8788
},
8889
{
8990
cell_type: 'code',

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ interface ISysInfoProps
1313
notebook_version: string;
1414
version: string;
1515
theme: string;
16+
connection: string;
1617
}
1718

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

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

2628
return (
2729
<div className='sysinfo-wrapper'>

src/test/datascience/execution.unit.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class MockJupyterServer implements INotebookServer {
5151
this.kernelSpec = kernelSpec;
5252

5353
// Validate connection info and kernel spec
54-
if (conninfo.baseUrl && /[a-z,A-Z,0-9,-,.,_]+/.test(kernelSpec.name)) {
54+
if (conninfo.baseUrl && kernelSpec.name && /[a-z,A-Z,0-9,-,.,_]+/.test(kernelSpec.name)) {
5555
return Promise.resolve();
5656
}
5757
return Promise.reject('invalid server startup');
@@ -81,6 +81,9 @@ class MockJupyterServer implements INotebookServer {
8181
public setInitialDirectory(directory: string): Promise<void> {
8282
throw new Error('Method not implemented');
8383
}
84+
public getConnectionInfo(): IConnection | undefined {
85+
throw new Error('Method not implemented');
86+
}
8487
public async shutdown() {
8588
return Promise.resolve();
8689
}

0 commit comments

Comments
 (0)