mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-26 01:25:38 -05:00
Hide installation options in Configure Python wizard if python is already setup. (#10635)
This commit is contained in:
@@ -15,23 +15,37 @@ const localize = nls.loadMessageBundle();
|
||||
|
||||
export class ConfigurePathPage extends BasePage {
|
||||
private readonly BrowseButtonText = localize('configurePython.browseButtonText', "Browse");
|
||||
private readonly LocationTextBoxTitle = localize('configurePython.locationTextBoxText', "Python Install Location");
|
||||
private readonly SelectFileLabel = localize('configurePython.selectFileLabel', "Select");
|
||||
|
||||
private pythonLocationDropdown: azdata.DropDownComponent;
|
||||
private pythonDropdownLoader: azdata.LoadingComponent;
|
||||
private browseButton: azdata.ButtonComponent;
|
||||
private newInstallButton: azdata.RadioButtonComponent;
|
||||
private existingInstallButton: azdata.RadioButtonComponent;
|
||||
|
||||
private selectInstallEnabled: boolean;
|
||||
private usingCustomPath: boolean = false;
|
||||
|
||||
public async initialize(): Promise<boolean> {
|
||||
let wizardDescription: string;
|
||||
if (this.model.kernelName) {
|
||||
wizardDescription = localize('configurePython.descriptionWithKernel', "The {0} kernel requires a Python runtime to be configured and dependencies to be installed.", this.model.kernelName);
|
||||
} else {
|
||||
wizardDescription = localize('configurePython.descriptionWithoutKernel', "Notebook kernels require a Python runtime to be configured and dependencies to be installed.");
|
||||
}
|
||||
let wizardDescriptionLabel = this.view.modelBuilder.text()
|
||||
.withProperties<azdata.TextComponentProperties>({
|
||||
value: wizardDescription,
|
||||
CSSStyles: {
|
||||
'padding': '0px',
|
||||
'margin': '0px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
this.pythonLocationDropdown = this.view.modelBuilder.dropDown()
|
||||
.withProperties<azdata.DropDownProperties>({
|
||||
value: undefined,
|
||||
values: [],
|
||||
width: '100%'
|
||||
width: '400px'
|
||||
}).component();
|
||||
this.pythonDropdownLoader = this.view.modelBuilder.loadingComponent()
|
||||
.withItem(this.pythonLocationDropdown)
|
||||
@@ -39,17 +53,16 @@ export class ConfigurePathPage extends BasePage {
|
||||
loading: false
|
||||
})
|
||||
.component();
|
||||
|
||||
this.browseButton = this.view.modelBuilder.button()
|
||||
let browseButton = this.view.modelBuilder.button()
|
||||
.withProperties<azdata.ButtonProperties>({
|
||||
label: this.BrowseButtonText,
|
||||
width: '70px'
|
||||
}).component();
|
||||
this.browseButton.onDidClick(() => this.handleBrowse());
|
||||
browseButton.onDidClick(() => this.handleBrowse());
|
||||
|
||||
this.createInstallRadioButtons(this.view.modelBuilder, this.model.useExistingPython);
|
||||
|
||||
let formModel = this.view.modelBuilder.formContainer()
|
||||
let selectInstallForm = this.view.modelBuilder.formContainer()
|
||||
.withFormItems([{
|
||||
component: this.newInstallButton,
|
||||
title: localize('configurePython.installationType', "Installation Type")
|
||||
@@ -58,14 +71,66 @@ export class ConfigurePathPage extends BasePage {
|
||||
title: ''
|
||||
}, {
|
||||
component: this.pythonDropdownLoader,
|
||||
title: this.LocationTextBoxTitle
|
||||
title: localize('configurePython.locationTextBoxText', "Python Install Location")
|
||||
}, {
|
||||
component: this.browseButton,
|
||||
component: browseButton,
|
||||
title: ''
|
||||
}]).component();
|
||||
let selectInstallContainer = this.view.modelBuilder.divContainer()
|
||||
.withItems([selectInstallForm])
|
||||
.withProperties<azdata.DivContainerProperties>({
|
||||
clickable: false
|
||||
}).component();
|
||||
|
||||
await this.view.initializeModel(formModel);
|
||||
let allParentItems = [selectInstallContainer];
|
||||
if (this.model.pythonLocation) {
|
||||
let installedPathTextBox = this.view.modelBuilder.inputBox().withProperties<azdata.TextComponentProperties>({
|
||||
value: this.model.pythonLocation,
|
||||
enabled: false,
|
||||
width: '400px'
|
||||
}).component();
|
||||
let editPathButton = this.view.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||
label: 'Edit',
|
||||
width: '70px'
|
||||
}).component();
|
||||
let editPathForm = this.view.modelBuilder.formContainer()
|
||||
.withFormItems([{
|
||||
title: localize('configurePython.pythonConfigured', "Python runtime configured!"),
|
||||
component: installedPathTextBox
|
||||
}, {
|
||||
title: '',
|
||||
component: editPathButton
|
||||
}]).component();
|
||||
|
||||
let editPathContainer = this.view.modelBuilder.divContainer()
|
||||
.withItems([editPathForm])
|
||||
.withProperties<azdata.DivContainerProperties>({
|
||||
clickable: false
|
||||
}).component();
|
||||
allParentItems.push(editPathContainer);
|
||||
|
||||
editPathButton.onDidClick(async () => {
|
||||
editPathContainer.display = 'none';
|
||||
selectInstallContainer.display = 'block';
|
||||
this.selectInstallEnabled = true;
|
||||
});
|
||||
selectInstallContainer.display = 'none';
|
||||
|
||||
this.selectInstallEnabled = false;
|
||||
} else {
|
||||
this.selectInstallEnabled = true;
|
||||
}
|
||||
|
||||
let parentContainer = this.view.modelBuilder.flexContainer()
|
||||
.withLayout({ flexFlow: 'column' }).component();
|
||||
parentContainer.addItem(wizardDescriptionLabel, {
|
||||
CSSStyles: {
|
||||
'padding': '0px 30px 0px 30px'
|
||||
}
|
||||
});
|
||||
parentContainer.addItems(allParentItems);
|
||||
|
||||
await this.view.initializeModel(parentContainer);
|
||||
await this.updatePythonPathsDropdown(this.model.useExistingPython);
|
||||
|
||||
return true;
|
||||
@@ -75,19 +140,24 @@ export class ConfigurePathPage extends BasePage {
|
||||
}
|
||||
|
||||
public async onPageLeave(): Promise<boolean> {
|
||||
let pythonLocation = utils.getDropdownValue(this.pythonLocationDropdown);
|
||||
if (!pythonLocation || pythonLocation.length === 0) {
|
||||
this.instance.showErrorMessage(this.instance.InvalidLocationMsg);
|
||||
if (this.pythonDropdownLoader.loading) {
|
||||
return false;
|
||||
}
|
||||
if (this.selectInstallEnabled) {
|
||||
let pythonLocation = utils.getDropdownValue(this.pythonLocationDropdown);
|
||||
if (!pythonLocation || pythonLocation.length === 0) {
|
||||
this.instance.showErrorMessage(this.instance.InvalidLocationMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.model.pythonLocation = pythonLocation;
|
||||
this.model.useExistingPython = !!this.existingInstallButton.checked;
|
||||
|
||||
this.model.pythonLocation = pythonLocation;
|
||||
this.model.useExistingPython = !!this.existingInstallButton.checked;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private async updatePythonPathsDropdown(useExistingPython: boolean): Promise<void> {
|
||||
this.instance.wizard.nextButton.enabled = false;
|
||||
this.pythonDropdownLoader.loading = true;
|
||||
try {
|
||||
let pythonPaths: PythonPathInfo[];
|
||||
@@ -121,6 +191,7 @@ export class ConfigurePathPage extends BasePage {
|
||||
values: dropdownValues
|
||||
});
|
||||
} finally {
|
||||
this.instance.wizard.nextButton.enabled = true;
|
||||
this.pythonDropdownLoader.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ export class ConfigurePythonWizard {
|
||||
kernelName: kernelName,
|
||||
pythonPathsPromise: this.pythonPathsPromise,
|
||||
installation: this.jupyterInstallation,
|
||||
pythonLocation: JupyterServerInstallation.getPythonPathSetting(this.apiWrapper),
|
||||
useExistingPython: JupyterServerInstallation.getExistingPythonSetting(this.apiWrapper)
|
||||
};
|
||||
|
||||
|
||||
@@ -6,20 +6,31 @@
|
||||
import * as azdata from 'azdata';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { BasePage } from './basePage';
|
||||
import { JupyterServerInstallation, PythonPkgDetails } from '../../jupyter/jupyterServerInstallation';
|
||||
import { JupyterServerInstallation } from '../../jupyter/jupyterServerInstallation';
|
||||
import { python3DisplayName, pysparkDisplayName, sparkScalaDisplayName, sparkRDisplayName, powershellDisplayName, allKernelsName } from '../../common/constants';
|
||||
import { getDropdownValue } from '../../common/utils';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
interface RequiredPackageInfo {
|
||||
name: string;
|
||||
existingVersion: string;
|
||||
requiredVersion: string;
|
||||
}
|
||||
|
||||
namespace cssStyles {
|
||||
export const tableHeader = { 'text-align': 'left', 'font-weight': 'lighter', 'font-size': '10px', 'user-select': 'text', 'border': 'none' };
|
||||
export const tableRow = { 'border-top': 'solid 1px #ccc', 'border-bottom': 'solid 1px #ccc', 'border-left': 'none', 'border-right': 'none' };
|
||||
}
|
||||
|
||||
export class PickPackagesPage extends BasePage {
|
||||
private kernelLabel: azdata.TextComponent | undefined;
|
||||
private kernelDropdown: azdata.DropDownComponent | undefined;
|
||||
private requiredPackagesTable: azdata.DeclarativeTableComponent;
|
||||
private packageTableSpinner: azdata.LoadingComponent;
|
||||
|
||||
private installedPackagesPromise: Promise<PythonPkgDetails[]>;
|
||||
private installedPackages: PythonPkgDetails[];
|
||||
private packageVersionRetrieval: Promise<void>;
|
||||
private packageVersionMap = new Map<string, string>();
|
||||
|
||||
public async initialize(): Promise<boolean> {
|
||||
if (this.model.kernelName) {
|
||||
@@ -39,22 +50,46 @@ export class PickPackagesPage extends BasePage {
|
||||
});
|
||||
}
|
||||
|
||||
let nameColumn = localize('configurePython.pkgNameColumn', "Name");
|
||||
let existingVersionColumn = localize('configurePython.existingVersionColumn', "Existing Version");
|
||||
let requiredVersionColumn = localize('configurePython.requiredVersionColumn', "Required Version");
|
||||
this.requiredPackagesTable = this.view.modelBuilder.declarativeTable().withProperties<azdata.DeclarativeTableProperties>({
|
||||
columns: [{
|
||||
displayName: localize('configurePython.pkgNameColumn', "Name"),
|
||||
displayName: nameColumn,
|
||||
ariaLabel: nameColumn,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
isReadOnly: true,
|
||||
width: '200px'
|
||||
width: '200px',
|
||||
headerCssStyles: {
|
||||
...cssStyles.tableHeader
|
||||
},
|
||||
rowCssStyles: {
|
||||
...cssStyles.tableRow
|
||||
}
|
||||
}, {
|
||||
displayName: localize('configurePython.existingVersionColumn', "Existing Version"),
|
||||
displayName: existingVersionColumn,
|
||||
ariaLabel: existingVersionColumn,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
isReadOnly: true,
|
||||
width: '200px'
|
||||
width: '200px',
|
||||
headerCssStyles: {
|
||||
...cssStyles.tableHeader
|
||||
},
|
||||
rowCssStyles: {
|
||||
...cssStyles.tableRow
|
||||
}
|
||||
}, {
|
||||
displayName: localize('configurePython.requiredVersionColumn', "Required Version"),
|
||||
displayName: requiredVersionColumn,
|
||||
ariaLabel: requiredVersionColumn,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
isReadOnly: true,
|
||||
width: '200px'
|
||||
width: '200px',
|
||||
headerCssStyles: {
|
||||
...cssStyles.tableHeader
|
||||
},
|
||||
rowCssStyles: {
|
||||
...cssStyles.tableRow
|
||||
}
|
||||
}],
|
||||
data: [[]]
|
||||
}).component();
|
||||
@@ -74,9 +109,16 @@ export class PickPackagesPage extends BasePage {
|
||||
}
|
||||
|
||||
public async onPageEnter(): Promise<void> {
|
||||
this.packageVersionMap.clear();
|
||||
let pythonExe = JupyterServerInstallation.getPythonExePath(this.model.pythonLocation, this.model.useExistingPython);
|
||||
this.installedPackagesPromise = this.model.installation.getInstalledPipPackages(pythonExe);
|
||||
this.installedPackages = undefined;
|
||||
this.packageVersionRetrieval = this.model.installation.getInstalledPipPackages(pythonExe)
|
||||
.then(installedPackages => {
|
||||
if (installedPackages) {
|
||||
installedPackages.forEach(pkg => {
|
||||
this.packageVersionMap.set(pkg.name, pkg.version);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (this.kernelDropdown) {
|
||||
if (this.model.kernelName) {
|
||||
@@ -89,45 +131,39 @@ export class PickPackagesPage extends BasePage {
|
||||
}
|
||||
|
||||
public async onPageLeave(): Promise<boolean> {
|
||||
return true;
|
||||
return !this.packageTableSpinner.loading;
|
||||
}
|
||||
|
||||
private async updateRequiredPackages(kernelName: string): Promise<void> {
|
||||
this.instance.wizard.doneButton.enabled = false;
|
||||
this.packageTableSpinner.loading = true;
|
||||
try {
|
||||
let pkgVersionMap = new Map<string, { currentVersion: string, newVersion: string }>();
|
||||
|
||||
// Fetch list of required packages for the specified kernel
|
||||
let requiredPackages = JupyterServerInstallation.getRequiredPackagesForKernel(kernelName);
|
||||
let requiredPkgVersions: RequiredPackageInfo[] = [];
|
||||
let requiredPackages = this.model.installation.getRequiredPackagesForKernel(kernelName);
|
||||
requiredPackages.forEach(pkg => {
|
||||
pkgVersionMap.set(pkg.name, { currentVersion: undefined, newVersion: pkg.version });
|
||||
requiredPkgVersions.push({ name: pkg.name, existingVersion: undefined, requiredVersion: pkg.version });
|
||||
});
|
||||
|
||||
// For each required package, check if there is another version of that package already installed
|
||||
if (!this.installedPackages) {
|
||||
this.installedPackages = await this.installedPackagesPromise;
|
||||
}
|
||||
this.installedPackages.forEach(pkg => {
|
||||
let info = pkgVersionMap.get(pkg.name);
|
||||
if (info) {
|
||||
info.currentVersion = pkg.version;
|
||||
pkgVersionMap.set(pkg.name, info);
|
||||
await this.packageVersionRetrieval;
|
||||
requiredPkgVersions.forEach(pkgVersion => {
|
||||
let installedPackageVersion = this.packageVersionMap.get(pkgVersion.name);
|
||||
if (installedPackageVersion) {
|
||||
pkgVersion.existingVersion = installedPackageVersion;
|
||||
}
|
||||
});
|
||||
|
||||
if (pkgVersionMap.size > 0) {
|
||||
let packageData = [];
|
||||
for (let [key, value] of pkgVersionMap.entries()) {
|
||||
packageData.push([key, value.currentVersion ?? '-', value.newVersion]);
|
||||
}
|
||||
this.requiredPackagesTable.data = packageData;
|
||||
if (requiredPkgVersions.length > 0) {
|
||||
this.requiredPackagesTable.data = requiredPkgVersions.map(pkg => [pkg.name, pkg.existingVersion ?? '-', pkg.requiredVersion]);
|
||||
this.model.packagesToInstall = requiredPackages;
|
||||
} else {
|
||||
this.instance.showErrorMessage(localize('msgUnsupportedKernel', "Could not retrieve packages for unsupported kernel {0}", kernelName));
|
||||
this.instance.showErrorMessage(localize('msgUnsupportedKernel', "Could not retrieve packages for kernel {0}", kernelName));
|
||||
this.requiredPackagesTable.data = [['-', '-', '-']];
|
||||
this.model.packagesToInstall = undefined;
|
||||
}
|
||||
} finally {
|
||||
this.instance.wizard.doneButton.enabled = true;
|
||||
this.packageTableSpinner.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user