mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Fix bugs in selecting a system version of Python for Notebook dependencies (#6250)
This commit is contained in:
@@ -58,6 +58,7 @@ export class JupyterController implements vscode.Disposable {
|
|||||||
this.extensionContext.extensionPath,
|
this.extensionContext.extensionPath,
|
||||||
this.outputChannel,
|
this.outputChannel,
|
||||||
this.apiWrapper);
|
this.apiWrapper);
|
||||||
|
await this._jupyterInstallation.configurePackagePaths();
|
||||||
|
|
||||||
// Add command/task handlers
|
// Add command/task handlers
|
||||||
this.apiWrapper.registerTaskHandler(constants.jupyterOpenNotebookTask, (profile: azdata.IConnectionProfile) => {
|
this.apiWrapper.registerTaskHandler(constants.jupyterOpenNotebookTask, (profile: azdata.IConnectionProfile) => {
|
||||||
|
|||||||
@@ -64,8 +64,6 @@ export class JupyterServerInstallation {
|
|||||||
this._usingConda = false;
|
this._usingConda = false;
|
||||||
this._installInProgress = false;
|
this._installInProgress = false;
|
||||||
this._usingExistingPython = JupyterServerInstallation.getExistingPythonSetting(this.apiWrapper);
|
this._usingExistingPython = JupyterServerInstallation.getExistingPythonSetting(this.apiWrapper);
|
||||||
|
|
||||||
this.configurePackagePaths();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async installDependencies(backgroundOperation: azdata.BackgroundOperation): Promise<void> {
|
private async installDependencies(backgroundOperation: azdata.BackgroundOperation): Promise<void> {
|
||||||
@@ -78,22 +76,23 @@ export class JupyterServerInstallation {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.installPythonPackage(backgroundOperation);
|
await this.installPythonPackage(backgroundOperation);
|
||||||
|
|
||||||
|
this.outputChannel.appendLine(msgPythonDownloadComplete);
|
||||||
|
backgroundOperation.updateStatus(azdata.TaskStatus.InProgress, msgPythonDownloadComplete);
|
||||||
|
|
||||||
|
if (this._usingConda) {
|
||||||
|
await this.installCondaDependencies();
|
||||||
|
} else if (this._usingExistingPython) {
|
||||||
|
await this.installPipDependencies();
|
||||||
|
} else {
|
||||||
|
await this.installOfflinePipDependencies();
|
||||||
|
}
|
||||||
|
let doOnlineInstall = this._usingExistingPython;
|
||||||
|
await this.installSparkMagic(doOnlineInstall);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.outputChannel.appendLine(msgDependenciesInstallationFailed(utils.getErrorMessage(err)));
|
this.outputChannel.appendLine(msgDependenciesInstallationFailed(utils.getErrorMessage(err)));
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
this.outputChannel.appendLine(msgPythonDownloadComplete);
|
|
||||||
backgroundOperation.updateStatus(azdata.TaskStatus.InProgress, msgPythonDownloadComplete);
|
|
||||||
|
|
||||||
if (this._usingConda) {
|
|
||||||
await this.installCondaDependencies();
|
|
||||||
} else if (this._usingExistingPython) {
|
|
||||||
await this.installPipDependencies();
|
|
||||||
} else {
|
|
||||||
await this.installOfflinePipDependencies();
|
|
||||||
}
|
|
||||||
let doOnlineInstall = this._usingExistingPython;
|
|
||||||
await this.installSparkMagic(doOnlineInstall);
|
|
||||||
|
|
||||||
fs.remove(this._pythonPackageDir, (err: Error) => {
|
fs.remove(this._pythonPackageDir, (err: Error) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -134,10 +133,17 @@ export class JupyterServerInstallation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pythonPackagePathLocal = this._pythonInstallationPath + '/' + packageName;
|
let installPath: string;
|
||||||
|
if (this._usingExistingPython) {
|
||||||
|
installPath = utils.getUserHome();
|
||||||
|
} else {
|
||||||
|
installPath = this._pythonInstallationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pythonPackagePathLocal = path.join(installPath, packageName);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
backgroundOperation.updateStatus(azdata.TaskStatus.InProgress, msgDownloadPython(platformId, pythonDownloadUrl));
|
backgroundOperation.updateStatus(azdata.TaskStatus.InProgress, msgDownloadPython(platformId, pythonDownloadUrl));
|
||||||
fs.mkdirs(this._pythonInstallationPath, (err) => {
|
fs.mkdirs(installPath, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
backgroundOperation.updateStatus(azdata.TaskStatus.InProgress, msgPythonDirectoryError);
|
backgroundOperation.updateStatus(azdata.TaskStatus.InProgress, msgPythonDirectoryError);
|
||||||
return reject(err);
|
return reject(err);
|
||||||
@@ -177,7 +183,7 @@ export class JupyterServerInstallation {
|
|||||||
.on('close', () => {
|
.on('close', () => {
|
||||||
//unpack python zip/tar file
|
//unpack python zip/tar file
|
||||||
this.outputChannel.appendLine(msgPythonUnpackPending);
|
this.outputChannel.appendLine(msgPythonUnpackPending);
|
||||||
let pythonSourcePath = path.join(this._pythonInstallationPath, constants.pythonBundleVersion);
|
let pythonSourcePath = path.join(installPath, constants.pythonBundleVersion);
|
||||||
if (!this._usingExistingPython && fs.existsSync(pythonSourcePath)) {
|
if (!this._usingExistingPython && fs.existsSync(pythonSourcePath)) {
|
||||||
try {
|
try {
|
||||||
fs.removeSync(pythonSourcePath);
|
fs.removeSync(pythonSourcePath);
|
||||||
@@ -186,7 +192,7 @@ export class JupyterServerInstallation {
|
|||||||
return reject(err);
|
return reject(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
decompress(pythonPackagePathLocal, this._pythonInstallationPath).then(files => {
|
decompress(pythonPackagePathLocal, installPath).then(files => {
|
||||||
//Delete zip/tar file
|
//Delete zip/tar file
|
||||||
fs.unlink(pythonPackagePathLocal, (err) => {
|
fs.unlink(pythonPackagePathLocal, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -210,13 +216,17 @@ export class JupyterServerInstallation {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private configurePackagePaths(): void {
|
public async configurePackagePaths(): Promise<void> {
|
||||||
//Python source path up to bundle version
|
//Python source path up to bundle version
|
||||||
let pythonSourcePath = this._usingExistingPython
|
let pythonSourcePath = this._usingExistingPython
|
||||||
? this._pythonInstallationPath
|
? this._pythonInstallationPath
|
||||||
: path.join(this._pythonInstallationPath, constants.pythonBundleVersion);
|
: path.join(this._pythonInstallationPath, constants.pythonBundleVersion);
|
||||||
|
|
||||||
this._pythonPackageDir = path.join(pythonSourcePath, 'offlinePackages');
|
if (this._usingExistingPython) {
|
||||||
|
this._pythonPackageDir = path.join(utils.getUserHome(), 'offlinePackages');
|
||||||
|
} else {
|
||||||
|
this._pythonPackageDir = path.join(pythonSourcePath, 'offlinePackages');
|
||||||
|
}
|
||||||
|
|
||||||
// Update python paths and properties to reference user's local python.
|
// Update python paths and properties to reference user's local python.
|
||||||
let pythonBinPathSuffix = process.platform === constants.winPlatform ? '' : 'bin';
|
let pythonBinPathSuffix = process.platform === constants.winPlatform ? '' : 'bin';
|
||||||
@@ -246,6 +256,13 @@ export class JupyterServerInstallation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._usingExistingPython) {
|
||||||
|
let pythonUserDir = await this.getPythonUserDir(this._pythonExecutable);
|
||||||
|
if (pythonUserDir) {
|
||||||
|
this.pythonEnvVarPath = pythonUserDir + delimiter + this.pythonEnvVarPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Delete existing Python variables in ADS to prevent conflict with other installs
|
// Delete existing Python variables in ADS to prevent conflict with other installs
|
||||||
delete process.env['PYTHONPATH'];
|
delete process.env['PYTHONPATH'];
|
||||||
delete process.env['PYTHONSTARTUP'];
|
delete process.env['PYTHONSTARTUP'];
|
||||||
@@ -261,19 +278,7 @@ export class JupyterServerInstallation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private isPythonRunning(pythonInstallPath: string): Promise<boolean> {
|
private isPythonRunning(pythonInstallPath: string): Promise<boolean> {
|
||||||
let pythonExePath = JupyterServerInstallation.getPythonExePath(pythonInstallPath, this._usingExistingPython);
|
return Promise.resolve(false);
|
||||||
return new Promise<boolean>(resolve => {
|
|
||||||
fs.open(pythonExePath, 'r+', (err, fd) => {
|
|
||||||
if (!err) {
|
|
||||||
fs.close(fd, err => {
|
|
||||||
this.apiWrapper.showErrorMessage(utils.getErrorMessage(err));
|
|
||||||
});
|
|
||||||
resolve(false);
|
|
||||||
} else {
|
|
||||||
resolve(err.code === 'EBUSY' || err.code === 'EPERM');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -298,7 +303,7 @@ export class JupyterServerInstallation {
|
|||||||
this._pythonInstallationPath = installSettings.installPath;
|
this._pythonInstallationPath = installSettings.installPath;
|
||||||
this._usingExistingPython = installSettings.existingPython;
|
this._usingExistingPython = installSettings.existingPython;
|
||||||
}
|
}
|
||||||
this.configurePackagePaths();
|
await this.configurePackagePaths();
|
||||||
|
|
||||||
let updateConfig = async () => {
|
let updateConfig = async () => {
|
||||||
let notebookConfig = this.apiWrapper.getConfiguration(constants.notebookConfigKey);
|
let notebookConfig = this.apiWrapper.getConfiguration(constants.notebookConfigKey);
|
||||||
@@ -359,7 +364,7 @@ export class JupyterServerInstallation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public installPipPackage(packageName: string, version: string): Promise<void> {
|
public installPipPackage(packageName: string, version: string): Promise<void> {
|
||||||
let cmd = `"${this.pythonExecutable}" -m pip install ${packageName}==${version}`;
|
let cmd = `"${this.pythonExecutable}" -m pip install --user ${packageName}==${version}`;
|
||||||
return this.executeStreamedCommand(cmd);
|
return this.executeStreamedCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,7 +407,7 @@ export class JupyterServerInstallation {
|
|||||||
let installJupyterCommand: string;
|
let installJupyterCommand: string;
|
||||||
if (process.platform === constants.winPlatform) {
|
if (process.platform === constants.winPlatform) {
|
||||||
let requirements = path.join(this._pythonPackageDir, 'requirements.txt');
|
let requirements = path.join(this._pythonPackageDir, 'requirements.txt');
|
||||||
installJupyterCommand = `"${this._pythonExecutable}" -m pip install --no-index -r "${requirements}" --find-links "${this._pythonPackageDir}" --no-warn-script-location`;
|
installJupyterCommand = `"${this._pythonExecutable}" -m pip install --user --no-index -r "${requirements}" --find-links "${this._pythonPackageDir}" --no-warn-script-location`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (installJupyterCommand) {
|
if (installJupyterCommand) {
|
||||||
@@ -420,9 +425,9 @@ export class JupyterServerInstallation {
|
|||||||
if (process.platform === constants.winPlatform || this._usingExistingPython) {
|
if (process.platform === constants.winPlatform || this._usingExistingPython) {
|
||||||
let sparkWheel = path.join(this._pythonPackageDir, `sparkmagic-${constants.sparkMagicVersion}-py3-none-any.whl`);
|
let sparkWheel = path.join(this._pythonPackageDir, `sparkmagic-${constants.sparkMagicVersion}-py3-none-any.whl`);
|
||||||
if (doOnlineInstall) {
|
if (doOnlineInstall) {
|
||||||
installSparkMagic = `"${this._pythonExecutable}" -m pip install "${sparkWheel}" --no-warn-script-location`;
|
installSparkMagic = `"${this._pythonExecutable}" -m pip install --user "${sparkWheel}" --no-warn-script-location`;
|
||||||
} else {
|
} else {
|
||||||
installSparkMagic = `"${this._pythonExecutable}" -m pip install --no-index "${sparkWheel}" --find-links "${this._pythonPackageDir}" --no-warn-script-location`;
|
installSparkMagic = `"${this._pythonExecutable}" -m pip install --user --no-index "${sparkWheel}" --find-links "${this._pythonPackageDir}" --no-warn-script-location`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,10 +442,10 @@ export class JupyterServerInstallation {
|
|||||||
this.outputChannel.show(true);
|
this.outputChannel.show(true);
|
||||||
this.outputChannel.appendLine(localize('msgInstallStart', "Installing required packages to run Notebooks..."));
|
this.outputChannel.appendLine(localize('msgInstallStart', "Installing required packages to run Notebooks..."));
|
||||||
|
|
||||||
let installCommand = `"${this._pythonExecutable}" -m pip install jupyter==1.0.0 pandas==0.24.2`;
|
let installCommand = `"${this._pythonExecutable}" -m pip install --user jupyter==1.0.0 pandas==0.24.2`;
|
||||||
await this.executeStreamedCommand(installCommand);
|
await this.executeStreamedCommand(installCommand);
|
||||||
|
|
||||||
installCommand = `"${this._pythonExecutable}" -m pip install prose-codeaccelerator==1.3.0 --extra-index-url https://prose-python-packages.azurewebsites.net`;
|
installCommand = `"${this._pythonExecutable}" -m pip install --user prose-codeaccelerator==1.3.0 --extra-index-url https://prose-python-packages.azurewebsites.net`;
|
||||||
await this.executeStreamedCommand(installCommand);
|
await this.executeStreamedCommand(installCommand);
|
||||||
|
|
||||||
this.outputChannel.appendLine(localize('msgJupyterInstallDone', "... Jupyter installation complete."));
|
this.outputChannel.appendLine(localize('msgJupyterInstallDone', "... Jupyter installation complete."));
|
||||||
@@ -456,7 +461,7 @@ export class JupyterServerInstallation {
|
|||||||
}
|
}
|
||||||
await this.executeStreamedCommand(installCommand);
|
await this.executeStreamedCommand(installCommand);
|
||||||
|
|
||||||
installCommand = `"${this._pythonExecutable}" -m pip install prose-codeaccelerator==1.3.0 --extra-index-url https://prose-python-packages.azurewebsites.net`;
|
installCommand = `"${this._pythonExecutable}" -m pip install --user prose-codeaccelerator==1.3.0 --extra-index-url https://prose-python-packages.azurewebsites.net`;
|
||||||
await this.executeStreamedCommand(installCommand);
|
await this.executeStreamedCommand(installCommand);
|
||||||
|
|
||||||
this.outputChannel.appendLine(localize('msgJupyterInstallDone', "... Jupyter installation complete."));
|
this.outputChannel.appendLine(localize('msgJupyterInstallDone', "... Jupyter installation complete."));
|
||||||
@@ -565,6 +570,30 @@ export class JupyterServerInstallation {
|
|||||||
useExistingInstall ? '' : constants.pythonBundleVersion,
|
useExistingInstall ? '' : constants.pythonBundleVersion,
|
||||||
process.platform === constants.winPlatform ? 'python.exe' : 'bin/python3');
|
process.platform === constants.winPlatform ? 'python.exe' : 'bin/python3');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getPythonUserDir(pythonExecutable: string): Promise<string> {
|
||||||
|
let sitePath: string;
|
||||||
|
if (process.platform === constants.winPlatform) {
|
||||||
|
sitePath = 'USER_SITE';
|
||||||
|
} else {
|
||||||
|
sitePath = 'USER_BASE';
|
||||||
|
}
|
||||||
|
let cmd = `"${pythonExecutable}" -c "import site;print(site.${sitePath})"`;
|
||||||
|
|
||||||
|
let packagesDir = await utils.executeBufferedCommand(cmd, {});
|
||||||
|
if (packagesDir && packagesDir.length > 0) {
|
||||||
|
packagesDir = packagesDir.trim();
|
||||||
|
if (process.platform === constants.winPlatform) {
|
||||||
|
packagesDir = path.resolve(path.join(packagesDir, '..', 'Scripts'));
|
||||||
|
} else {
|
||||||
|
packagesDir = path.join(packagesDir, 'bin');
|
||||||
|
}
|
||||||
|
|
||||||
|
return packagesDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PythonPkgDetails {
|
export interface PythonPkgDetails {
|
||||||
|
|||||||
@@ -118,7 +118,18 @@ export class LocalJupyterServerManager implements nb.ServerManager, vscode.Dispo
|
|||||||
// /path2/nb3.ipynb
|
// /path2/nb3.ipynb
|
||||||
// ... will result in 2 notebook servers being started, one for /path1/ and one for /path2/
|
// ... will result in 2 notebook servers being started, one for /path1/ and one for /path2/
|
||||||
let notebookDir = this.apiWrapper.getWorkspacePathFromUri(vscode.Uri.file(this.documentPath));
|
let notebookDir = this.apiWrapper.getWorkspacePathFromUri(vscode.Uri.file(this.documentPath));
|
||||||
notebookDir = notebookDir || path.dirname(this.documentPath);
|
if (!notebookDir) {
|
||||||
|
let docDir = path.dirname(this.documentPath);
|
||||||
|
if (docDir === '.') {
|
||||||
|
// If the user is using a system version of python, then
|
||||||
|
// '.' will try to create a notebook in a system directory.
|
||||||
|
// Since this will fail due to permissions, use the user's
|
||||||
|
// home folder instead.
|
||||||
|
notebookDir = utils.getUserHome();
|
||||||
|
} else {
|
||||||
|
notebookDir = docDir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO handle notification of start/stop status
|
// TODO handle notification of start/stop status
|
||||||
// notebookContext.updateLoadingMessage(localizedConstants.msgJupyterStarting);
|
// notebookContext.updateLoadingMessage(localizedConstants.msgJupyterStarting);
|
||||||
|
|||||||
Reference in New Issue
Block a user