Skip to content

Commit aba1d20

Browse files
authored
Add trust feature for VS Code Notebooks (#12818)
1 parent 9e4e356 commit aba1d20

35 files changed

+829
-246
lines changed

build/ci/vscode-python-pr-validation.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ stages:
5656
'DataScience':
5757
TestsToRun: 'testDataScience'
5858
NeedsPythonTestReqs: true
59+
NeedsPythonFunctionalReqs: true
5960
'Smoke':
6061
TestsToRun: 'testSmoke'
6162
NeedsPythonTestReqs: true

package.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,25 @@
694694
},
695695
"enablement": "python.datascience.notebookeditor.canrestartNotebookkernel"
696696
},
697+
{
698+
"command": "python.datascience.notebookeditor.trust",
699+
"title": "%DataScience.trustNotebookCommandTitle%",
700+
"category": "Python",
701+
"icon": {
702+
"light": "resources/light/un-trusted.svg",
703+
"dark": "resources/dark/un-trusted.svg"
704+
},
705+
"enablement": "notebookEditorFocused && !python.datascience.isnotebooktrusted && python.datascience.trustfeatureenabled"
706+
},
707+
{
708+
"command": "python.datascience.notebookeditor.trusted",
709+
"title": "%DataScience.notebookIsTrusted%",
710+
"category": "Python",
711+
"icon": {
712+
"light": "resources/light/trusted.svg",
713+
"dark": "resources/dark/trusted.svg"
714+
}
715+
},
697716
{
698717
"command": "python.datascience.notebookeditor.runallcells",
699718
"title": "%python.command.python.datascience.notebookeditor.runallcells.title%",
@@ -860,6 +879,18 @@
860879
"group": "navigation",
861880
"when": "notebookEditorFocused"
862881
},
882+
{
883+
"command": "python.datascience.notebookeditor.trust",
884+
"title": "%DataScience.trustNotebookCommandTitle%",
885+
"group": "navigation@1",
886+
"when": "notebookEditorFocused && !python.datascience.isnotebooktrusted && python.datascience.trustfeatureenabled"
887+
},
888+
{
889+
"command": "python.datascience.notebookeditor.trusted",
890+
"title": "%DataScience.notebookIsTrusted%",
891+
"group": "navigation@1",
892+
"when": "notebookEditorFocused && python.datascience.isnotebooktrusted && python.datascience.trustfeatureenabled"
893+
},
863894
{
864895
"command": "python.datascience.export",
865896
"title": "%DataScience.notebookExportAs%",
@@ -1138,6 +1169,18 @@
11381169
"category": "Python",
11391170
"when": "python.datascience.isnativeactive && python.datascience.featureenabled && python.datascience.isnotebooktrusted"
11401171
},
1172+
{
1173+
"command": "python.datascience.notebookeditor.trust",
1174+
"title": "%DataScience.trustNotebookCommandTitle%",
1175+
"category": "Python",
1176+
"when": "python.datascience.featureenabled && notebookEditorFocused && !python.datascience.isnotebooktrusted && python.datascience.trustfeatureenabled"
1177+
},
1178+
{
1179+
"command": "python.datascience.notebookeditor.trusted",
1180+
"title": "%DataScience.notebookIsTrusted%",
1181+
"category": "Python",
1182+
"when": "config.noExists"
1183+
},
11411184
{
11421185
"command": "python.datascience.notebookeditor.runallcells",
11431186
"title": "%python.command.python.datascience.notebookeditor.runallcells.title%",

package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@
370370
"DataScience.fetchingDataViewer": "Fetching data ...",
371371
"DataScience.noRowsInDataViewer": "No rows match current filter",
372372
"DataScience.jupyterServer": "Jupyter Server",
373+
"DataScience.trustNotebookCommandTitle": "Trust notebook",
373374
"DataScience.notebookIsTrusted": "Trusted",
374375
"DataScience.notebookIsNotTrusted": "Not Trusted",
375376
"DataScience.noKernel": "No Kernel",

resources/dark/trusted.svg

Lines changed: 4 additions & 0 deletions
Loading

resources/dark/un-trusted.svg

Lines changed: 4 additions & 0 deletions
Loading

resources/light/run-file.svg

Lines changed: 3 additions & 2 deletions
Loading

resources/light/trusted.svg

Lines changed: 4 additions & 0 deletions
Loading

resources/light/un-trusted.svg

Lines changed: 4 additions & 0 deletions
Loading

src/client/common/application/commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,6 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu
183183
[DSCommands.OpenNotebookNonCustomEditor]: [Uri];
184184
[DSCommands.GatherQuality]: [string];
185185
[DSCommands.EnableLoadingWidgetsFrom3rdPartySource]: [undefined | never];
186+
[DSCommands.TrustNotebook]: [undefined | never | Uri];
187+
[DSCommands.TrustedNotebook]: [undefined | never];
186188
}

src/client/datascience/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ export namespace Commands {
9090
export const SaveAsNotebookNonCustomEditor = 'python.datascience.notebookeditor.saveAs';
9191
export const OpenNotebookNonCustomEditor = 'python.datascience.notebookeditor.open';
9292
export const GatherQuality = 'python.datascience.gatherquality';
93+
export const TrustNotebook = 'python.datascience.notebookeditor.trust';
94+
export const TrustedNotebook = 'python.datascience.notebookeditor.trusted';
9395
export const EnableLoadingWidgetsFrom3rdPartySource =
9496
'python.datascience.enableLoadingWidgetScriptsFromThirdPartySource';
9597
}

src/client/datascience/context/activeEditorContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export class ActiveEditorContextService implements IExtensionSingleActivationSer
122122
.getOrCreateNotebook({ identity: activeEditor.file, getOnly: true })
123123
.then((nb) => {
124124
if (activeEditor === this.notebookEditorProvider.activeEditor) {
125-
const canStart = nb && nb.status !== ServerStatus.NotStarted;
125+
const canStart = nb && nb.status !== ServerStatus.NotStarted && activeEditor.model?.isTrusted;
126126
this.canRestartNotebookKernelContext.set(!!canStart).ignoreErrors();
127127
}
128128
})

src/client/datascience/export/exportUtil.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class ExportUtil {
5757
await this.jupyterExporter.exportToFile(cells, tempFile.filePath, false);
5858
const newPath = path.join(tempDir.path, '.ipynb');
5959
await this.fileSystem.copyFile(tempFile.filePath, newPath);
60-
model = await this.notebookStorage.load(Uri.file(newPath));
60+
model = await this.notebookStorage.get(Uri.file(newPath));
6161
} finally {
6262
tempFile.dispose();
6363
tempDir.dispose();
@@ -67,7 +67,7 @@ export class ExportUtil {
6767
}
6868

6969
public async removeSvgs(source: Uri) {
70-
const model = await this.notebookStorage.load(source);
70+
const model = await this.notebookStorage.get(source);
7171

7272
const newCells: ICell[] = [];
7373
for (const cell of model.cells) {

src/client/datascience/interactive-ipynb/nativeEditor.ts

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import * as path from 'path';
66
import {
77
CancellationToken,
88
CancellationTokenSource,
9-
commands,
109
Event,
1110
EventEmitter,
1211
Memento,
@@ -98,7 +97,6 @@ import { KernelSwitcher } from '../jupyter/kernels/kernelSwitcher';
9897
const nativeEditorDir = path.join(EXTENSION_ROOT_DIR, 'out', 'datascience-ui', 'notebook');
9998
@injectable()
10099
export class NativeEditor extends InteractiveBase implements INotebookEditor {
101-
public readonly type: 'old' | 'custom' = 'custom';
102100
public get onDidChangeViewState(): Event<void> {
103101
return this._onDidChangeViewState.event;
104102
}
@@ -140,6 +138,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
140138
public get isDirty(): boolean {
141139
return this.model ? this.model.isDirty : false;
142140
}
141+
public readonly type: 'old' | 'custom' = 'custom';
143142
public model: Readonly<INotebookModel> | undefined;
144143
protected savedEvent: EventEmitter<INotebookEditor> = new EventEmitter<INotebookEditor>();
145144
protected closedEvent: EventEmitter<INotebookEditor> = new EventEmitter<INotebookEditor>();
@@ -152,6 +151,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
152151
private startupTimer: StopWatch = new StopWatch();
153152
private loadedAllCells: boolean = false;
154153
private executeCancelTokens = new Set<CancellationTokenSource>();
154+
private previouslyNotTrusted: boolean = false;
155155

156156
constructor(
157157
@multiInject(IInteractiveWindowListener) listeners: IInteractiveWindowListener[],
@@ -227,6 +227,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
227227
);
228228
asyncRegistry.push(this);
229229

230+
asyncRegistry.push(this.trustService.onDidSetNotebookTrust(this.monitorChangesToTrust, this));
230231
this.synchronizer.subscribeToUserActions(this, this.postMessage.bind(this));
231232
}
232233

@@ -251,6 +252,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
251252

252253
// Sign up for dirty events
253254
model.changed(this.modelChanged.bind(this));
255+
this.previouslyNotTrusted = model.isTrusted;
254256
}
255257

256258
// tslint:disable-next-line: no-any
@@ -596,7 +598,13 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
596598
}
597599
}
598600
}
599-
601+
private async monitorChangesToTrust() {
602+
if (this.previouslyNotTrusted && this.model?.isTrusted) {
603+
this.previouslyNotTrusted = false;
604+
// Tell UI to update main state
605+
this.postMessage(InteractiveWindowMessages.TrustNotebookComplete).ignoreErrors();
606+
}
607+
}
600608
private renameVariableExplorerHeights(name: string, updatedName: string) {
601609
// Updates the workspace storage to reflect the updated name of the notebook
602610
// should be called if the name of the notebook changes
@@ -612,38 +620,8 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
612620
}
613621

614622
private async launchNotebookTrustPrompt() {
615-
const prompts = [
616-
localize.DataScience.trustNotebook(),
617-
localize.DataScience.doNotTrustNotebook(),
618-
localize.DataScience.trustAllNotebooks()
619-
];
620-
const selection = await this.applicationShell.showErrorMessage(
621-
localize.DataScience.launchNotebookTrustPrompt(),
622-
...prompts
623-
);
624-
if (!selection) {
625-
return;
626-
}
627-
if (this.model && selection === localize.DataScience.trustNotebook() && !this.model.isTrusted) {
628-
try {
629-
const contents = this.model.getContent();
630-
await this.trustService.trustNotebook(this.model.file, contents);
631-
// Update model trust
632-
this.model.update({
633-
source: 'user',
634-
kind: 'updateTrust',
635-
oldDirty: this.model.isDirty,
636-
newDirty: this.model.isDirty,
637-
isNotebookTrusted: true
638-
});
639-
// Tell UI to update main state
640-
await this.postMessage(InteractiveWindowMessages.TrustNotebookComplete);
641-
} catch (err) {
642-
traceError(err);
643-
}
644-
} else if (selection === localize.DataScience.trustAllNotebooks()) {
645-
// Take the user to the settings UI where they can manually turn on the alwaysTrustNotebooks setting
646-
commands.executeCommand('workbench.action.openSettings', 'python.dataScience.alwaysTrustNotebooks');
623+
if (this.model && !this.model.isTrusted) {
624+
await this.commandManager.executeCommand(Commands.TrustNotebook, this.model.file);
647625
}
648626
}
649627

src/client/datascience/interactive-ipynb/nativeEditorProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ export class NativeEditorProvider implements INotebookEditorProvider, CustomEdit
184184
this.untitledCounter = getNextUntitledCounter(file, this.untitledCounter);
185185

186186
// Load our model from our storage object.
187-
const model = await this.storage.load(file, contents, options);
187+
const model = await this.storage.get(file, contents, options);
188188

189189
// Make sure to listen to events on the model
190190
this.trackModel(model);

src/client/datascience/interactive-ipynb/nativeEditorStorage.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,20 +74,20 @@ export class NativeEditorStorage implements INotebookStorage {
7474
return `${path.basename(model.file.fsPath)}-${uuid()}`;
7575
}
7676

77-
public load(
77+
public get(
7878
file: Uri,
7979
possibleContents?: string,
8080
backupId?: string,
8181
forVSCodeNotebook?: boolean
8282
): Promise<INotebookModel>;
83-
public load(
83+
public get(
8484
file: Uri,
8585
possibleContents?: string,
8686
// tslint:disable-next-line: unified-signatures
8787
skipDirtyContents?: boolean,
8888
forVSCodeNotebook?: boolean
8989
): Promise<INotebookModel>;
90-
public load(
90+
public get(
9191
file: Uri,
9292
possibleContents?: string,
9393
// tslint:disable-next-line: no-any

0 commit comments

Comments
 (0)