Skip to content

Use environment NB Convert #14308

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -1401,7 +1401,6 @@ src/client/datascience/export/types.ts
src/client/datascience/export/exportToPDF.ts
src/client/datascience/export/exportManagerFilePicker.ts
src/client/datascience/export/exportBase.ts
src/client/datascience/export/exportDependencyChecker.ts
src/client/datascience/export/exportFileOpener.ts
src/client/datascience/notebookAndInteractiveTracker.ts
src/client/datascience/statusProvider.ts
Expand Down
1 change: 1 addition & 0 deletions news/2 Fixes/14143.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
For exporting, first check the notebook or interative window interpreter before the jupyter selected interpreter.
9 changes: 5 additions & 4 deletions src/client/common/application/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Commands as LSCommands } from '../../activation/commands';
import { Commands as DSCommands } from '../../datascience/constants';
import { KernelConnectionMetadata } from '../../datascience/jupyter/kernels/types';
import { INotebookModel, ISwitchKernelOptions } from '../../datascience/types';
import { PythonEnvironment } from '../../pythonEnvironments/info';
import { CommandSource } from '../../testing/common/constants';
import { TestFunction, TestsToRun } from '../../testing/common/types';
import { TestDataItem, TestWorkspaceFolder } from '../../testing/types';
Expand Down Expand Up @@ -188,10 +189,10 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu
[DSCommands.GotoPrevCellInFile]: [];
[DSCommands.ScrollToCell]: [Uri, string];
[DSCommands.ViewJupyterOutput]: [];
[DSCommands.ExportAsPythonScript]: [INotebookModel];
[DSCommands.ExportToHTML]: [INotebookModel, string | undefined];
[DSCommands.ExportToPDF]: [INotebookModel, string | undefined];
[DSCommands.Export]: [Uri | INotebookModel, string | undefined];
[DSCommands.ExportAsPythonScript]: [INotebookModel, PythonEnvironment | undefined];
[DSCommands.ExportToHTML]: [INotebookModel, string | undefined, PythonEnvironment | undefined];
[DSCommands.ExportToPDF]: [INotebookModel, string | undefined, PythonEnvironment | undefined];
[DSCommands.Export]: [Uri | INotebookModel, string | undefined, PythonEnvironment | undefined];
[DSCommands.SetJupyterKernel]: [KernelConnectionMetadata, Uri, undefined | Uri];
[DSCommands.SwitchJupyterKernel]: [ISwitchKernelOptions | undefined];
[DSCommands.SelectJupyterCommandLine]: [undefined | Uri];
Expand Down
47 changes: 31 additions & 16 deletions src/client/datascience/commands/exportCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { IApplicationShell, ICommandManager } from '../../common/application/typ
import { IDisposable } from '../../common/types';
import { DataScience } from '../../common/utils/localize';
import { isUri } from '../../common/utils/misc';
import { PythonEnvironment } from '../../pythonEnvironments/info';
import { sendTelemetryEvent } from '../../telemetry';
import { Commands, Telemetry } from '../constants';
import { ExportManager } from '../export/exportManager';
Expand All @@ -33,15 +34,17 @@ export class ExportCommands implements IDisposable {
@inject(IDataScienceFileSystem) private readonly fs: IDataScienceFileSystem
) {}
public register() {
this.registerCommand(Commands.ExportAsPythonScript, (model) => this.export(model, ExportFormat.python));
this.registerCommand(Commands.ExportToHTML, (model, defaultFileName?) =>
this.export(model, ExportFormat.html, defaultFileName)
this.registerCommand(Commands.ExportAsPythonScript, (model, interpreter?) =>
this.export(model, ExportFormat.python, undefined, interpreter)
);
this.registerCommand(Commands.ExportToPDF, (model, defaultFileName?) =>
this.export(model, ExportFormat.pdf, defaultFileName)
this.registerCommand(Commands.ExportToHTML, (model, defaultFileName?, interpreter?) =>
this.export(model, ExportFormat.html, defaultFileName, interpreter)
);
this.registerCommand(Commands.Export, (model, defaultFileName?) =>
this.export(model, undefined, defaultFileName)
this.registerCommand(Commands.ExportToPDF, (model, defaultFileName?, interpreter?) =>
this.export(model, ExportFormat.pdf, defaultFileName, interpreter)
);
this.registerCommand(Commands.Export, (model, defaultFileName?, interpreter?) =>
this.export(model, undefined, defaultFileName, interpreter)
);
}

Expand All @@ -58,7 +61,12 @@ export class ExportCommands implements IDisposable {
this.disposables.push(disposable);
}

private async export(modelOrUri: Uri | INotebookModel, exportMethod?: ExportFormat, defaultFileName?: string) {
private async export(
modelOrUri: Uri | INotebookModel,
exportMethod?: ExportFormat,
defaultFileName?: string,
interpreter?: PythonEnvironment
) {
defaultFileName = typeof defaultFileName === 'string' ? defaultFileName : undefined;
let model: INotebookModel | undefined;
if (modelOrUri && isUri(modelOrUri)) {
Expand All @@ -85,11 +93,13 @@ export class ExportCommands implements IDisposable {
}

if (exportMethod) {
await this.exportManager.export(exportMethod, model, defaultFileName);
await this.exportManager.export(exportMethod, model, defaultFileName, interpreter);
} else {
// if we don't have an export method we need to ask for one and display the
// quickpick menu
const pickedItem = await this.showExportQuickPickMenu(model, defaultFileName).then((item) => item);
const pickedItem = await this.showExportQuickPickMenu(model, defaultFileName, interpreter).then(
(item) => item
);
if (pickedItem !== undefined) {
pickedItem.handler();
} else {
Expand All @@ -98,7 +108,11 @@ export class ExportCommands implements IDisposable {
}
}

private getExportQuickPickItems(model: INotebookModel, defaultFileName?: string): IExportQuickPickItem[] {
private getExportQuickPickItems(
model: INotebookModel,
defaultFileName?: string,
interpreter?: PythonEnvironment
): IExportQuickPickItem[] {
return [
{
label: DataScience.exportPythonQuickPickLabel(),
Expand All @@ -107,7 +121,7 @@ export class ExportCommands implements IDisposable {
sendTelemetryEvent(Telemetry.ClickedExportNotebookAsQuickPick, undefined, {
format: ExportFormat.python
});
this.commandManager.executeCommand(Commands.ExportAsPythonScript, model);
this.commandManager.executeCommand(Commands.ExportAsPythonScript, model, interpreter);
}
},
{
Expand All @@ -117,7 +131,7 @@ export class ExportCommands implements IDisposable {
sendTelemetryEvent(Telemetry.ClickedExportNotebookAsQuickPick, undefined, {
format: ExportFormat.html
});
this.commandManager.executeCommand(Commands.ExportToHTML, model, defaultFileName);
this.commandManager.executeCommand(Commands.ExportToHTML, model, defaultFileName, interpreter);
}
},
{
Expand All @@ -127,17 +141,18 @@ export class ExportCommands implements IDisposable {
sendTelemetryEvent(Telemetry.ClickedExportNotebookAsQuickPick, undefined, {
format: ExportFormat.pdf
});
this.commandManager.executeCommand(Commands.ExportToPDF, model, defaultFileName);
this.commandManager.executeCommand(Commands.ExportToPDF, model, defaultFileName, interpreter);
}
}
];
}

private async showExportQuickPickMenu(
model: INotebookModel,
defaultFileName?: string
defaultFileName?: string,
interpreter?: PythonEnvironment
): Promise<IExportQuickPickItem | undefined> {
const items = this.getExportQuickPickItems(model, defaultFileName);
const items = this.getExportQuickPickItems(model, defaultFileName, interpreter);

const options: QuickPickOptions = {
ignoreFocusOut: false,
Expand Down
1 change: 0 additions & 1 deletion src/client/datascience/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,6 @@ export namespace LiveShare {

export namespace LiveShareCommands {
export const isNotebookSupported = 'isNotebookSupported';
export const getImportPackageVersion = 'getImportPackageVersion';
export const connectToNotebookServer = 'connectToNotebookServer';
export const getUsableJupyterPython = 'getUsableJupyterPython';
export const executeObservable = 'executeObservable';
Expand Down
22 changes: 14 additions & 8 deletions src/client/datascience/export/exportBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as path from 'path';
import { CancellationToken, Uri } from 'vscode';

import { IPythonExecutionFactory, IPythonExecutionService } from '../../common/process/types';
import { PythonEnvironment } from '../../pythonEnvironments/info';
import { reportAction } from '../progress/decorator';
import { ReportableAction } from '../progress/types';
import { IDataScienceFileSystem, IJupyterSubCommandExecutionService, INotebookImporter } from '../types';
Expand All @@ -18,21 +19,27 @@ export class ExportBase implements IExport {
@inject(INotebookImporter) protected readonly importer: INotebookImporter
) {}

// tslint:disable-next-line: no-empty
public async export(_source: Uri, _target: Uri, _token: CancellationToken): Promise<void> {}
public async export(
_source: Uri,
_target: Uri,
_interpreter: PythonEnvironment,
_token: CancellationToken
// tslint:disable-next-line: no-empty
): Promise<void> {}

@reportAction(ReportableAction.PerformingExport)
public async executeCommand(
source: Uri,
target: Uri,
format: ExportFormat,
interpreter: PythonEnvironment,
token: CancellationToken
): Promise<void> {
if (token.isCancellationRequested) {
return;
}

const service = await this.getExecutionService(source);
const service = await this.getExecutionService(source, interpreter);
if (!service) {
return;
}
Expand Down Expand Up @@ -75,11 +82,10 @@ export class ExportBase implements IExport {
}
}

protected async getExecutionService(source: Uri): Promise<IPythonExecutionService | undefined> {
const interpreter = await this.jupyterService.getSelectedInterpreter();
if (!interpreter) {
return;
}
protected async getExecutionService(
source: Uri,
interpreter: PythonEnvironment
): Promise<IPythonExecutionService | undefined> {
return this.pythonExecutionFactory.createActivatedEnvironment({
resource: source,
interpreter,
Expand Down
30 changes: 0 additions & 30 deletions src/client/datascience/export/exportDependencyChecker.ts

This file was deleted.

60 changes: 60 additions & 0 deletions src/client/datascience/export/exportInterpreterFinder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { inject, injectable } from 'inversify';
import * as localize from '../../common/utils/localize';
import { PythonEnvironment } from '../../pythonEnvironments/info';
import { JupyterInterpreterService } from '../jupyter/interpreter/jupyterInterpreterService';
import { ProgressReporter } from '../progress/progressReporter';
import { IJupyterInterpreterDependencyManager, INbConvertInterpreterDependencyChecker } from '../types';
import { ExportFormat } from './types';

@injectable()
export class ExportInterpreterFinder {
constructor(
@inject(IJupyterInterpreterDependencyManager)
private readonly dependencyManager: IJupyterInterpreterDependencyManager,
@inject(ProgressReporter) private readonly progressReporter: ProgressReporter,
@inject(INbConvertInterpreterDependencyChecker)
private readonly nbConvertDependencyChecker: INbConvertInterpreterDependencyChecker,
@inject(JupyterInterpreterService) private readonly jupyterInterpreterService: JupyterInterpreterService
) {}

// For the given ExportFormat and a possible candidateInterpreter return an interpreter capable of running nbconvert or throw
public async getExportInterpreter(
format: ExportFormat,
candidateInterpreter?: PythonEnvironment
): Promise<PythonEnvironment> {
// Before we try the import, see if we don't support it, if we don't give a chance to install dependencies
const reporter = this.progressReporter.createProgressIndicator(`Exporting to ${format}`);
try {
// If an interpreter was passed in, first see if that interpreter supports NB Convert
// if it does, we are good to go, but don't install nbconvert into it
if (candidateInterpreter && (await this.checkNotebookInterpreter(candidateInterpreter))) {
return candidateInterpreter;
}

// If an interpreter was not passed in, work with the main jupyter interperter
const selectedJupyterInterpreter = await this.jupyterInterpreterService.getSelectedInterpreter();

if (selectedJupyterInterpreter) {
if (await this.checkNotebookInterpreter(selectedJupyterInterpreter)) {
return selectedJupyterInterpreter;
} else {
// Give the user a chance to install nbconvert into the selected jupyter interpreter
await this.dependencyManager.installMissingDependencies();
if (await this.checkNotebookInterpreter(selectedJupyterInterpreter)) {
return selectedJupyterInterpreter;
}
}
}

throw new Error(localize.DataScience.jupyterNbConvertNotSupported());
} finally {
reporter.dispose();
}
}

// For this specific interpreter associated with a notebook check to see if it supports import
// and export with nbconvert
private async checkNotebookInterpreter(interpreter: PythonEnvironment) {
return this.nbConvertDependencyChecker.isNbConvertInstalled(interpreter);
}
}
Loading