Thu, 10 Jan 2019 14:18:48 +0100
Updated copyright for 2019.
# -*- coding: utf-8 -*- # Copyright (c) 2014 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing a dialog to enter the parameters for the virtual environment. """ from __future__ import unicode_literals try: str = unicode except NameError: pass import os import sys import re from PyQt5.QtCore import pyqtSlot, QProcess, QTimer from PyQt5.QtWidgets import QDialog, QDialogButtonBox from E5Gui.E5PathPicker import E5PathPickerModes from .Ui_VirtualenvConfigurationDialog import Ui_VirtualenvConfigurationDialog import Preferences import Utilities class VirtualenvConfigurationDialog(QDialog, Ui_VirtualenvConfigurationDialog): """ Class implementing a dialog to enter the parameters for the virtual environment. """ def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(VirtualenvConfigurationDialog, self).__init__(parent) self.setupUi(self) self.targetDirectoryPicker.setMode(E5PathPickerModes.DirectoryMode) self.targetDirectoryPicker.setWindowTitle( self.tr("Virtualenv Target Directory")) self.targetDirectoryPicker.setDefaultDirectory(Utilities.getHomeDir()) self.extraSearchPathPicker.setMode(E5PathPickerModes.DirectoryMode) self.extraSearchPathPicker.setWindowTitle( self.tr("Extra Search Path for setuptools/pip")) self.extraSearchPathPicker.setDefaultDirectory(Utilities.getHomeDir()) self.pythonExecPicker.setMode(E5PathPickerModes.OpenFileMode) self.pythonExecPicker.setWindowTitle( self.tr("Python Interpreter")) self.pythonExecPicker.setDefaultDirectory( sys.executable.replace("w.exe", ".exe")) self.__versionRe = re.compile(r""".*?(\d+\.\d+\.\d+).*""") self.__virtualenvFound = False self.__pyvenvFound = False self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) self.__mandatoryStyleSheet = "QLineEdit {border: 2px solid;}" self.targetDirectoryPicker.setStyleSheet(self.__mandatoryStyleSheet) self.nameEdit.setStyleSheet(self.__mandatoryStyleSheet) self.__setVirtualenvVersion() self.__setPyvenvVersion() if self.__virtualenvFound: self.virtualenvButton.setChecked(True) elif self.__pyvenvFound: self.pyvenvButton.setChecked(True) msh = self.minimumSizeHint() self.resize(max(self.width(), msh.width()), msh.height()) def __updateOK(self): """ Private method to update the enabled status of the OK button. """ self.buttonBox.button(QDialogButtonBox.Ok).setEnabled( (self.__virtualenvFound or self.__pyvenvFound) and bool(self.targetDirectoryPicker.text()) and bool(self.nameEdit.text()) ) def __updateUi(self): """ Private method to update the UI depending on the selected virtual environment creator (virtualenv or pyvenv). """ enable = self.virtualenvButton.isChecked() self.extraSearchPathLabel.setEnabled(enable) self.extraSearchPathPicker.setEnabled(enable) self.promptPrefixLabel.setEnabled(enable) self.promptPrefixEdit.setEnabled(enable) self.verbosityLabel.setEnabled(enable) self.verbositySpinBox.setEnabled(enable) self.versionLabel.setEnabled(enable) self.versionComboBox.setEnabled(enable) self.unzipCheckBox.setEnabled(enable) self.noSetuptoolsCheckBox.setEnabled(enable) self.symlinkCheckBox.setEnabled(not enable) self.upgradeCheckBox.setEnabled(not enable) @pyqtSlot(str) def on_targetDirectoryPicker_textChanged(self, txt): """ Private slot handling a change of the target directory. @param txt target directory @type str """ self.__updateOK() @pyqtSlot(str) def on_pythonExecPicker_textChanged(self, txt): """ Private slot to react to a change of the Python executable. @param txt contents of the picker's line edit @type str """ self.__setVirtualenvVersion() self.__setPyvenvVersion() self.__updateOK() @pyqtSlot(bool) def on_virtualenvButton_toggled(self, checked): """ Private slot to react to the selection of 'virtualenv'. @param checked state of the checkbox @type bool """ self.__updateUi() @pyqtSlot(bool) def on_pyvenvButton_toggled(self, checked): """ Private slot to react to the selection of 'pyvenv'. @param checked state of the checkbox @type bool """ self.__updateUi() def __setVirtualenvVersion(self): """ Private method to determine the virtualenv version and set the respective label. """ calls = [ (sys.executable.replace("w.exe", ".exe"), ["-m", "virtualenv", "--version"]), ("virtualenv", ["--version"]), ] if self.pythonExecPicker.text(): calls.append((self.pythonExecPicker.text(), ["-m", "virtualenv", "--version"])) proc = QProcess() for prog, args in calls: proc.start(prog, args) if not proc.waitForStarted(5000): # try next entry continue if not proc.waitForFinished(5000): # process hangs, kill it QTimer.singleShot(2000, proc.kill) proc.waitForFinished(3000) version = self.tr('<virtualenv did not finish within 5s.>') self.__virtualenvFound = False break if proc.exitCode() != 0: # returned with error code, try next continue output = str(proc.readAllStandardOutput(), Preferences.getSystem("IOEncoding"), 'replace').strip() match = re.match(self.__versionRe, output) if match: self.__virtualenvFound = True version = match.group(1) break else: self.__virtualenvFound = False version = self.tr('<No suitable virtualenv found.>') self.virtualenvButton.setText(self.tr( "virtualenv Version: {0}".format(version))) self.virtualenvButton.setEnabled(self.__virtualenvFound) if not self.__virtualenvFound: self.virtualenvButton.setChecked(False) def __setPyvenvVersion(self): """ Private method to determine the pyvenv version and set the respective label. """ calls = [] if self.pythonExecPicker.text(): calls.append((self.pythonExecPicker.text(), ["-m", "venv"])) calls.extend([ (sys.executable.replace("w.exe", ".exe"), ["-m", "venv"]), ("python3", ["-m", "venv"]), ("python", ["-m", "venv"]), ]) proc = QProcess() for prog, args in calls: proc.start(prog, args) if not proc.waitForStarted(5000): # try next entry continue if not proc.waitForFinished(5000): # process hangs, kill it QTimer.singleShot(2000, proc.kill) proc.waitForFinished(3000) version = self.tr('<pyvenv did not finish within 5s.>') self.__pyvenvFound = False break if proc.exitCode() not in [0, 2]: # returned with error code, try next continue proc.start(prog, ["--version"]) proc.waitForFinished(5000) output = str(proc.readAllStandardOutput(), Preferences.getSystem("IOEncoding"), 'replace').strip() match = re.match(self.__versionRe, output) if match: self.__pyvenvFound = True version = match.group(1) break else: self.__pyvenvFound = False version = self.tr('<No suitable pyvenv found.>') self.pyvenvButton.setText(self.tr( "pyvenv Version: {0}".format(version))) self.pyvenvButton.setEnabled(self.__pyvenvFound) if not self.__pyvenvFound: self.pyvenvButton.setChecked(False) def __generateTargetDir(self): """ Private method to generate a valid target directory path. @return target directory path @rtype str """ targetDirectory = Utilities.toNativeSeparators( self.targetDirectoryPicker.text()) if not os.path.isabs(targetDirectory): targetDirectory = os.path.join(os.path.expanduser("~"), targetDirectory) return targetDirectory def __generateArguments(self): """ Private method to generate the process arguments. @return process arguments @rtype list of str """ args = [] if self.virtualenvButton.isChecked(): if self.extraSearchPathPicker.text(): args.append("--extra-search-dir={0}".format( Utilities.toNativeSeparators( self.extraSearchPathPicker.text()))) if self.promptPrefixEdit.text(): args.append("--prompt={0}".format( self.promptPrefixEdit.text().replace(" ", "_"))) if self.pythonExecPicker.text(): args.append("--python={0}".format( Utilities.toNativeSeparators( self.pythonExecPicker.text()))) elif self.versionComboBox.currentText(): args.append("--python=python{0}".format( self.versionComboBox.currentText())) if self.verbositySpinBox.value() == 1: args.append("--verbose") elif self.verbositySpinBox.value() == -1: args.append("--quiet") if self.clearCheckBox.isChecked(): args.append("--clear") if self.systemCheckBox.isChecked(): args.append("--system-site-packages") if self.unzipCheckBox.isChecked(): args.append("--unzip-setuptools") if self.noSetuptoolsCheckBox.isChecked(): args.append("--no-setuptools") if self.noPipCcheckBox.isChecked(): args.append("--no-pip") if self.copyCheckBox.isChecked(): args.append("--always-copy") elif self.pyvenvButton.isChecked(): if self.clearCheckBox.isChecked(): args.append("--clear") if self.systemCheckBox.isChecked(): args.append("--system-site-packages") if self.noPipCcheckBox.isChecked(): args.append("--without-pip") if self.copyCheckBox.isChecked(): args.append("--copies") if self.symlinkCheckBox.isChecked(): args.append("--symlinks") if self.upgradeCheckBox.isChecked(): args.append("--upgrade") targetDirectory = self.__generateTargetDir() args.append(targetDirectory) return args def getData(self): """ Public method to retrieve the dialog data. @return tuple containing a flag indicating the pyvenv selection, the process arguments, a name for the virtual environment, a flag indicating to open the target directory after creation, a flag indicating to write a log file, a flag indicating to write a script, the name of the target directory and the name of the Python interpreter to use @rtype tuple of (bool, list of str, str, bool, bool, bool, str, str) """ args = self.__generateArguments() targetDirectory = self.__generateTargetDir() return ( self.pyvenvButton.isChecked(), args, self.nameEdit.text(), self.openCheckBox.isChecked(), self.logCheckBox.isChecked(), self.scriptCheckBox.isChecked(), targetDirectory, Utilities.toNativeSeparators(self.pythonExecPicker.text()), )