Skip to content

Commit 8e67028

Browse files
authored
Support jupyter output in the remote scenario too (#10241)
* Add some output for remote situations * Add news entry
1 parent ae86ae4 commit 8e67028

File tree

10 files changed

+95
-22
lines changed

10 files changed

+95
-22
lines changed

news/2 Fixes/9177.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Jupyter output tab was not showing anything when connecting to a remote server.

package.nls.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,5 +443,8 @@
443443
"DataScience.gettingListOfKernelSpecs": "Fetching Kernel specs",
444444
"DataScience.startingJupyterNotebook": "Starting Jupyter Notebook",
445445
"DataScience.registeringKernel": "Registering Kernel",
446-
"DataScience.trimmedOutput": "Output was trimmed for performance reasons.\nTo see the full output set the setting \"python.dataScience.textOutputLimit\" to 0."
446+
"DataScience.trimmedOutput": "Output was trimmed for performance reasons.\nTo see the full output set the setting \"python.dataScience.textOutputLimit\" to 0.",
447+
"DataScience.connectingToJupyterUri" : "Connecting to Jupyter server at {0}",
448+
"DataScience.createdNewNotebook" : "{0}: Creating new notebook ",
449+
"DataScience.createdNewKernel" : "{0}: Kernel started: {1}"
447450
}

src/client/common/utils/localize.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,14 @@ export namespace DataScience {
791791
'DataScience.jupyterCommandLinePrompt',
792792
'Enter your custom command line for Jupyter'
793793
);
794+
795+
export const connectingToJupyterUri = localize(
796+
'DataScience.connectingToJupyterUri',
797+
'Connecting to Jupyter server at {0}'
798+
);
799+
export const createdNewNotebook = localize('DataScience.createdNewNotebook', '{0}: Creating new notebook ');
800+
801+
export const createdNewKernel = localize('DataScience.createdNewKernel', '{0}: Kernel started: {1}');
794802
}
795803

796804
export namespace DebugConfigStrings {

src/client/datascience/jupyter/jupyterServer.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ import { CancellationToken } from 'vscode-jsonrpc';
88
import { ILiveShareApi } from '../../common/application/types';
99
import '../../common/extensions';
1010
import { traceError, traceInfo } from '../../common/logger';
11-
import { IAsyncDisposableRegistry, IConfigurationService, IDisposableRegistry, Resource } from '../../common/types';
11+
import {
12+
IAsyncDisposableRegistry,
13+
IConfigurationService,
14+
IDisposableRegistry,
15+
IOutputChannel,
16+
Resource
17+
} from '../../common/types';
1218
import { createDeferred, Deferred } from '../../common/utils/async';
1319
import * as localize from '../../common/utils/localize';
1420
import { noop } from '../../common/utils/misc';
@@ -42,7 +48,8 @@ export class JupyterServerBase implements INotebookServer {
4248
private disposableRegistry: IDisposableRegistry,
4349
protected readonly configService: IConfigurationService,
4450
private sessionManagerFactory: IJupyterSessionManagerFactory,
45-
private loggers: INotebookExecutionLogger[]
51+
private loggers: INotebookExecutionLogger[],
52+
private jupyterOutputChannel: IOutputChannel
4653
) {
4754
this.asyncRegistry.push(this);
4855
}
@@ -67,6 +74,9 @@ export class JupyterServerBase implements INotebookServer {
6774
});
6875
}
6976

77+
// Indicate we have a new session on the output channel
78+
this.logRemoteOutput(localize.DataScience.connectingToJupyterUri().format(launchInfo.connectionInfo.baseUrl));
79+
7080
// Create our session manager
7181
this.sessionManager = await this.sessionManagerFactory.create(launchInfo.connectionInfo);
7282
// Try creating a session just to ensure we're connected. Callers of this function check to make sure jupyter
@@ -104,7 +114,11 @@ export class JupyterServerBase implements INotebookServer {
104114
this.loggers,
105115
notebookMetadata,
106116
cancelToken
107-
);
117+
).then(r => {
118+
const baseUrl = this.launchInfo?.connectionInfo.baseUrl || '';
119+
this.logRemoteOutput(localize.DataScience.createdNewNotebook().format(baseUrl));
120+
return r;
121+
});
108122
}
109123

110124
public async shutdown(): Promise<void> {
@@ -217,4 +231,10 @@ export class JupyterServerBase implements INotebookServer {
217231
this.launchInfo.kernelSpec = undefined;
218232
}
219233
}
234+
235+
private logRemoteOutput(output: string) {
236+
if (this.launchInfo && !this.launchInfo.connectionInfo.localLaunch) {
237+
this.jupyterOutputChannel.appendLine(output);
238+
}
239+
}
220240
}

src/client/datascience/jupyter/jupyterServerWrapper.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,23 @@
22
// Licensed under the MIT License.
33
'use strict';
44
import { nbformat } from '@jupyterlab/coreutils';
5-
import { inject, injectable, multiInject, optional } from 'inversify';
5+
import { inject, injectable, multiInject, named, optional } from 'inversify';
66
import * as uuid from 'uuid/v4';
77
import { Uri } from 'vscode';
88
import { CancellationToken } from 'vscode-jsonrpc';
99
import * as vsls from 'vsls/vscode';
1010
import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../common/application/types';
1111
import '../../common/extensions';
1212
import { IFileSystem } from '../../common/platform/types';
13-
import { IAsyncDisposableRegistry, IConfigurationService, IDisposableRegistry, Resource } from '../../common/types';
13+
import {
14+
IAsyncDisposableRegistry,
15+
IConfigurationService,
16+
IDisposableRegistry,
17+
IOutputChannel,
18+
Resource
19+
} from '../../common/types';
1420
import { IInterpreterService } from '../../interpreter/contracts';
21+
import { JUPYTER_OUTPUT_CHANNEL } from '../constants';
1522
import {
1623
IConnection,
1724
IDataScience,
@@ -43,7 +50,8 @@ type JupyterServerClassType = {
4350
appShell: IApplicationShell,
4451
fs: IFileSystem,
4552
kernelSelector: KernelSelector,
46-
interpreterService: IInterpreterService
53+
interpreterService: IInterpreterService,
54+
outputChannel: IOutputChannel
4755
): IJupyterServerInterface;
4856
};
4957
// tslint:enable:callable-types
@@ -69,7 +77,8 @@ export class JupyterServerWrapper implements INotebookServer, ILiveShareHasRole
6977
@inject(IApplicationShell) appShell: IApplicationShell,
7078
@inject(IFileSystem) fs: IFileSystem,
7179
@inject(IInterpreterService) interpreterService: IInterpreterService,
72-
@inject(KernelSelector) kernelSelector: KernelSelector
80+
@inject(KernelSelector) kernelSelector: KernelSelector,
81+
@inject(IOutputChannel) @named(JUPYTER_OUTPUT_CHANNEL) jupyterOutput: IOutputChannel
7382
) {
7483
// The server factory will create the appropriate HostJupyterServer or GuestJupyterServer based on
7584
// the liveshare state.
@@ -88,7 +97,8 @@ export class JupyterServerWrapper implements INotebookServer, ILiveShareHasRole
8897
appShell,
8998
fs,
9099
kernelSelector,
91-
interpreterService
100+
interpreterService,
101+
jupyterOutput
92102
);
93103
}
94104

src/client/datascience/jupyter/jupyterSession.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { ServerStatus } from '../../../datascience-ui/interactive-common/mainSta
1919
import { Cancellation } from '../../common/cancellation';
2020
import { isTestExecution } from '../../common/constants';
2121
import { traceInfo, traceWarning } from '../../common/logger';
22+
import { IOutputChannel } from '../../common/types';
2223
import { sleep, waitForPromise } from '../../common/utils/async';
2324
import * as localize from '../../common/utils/localize';
2425
import { noop } from '../../common/utils/misc';
@@ -69,7 +70,8 @@ export class JupyterSession implements IJupyterSession {
6970
private kernelSpec: IJupyterKernelSpec | LiveKernelModel | undefined,
7071
private sessionManager: SessionManager,
7172
private contentsManager: ContentsManager,
72-
private readonly kernelSelector: KernelSelector
73+
private readonly kernelSelector: KernelSelector,
74+
private readonly outputChannel: IOutputChannel
7375
) {
7476
this.statusHandler = this.onStatusChanged.bind(this);
7577
}
@@ -386,11 +388,25 @@ export class JupyterSession implements IJupyterSession {
386388
};
387389

388390
return Cancellation.race(
389-
() => this.sessionManager!.startNew(options).catch(ex => Promise.reject(new JupyterSessionStartError(ex))),
391+
() =>
392+
this.sessionManager!.startNew(options)
393+
.then(s => {
394+
this.logRemoteOutput(
395+
localize.DataScience.createdNewKernel().format(this.connInfo.baseUrl, s.kernel.id)
396+
);
397+
return s;
398+
})
399+
.catch(ex => Promise.reject(new JupyterSessionStartError(ex))),
390400
cancelToken
391401
);
392402
}
393403

404+
private logRemoteOutput(output: string) {
405+
if (this.connInfo && !this.connInfo.localLaunch) {
406+
this.outputChannel.appendLine(output);
407+
}
408+
}
409+
394410
private async waitForKernelPromise(
395411
kernelPromise: Promise<void>,
396412
timeout: number,

src/client/datascience/jupyter/jupyterSessionManager.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Agent as HttpsAgent } from 'https';
66
import { CancellationToken } from 'vscode-jsonrpc';
77

88
import { traceInfo } from '../../common/logger';
9-
import { IConfigurationService } from '../../common/types';
9+
import { IConfigurationService, IOutputChannel } from '../../common/types';
1010
import * as localize from '../../common/utils/localize';
1111
import {
1212
IConnection,
@@ -33,7 +33,8 @@ export class JupyterSessionManager implements IJupyterSessionManager {
3333
private jupyterPasswordConnect: IJupyterPasswordConnect,
3434
private config: IConfigurationService,
3535
private failOnPassword: boolean | undefined,
36-
private kernelSelector: KernelSelector
36+
private kernelSelector: KernelSelector,
37+
private outputChannel: IOutputChannel
3738
) {}
3839

3940
public async dispose() {
@@ -115,7 +116,8 @@ export class JupyterSessionManager implements IJupyterSessionManager {
115116
kernelSpec,
116117
this.sessionManager,
117118
this.contentsManager,
118-
this.kernelSelector
119+
this.kernelSelector,
120+
this.outputChannel
119121
);
120122
try {
121123
await session.connect(cancelToken);

src/client/datascience/jupyter/jupyterSessionManagerFactory.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33
'use strict';
4-
import { inject, injectable } from 'inversify';
4+
import { inject, injectable, named } from 'inversify';
55

6-
import { IConfigurationService } from '../../common/types';
6+
import { IConfigurationService, IOutputChannel } from '../../common/types';
7+
import { JUPYTER_OUTPUT_CHANNEL } from '../constants';
78
import { IConnection, IJupyterPasswordConnect, IJupyterSessionManager, IJupyterSessionManagerFactory } from '../types';
89
import { JupyterSessionManager } from './jupyterSessionManager';
910
import { KernelSelector } from './kernels/kernelSelector';
@@ -13,7 +14,8 @@ export class JupyterSessionManagerFactory implements IJupyterSessionManagerFacto
1314
constructor(
1415
@inject(IJupyterPasswordConnect) private jupyterPasswordConnect: IJupyterPasswordConnect,
1516
@inject(IConfigurationService) private config: IConfigurationService,
16-
@inject(KernelSelector) private kernelSelector: KernelSelector
17+
@inject(KernelSelector) private kernelSelector: KernelSelector,
18+
@inject(IOutputChannel) @named(JUPYTER_OUTPUT_CHANNEL) private jupyterOutput: IOutputChannel
1719
) {}
1820

1921
/**
@@ -26,7 +28,8 @@ export class JupyterSessionManagerFactory implements IJupyterSessionManagerFacto
2628
this.jupyterPasswordConnect,
2729
this.config,
2830
failOnPassword,
29-
this.kernelSelector
31+
this.kernelSelector,
32+
this.jupyterOutput
3033
);
3134
await result.initialize(connInfo);
3235
return result;

src/client/datascience/jupyter/liveshare/hostJupyterServer.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ import { nbformat } from '@jupyterlab/coreutils';
1212
import { IApplicationShell, ILiveShareApi, IWorkspaceService } from '../../../common/application/types';
1313
import { traceInfo } from '../../../common/logger';
1414
import { IFileSystem } from '../../../common/platform/types';
15-
import { IAsyncDisposableRegistry, IConfigurationService, IDisposableRegistry, Resource } from '../../../common/types';
15+
import {
16+
IAsyncDisposableRegistry,
17+
IConfigurationService,
18+
IDisposableRegistry,
19+
IOutputChannel,
20+
Resource
21+
} from '../../../common/types';
1622
import * as localize from '../../../common/utils/localize';
1723
import { IInterpreterService } from '../../../interpreter/contracts';
1824
import { Identifiers, LiveShare, LiveShareCommands, RegExpValues } from '../../constants';
@@ -52,9 +58,10 @@ export class HostJupyterServer extends LiveShareParticipantHost(JupyterServerBas
5258
private appService: IApplicationShell,
5359
private fs: IFileSystem,
5460
private readonly kernelSelector: KernelSelector,
55-
private readonly interpreterService: IInterpreterService
61+
private readonly interpreterService: IInterpreterService,
62+
outputChannel: IOutputChannel
5663
) {
57-
super(liveShare, asyncRegistry, disposableRegistry, configService, sessionManager, loggers);
64+
super(liveShare, asyncRegistry, disposableRegistry, configService, sessionManager, loggers, outputChannel);
5865
}
5966

6067
public async dispose(): Promise<void> {

src/test/datascience/jupyter/jupyterSession.unit.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { JupyterSession } from '../../../client/datascience/jupyter/jupyterSessi
1717
import { KernelSelector } from '../../../client/datascience/jupyter/kernels/kernelSelector';
1818
import { LiveKernelModel } from '../../../client/datascience/jupyter/kernels/types';
1919
import { IConnection, IJupyterKernelSpec } from '../../../client/datascience/types';
20+
import { MockOutputChannel } from '../../mockClasses';
2021

2122
// tslint:disable: max-func-body-length
2223
suite('Data Science - JupyterSession', () => {
@@ -63,6 +64,7 @@ suite('Data Science - JupyterSession', () => {
6364
kernelChangedSignal = mock(Signal);
6465
when(session.statusChanged).thenReturn(instance(statusChangedSignal));
6566
when(session.kernelChanged).thenReturn(instance(kernelChangedSignal));
67+
const channel = new MockOutputChannel('JUPYTER');
6668
// tslint:disable-next-line: no-any
6769
(instance(session) as any).then = undefined;
6870
sessionManager = mock(SessionManager);
@@ -73,7 +75,8 @@ suite('Data Science - JupyterSession', () => {
7375
kernelSpec.object,
7476
instance(sessionManager),
7577
instance(contentsManager),
76-
instance(kernelSelector)
78+
instance(kernelSelector),
79+
channel
7780
);
7881
});
7982

0 commit comments

Comments
 (0)