From ff2d2d0339803baf11981d8fd1aa850f347810ae Mon Sep 17 00:00:00 2001 From: Cory Rivera Date: Fri, 14 Aug 2020 12:36:40 -0700 Subject: [PATCH] Remove preview flag for Configure Python wizard. (#11813) --- .../configurePython/configurePythonDialog.ts | 302 ------------------ .../notebook/src/jupyter/jupyterController.ts | 22 +- .../src/jupyter/jupyterServerInstallation.ts | 131 +------- .../src/jupyter/jupyterServerManager.ts | 3 - .../src/jupyter/jupyterSessionManager.ts | 6 +- 5 files changed, 25 insertions(+), 439 deletions(-) delete mode 100644 extensions/notebook/src/dialog/configurePython/configurePythonDialog.ts diff --git a/extensions/notebook/src/dialog/configurePython/configurePythonDialog.ts b/extensions/notebook/src/dialog/configurePython/configurePythonDialog.ts deleted file mode 100644 index 4a5de79eea..0000000000 --- a/extensions/notebook/src/dialog/configurePython/configurePythonDialog.ts +++ /dev/null @@ -1,302 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -import * as azdata from 'azdata'; -import { promises as fs } from 'fs'; -import * as utils from '../../common/utils'; - -import { JupyterServerInstallation } from '../../jupyter/jupyterServerInstallation'; -import { Deferred } from '../../common/promise'; -import { PythonPathLookup, PythonPathInfo } from '../pythonPathLookup'; - -const localize = nls.loadMessageBundle(); - -export class ConfigurePythonDialog { - private dialog: azdata.window.Dialog; - - private readonly DialogTitle = localize('configurePython.dialogName', "Configure Python for Notebooks"); - private readonly InstallButtonText = localize('configurePython.okButtonText', "Install"); - private readonly CancelButtonText = localize('configurePython.cancelButtonText', "Cancel"); - private readonly BrowseButtonText = localize('configurePython.browseButtonText', "Browse"); - private readonly LocationTextBoxTitle = localize('configurePython.locationTextBoxText', "Python Install Location"); - private readonly SelectFileLabel = localize('configurePython.selectFileLabel', "Select"); - private readonly InstallationNote = localize('configurePython.installNote', "This installation will take some time. It is recommended to not close the application until the installation is complete."); - private readonly InvalidLocationMsg = localize('configurePython.invalidLocationMsg', "The specified install location is invalid."); - private readonly PythonNotFoundMsg = localize('configurePython.pythonNotFoundMsg', "No python installation was found at the specified location."); - - private pythonLocationDropdown: azdata.DropDownComponent; - private pythonDropdownLoader: azdata.LoadingComponent; - private browseButton: azdata.ButtonComponent; - private newInstallButton: azdata.RadioButtonComponent; - private existingInstallButton: azdata.RadioButtonComponent; - - private setupComplete: Deferred; - private pythonPathsPromise: Promise; - private usingCustomPath: boolean; - - constructor(private jupyterInstallation: JupyterServerInstallation) { - this.setupComplete = new Deferred(); - this.pythonPathsPromise = (new PythonPathLookup()).getSuggestions(); - this.usingCustomPath = false; - } - - /** - * Opens a dialog to configure python installation for notebooks. - * @param rejectOnCancel Specifies whether an error should be thrown after clicking Cancel. - * @returns A promise that is resolved when the python installation completes. - */ - public showDialog(rejectOnCancel: boolean = false): Promise { - this.dialog = azdata.window.createModelViewDialog(this.DialogTitle); - - this.initializeContent(); - - this.dialog.okButton.label = this.InstallButtonText; - this.dialog.cancelButton.label = this.CancelButtonText; - this.dialog.cancelButton.onClick(() => { - if (rejectOnCancel) { - this.setupComplete.reject(localize('configurePython.pythonInstallDeclined', "Python installation was declined.")); - } else { - this.setupComplete.resolve(); - } - }); - - this.dialog.registerCloseValidator(() => this.handleInstall()); - - azdata.window.openDialog(this.dialog); - - return this.setupComplete.promise; - } - - private initializeContent(): void { - this.dialog.registerContent(async view => { - this.pythonLocationDropdown = view.modelBuilder.dropDown() - .withProperties({ - value: undefined, - values: [], - width: '100%' - }).component(); - this.pythonDropdownLoader = view.modelBuilder.loadingComponent() - .withItem(this.pythonLocationDropdown) - .withProperties({ - loading: false - }) - .component(); - - this.browseButton = view.modelBuilder.button() - .withProperties({ - label: this.BrowseButtonText, - width: '70px' - }).component(); - this.browseButton.onDidClick(() => this.handleBrowse()); - - let installationNoteText = view.modelBuilder.text().withProperties({ - value: this.InstallationNote - }).component(); - let noteWrapper = view.modelBuilder.flexContainer().component(); - noteWrapper.addItem(installationNoteText, { - flex: '1 1 auto', - CSSStyles: { - 'margin-top': '60px', - 'padding-left': '15px', - 'padding-right': '15px', - 'border': '1px solid' - } - }); - - let useExistingPython = JupyterServerInstallation.getExistingPythonSetting(); - this.createInstallRadioButtons(view.modelBuilder, useExistingPython); - - let formModel = view.modelBuilder.formContainer() - .withFormItems([{ - component: this.newInstallButton, - title: localize('configurePython.installationType', "Installation Type") - }, { - component: this.existingInstallButton, - title: '' - }, { - component: this.pythonDropdownLoader, - title: this.LocationTextBoxTitle - }, { - component: this.browseButton, - title: '' - }, { - component: noteWrapper, - title: '' - }]).component(); - - await view.initializeModel(formModel); - - await this.updatePythonPathsDropdown(useExistingPython); - }); - } - - private async updatePythonPathsDropdown(useExistingPython: boolean): Promise { - await this.pythonDropdownLoader.updateProperties({ loading: true }); - try { - let pythonPaths: PythonPathInfo[]; - let dropdownValues: azdata.CategoryValue[]; - if (useExistingPython) { - pythonPaths = await this.pythonPathsPromise; - if (pythonPaths && pythonPaths.length > 0) { - dropdownValues = pythonPaths.map(path => { - return { - displayName: `${path.installDir} (Python ${path.version})`, - name: path.installDir - }; - }); - } else { - dropdownValues = [{ - displayName: 'No supported Python versions found.', - name: '' - }]; - } - } else { - let defaultPath = JupyterServerInstallation.DefaultPythonLocation; - dropdownValues = [{ - displayName: `${defaultPath} (Default)`, - name: defaultPath - }]; - } - - this.usingCustomPath = false; - await this.pythonLocationDropdown.updateProperties({ - value: dropdownValues[0], - values: dropdownValues - }); - } finally { - await this.pythonDropdownLoader.updateProperties({ loading: false }); - } - } - - private createInstallRadioButtons(modelBuilder: azdata.ModelBuilder, useExistingPython: boolean): void { - let buttonGroup = 'installationType'; - this.newInstallButton = modelBuilder.radioButton() - .withProperties({ - name: buttonGroup, - label: localize('configurePython.newInstall', "New Python installation"), - checked: !useExistingPython - }).component(); - this.newInstallButton.onDidClick(() => { - this.existingInstallButton.checked = false; - this.updatePythonPathsDropdown(false) - .catch(err => { - this.showErrorMessage(utils.getErrorMessage(err)); - }); - }); - - this.existingInstallButton = modelBuilder.radioButton() - .withProperties({ - name: buttonGroup, - label: localize('configurePython.existingInstall', "Use existing Python installation"), - checked: useExistingPython - }).component(); - this.existingInstallButton.onDidClick(() => { - this.newInstallButton.checked = false; - this.updatePythonPathsDropdown(true) - .catch(err => { - this.showErrorMessage(utils.getErrorMessage(err)); - }); - }); - } - - private async handleInstall(): Promise { - let pythonLocation = (this.pythonLocationDropdown.value as azdata.CategoryValue).name; - if (!pythonLocation || pythonLocation.length === 0) { - this.showErrorMessage(this.InvalidLocationMsg); - return false; - } - - let useExistingPython = !!this.existingInstallButton.checked; - try { - let isValid = await this.isFileValid(pythonLocation); - if (!isValid) { - return false; - } - - if (useExistingPython) { - let exePath = JupyterServerInstallation.getPythonExePath(pythonLocation, true); - let pythonExists = await utils.exists(exePath); - if (!pythonExists) { - this.showErrorMessage(this.PythonNotFoundMsg); - return false; - } - } - } catch (err) { - this.showErrorMessage(utils.getErrorMessage(err)); - return false; - } - - // Don't wait on installation, since there's currently no Cancel functionality - this.jupyterInstallation.startInstallProcess(false, { installPath: pythonLocation, existingPython: useExistingPython }) - .then(() => { - this.setupComplete.resolve(); - }) - .catch(err => { - this.setupComplete.reject(utils.getErrorMessage(err)); - }); - - return true; - } - - private async isFileValid(pythonLocation: string): Promise { - let self = this; - try { - const stats = await fs.stat(pythonLocation); - if (stats.isFile()) { - self.showErrorMessage(self.InvalidLocationMsg); - return false; - } - } catch (err) { - // Ignore error if folder doesn't exist, since it will be - // created during installation - if (err.code !== 'ENOENT') { - self.showErrorMessage(err.message); - return false; - } - } - return true; - } - - private async handleBrowse(): Promise { - let options: vscode.OpenDialogOptions = { - defaultUri: vscode.Uri.file(utils.getUserHome()), - canSelectFiles: false, - canSelectFolders: true, - canSelectMany: false, - openLabel: this.SelectFileLabel - }; - - let fileUris: vscode.Uri[] = await vscode.window.showOpenDialog(options); - if (fileUris && fileUris[0]) { - let existingValues = this.pythonLocationDropdown.values; - let filePath = fileUris[0].fsPath; - let newValue = { - displayName: `${filePath} (Custom)`, - name: filePath - }; - - if (this.usingCustomPath) { - existingValues[0] = newValue; - } else { - existingValues.unshift(newValue); - this.usingCustomPath = true; - } - - await this.pythonLocationDropdown.updateProperties({ - value: existingValues[0], - values: existingValues - }); - } - } - - private showErrorMessage(message: string): void { - this.dialog.message = { - text: message, - level: azdata.window.MessageLevel.Error - }; - } -} diff --git a/extensions/notebook/src/jupyter/jupyterController.ts b/extensions/notebook/src/jupyter/jupyterController.ts index d67f0fc22a..c0c3defd6f 100644 --- a/extensions/notebook/src/jupyter/jupyterController.ts +++ b/extensions/notebook/src/jupyter/jupyterController.ts @@ -29,7 +29,6 @@ import { LocalPipPackageManageProvider } from './localPipPackageManageProvider'; import { LocalCondaPackageManageProvider } from './localCondaPackageManageProvider'; import { ManagePackagesDialogModel, ManagePackageDialogOptions } from '../dialog/managePackages/managePackagesDialogModel'; import { PyPiClient } from './pypiClient'; -import { ConfigurePythonDialog } from '../dialog/configurePython/configurePythonDialog'; import { IconPathHelper } from '../common/iconHelper'; let untitledCounter = 0; @@ -248,20 +247,13 @@ export class JupyterController implements vscode.Disposable { } public doConfigurePython(jupyterInstaller: JupyterServerInstallation): void { - if (jupyterInstaller.previewFeaturesEnabled) { - let pythonWizard = new ConfigurePythonWizard(jupyterInstaller); - pythonWizard.start().catch((err: any) => { - vscode.window.showErrorMessage(utils.getErrorMessage(err)); - }); - pythonWizard.setupComplete.catch((err: any) => { - vscode.window.showErrorMessage(utils.getErrorMessage(err)); - }); - } else { - let pythonDialog = new ConfigurePythonDialog(jupyterInstaller); - pythonDialog.showDialog().catch((err: any) => { - vscode.window.showErrorMessage(utils.getErrorMessage(err)); - }); - } + let pythonWizard = new ConfigurePythonWizard(jupyterInstaller); + pythonWizard.start().catch((err: any) => { + vscode.window.showErrorMessage(utils.getErrorMessage(err)); + }); + pythonWizard.setupComplete.catch((err: any) => { + vscode.window.showErrorMessage(utils.getErrorMessage(err)); + }); } public get jupyterInstallation() { diff --git a/extensions/notebook/src/jupyter/jupyterServerInstallation.ts b/extensions/notebook/src/jupyter/jupyterServerInstallation.ts index 197846de7f..cf54ccc85a 100644 --- a/extensions/notebook/src/jupyter/jupyterServerInstallation.ts +++ b/extensions/notebook/src/jupyter/jupyterServerInstallation.ts @@ -17,9 +17,6 @@ import * as constants from '../common/constants'; import * as utils from '../common/utils'; import { Deferred } from '../common/promise'; import { ConfigurePythonWizard } from '../dialog/configurePython/configurePythonWizard'; -import { IPrompter, IQuestion, confirm } from '../prompts/question'; -import CodeAdapter from '../prompts/adapter'; -import { ConfigurePythonDialog } from '../dialog/configurePython/configurePythonDialog'; const localize = nls.loadMessageBundle(); const msgInstallPkgProgress = localize('msgInstallPkgProgress', "Notebook dependencies installation is in progress"); @@ -77,8 +74,6 @@ export class JupyterServerInstallation implements IJupyterServerInstallation { public static readonly DefaultPythonLocation = path.join(utils.getUserHome(), 'azuredatastudio-python'); - private _prompter: IPrompter; - private readonly _commonPackages: PythonPkgDetails[] = [ { name: 'jupyter', @@ -129,8 +124,6 @@ export class JupyterServerInstallation implements IJupyterServerInstallation { this._usingConda = false; this._installInProgress = false; - this._prompter = new CodeAdapter(); - if (process.platform !== constants.winPlatform) { this._expectedCondaPackages = this._commonPackages.concat([{ name: 'pykerberos', version: '1.2.1' }]); } else { @@ -189,7 +182,7 @@ export class JupyterServerInstallation implements IJupyterServerInstallation { if (!pythonExists || forceInstall) { await this.installPythonPackage(backgroundOperation, this._usingExistingPython, this._pythonInstallationPath, this.outputChannel); } - await this.upgradePythonPackages(false, forceInstall, specificPackages); + await this.upgradePythonPackages(forceInstall, specificPackages); } catch (err) { this.outputChannel.appendLine(msgDependenciesInstallationFailed(utils.getErrorMessage(err))); throw err; @@ -476,53 +469,12 @@ export class JupyterServerInstallation implements IJupyterServerInstallation { let isPythonInstalled = JupyterServerInstallation.isPythonInstalled(); let areRequiredPackagesInstalled = await this.areRequiredPackagesInstalled(kernelDisplayName); if (!isPythonInstalled || !areRequiredPackagesInstalled) { - if (this.previewFeaturesEnabled) { - let pythonWizard = new ConfigurePythonWizard(this); - await pythonWizard.start(kernelDisplayName, true); - return pythonWizard.setupComplete.then(() => { - this._kernelSetupCache.set(kernelDisplayName, true); - }); - } else { - let pythonDialog = new ConfigurePythonDialog(this); - return pythonDialog.showDialog(true); - } - } - } - - /** - * Prompts user to upgrade certain python packages if they're below the minimum expected version. - */ - public async promptForPackageUpgrade(kernelName: string): Promise { - if (this._runningOnSAW) { - return Promise.resolve(); - } - if (this._installInProgress) { - vscode.window.showInformationMessage(msgWaitingForInstall); - return this._installCompletion.promise; - } - - let requiredPackages: PythonPkgDetails[]; - if (this.previewFeaturesEnabled) { - if (this._kernelSetupCache.get(kernelName)) { - return; - } - requiredPackages = this.getRequiredPackagesForKernel(kernelName); - } - - this._installInProgress = true; - this._installCompletion = new Deferred(); - this.upgradePythonPackages(true, false, requiredPackages) - .then(() => { - this._installCompletion.resolve(); - this._installInProgress = false; - this._kernelSetupCache.set(kernelName, true); - }) - .catch(err => { - let errorMsg = msgDependenciesInstallationFailed(utils.getErrorMessage(err)); - this._installCompletion.reject(errorMsg); - this._installInProgress = false; + let pythonWizard = new ConfigurePythonWizard(this); + await pythonWizard.start(kernelDisplayName, true); + return pythonWizard.setupComplete.then(() => { + this._kernelSetupCache.set(kernelDisplayName, true); }); - return this._installCompletion.promise; + } } private async areRequiredPackagesInstalled(kernelDisplayName: string): Promise { @@ -546,7 +498,7 @@ export class JupyterServerInstallation implements IJupyterServerInstallation { return true; } - private async upgradePythonPackages(promptForUpgrade: boolean, forceInstall: boolean, specificPackages?: PythonPkgDetails[]): Promise { + private async upgradePythonPackages(forceInstall: boolean, specificPackages?: PythonPkgDetails[]): Promise { let expectedCondaPackages: PythonPkgDetails[]; let expectedPipPackages: PythonPkgDetails[]; if (specificPackages) { @@ -598,63 +550,18 @@ export class JupyterServerInstallation implements IJupyterServerInstallation { } if (condaPackagesToInstall.length > 0 || pipPackagesToInstall.length > 0) { - let doUpgrade: boolean; - if (promptForUpgrade) { - doUpgrade = await this._prompter.promptSingle({ - type: confirm, - message: localize('confirmPackageUpgrade', "Some required python packages need to be installed. Would you like to install them now?"), - default: true - }); - if (!doUpgrade) { - throw new Error(localize('configurePython.packageInstallDeclined', "Package installation was declined.")); - } - } else { - doUpgrade = true; - } - - if (doUpgrade) { - let installPromise = new Promise(async (resolve, reject) => { - try { - if (this._usingConda) { - await this.installCondaPackages(condaPackagesToInstall, true); - } - await this.installPipPackages(pipPackagesToInstall, true); - resolve(); - } catch (err) { - reject(err); + let installPromise = new Promise(async (resolve, reject) => { + try { + if (this._usingConda) { + await this.installCondaPackages(condaPackagesToInstall, true); } - }); - - if (promptForUpgrade) { - let packagesStr = condaPackagesToInstall.concat(pipPackagesToInstall).map(pkg => { - return `${pkg.name}>=${pkg.version}`; - }).join(' '); - let taskName = localize('upgradePackages.pipInstall', - "Installing {0}", - packagesStr); - - let backgroundTaskComplete = new Deferred(); - azdata.tasks.startBackgroundOperation({ - displayName: taskName, - description: taskName, - isCancelable: false, - operation: async op => { - try { - await installPromise; - op.updateStatus(azdata.TaskStatus.Succeeded); - backgroundTaskComplete.resolve(); - } catch (err) { - let errorMsg = utils.getErrorMessage(err); - op.updateStatus(azdata.TaskStatus.Failed, errorMsg); - backgroundTaskComplete.reject(errorMsg); - } - } - }); - await backgroundTaskComplete.promise; - } else { - await installPromise; + await this.installPipPackages(pipPackagesToInstall, true); + resolve(); + } catch (err) { + reject(err); } - } + }); + await installPromise; } } @@ -875,10 +782,6 @@ export class JupyterServerInstallation implements IJupyterServerInstallation { public getRequiredPackagesForKernel(kernelName: string): PythonPkgDetails[] { return this._requiredKernelPackages.get(kernelName) ?? []; } - - public get previewFeaturesEnabled(): boolean { - return vscode.workspace.getConfiguration('workbench').get('enablePreviewFeatures'); - } } export interface PythonPkgDetails { diff --git a/extensions/notebook/src/jupyter/jupyterServerManager.ts b/extensions/notebook/src/jupyter/jupyterServerManager.ts index 462fc5638c..71f1ce6ecb 100644 --- a/extensions/notebook/src/jupyter/jupyterServerManager.ts +++ b/extensions/notebook/src/jupyter/jupyterServerManager.ts @@ -105,9 +105,6 @@ export class LocalJupyterServerManager implements nb.ServerManager, vscode.Dispo private async doStartServer(kernelSpec: nb.IKernelSpec): Promise { // We can't find or create servers until the installation is complete let installation = this.options.jupyterInstallation; await installation.promptForPythonInstall(kernelSpec.display_name); - if (!installation.previewFeaturesEnabled) { - await installation.promptForPackageUpgrade(kernelSpec.display_name); - } vscode.commands.executeCommand(BuiltInCommands.SetContext, CommandContext.NotebookPythonInstalled, true); // Calculate the path to use as the notebook-dir for Jupyter based on the path of the uri of the diff --git a/extensions/notebook/src/jupyter/jupyterSessionManager.ts b/extensions/notebook/src/jupyter/jupyterSessionManager.ts index 0bb78fb42a..14a91f699f 100644 --- a/extensions/notebook/src/jupyter/jupyterSessionManager.ts +++ b/extensions/notebook/src/jupyter/jupyterSessionManager.ts @@ -237,11 +237,7 @@ export class JupyterSession implements nb.ISession { public async changeKernel(kernelInfo: nb.IKernelSpec): Promise { if (this._installation) { try { - if (this._installation.previewFeaturesEnabled) { - await this._installation.promptForPythonInstall(kernelInfo.display_name); - } else { - await this._installation.promptForPackageUpgrade(kernelInfo.display_name); - } + await this._installation.promptForPythonInstall(kernelInfo.display_name); } catch (err) { // Have to swallow the error here to prevent hangs when changing back to the old kernel. console.error(err.toString());