--- a/VirtualEnv/VirtualenvManager.py Sat Feb 16 10:27:50 2019 +0100 +++ b/VirtualEnv/VirtualenvManager.py Sat Mar 02 11:15:24 2019 +0100 @@ -15,21 +15,32 @@ import json import copy -from PyQt5.QtCore import pyqtSlot, QObject +from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject from PyQt5.QtWidgets import QDialog from E5Gui import E5MessageBox +from E5Gui.E5Application import e5App import Preferences -import Utilities class VirtualenvManager(QObject): """ Class implementing an object to manage Python virtual environments. + + @signal virtualEnvironmentAdded() emitted to indicate the addition of + a virtual environment + @signal virtualEnvironmentRemoved() emitted to indicate the removal and + deletion of a virtual environment + @signal virtualEnvironmentChanged(name) emitted to indicate a change of + a virtual environment """ DefaultKey = "<default>" + virtualEnvironmentAdded = pyqtSignal() + virtualEnvironmentRemoved = pyqtSignal() + virtualEnvironmentChanged = pyqtSignal(str) + def __init__(self, parent=None): """ Constructor @@ -61,17 +72,20 @@ # variant: Python variant (2 or 3) # is_global: a flag indicating a global environment # is_conda: a flag indicating an Anaconda environment + # is_remote: a flag indicating a remotely accessed environment # exec_path: a string to be prefixed to the PATH environment # setting # for venvName in environments: - interpreter = environments[venvName]["interpreter"] - if os.access(interpreter, os.X_OK): - environment = environments[venvName] + environment = environments[venvName] + if ("is_remote" in environment and environment["is_remote"]) or \ + os.access(environment["interpreter"], os.X_OK): if "is_global" not in environment: environment["is_global"] = environment["path"] == "" if "is_conda" not in environment: environment["is_conda"] = False + if "is_remote" not in environment: + environment["is_remote"] = False if "exec_path" not in environment: environment["exec_path"] = "" self.__virtualEnvironments[venvName] = environment @@ -92,6 +106,7 @@ "variant": sys.version_info[0], "is_global": True, "is_conda": False, + "is_remote": False, "exec_path": "", } @@ -105,6 +120,7 @@ "PyVenv/VirtualEnvironments", json.dumps(self.__virtualEnvironments) ) + Preferences.syncPreferences() def getDefaultEnvironment(self): """ @@ -147,21 +163,29 @@ dlg = VirtualenvConfigurationDialog() if dlg.exec_() == QDialog.Accepted: - (pyvenv, args, name, openTarget, createLog, createScript, - targetDir, interpreter) = dlg.getData() + resultDict = dlg.getData() - # now do the call - from .VirtualenvExecDialog import VirtualenvExecDialog - dia = VirtualenvExecDialog(pyvenv, targetDir, name, openTarget, - createLog, createScript, interpreter, - self) - dia.show() - dia.start(args) - dia.exec_() + if resultDict["envType"] == "conda": + # create the conda environment + conda = e5App().getObject("Conda") + ok, prefix, interpreter = conda.createCondaEnvironment( + resultDict["arguments"]) + if ok and "--dry-run" not in resultDict["arguments"]: + self.addVirtualEnv(resultDict["logicalName"], + prefix, + venvInterpreter=interpreter, + isConda=True) + else: + # now do the call + from .VirtualenvExecDialog import VirtualenvExecDialog + dia = VirtualenvExecDialog(resultDict, self) + dia.show() + dia.start(resultDict["arguments"]) + dia.exec_() def addVirtualEnv(self, venvName, venvDirectory, venvInterpreter="", venvVariant=3, isGlobal=False, isConda=False, - execPath=""): + isRemote=False, execPath=""): """ Public method to add a virtual environment. @@ -177,6 +201,8 @@ @type bool @param isConda flag indicating an Anaconda virtual environment @type bool + @param isRemote flag indicating a remotely accessed environment + @type bool @param execPath search path string to be prepended to the PATH environment variable @type str @@ -190,7 +216,14 @@ .format(venvName), icon=E5MessageBox.Warning) if not ok: - return + from .VirtualenvNameDialog import VirtualenvNameDialog + dlg = VirtualenvNameDialog( + list(self.__virtualEnvironments.keys()), + venvName) + if dlg.exec_() != QDialog.Accepted: + return + + venvName = dlg.getName() if not venvInterpreter: from .VirtualenvInterpreterSelectionDialog import \ @@ -198,9 +231,6 @@ dlg = VirtualenvInterpreterSelectionDialog(venvName, venvDirectory) if dlg.exec_() == QDialog.Accepted: venvInterpreter, venvVariant = dlg.getData() - if not Utilities.startswithPath(venvInterpreter, - venvDirectory): - isGlobal = True if venvInterpreter: self.__virtualEnvironments[venvName] = { @@ -209,16 +239,19 @@ "variant": venvVariant, "is_global": isGlobal, "is_conda": isConda, + "is_remote": isRemote, "exec_path": execPath, } self.__saveSettings() + self.virtualEnvironmentAdded.emit() if self.__virtualenvManagerDialog: self.__virtualenvManagerDialog.refresh() def setVirtualEnv(self, venvName, venvDirectory, venvInterpreter, - venvVariant, isGlobal, isConda, execPath): + venvVariant, isGlobal, isConda, isRemote, + execPath): """ Public method to change a virtual environment. @@ -234,6 +267,8 @@ @type bool @param isConda flag indicating an Anaconda virtual environment @type bool + @param isRemote flag indicating a remotely accessed environment + @type bool @param execPath search path string to be prepended to the PATH environment variable @type str @@ -254,17 +289,19 @@ "variant": venvVariant, "is_global": isGlobal, "is_conda": isConda, + "is_remote": isRemote, "exec_path": execPath, } self.__saveSettings() + self.virtualEnvironmentChanged.emit(venvName) if self.__virtualenvManagerDialog: self.__virtualenvManagerDialog.refresh() def renameVirtualEnv(self, oldVenvName, venvName, venvDirectory, venvInterpreter, venvVariant, isGlobal, isConda, - execPath): + isRemote, execPath): """ Public method to substitute a virtual environment entry with a new name. @@ -283,6 +320,8 @@ @type bool @param isConda flag indicating an Anaconda virtual environment @type bool + @param isRemote flag indicating a remotely accessed environment + @type bool @param execPath search path string to be prepended to the PATH environment variable @type str @@ -299,7 +338,8 @@ del self.__virtualEnvironments[oldVenvName] self.addVirtualEnv(venvName, venvDirectory, venvInterpreter, - venvVariant, isGlobal, isConda, execPath) + venvVariant, isGlobal, isConda, isRemote, + execPath) def deleteVirtualEnvs(self, venvNames): """ @@ -327,12 +367,21 @@ if dlg.exec_() == QDialog.Accepted: for venvName in venvNames: if self.__isEnvironmentDeleteable(venvName): - shutil.rmtree( - self.__virtualEnvironments[venvName]["path"], True) - del self.__virtualEnvironments[venvName] + if self.isCondaEnvironment(venvName): + conda = e5App().getObject("Conda") + path = self.__virtualEnvironments[venvName]["path"] + res = conda.removeCondaEnvironment(prefix=path) + if res: + del self.__virtualEnvironments[venvName] + else: + shutil.rmtree( + self.__virtualEnvironments[venvName]["path"], + True) + del self.__virtualEnvironments[venvName] self.__saveSettings() + self.virtualEnvironmentRemoved.emit() if self.__virtualenvManagerDialog: self.__virtualenvManagerDialog.refresh() @@ -351,6 +400,7 @@ ok = True ok &= bool(self.__virtualEnvironments[venvName]["path"]) ok &= not self.__virtualEnvironments[venvName]["is_global"] + ok &= not self.__virtualEnvironments[venvName]["is_remote"] ok &= os.access(self.__virtualEnvironments[venvName]["path"], os.W_OK) @@ -385,6 +435,7 @@ self.__saveSettings() + self.virtualEnvironmentRemoved.emit() if self.__virtualenvManagerDialog: self.__virtualenvManagerDialog.refresh() @@ -521,6 +572,21 @@ else: return False + def isRemoteEnvironment(self, venvName): + """ + Public method to test, if a given environment is a remotely accessed + environment. + + @param venvName logical name of the virtual environment + @type str + @return flag indicating a remotely accessed environment + @rtype bool + """ + if venvName in self.__virtualEnvironments: + return self.__virtualEnvironments[venvName]["is_remote"] + else: + return False + def getVirtualenvExecPath(self, venvName): """ Public method to get the search path prefix of a virtual environment.