Skip to content

Commit 8386436

Browse files
authored
Show custom TensorBoard package installation prompt (#14929)
1 parent 0210ee1 commit 8386436

File tree

9 files changed

+62
-26
lines changed

9 files changed

+62
-26
lines changed

package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@
232232
"TensorBoard.selectAFolderDetail": "Select a log directory containing tfevent files",
233233
"TensorBoard.selectAnotherFolderDetail": "Use the file explorer to select another folder",
234234
"TensorBoard.useCurrentWorkingDirectoryDetail": "TensorBoard will search for tfevent files in all subdirectories of the current working directory",
235+
"TensorBoard.installPrompt": "The package TensorBoard is required to launch a TensorBoard session. Would you like to install it?",
235236
"TensorBoard.launchNativeTensorBoardSessionCodeAction": "Launch TensorBoard session",
236237
"TensorBoard.launchNativeTensorBoardSessionCodeLens": "▶ Launch TensorBoard Session"
237238
}

src/client/common/installer/productInstaller.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
Product,
2929
ProductType
3030
} from '../types';
31-
import { Common, Installer, Linters } from '../utils/localize';
31+
import { Common, Installer, Linters, TensorBoard } from '../utils/localize';
3232
import { isResource, noop } from '../utils/misc';
3333
import { ProductNames } from './productNames';
3434
import {
@@ -570,6 +570,20 @@ export class DataScienceInstaller extends BaseInstaller {
570570
}
571571
}
572572

573+
export class TensorBoardInstaller extends DataScienceInstaller {
574+
protected async promptToInstallImplementation(
575+
product: Product,
576+
resource: Uri,
577+
cancel: CancellationToken
578+
): Promise<InstallerResponse> {
579+
// Show a prompt message specific to TensorBoard
580+
const yes = Common.bannerLabelYes();
581+
const no = Common.bannerLabelNo();
582+
const selection = await this.appShell.showErrorMessage(TensorBoard.installPrompt(), ...[yes, no]);
583+
return selection === yes ? this.install(product, resource, cancel) : InstallerResponse.Ignore;
584+
}
585+
}
586+
573587
@injectable()
574588
export class ProductInstaller implements IInstaller {
575589
private readonly productService: IProductService;
@@ -626,6 +640,8 @@ export class ProductInstaller implements IInstaller {
626640
return new RefactoringLibraryInstaller(this.serviceContainer, this.outputChannel);
627641
case ProductType.DataScience:
628642
return new DataScienceInstaller(this.serviceContainer, this.outputChannel);
643+
case ProductType.TensorBoard:
644+
return new TensorBoardInstaller(this.serviceContainer, this.outputChannel);
629645
default:
630646
break;
631647
}

src/client/common/installer/productPath.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,13 @@ export class DataScienceProductPathService extends BaseProductPathsService {
111111
return this.productInstaller.translateProductToModuleName(product, ModuleNamePurpose.run);
112112
}
113113
}
114+
115+
@injectable()
116+
export class TensorBoardProductPathService extends BaseProductPathsService {
117+
constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) {
118+
super(serviceContainer);
119+
}
120+
public getExecutableNameFromSettings(product: Product, _?: Uri): string {
121+
return this.productInstaller.translateProductToModuleName(product, ModuleNamePurpose.run);
122+
}
123+
}

src/client/common/installer/productService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class ProductService implements IProductService {
3434
this.ProductTypes.set(Product.nbconvert, ProductType.DataScience);
3535
this.ProductTypes.set(Product.kernelspec, ProductType.DataScience);
3636
this.ProductTypes.set(Product.pandas, ProductType.DataScience);
37-
this.ProductTypes.set(Product.tensorboard, ProductType.DataScience);
37+
this.ProductTypes.set(Product.tensorboard, ProductType.TensorBoard);
3838
}
3939
public getProductType(product: Product): ProductType {
4040
return this.ProductTypes.get(product)!;

src/client/common/installer/serviceRegistry.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
FormatterProductPathService,
1919
LinterProductPathService,
2020
RefactoringLibraryProductPathService,
21+
TensorBoardProductPathService,
2122
TestFrameworkProductPathService
2223
} from './productPath';
2324
import { ProductService } from './productService';
@@ -75,5 +76,10 @@ export function registerTypes(serviceManager: IServiceManager) {
7576
DataScienceProductPathService,
7677
ProductType.DataScience
7778
);
79+
serviceManager.addSingleton<IProductPathService>(
80+
IProductPathService,
81+
TensorBoardProductPathService,
82+
ProductType.TensorBoard
83+
);
7884
serviceManager.addSingleton<IWebviewPanelProvider>(IWebviewPanelProvider, WebviewPanelProvider);
7985
}

src/client/common/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ export enum ProductType {
7878
TestFramework = 'TestFramework',
7979
RefactoringLibrary = 'RefactoringLibrary',
8080
WorkspaceSymbols = 'WorkspaceSymbols',
81-
DataScience = 'DataScience'
81+
DataScience = 'DataScience',
82+
TensorBoard = 'TensorBoard'
8283
}
8384

8485
export enum Product {

src/client/common/utils/localize.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ export namespace TensorBoard {
166166
'TensorBoard.selectAnotherFolderDetail',
167167
'Use the file explorer to select another folder'
168168
);
169+
export const installPrompt = localize(
170+
'TensorBoard.installPrompt',
171+
'The package TensorBoard is required to launch a TensorBoard session. Would you like to install it?'
172+
);
169173
export const launchNativeTensorBoardSessionCodeLens = localize(
170174
'TensorBoard.launchNativeTensorBoardSessionCodeLens',
171175
'▶ Launch TensorBoard Session'

src/client/tensorBoard/tensorBoardSession.ts

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,15 @@ export class TensorBoardSession {
6767
// Ensure that the TensorBoard package is installed before we attempt
6868
// to start a TensorBoard session.
6969
private async ensureTensorboardIsInstalled() {
70-
traceInfo('Ensuring TensorBoard package is installed');
71-
if (await this.installer.isInstalled(Product.tensorboard)) {
72-
return true;
73-
}
70+
traceInfo('Ensuring TensorBoard package is installed into active interpreter');
7471
const interpreter =
7572
(await this.interpreterService.getActiveInterpreter()) ||
7673
(await this.commandManager.executeCommand('python.setInterpreter'));
7774
if (!interpreter) {
78-
return undefined;
75+
return false;
76+
}
77+
if (await this.installer.isInstalled(Product.tensorboard, interpreter)) {
78+
return true;
7979
}
8080
const tokenSource = new CancellationTokenSource();
8181
const installerToken = tokenSource.token;
@@ -91,7 +91,7 @@ export class TensorBoardSession {
9191
return response === InstallerResponse.Installed;
9292
}
9393

94-
private async showFilePicker() {
94+
private async showFilePicker(): Promise<string | undefined> {
9595
const selection = await window.showOpenDialog({
9696
canSelectFiles: false,
9797
canSelectFolders: true,
@@ -102,6 +102,7 @@ export class TensorBoardSession {
102102
if (selection) {
103103
return selection[0].fsPath;
104104
}
105+
return undefined;
105106
}
106107

107108
private getQuickPickItems(logDir: string | undefined) {
@@ -132,23 +133,12 @@ export class TensorBoardSession {
132133
const selectAFolder = TensorBoard.selectAFolder();
133134
const selectAnotherFolder = TensorBoard.selectAnotherFolder();
134135
const items: QuickPickItem[] = this.getQuickPickItems(logDir);
135-
const quickPick = window.createQuickPick();
136-
quickPick.title = TensorBoard.logDirectoryPrompt();
137-
quickPick.canSelectMany = false;
138-
quickPick.enabled = false;
139-
quickPick.items = items;
140-
if (logDir) {
141-
quickPick.placeholder = TensorBoard.currentDirectory().format(logDir);
142-
}
143-
const selection = createDeferred<QuickPickItem>();
144-
quickPick.onDidAccept(() => {
145-
quickPick.hide();
146-
selection.resolve(quickPick.selectedItems[0]);
136+
const item = await window.showQuickPick(items, {
137+
canPickMany: false,
138+
ignoreFocusOut: false,
139+
placeHolder: logDir ? TensorBoard.currentDirectory().format(logDir) : undefined
147140
});
148-
quickPick.show();
149-
const item = await selection.promise;
150-
quickPick.dispose();
151-
switch (item.label) {
141+
switch (item?.label) {
152142
case useCurrentWorkingDirectory:
153143
return logDir;
154144
case selectAFolder:
@@ -205,6 +195,7 @@ export class TensorBoardSession {
205195
throw new Error(`Timed out after ${timeout / 1000} seconds waiting for TensorBoard to launch.`);
206196
case 'canceled':
207197
traceInfo('Canceled starting TensorBoard session.');
198+
observable.dispose();
208199
return false;
209200
case 'success':
210201
this.process = observable.proc;

src/test/common/installer.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import {
5252
FormatterProductPathService,
5353
LinterProductPathService,
5454
RefactoringLibraryProductPathService,
55+
TensorBoardProductPathService,
5556
TestFrameworkProductPathService
5657
} from '../../client/common/installer/productPath';
5758
import { ProductService } from '../../client/common/installer/productService';
@@ -208,6 +209,11 @@ suite('Installer', () => {
208209
RefactoringLibraryProductPathService,
209210
ProductType.RefactoringLibrary
210211
);
212+
ioc.serviceManager.addSingleton<IProductPathService>(
213+
IProductPathService,
214+
TensorBoardProductPathService,
215+
ProductType.TensorBoard
216+
);
211217

212218
ioc.serviceManager.addSingleton<IActiveResourceService>(IActiveResourceService, ActiveResourceService);
213219
ioc.serviceManager.addSingleton<IInterpreterPathService>(IInterpreterPathService, InterpreterPathService);
@@ -362,7 +368,8 @@ suite('Installer', () => {
362368
}
363369
getNamesAndValues<Product>(Product).forEach((prod) => {
364370
test(`Ensure install for Product: '${prod.name}' executes the right command in IModuleInstaller`, async function () {
365-
if (new ProductService().getProductType(prod.value) === ProductType.DataScience) {
371+
const productType = new ProductService().getProductType(prod.value);
372+
if (productType === ProductType.DataScience || productType === ProductType.TensorBoard) {
366373
// tslint:disable-next-line: no-invalid-this
367374
return this.skip();
368375
}

0 commit comments

Comments
 (0)