diff -r 0d7392e49c48 -r 2c26b4fe25db src/eric7/VirtualEnv/VirtualenvManager.py --- a/src/eric7/VirtualEnv/VirtualenvManager.py Tue Sep 05 14:17:29 2023 +0200 +++ b/src/eric7/VirtualEnv/VirtualenvManager.py Wed Sep 06 10:28:50 2023 +0200 @@ -22,6 +22,8 @@ from eric7.SystemUtilities import FileSystemUtilities, PythonUtilities from eric7.UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog +from .VirtualenvMeta import VirtualenvMetaData + class VirtualenvManager(QObject): """ @@ -85,27 +87,17 @@ # setting # description a description of the environment # - envsToDelete = [] for venvName in environments: environment = environments[venvName] + environment["name"] = 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"] = "" - if "description" not in environment: - environment["description"] = "" - self.__virtualEnvironments[venvName] = environment - - # now remove unsupported environments - for venvName in envsToDelete: - del environments[venvName] + self.__virtualEnvironments[venvName] = VirtualenvMetaData.from_dict( + environment + ) # check, if the interpreter used to run eric is in the environments defaultPy = PythonUtilities.getPythonExecutable() @@ -120,16 +112,13 @@ break if not found: # add an environment entry for the default interpreter - self.__virtualEnvironments[VirtualenvManager.DefaultKey] = { - "path": "", - "interpreter": defaultPy, - "variant": 3, - "is_global": True, - "is_conda": False, - "is_remote": False, - "exec_path": "", - "description": "", - } + self.__virtualEnvironments[ + VirtualenvManager.DefaultKey + ] = VirtualenvMetaData( + name=VirtualenvManager.DefaultKey, + interpreter=defaultPy, + is_globa=True, + ) self.__saveSettings() @@ -142,7 +131,10 @@ ) Preferences.getSettings().setValue( - "PyVenv/VirtualEnvironments", json.dumps(self.__virtualEnvironments) + "PyVenv/VirtualEnvironments", + json.dumps( + {env.name: env.as_dict() for env in self.__virtualEnvironments.values()} + ), ) Preferences.syncPreferences() @@ -162,9 +154,9 @@ having an interpreter matching sys.executable (i.e. the one used to execute eric with) - @return tuple containing the environment name and a dictionary - containing a copy of the default virtual environment - @rtype tuple of (str, dict) + @return tuple containing the environment name and a copy of the metadata + of the default virtual environment + @rtype tuple of (str, VirtualenvMetaData) """ if VirtualenvManager.DefaultKey in self.__virtualEnvironments: return ( @@ -181,14 +173,14 @@ @param interpreter path of the interpreter @type str - @return tuple containing the environment name and a dictionary - containing a copy of the default virtual environment - @rtype tuple of (str, dict) + @return tuple containing the environment name and a copy of the metadata + of the virtual environment the interpreter belongs to + @rtype tuple of (str, VirtualenvMetaData) """ py = FileSystemUtilities.normcaseabspath(interpreter.replace("w.exe", ".exe")) for venvName in self.__virtualEnvironments: if py == FileSystemUtilities.normcaseabspath( - self.__virtualEnvironments[venvName]["interpreter"] + self.__virtualEnvironments[venvName].interpreter ): return (venvName, copy.copy(self.__virtualEnvironments[venvName])) @@ -223,10 +215,12 @@ ) if ok and "--dry-run" not in resultDict["arguments"]: self.addVirtualEnv( - resultDict["logicalName"], - prefix, - venvInterpreter=interpreter, - isConda=True, + VirtualenvMetaData( + name=resultDict["logicalName"], + path=prefix, + interpreter=interpreter, + is_conda=True, + ) ) else: # now do the call @@ -262,155 +256,78 @@ dia.start(args) dia.exec() - def addVirtualEnv( - self, - venvName, - venvDirectory, - venvInterpreter="", - isGlobal=False, - isConda=False, - isRemote=False, - execPath="", - description="", - ): + def addVirtualEnv(self, metadata): """ Public method to add a virtual environment. - @param venvName logical name for the virtual environment - @type str - @param venvDirectory directory of the virtual environment - @type str - @param venvInterpreter interpreter of the virtual environment - @type str - @param isGlobal flag indicating a global environment - @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 - @param description descriptive text for the environment - @type str + @param metadata object containing the metadata of the virtual environment + @type VirtualenvMetaData """ from .VirtualenvInterpreterSelectionDialog import ( VirtualenvInterpreterSelectionDialog, ) from .VirtualenvNameDialog import VirtualenvNameDialog - if venvName in self.__virtualEnvironments: + if metadata.name in self.__virtualEnvironments: ok = EricMessageBox.yesNo( None, self.tr("Add Virtual Environment"), self.tr( """A virtual environment named <b>{0}</b> exists""" """ already. Shall it be replaced?""" - ).format(venvName), + ).format(metadata.name), icon=EricMessageBox.Warning, ) if not ok: dlg = VirtualenvNameDialog( - list(self.__virtualEnvironments.keys()), venvName + list(self.__virtualEnvironments.keys()), metadata.name ) if dlg.exec() != QDialog.DialogCode.Accepted: return - venvName = dlg.getName() - - if not venvInterpreter: - dlg = VirtualenvInterpreterSelectionDialog(venvName, venvDirectory) - if dlg.exec() == QDialog.DialogCode.Accepted: - venvInterpreter = dlg.getData() + metadata.name = dlg.getName() - if venvInterpreter: - self.__virtualEnvironments[venvName] = { - "path": venvDirectory, - "interpreter": venvInterpreter, - "variant": 3, # always 3 - "is_global": isGlobal, - "is_conda": isConda, - "is_remote": isRemote, - "exec_path": execPath, - "description": description, - } + if not metadata.interpreter: + dlg = VirtualenvInterpreterSelectionDialog(metadata.name, metadata.path) + if dlg.exec() == QDialog.DialogCode.Accepted: + metadata.interpreter = dlg.getData() + if metadata.interpreter: + self.__virtualEnvironments[metadata.name] = metadata self.__saveSettings() self.virtualEnvironmentAdded.emit() self.virtualEnvironmentsListChanged.emit() - def setVirtualEnv( - self, - venvName, - venvDirectory, - venvInterpreter, - isGlobal, - isConda, - isRemote, - execPath, - description, - ): + def setVirtualEnv(self, metadata): """ Public method to change a virtual environment. - @param venvName logical name of the virtual environment - @type str - @param venvDirectory directory of the virtual environment - @type str - @param venvInterpreter interpreter of the virtual environment - @type str - @param isGlobal flag indicating a global environment - @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 - @param description descriptive text for the environment - @type str + @param metadata object containing the metadata of the virtual environment + @type VirtualenvMetaData """ - if venvName not in self.__virtualEnvironments: + if metadata.name not in self.__virtualEnvironments: EricMessageBox.yesNo( None, self.tr("Change Virtual Environment"), self.tr( """A virtual environment named <b>{0}</b> does not""" """ exist. Aborting!""" - ).format(venvName), + ).format(metadata.name), icon=EricMessageBox.Warning, ) return - self.__virtualEnvironments[venvName] = { - "path": venvDirectory, - "interpreter": venvInterpreter, - "variant": 3, # always 3 - "is_global": isGlobal, - "is_conda": isConda, - "is_remote": isRemote, - "exec_path": execPath, - "description": description, - } - + self.__virtualEnvironments[metadata.name] = metadata self.__saveSettings() - self.virtualEnvironmentChanged.emit(venvName) + self.virtualEnvironmentChanged.emit(metadata.name) self.virtualEnvironmentsListChanged.emit() def renameVirtualEnv( self, oldVenvName, - venvName, - venvDirectory, - venvInterpreter, - isGlobal, - isConda, - isRemote, - execPath, - description, + metadata, ): """ Public method to substitute a virtual environment entry with a new @@ -418,23 +335,8 @@ @param oldVenvName old name of the virtual environment @type str - @param venvName logical name for the virtual environment - @type str - @param venvDirectory directory of the virtual environment - @type str - @param venvInterpreter interpreter of the virtual environment - @type str - @param isGlobal flag indicating a global environment - @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 - @param description descriptive text for the environment - @type str + @param metadata object containing the metadata of the virtual environment + @type VirtualenvMetaData """ if oldVenvName not in self.__virtualEnvironments: EricMessageBox.yesNo( @@ -449,16 +351,7 @@ return del self.__virtualEnvironments[oldVenvName] - self.addVirtualEnv( - venvName, - venvDirectory, - venvInterpreter=venvInterpreter, - isGlobal=isGlobal, - isConda=isConda, - isRemote=isRemote, - execPath=execPath, - description=description, - ) + self.addVirtualEnv(metadata) def deleteVirtualEnvs(self, venvNames): """ @@ -470,11 +363,11 @@ venvMessages = [] for venvName in venvNames: if venvName in self.__virtualEnvironments and bool( - self.__virtualEnvironments[venvName]["path"] + self.__virtualEnvironments[venvName].path ): venvMessages.append( self.tr("{0} - {1}").format( - venvName, self.__virtualEnvironments[venvName]["path"] + venvName, self.__virtualEnvironments[venvName].path ) ) if venvMessages: @@ -492,13 +385,13 @@ if self.__isEnvironmentDeleteable(venvName): if self.isCondaEnvironment(venvName): conda = ericApp().getObject("Conda") - path = self.__virtualEnvironments[venvName]["path"] + path = self.__virtualEnvironments[venvName].path res = conda.removeCondaEnvironment(prefix=path) if res: del self.__virtualEnvironments[venvName] else: shutil.rmtree( - self.__virtualEnvironments[venvName]["path"], True + self.__virtualEnvironments[venvName].path, True ) del self.__virtualEnvironments[venvName] @@ -520,10 +413,10 @@ ok = False if venvName in self.__virtualEnvironments: 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) + 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) return ok @@ -539,7 +432,7 @@ if venvName in self.__virtualEnvironments: venvMessages.append( self.tr("{0} - {1}").format( - venvName, self.__virtualEnvironments[venvName]["path"] + venvName, self.__virtualEnvironments[venvName].path ) ) if venvMessages: @@ -564,14 +457,12 @@ def getEnvironmentEntries(self): """ - Public method to get a dictionary containing the defined virtual - environment entries. + Public method to get a list of the defined virtual environment entries. - @return dictionary containing a copy of the defined virtual - environments - @rtype dict + @return list containing a copy of the defined virtual environments + @rtype list """ - return copy.deepcopy(self.__virtualEnvironments) + return [copy.copy(env) for env in self.__virtualEnvironments.values()] @pyqtSlot() def showVirtualenvManagerDialog(self, modal=False): @@ -611,7 +502,7 @@ @rtype str """ if venvName in self.__virtualEnvironments: - return self.__virtualEnvironments[venvName]["interpreter"].replace( + return self.__virtualEnvironments[venvName].interpreter.replace( "w.exe", ".exe" ) elif venvName == VirtualenvManager.SystemKey: @@ -629,7 +520,7 @@ @type str """ if venvName in self.__virtualEnvironments: - self.__virtualEnvironments[venvName]["interpreter"] = venvInterpreter + self.__virtualEnvironments[venvName].interpreter = venvInterpreter self.__saveSettings() self.virtualEnvironmentChanged.emit(venvName) @@ -645,7 +536,7 @@ @rtype str """ if venvName in self.__virtualEnvironments: - return self.__virtualEnvironments[venvName]["path"] + return self.__virtualEnvironments[venvName].path else: return "" @@ -683,7 +574,7 @@ @rtype bool """ if venvName in self.__virtualEnvironments: - return self.__virtualEnvironments[venvName]["is_global"] + return self.__virtualEnvironments[venvName].is_global else: return False @@ -698,7 +589,7 @@ @rtype bool """ if venvName in self.__virtualEnvironments: - return self.__virtualEnvironments[venvName]["is_conda"] + return self.__virtualEnvironments[venvName].is_conda else: return False @@ -713,7 +604,7 @@ @rtype bool """ if venvName in self.__virtualEnvironments: - return self.__virtualEnvironments[venvName]["is_remote"] + return self.__virtualEnvironments[venvName].is_remote else: return False @@ -727,7 +618,7 @@ @rtype str """ if venvName in self.__virtualEnvironments: - return self.__virtualEnvironments[venvName]["exec_path"] + return self.__virtualEnvironments[venvName].exec_path else: return ""