Skip to content

Commit 0ac2757

Browse files
authored
Register notebook with VSC only in Insiders (#12681)
For #10496 If the API changes, and user is not using the VSC Notebooks or insiders, then swallow errors. Do not register API against stable version of VSCode. Also fixed a test. Basically we need to ensure VSC works when using this API and not crash if the API changes.
1 parent 8bb3ddf commit 0ac2757

File tree

3 files changed

+98
-54
lines changed

3 files changed

+98
-54
lines changed

src/client/common/application/notebook.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { UseProposedApi } from '../constants';
1717
import { NotebookEditorSupport } from '../experiments/groups';
1818
import { IDisposableRegistry, IExperimentsManager } from '../types';
1919
import {
20+
IApplicationEnvironment,
2021
IVSCodeNotebook,
2122
NotebookCellLanguageChangeEvent,
2223
NotebookCellOutputsChangeEvent,
@@ -26,24 +27,34 @@ import {
2627
@injectable()
2728
export class VSCodeNotebook implements IVSCodeNotebook {
2829
public get onDidChangeActiveNotebookEditor(): Event<NotebookEditor | undefined> {
29-
return this.notebook.onDidChangeActiveNotebookEditor;
30+
return this.canUseNotebookApi
31+
? this.notebook.onDidChangeActiveNotebookEditor
32+
: new EventEmitter<NotebookEditor | undefined>().event;
3033
}
3134
public get onDidOpenNotebookDocument(): Event<NotebookDocument> {
32-
return this.notebook.onDidOpenNotebookDocument;
35+
return this.canUseNotebookApi
36+
? this.notebook.onDidOpenNotebookDocument
37+
: new EventEmitter<NotebookDocument>().event;
3338
}
3439
public get onDidCloseNotebookDocument(): Event<NotebookDocument> {
35-
return this.notebook.onDidCloseNotebookDocument;
40+
return this.canUseNotebookApi
41+
? this.notebook.onDidCloseNotebookDocument
42+
: new EventEmitter<NotebookDocument>().event;
3643
}
3744
public get notebookDocuments(): ReadonlyArray<NotebookDocument> {
38-
return this.notebook.notebookDocuments;
45+
return this.canUseNotebookApi ? this.notebook.notebookDocuments : [];
3946
}
4047
public get notebookEditors() {
41-
return this.notebook.visibleNotebookEditors;
48+
return this.canUseNotebookApi ? this.notebook.visibleNotebookEditors : [];
4249
}
4350
public get onDidChangeNotebookDocument(): Event<
4451
NotebookCellsChangeEvent | NotebookCellOutputsChangeEvent | NotebookCellLanguageChangeEvent
4552
> {
46-
return this._onDidChangeNotebookDocument.event;
53+
return this.canUseNotebookApi
54+
? this._onDidChangeNotebookDocument.event
55+
: new EventEmitter<
56+
NotebookCellsChangeEvent | NotebookCellOutputsChangeEvent | NotebookCellLanguageChangeEvent
57+
>().event;
4758
}
4859
public get activeNotebookEditor(): NotebookEditor | undefined {
4960
if (!this.useProposedApi) {
@@ -63,14 +74,21 @@ export class VSCodeNotebook implements IVSCodeNotebook {
6374
>();
6475
private addedEventHandlers?: boolean;
6576
private _notebook?: typeof notebook;
77+
private readonly canUseNotebookApi?: boolean;
6678
private readonly handledCellChanges = new WeakSet<VSCNotebookCellsChangeEvent>();
6779
constructor(
6880
@inject(UseProposedApi) private readonly useProposedApi: boolean,
6981
@inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry,
70-
@inject(IExperimentsManager) readonly experimentManager: IExperimentsManager
82+
@inject(IExperimentsManager) readonly experimentManager: IExperimentsManager,
83+
@inject(IApplicationEnvironment) readonly env: IApplicationEnvironment
7184
) {
72-
if (this.useProposedApi && experimentManager.inExperiment(NotebookEditorSupport.nativeNotebookExperiment)) {
85+
if (
86+
this.useProposedApi &&
87+
experimentManager.inExperiment(NotebookEditorSupport.nativeNotebookExperiment) &&
88+
this.env.channel === 'insiders'
89+
) {
7390
this.addEventHandlers();
91+
this.canUseNotebookApi = true;
7492
}
7593
}
7694
public registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider): Disposable {

src/client/datascience/notebook/integration.ts

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
IVSCodeNotebook
1212
} from '../../common/application/types';
1313
import { NotebookEditorSupport } from '../../common/experiments/groups';
14+
import { traceError } from '../../common/logger';
1415
import { IFileSystem } from '../../common/platform/types';
1516
import { IDisposableRegistry, IExperimentsManager, IExtensionContext } from '../../common/types';
1617
import { DataScience } from '../../common/utils/localize';
@@ -63,38 +64,49 @@ export class NotebookIntegration implements IExtensionSingleActivationService {
6364
if (this.experiment.inExperiment(NotebookEditorSupport.nativeNotebookExperiment)) {
6465
await this.enableNotebooks(true);
6566
}
66-
this.disposables.push(
67-
this.vscNotebook.registerNotebookContentProvider(JupyterNotebookView, this.notebookContentProvider)
68-
);
69-
this.disposables.push(
70-
this.vscNotebook.registerNotebookKernel(JupyterNotebookView, ['**/*.ipynb'], this.notebookKernel)
71-
);
72-
this.disposables.push(
73-
this.vscNotebook.registerNotebookOutputRenderer(
74-
'jupyter-notebook-renderer',
75-
{
76-
mimeTypes: [
77-
'application/geo+json',
78-
'application/vdom.v1+json',
79-
'application/vnd.dataresource+json',
80-
'application/vnd.plotly.v1+json',
81-
'application/vnd.vega.v2+json',
82-
'application/vnd.vega.v3+json',
83-
'application/vnd.vega.v4+json',
84-
'application/vnd.vega.v5+json',
85-
'application/vnd.vegalite.v1+json',
86-
'application/vnd.vegalite.v2+json',
87-
'application/vnd.vegalite.v3+json',
88-
'application/vnd.vegalite.v4+json',
89-
'application/x-nteract-model-debug+json',
90-
'image/gif',
91-
'text/latex',
92-
'text/vnd.plotly.v1+html'
93-
]
94-
},
95-
this.renderer
96-
)
97-
);
67+
if (this.env.channel !== 'insiders') {
68+
return;
69+
}
70+
try {
71+
this.disposables.push(
72+
this.vscNotebook.registerNotebookContentProvider(JupyterNotebookView, this.notebookContentProvider)
73+
);
74+
this.disposables.push(
75+
this.vscNotebook.registerNotebookKernel(JupyterNotebookView, ['**/*.ipynb'], this.notebookKernel)
76+
);
77+
this.disposables.push(
78+
this.vscNotebook.registerNotebookOutputRenderer(
79+
'jupyter-notebook-renderer',
80+
{
81+
mimeTypes: [
82+
'application/geo+json',
83+
'application/vdom.v1+json',
84+
'application/vnd.dataresource+json',
85+
'application/vnd.plotly.v1+json',
86+
'application/vnd.vega.v2+json',
87+
'application/vnd.vega.v3+json',
88+
'application/vnd.vega.v4+json',
89+
'application/vnd.vega.v5+json',
90+
'application/vnd.vegalite.v1+json',
91+
'application/vnd.vegalite.v2+json',
92+
'application/vnd.vegalite.v3+json',
93+
'application/vnd.vegalite.v4+json',
94+
'application/x-nteract-model-debug+json',
95+
'image/gif',
96+
'text/latex',
97+
'text/vnd.plotly.v1+html'
98+
]
99+
},
100+
this.renderer
101+
)
102+
);
103+
} catch (ex) {
104+
// If something goes wrong, and we're not in Insiders & not using the NativeEditor experiment, then swallow errors.
105+
traceError('Failed to register VS Code Notebook API', ex);
106+
if (this.experiment.inExperiment(NotebookEditorSupport.nativeNotebookExperiment)) {
107+
throw ex;
108+
}
109+
}
98110
}
99111
private async enableNotebooks(useVSCodeNotebookAsDefaultEditor: boolean) {
100112
if (this.env.channel === 'stable') {

src/test/datascience/notebook/helper.ts

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as fs from 'fs-extra';
99
import * as path from 'path';
1010
import * as sinon from 'sinon';
1111
import * as tmp from 'tmp';
12-
import { commands } from 'vscode';
12+
import { commands, Uri } from 'vscode';
1313
import { NotebookCell } from '../../../../types/vscode-proposed';
1414
import { CellDisplayOutput } from '../../../../typings/vscode-proposed';
1515
import { IApplicationEnvironment, IVSCodeNotebook } from '../../../client/common/application/types';
@@ -198,26 +198,40 @@ export async function startJupyter() {
198198
await commands.executeCommand('workbench.action.files.saveAll');
199199
await closeActiveWindows();
200200

201-
// Create a new nb, add a python cell and execute it.
202-
// Doing that will start jupyter.
203-
await editorProvider.createNew();
204-
await (await insertPythonCell('print("Hello World")', 0)).waitForCellToGetAdded();
205-
const model = editorProvider.activeEditor?.model;
206-
editorProvider.activeEditor?.runAllCells();
207-
// Wait for 15s for Jupyter to start.
208-
await waitForCondition(async () => (model?.cells[0].data.outputs as []).length > 0, 15_000, 'Cell not executed');
209-
210-
const saveStub = sinon.stub(contentProvider, 'saveNotebook');
211-
const saveAsStub = sinon.stub(contentProvider, 'saveNotebookAs');
201+
const disposables: IDisposable[] = [];
212202
try {
203+
const templateIPynb = path.join(
204+
EXTENSION_ROOT_DIR_FOR_TESTS,
205+
'src',
206+
'test',
207+
'datascience',
208+
'notebook',
209+
'empty.ipynb'
210+
);
211+
const tempIPynb = await createTemporaryNotebook(templateIPynb, disposables);
212+
await editorProvider.open(Uri.file(tempIPynb));
213+
await (await insertPythonCell('print("Hello World")', 0)).waitForCellToGetAdded();
214+
const model = editorProvider.activeEditor?.model;
215+
editorProvider.activeEditor?.runAllCells();
216+
// Wait for 15s for Jupyter to start.
217+
await waitForCondition(
218+
async () => (model?.cells[0].data.outputs as []).length > 0,
219+
15_000,
220+
'Cell not executed'
221+
);
222+
223+
const saveStub = sinon.stub(contentProvider, 'saveNotebook');
224+
const saveAsStub = sinon.stub(contentProvider, 'saveNotebookAs');
213225
// We cannot close notebooks if there are any uncommitted changes (UI could hang with prompts etc).
214226
saveStub.callsFake(noop as any);
215227
saveAsStub.callsFake(noop as any);
228+
disposables.push({
229+
dispose: () => saveStub.restore()
230+
});
216231
await commands.executeCommand('workbench.action.files.saveAll');
217232
await closeActiveWindows();
218233
} finally {
219-
saveStub.restore();
220-
saveAsStub.restore();
234+
disposables.forEach((d) => d.dispose());
221235
}
222236
}
223237

0 commit comments

Comments
 (0)