@@ -9,27 +9,17 @@ import { ReplaySubject } from 'rxjs/ReplaySubject';
99import { Event , EventEmitter } from 'vscode' ;
1010import { CancellationToken } from 'vscode-jsonrpc' ;
1111import { ServerStatus } from '../../datascience-ui/interactive-common/mainState' ;
12- import { isTestExecution } from '../common/constants' ;
1312import { traceError , traceInfo , traceWarning } from '../common/logger' ;
14- import { IDisposable } from '../common/types' ;
1513import { waitForPromise } from '../common/utils/async' ;
1614import * as localize from '../common/utils/localize' ;
1715import { noop } from '../common/utils/misc' ;
1816import { sendTelemetryEvent } from '../telemetry' ;
1917import { Telemetry } from './constants' ;
20- import { JupyterWebSockets } from './jupyter/jupyterWebSocket' ;
2118import { JupyterKernelPromiseFailedError } from './jupyter/kernels/jupyterKernelPromiseFailedError' ;
2219import { KernelSelector } from './jupyter/kernels/kernelSelector' ;
2320import { LiveKernelModel } from './jupyter/kernels/types' ;
24- import { IKernelProcess } from './kernel-launcher/types' ;
25- import { IJupyterKernelSpec , IJupyterSession , KernelSocketInformation } from './types' ;
26-
27- export type ISession = Session . ISession & {
28- // Whether this is a remote session that we attached to.
29- isRemoteSession ?: boolean ;
30- // If a kernel process is associated with this session
31- process ?: IKernelProcess ;
32- } ;
21+ import { suppressShutdownErrors } from './raw-kernel/rawKernel' ;
22+ import { IJupyterKernelSpec , IJupyterSession , ISessionWithSocket , KernelSocketInformation } from './types' ;
3323
3424/**
3525 * Exception raised when starting a Jupyter Session fails.
@@ -47,49 +37,9 @@ export class JupyterSessionStartError extends Error {
4737}
4838
4939export abstract class BaseJupyterSession implements IJupyterSession {
50- protected get session ( ) : ISession | undefined {
40+ protected get session ( ) : ISessionWithSocket | undefined {
5141 return this . _session ;
5242 }
53- protected set session ( session : ISession | undefined ) {
54- const oldSession = this . _session ;
55- this . _session = session ;
56-
57- // When setting the session clear our current exit handler and hook up to the
58- // new session process
59- if ( this . processExitHandler ) {
60- this . processExitHandler . dispose ( ) ;
61- this . processExitHandler = undefined ;
62- }
63- if ( session ?. process ) {
64- // Watch to see if our process exits
65- this . processExitHandler = session . process . exited ( ( exitCode ) => {
66- traceError ( `Raw kernel process exited code: ${ exitCode } ` ) ;
67- this . shutdown ( ) . catch ( ( reason ) => {
68- traceError ( `Error shutting down jupyter session: ${ reason } ` ) ;
69- } ) ;
70- // Next code the user executes will show a session disposed message
71- } ) ;
72- }
73-
74- // If we have a new session, then emit the new kernel connection information.
75- if ( session && oldSession !== session ) {
76- const socket = JupyterWebSockets . get ( session . kernel . id ) ;
77- if ( ! socket ) {
78- traceError ( `Unable to find WebSocket connetion assocated with kernel ${ session . kernel . id } ` ) ;
79- this . _kernelSocket . next ( undefined ) ;
80- return ;
81- }
82- this . _kernelSocket . next ( {
83- options : {
84- clientId : session . kernel . clientId ,
85- id : session . kernel . id ,
86- model : { ...session . kernel . model } ,
87- userName : session . kernel . username
88- } ,
89- socket : socket
90- } ) ;
91- }
92- }
9343 protected kernelSpec : IJupyterKernelSpec | LiveKernelModel | undefined ;
9444 public get kernelSocket ( ) : Observable < KernelSocketInformation | undefined > {
9545 return this . _kernelSocket ;
@@ -117,33 +67,23 @@ export abstract class BaseJupyterSession implements IJupyterSession {
11767 return this . connected ;
11868 }
11969 protected onStatusChangedEvent : EventEmitter < ServerStatus > = new EventEmitter < ServerStatus > ( ) ;
120- protected statusHandler : Slot < ISession , Kernel . Status > ;
70+ protected statusHandler : Slot < ISessionWithSocket , Kernel . Status > ;
12171 protected connected : boolean = false ;
122- protected restartSessionPromise : Promise < ISession | undefined > | undefined ;
123- private _session : ISession | undefined ;
72+ protected restartSessionPromise : Promise < ISessionWithSocket | undefined > | undefined ;
73+ private _session : ISessionWithSocket | undefined ;
12474 private _kernelSocket = new ReplaySubject < KernelSocketInformation | undefined > ( ) ;
12575 private _jupyterLab ?: typeof import ( '@jupyterlab/services' ) ;
126- private processExitHandler : IDisposable | undefined ;
12776
12877 constructor ( protected readonly kernelSelector : KernelSelector ) {
12978 this . statusHandler = this . onStatusChanged . bind ( this ) ;
13079 }
13180 public dispose ( ) : Promise < void > {
132- if ( this . processExitHandler ) {
133- this . processExitHandler . dispose ( ) ;
134- this . processExitHandler = undefined ;
135- }
13681 return this . shutdown ( ) ;
13782 }
13883 // Abstracts for each Session type to implement
13984 public abstract async waitForIdle ( timeout : number ) : Promise < void > ;
14085
14186 public async shutdown ( ) : Promise < void > {
142- if ( this . processExitHandler ) {
143- this . processExitHandler . dispose ( ) ;
144- this . processExitHandler = undefined ;
145- }
146-
14787 if ( this . session ) {
14888 try {
14989 traceInfo ( 'Shutdown session - current session' ) ;
@@ -157,7 +97,7 @@ export abstract class BaseJupyterSession implements IJupyterSession {
15797 } catch {
15898 noop ( ) ;
15999 }
160- this . session = undefined ;
100+ this . setSession ( undefined ) ;
161101 this . restartSessionPromise = undefined ;
162102 }
163103 if ( this . onStatusChangedEvent ) {
@@ -179,7 +119,7 @@ export abstract class BaseJupyterSession implements IJupyterSession {
179119 }
180120
181121 public async changeKernel ( kernel : IJupyterKernelSpec | LiveKernelModel , timeoutMS : number ) : Promise < void > {
182- let newSession : ISession | undefined ;
122+ let newSession : ISessionWithSocket | undefined ;
183123
184124 // If we are already using this kernel in an active session just return back
185125 if ( this . kernelSpec ?. name === kernel . name && this . session ) {
@@ -198,13 +138,13 @@ export abstract class BaseJupyterSession implements IJupyterSession {
198138 this . kernelSpec = kernel ;
199139
200140 // Save the new session
201- this . session = newSession ;
141+ this . setSession ( newSession ) ;
202142
203143 // Listen for session status changes
204144 this . session ?. statusChanged . connect ( this . statusHandler ) ; // NOSONAR
205145
206146 // Start the restart session promise too.
207- this . restartSessionPromise = this . createRestartSession ( kernel , this . session ) ;
147+ this . restartSessionPromise = this . createRestartSession ( kernel , newSession ) ;
208148 }
209149
210150 public async restart ( _timeout : number ) : Promise < void > {
@@ -227,7 +167,7 @@ export abstract class BaseJupyterSession implements IJupyterSession {
227167 const oldStatusHandler = this . statusHandler ;
228168
229169 // Just switch to the other session. It should already be ready
230- this . session = await this . restartSessionPromise ;
170+ this . setSession ( await this . restartSessionPromise ) ;
231171 if ( ! this . session ) {
232172 throw new Error ( localize . DataScience . sessionDisposed ( ) ) ;
233173 }
@@ -357,19 +297,42 @@ export abstract class BaseJupyterSession implements IJupyterSession {
357297 protected abstract startRestartSession ( ) : void ;
358298 protected abstract async createRestartSession (
359299 kernelSpec : IJupyterKernelSpec | LiveKernelModel | undefined ,
360- session : ISession ,
300+ session : ISessionWithSocket ,
361301 cancelToken ?: CancellationToken
362- ) : Promise < ISession > ;
302+ ) : Promise < ISessionWithSocket > ;
363303
364304 // Sub classes need to implement their own kernel change specific code
365305 protected abstract createNewKernelSession (
366306 kernel : IJupyterKernelSpec | LiveKernelModel ,
367307 timeoutMS : number
368- ) : Promise < ISession > ;
308+ ) : Promise < ISessionWithSocket > ;
309+
310+ // Changes the current session.
311+ protected setSession ( session : ISessionWithSocket | undefined ) {
312+ const oldSession = this . _session ;
313+ this . _session = session ;
369314
315+ // If we have a new session, then emit the new kernel connection information.
316+ if ( session && oldSession !== session ) {
317+ if ( ! session . kernelSocketInformation ) {
318+ traceError ( `Unable to find WebSocket connection assocated with kernel ${ session . kernel . id } ` ) ;
319+ this . _kernelSocket . next ( undefined ) ;
320+ } else {
321+ this . _kernelSocket . next ( {
322+ options : {
323+ clientId : session . kernel . clientId ,
324+ id : session . kernel . id ,
325+ model : { ...session . kernel . model } ,
326+ userName : session . kernel . username
327+ } ,
328+ socket : session . kernelSocketInformation . socket
329+ } ) ;
330+ }
331+ }
332+ }
370333 protected async shutdownSession (
371- session : ISession | undefined ,
372- statusHandler : Slot < ISession , Kernel . Status > | undefined
334+ session : ISessionWithSocket | undefined ,
335+ statusHandler : Slot < ISessionWithSocket , Kernel . Status > | undefined
373336 ) : Promise < void > {
374337 if ( session && session . kernel ) {
375338 const kernelId = session . kernel . id ;
@@ -384,30 +347,9 @@ export abstract class BaseJupyterSession implements IJupyterSession {
384347 return ;
385348 }
386349 try {
387- // When running under a test, mark all futures as done so we
388- // don't hit this problem:
389- // https://github.com/jupyterlab/jupyterlab/issues/4252
390- // tslint:disable:no-any
391- if ( isTestExecution ( ) ) {
392- const defaultKernel = session . kernel as any ;
393- if ( defaultKernel && defaultKernel . _futures ) {
394- const futures = defaultKernel . _futures as Map < any , any > ;
395- if ( futures ) {
396- futures . forEach ( ( f ) => {
397- if ( f . _status !== undefined ) {
398- f . _status |= 4 ;
399- }
400- } ) ;
401- }
402- }
403- if ( defaultKernel && defaultKernel . _reconnectLimit ) {
404- defaultKernel . _reconnectLimit = 0 ;
405- }
406- await waitForPromise ( session . shutdown ( ) , 1000 ) ;
407- } else {
408- // Shutdown may fail if the process has been killed
409- await waitForPromise ( session . shutdown ( ) , 1000 ) ;
410- }
350+ suppressShutdownErrors ( session . kernel ) ;
351+ // Shutdown may fail if the process has been killed
352+ await waitForPromise ( session . shutdown ( ) , 1000 ) ;
411353 } catch {
412354 noop ( ) ;
413355 }
0 commit comments