diff --git a/news/1 Enhancements/16461.md b/news/1 Enhancements/16461.md new file mode 100644 index 000000000000..58f5ca182794 --- /dev/null +++ b/news/1 Enhancements/16461.md @@ -0,0 +1 @@ +Support starting a TensorBoard session with a remote URL hosting log files. \ No newline at end of file diff --git a/package.nls.json b/package.nls.json index d09b730f5f72..6951ff1bf83a 100644 --- a/package.nls.json +++ b/package.nls.json @@ -252,5 +252,7 @@ "TensorBoard.installProfilerPluginPrompt": "We recommend installing the PyTorch Profiler TensorBoard plugin. Would you like to install the package?", "TensorBoard.upgradePrompt": "Integrated TensorBoard support is only available for TensorBoard >= 2.4.1. Would you like to upgrade your copy of TensorBoard?", "TensorBoard.launchNativeTensorBoardSessionCodeAction": "Launch TensorBoard session", - "TensorBoard.launchNativeTensorBoardSessionCodeLens": "▶ Launch TensorBoard Session" + "TensorBoard.launchNativeTensorBoardSessionCodeLens": "▶ Launch TensorBoard Session", + "TensorBoard.enterRemoteUrl": "Enter remote URL", + "TensorBoard.enterRemoteUrlDetail": "Enter a URL pointing to a remote directory containing your TensorBoard log files" } diff --git a/src/client/common/utils/localize.ts b/src/client/common/utils/localize.ts index c61d7178f976..be080651925d 100644 --- a/src/client/common/utils/localize.ts +++ b/src/client/common/utils/localize.ts @@ -139,6 +139,11 @@ export namespace Jupyter { } export namespace TensorBoard { + export const enterRemoteUrl = localize('TensorBoard.enterRemoteUrl', 'Enter remote URL'); + export const enterRemoteUrlDetail = localize( + 'TensorBoard.enterRemoteUrlDetail', + 'Enter a URL pointing to a remote directory containing your TensorBoard log files', + ); export const useCurrentWorkingDirectoryDetail = localize( 'TensorBoard.useCurrentWorkingDirectoryDetail', 'TensorBoard will search for tfevent files in all subdirectories of the current working directory', diff --git a/src/client/tensorBoard/tensorBoardSession.ts b/src/client/tensorBoard/tensorBoardSession.ts index 8a9598f67f75..2a05f6dc0b48 100644 --- a/src/client/tensorBoard/tensorBoardSession.ts +++ b/src/client/tensorBoard/tensorBoardSession.ts @@ -268,7 +268,6 @@ export class TensorBoardSession { return tensorboardInstallStatus === ProductInstallStatus.Installed; } - // eslint-disable-next-line class-methods-use-this private async showFilePicker(): Promise { const selection = await this.applicationShell.showOpenDialog({ canSelectFiles: false, @@ -285,6 +284,8 @@ export class TensorBoardSession { // eslint-disable-next-line class-methods-use-this private getQuickPickItems(logDir: string | undefined) { + const items = []; + if (logDir) { const useCwd = { label: TensorBoard.useCurrentWorkingDirectory(), @@ -294,13 +295,21 @@ export class TensorBoardSession { label: TensorBoard.selectAnotherFolder(), detail: TensorBoard.selectAnotherFolderDetail(), }; - return [useCwd, selectAnotherFolder]; + items.push(useCwd, selectAnotherFolder); + } else { + const selectAFolder = { + label: TensorBoard.selectAFolder(), + detail: TensorBoard.selectAFolderDetail(), + }; + items.push(selectAFolder); } - const selectAFolder = { - label: TensorBoard.selectAFolder(), - detail: TensorBoard.selectAFolderDetail(), - }; - return [selectAFolder]; + + items.push({ + label: TensorBoard.enterRemoteUrl(), + detail: TensorBoard.enterRemoteUrlDetail(), + }); + + return items; } // Display a quickpick asking the user to acknowledge our autopopulated log directory or @@ -319,6 +328,7 @@ export class TensorBoardSession { const useCurrentWorkingDirectory = TensorBoard.useCurrentWorkingDirectory(); const selectAFolder = TensorBoard.selectAFolder(); const selectAnotherFolder = TensorBoard.selectAnotherFolder(); + const enterRemoteUrl = TensorBoard.enterRemoteUrl(); const items: QuickPickItem[] = this.getQuickPickItems(logDir); const item = await this.applicationShell.showQuickPick(items, { canPickMany: false, @@ -331,6 +341,10 @@ export class TensorBoardSession { case selectAFolder: case selectAnotherFolder: return this.showFilePicker(); + case enterRemoteUrl: + return this.applicationShell.showInputBox({ + prompt: TensorBoard.enterRemoteUrlDetail(), + }); default: return undefined; } diff --git a/src/test/tensorBoard/tensorBoardSession.test.ts b/src/test/tensorBoard/tensorBoardSession.test.ts index 3926b04021c1..707b88ac8811 100644 --- a/src/test/tensorBoard/tensorBoardSession.test.ts +++ b/src/test/tensorBoard/tensorBoardSession.test.ts @@ -136,7 +136,6 @@ suite('TensorBoard session creation', async () => { assert.ok(daemon?.killed, 'TensorBoard session process not killed after webview closed'); }); test('When user selects file picker, display file picker', async () => { - errorMessageStub = sandbox.stub(applicationShell, 'showErrorMessage'); // Stub user selections sandbox.stub(applicationShell, 'showQuickPick').resolves({ label: TensorBoard.selectAnotherFolder() }); const filePickerStub = sandbox.stub(applicationShell, 'showOpenDialog'); @@ -150,6 +149,22 @@ suite('TensorBoard session creation', async () => { assert.ok(filePickerStub.called, 'User requests to select another folder and file picker was not shown'); }); + test('When user selects remote URL, display input box', async () => { + sandbox.stub(applicationShell, 'showQuickPick').resolves({ label: TensorBoard.enterRemoteUrl() }); + const inputBoxStub = sandbox.stub(applicationShell, 'showInputBox'); + + // Create session + await commandManager.executeCommand( + 'python.launchTensorBoard', + TensorBoardEntrypoint.palette, + TensorBoardEntrypointTrigger.palette, + ); + + assert.ok( + inputBoxStub.called, + 'User requested to enter remote URL and input box to enter URL was not shown', + ); + }); }); suite('Installation prompt message', async () => { async function createSessionAndVerifyMessage(message: string) {