Skip to content

Commit 0c04792

Browse files
authored
Attempt to address missing kernelspec issue (microsoft#3887)
* Potential fix for 3669 KernelSpec enumeration seems to be failing inside of Jupyter
1 parent 2e5e9b5 commit 0c04792

File tree

6 files changed

+52
-18
lines changed

6 files changed

+52
-18
lines changed

news/2 Fixes/3699.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Survive missing kernelspecs as a default will be created.

src/client/common/asyncDisposableRegistry.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ export class AsyncDisposableRegistry implements IAsyncDisposableRegistry {
1515
await Promise.all(promises);
1616
}
1717

18-
public push(disposable: IDisposable) {
19-
this.list.push(disposable);
18+
public push(disposable: IDisposable | undefined) {
19+
if (disposable) {
20+
this.list.push(disposable);
21+
}
2022
}
2123
}

src/client/datascience/jupyter/jupyterExecution.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,9 @@ export class JupyterExecution implements IJupyterExecution, Disposable {
209209
kernelSpec = await this.getMatchingKernelSpec(connection, cancelToken);
210210
}
211211

212-
// If still not found, throw an error
212+
// If still not found, log an error (this seems possible for some people, so use the default)
213213
if (!kernelSpec) {
214-
throw new Error(localize.DataScience.jupyterKernelSpecNotFound());
214+
this.logger.logError(localize.DataScience.jupyterKernelSpecNotFound());
215215
}
216216

217217
// Try to connect to our jupyter process
@@ -254,7 +254,7 @@ export class JupyterExecution implements IJupyterExecution, Disposable {
254254
return result.stdout;
255255
}
256256

257-
private async getMatchingKernelSpec(connection?: IConnection, cancelToken?: CancellationToken): Promise<IJupyterKernelSpec | undefined> {
257+
protected async getMatchingKernelSpec(connection?: IConnection, cancelToken?: CancellationToken): Promise<IJupyterKernelSpec | undefined> {
258258
// If not using an active connection, check on disk
259259
if (!connection) {
260260
// Get our best interpreter. We want its python path
@@ -681,18 +681,23 @@ export class JupyterExecution implements IJupyterExecution, Disposable {
681681
const kernelSpecCommand = await this.findBestCommand(KernelSpecCommand);
682682

683683
if (kernelSpecCommand) {
684-
// Ask for our current list.
685-
const list = await kernelSpecCommand.exec(['list'], { throwOnStdErr: true, encoding: 'utf8' });
686-
687-
// This should give us back a key value pair we can parse
688-
const lines = list.stdout.splitLines({ trim: false, removeEmptyEntries: true });
689-
690-
// Generate all of the promises at once
691-
const promises = lines.map(l => this.readSpec(l));
692-
693-
// Then let them run concurrently (they are file io)
694-
const specs = await Promise.all(promises);
695-
return specs.filter(s => s);
684+
try {
685+
// Ask for our current list.
686+
const list = await kernelSpecCommand.exec(['list'], { throwOnStdErr: true, encoding: 'utf8' });
687+
688+
// This should give us back a key value pair we can parse
689+
const lines = list.stdout.splitLines({ trim: false, removeEmptyEntries: true });
690+
691+
// Generate all of the promises at once
692+
const promises = lines.map(l => this.readSpec(l));
693+
694+
// Then let them run concurrently (they are file io)
695+
const specs = await Promise.all(promises);
696+
return specs.filter(s => s);
697+
} catch {
698+
// This is failing for some folks. In that case return nothing
699+
return [];
700+
}
696701
}
697702
}
698703

src/client/datascience/jupyter/jupyterSessionManager.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ export class JupyterSessionManager implements IJupyterSessionManager {
4848
const spec = kernelspecs[k];
4949
return new JupyterKernelSpec(spec);
5050
});
51+
} catch {
52+
// For some reason this is failing. Just return nothing
53+
return [];
5154
} finally {
5255
// Cleanup the session manager as we don't need it anymore
5356
if (sessionManager) {

src/test/datascience/mockJupyterManager.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,9 @@ export class MockJupyterManager implements IJupyterSessionManager {
196196

197197
public startNew(connInfo: IConnection, kernelSpec: IJupyterKernelSpec, cancelToken?: CancellationToken) : Promise<IJupyterSession> {
198198
this.asyncRegistry.push(connInfo);
199-
this.asyncRegistry.push(kernelSpec);
199+
if (kernelSpec) {
200+
this.asyncRegistry.push(kernelSpec);
201+
}
200202
if (this.sessionTimeout && cancelToken) {
201203
const localTimeout = this.sessionTimeout;
202204
return Cancellation.race(async () => {

src/test/datascience/notebook.functional.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ import { JupyterExecution } from '../../client/datascience/jupyter/jupyterExecut
2323
import {
2424
CellState,
2525
ICell,
26+
IConnection,
2627
IJupyterExecution,
28+
IJupyterKernelSpec,
2729
INotebookExporter,
2830
INotebookImporter,
2931
INotebookServer,
@@ -721,6 +723,25 @@ plt.show()`,
721723
}
722724
});
723725

726+
runTest('Invalid kernel spec works', async () => {
727+
if (ioc.mockJupyter) {
728+
// Make a dummy class that will fail during launch
729+
class FailedKernelSpec extends JupyterExecution {
730+
protected async getMatchingKernelSpec(connection?: IConnection, cancelToken?: CancellationToken): Promise<IJupyterKernelSpec | undefined> {
731+
return Promise.resolve(undefined);
732+
}
733+
}
734+
ioc.serviceManager.rebind<IJupyterExecution>(IJupyterExecution, FailedKernelSpec);
735+
jupyterExecution = ioc.serviceManager.get<IJupyterExecution>(IJupyterExecution);
736+
addMockData(`a=1${os.EOL}a`, 1);
737+
738+
const server = await createNotebookServer(true);
739+
assert.ok(server, 'Empty kernel spec messes up creating a server');
740+
741+
await verifySimple(server, `a=1${os.EOL}a`, 1);
742+
}
743+
});
744+
724745
// Tests that should be running:
725746
// - Creation
726747
// - Failure

0 commit comments

Comments
 (0)