Skip to content

Commit 5026e13

Browse files
Change Kernel for raw kernel and change kernel launcher to launch with kernelspec python (#11284)
1 parent c1a7e8c commit 5026e13

File tree

5 files changed

+78
-41
lines changed

5 files changed

+78
-41
lines changed

src/client/datascience/baseJupyterSession.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ export abstract class BaseJupyterSession implements IJupyterSession {
136136
return this.shutdown();
137137
}
138138
// Abstracts for each Session type to implement
139-
public abstract async changeKernel(kernel: IJupyterKernelSpec | LiveKernelModel, timeoutMS: number): Promise<void>;
140139
public abstract async waitForIdle(timeout: number): Promise<void>;
141140

142141
public async shutdown(): Promise<void> {
@@ -179,6 +178,35 @@ export abstract class BaseJupyterSession implements IJupyterSession {
179178
}
180179
}
181180

181+
public async changeKernel(kernel: IJupyterKernelSpec | LiveKernelModel, timeoutMS: number): Promise<void> {
182+
let newSession: ISession | undefined;
183+
184+
// If we are already using this kernel in an active session just return back
185+
if (this.kernelSpec?.name === kernel.name && this.session) {
186+
return;
187+
}
188+
189+
newSession = await this.createNewKernelSession(kernel, timeoutMS);
190+
191+
// This is just like doing a restart, kill the old session (and the old restart session), and start new ones
192+
if (this.session) {
193+
this.shutdownSession(this.session, this.statusHandler).ignoreErrors();
194+
this.restartSessionPromise?.then((r) => this.shutdownSession(r, undefined)).ignoreErrors(); // NOSONAR
195+
}
196+
197+
// Update our kernel spec
198+
this.kernelSpec = kernel;
199+
200+
// Save the new session
201+
this.session = newSession;
202+
203+
// Listen for session status changes
204+
this.session?.statusChanged.connect(this.statusHandler); // NOSONAR
205+
206+
// Start the restart session promise too.
207+
this.restartSessionPromise = this.createRestartSession(kernel, this.session);
208+
}
209+
182210
public async restart(_timeout: number): Promise<void> {
183211
if (this.session?.isRemoteSession) {
184212
await this.session.kernel.restart();
@@ -333,6 +361,12 @@ export abstract class BaseJupyterSession implements IJupyterSession {
333361
cancelToken?: CancellationToken
334362
): Promise<ISession>;
335363

364+
// Sub classes need to implement their own kernel change specific code
365+
protected abstract createNewKernelSession(
366+
kernel: IJupyterKernelSpec | LiveKernelModel,
367+
timeoutMS: number
368+
): Promise<ISession>;
369+
336370
protected async shutdownSession(
337371
session: ISession | undefined,
338372
statusHandler: Slot<ISession, Kernel.Status> | undefined

src/client/datascience/jupyter/jupyterSession.ts

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,12 @@ export class JupyterSession extends BaseJupyterSession {
103103
this.connected = true;
104104
}
105105

106-
public async changeKernel(kernel: IJupyterKernelSpec | LiveKernelModel, timeoutMS: number): Promise<void> {
106+
public async createNewKernelSession(
107+
kernel: IJupyterKernelSpec | LiveKernelModel,
108+
timeoutMS: number
109+
): Promise<ISession> {
107110
let newSession: ISession | undefined;
108111

109-
// If we are already using this kernel in an active session just return back
110-
if (this.kernelSpec?.name === kernel.name && this.session) {
111-
return;
112-
}
113-
114112
try {
115113
// Don't immediately assume this kernel is valid. Try creating a session with it first.
116114
if (kernel.id && this.session && 'session' in kernel) {
@@ -129,23 +127,7 @@ export class JupyterSession extends BaseJupyterSession {
129127
throw new JupyterInvalidKernelError(kernel);
130128
}
131129

132-
// This is just like doing a restart, kill the old session (and the old restart session), and start new ones
133-
if (this.session) {
134-
this.shutdownSession(this.session, this.statusHandler).ignoreErrors();
135-
this.restartSessionPromise?.then((r) => this.shutdownSession(r, undefined)).ignoreErrors();
136-
}
137-
138-
// Update our kernel spec
139-
this.kernelSpec = kernel;
140-
141-
// Save the new session
142-
this.session = newSession;
143-
144-
// Listen for session status changes
145-
this.session?.statusChanged.connect(this.statusHandler); // NOSONAR
146-
147-
// Start the restart session promise too.
148-
this.restartSessionPromise = this.createRestartSession(kernel, this.session);
130+
return newSession;
149131
}
150132

151133
protected async createRestartSession(

src/client/datascience/kernel-launcher/kernelLauncher.ts

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { IFileSystem, TemporaryFile } from '../../common/platform/types';
1414
import { IPythonExecutionFactory } from '../../common/process/types';
1515
import { createDeferred, Deferred } from '../../common/utils/async';
1616
import * as localize from '../../common/utils/localize';
17-
import { isResource, noop } from '../../common/utils/misc';
17+
import { noop } from '../../common/utils/misc';
1818
import { IJupyterKernelSpec } from '../types';
1919
import { IKernelConnection, IKernelFinder, IKernelLauncher, IKernelProcess } from './types';
2020

@@ -50,18 +50,14 @@ class KernelProcess implements IKernelProcess {
5050
private executionFactory: IPythonExecutionFactory,
5151
private file: IFileSystem,
5252
private _connection: IKernelConnection,
53-
private _kernelSpec: IJupyterKernelSpec,
54-
private _interpreter: InterpreterUri
53+
private _kernelSpec: IJupyterKernelSpec
5554
) {
5655
this.readyPromise = createDeferred<void>();
5756
}
5857

5958
public async launch(): Promise<void> {
6059
this.connectionFile = await this.file.createTemporaryFile('.json');
6160

62-
const resource = isResource(this._interpreter) ? this._interpreter : undefined;
63-
const pythonPath = isResource(this._interpreter) ? undefined : this._interpreter.path;
64-
6561
const args = [...this._kernelSpec.argv];
6662
await this.file.writeFile(this.connectionFile.filePath, JSON.stringify(this._connection), {
6763
encoding: 'utf-8',
@@ -72,7 +68,10 @@ class KernelProcess implements IKernelProcess {
7268
args[4] = this.connectionFile.filePath;
7369
args.splice(0, 1);
7470

75-
const executionService = await this.executionFactory.create({ resource, pythonPath });
71+
const executionService = await this.executionFactory.create({
72+
resource: undefined,
73+
pythonPath: this._kernelSpec.path
74+
});
7675
const exeObs = executionService.execObservable(args, {});
7776

7877
if (exeObs.proc) {
@@ -139,13 +138,7 @@ export class KernelLauncher implements IKernelLauncher {
139138
}
140139

141140
const connection = await this.getKernelConnection();
142-
const kernelProcess = new KernelProcess(
143-
this.executionFactory,
144-
this.file,
145-
connection,
146-
kernelSpec,
147-
interpreterUri
148-
);
141+
const kernelProcess = new KernelProcess(this.executionFactory, this.file, connection, kernelSpec);
149142
await kernelProcess.launch();
150143
return kernelProcess;
151144
}

src/client/datascience/raw-kernel/rawJupyterSession.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,16 @@ export class RawJupyterSession extends BaseJupyterSession {
8888
return this.session.process?.kernelSpec;
8989
}
9090

91-
public async changeKernel(_kernel: IJupyterKernelSpec | LiveKernelModel, _timeoutMS: number): Promise<void> {
92-
throw new Error('Not implemented');
91+
public async createNewKernelSession(
92+
kernel: IJupyterKernelSpec | LiveKernelModel,
93+
_timeoutMS: number
94+
): Promise<ISession> {
95+
if (!this.resource || !kernel || 'session' in kernel) {
96+
// Don't allow for connecting to a LiveKernelModel
97+
throw new Error(localize.DataScience.sessionDisposed());
98+
}
99+
100+
return this.startRawSession(this.resource, kernel);
93101
}
94102

95103
protected startRestartSession() {

src/test/datascience/raw-kernel/rawJupyterSession.unit.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { EventEmitter } from 'vscode';
88
import { KernelSelector } from '../../../client/datascience/jupyter/kernels/kernelSelector';
99
import { IKernelLauncher, IKernelProcess } from '../../../client/datascience/kernel-launcher/types';
1010
import { RawJupyterSession } from '../../../client/datascience/raw-kernel/rawJupyterSession';
11-
import { IJMPConnection } from '../../../client/datascience/types';
11+
import { IJMPConnection, IJupyterKernelSpec } from '../../../client/datascience/types';
1212
import { IServiceContainer } from '../../../client/ioc/types';
1313

1414
// tslint:disable:no-any
@@ -89,6 +89,26 @@ suite('Data Science - RawJupyterSession', () => {
8989
kernelProcess.verifyAll();
9090
});
9191

92+
test('RawJupyterSession - changeKernel', async () => {
93+
kernelProcess.setup((kp) => kp.dispose).verifiable(typemoq.Times.exactly(2));
94+
95+
await rawJupyterSession.connect({} as any, 60_000);
96+
97+
const newKernel: IJupyterKernelSpec = {
98+
argv: [],
99+
display_name: 'new kernel',
100+
language: 'python',
101+
name: 'newkernel',
102+
path: 'path'
103+
};
104+
105+
await rawJupyterSession.changeKernel(newKernel, 60_000);
106+
107+
// Four connects and two processes disposed
108+
verify(kernelLauncher.launch(anything(), anything())).times(4);
109+
kernelProcess.verifyAll();
110+
});
111+
92112
test('RawJupyterSession - Kill process', async () => {
93113
const shutdown = sinon.stub(rawJupyterSession, 'shutdown');
94114
shutdown.resolves();

0 commit comments

Comments
 (0)