diff -r 9ede19ddfa98 -r 390a45748883 PipInterface/Pip.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PipInterface/Pip.py Sun Feb 17 19:19:30 2019 +0100 @@ -0,0 +1,1144 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2015 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Package implementing the pip GUI logic. +""" + +from __future__ import unicode_literals +try: + str = unicode # __IGNORE_EXCEPTION__ +except NameError: + pass + +import os +import sys + +from PyQt5.QtCore import pyqtSlot, QObject, QProcess +from PyQt5.QtWidgets import QMenu, QInputDialog, QDialog + +from E5Gui import E5MessageBox +from E5Gui.E5Action import E5Action +from E5Gui.E5Application import e5App + +from .PipDialog import PipDialog +from . import DefaultIndexUrlXml + +import Preferences +import Globals + +import UI.PixmapCache + + +class Pip(QObject): + """ + Class implementing the pip GUI logic. + """ + def __init__(self, plugin, parent=None): + """ + Constructor + + @param plugin reference to the plugin object + @type PipInterfacePlugin + @param parent parent + @type QObject + """ + super(Pip, self).__init__(parent) + + self.__plugin = plugin + self.__ui = parent + + self.__virtualenvManager = e5App().getObject("VirtualEnvManager") + self.__project = e5App().getObject("Project") + + self.__menus = {} # dictionary with references to menus + + self.__plugin.currentEnvironmentChanged.connect( + self.__handleTearOffMenu) + + def initActions(self): + """ + Public method to define the actions. + """ + self.actions = [] + + self.selectEnvironmentAct = E5Action( + self.tr('Virtual Environment for pip'), + self.tr('&Virtual Environment for pip'), + 0, 0, + self, 'pip_select_environment') + self.selectEnvironmentAct.setStatusTip(self.tr( + 'Selects the virtual environment to be used for pip')) + self.selectEnvironmentAct.setWhatsThis(self.tr( + """<b>Virtual Environment for pip</b>""" + """<p>This selects the virtual environment to be used for pip.""" + """</p>""" + )) + self.selectEnvironmentAct.triggered.connect(self.__selectPipVirtualenv) + self.actions.append(self.selectEnvironmentAct) + + ############################################## + ## Actions for listing packages + ############################################## + + self.listPackagesAct = E5Action( + self.tr('List Installed Packages'), + self.tr('&List Installed Packages...'), + 0, 0, + self, 'pip_list_packages') + self.listPackagesAct.setStatusTip(self.tr( + 'List all installed packages with versions')) + self.listPackagesAct.setWhatsThis(self.tr( + """<b>List Installed Packages</b>""" + """<p>This lists all the installed packages together""" + """ with their versions.</p>""" + )) + self.listPackagesAct.triggered.connect(self.__listPackages) + self.actions.append(self.listPackagesAct) + + self.listUptodatePackagesAct = E5Action( + self.tr('List Up-to-date Packages'), + self.tr('List Up-to-&date Packages...'), + 0, 0, + self, 'pip_list_uptodate_packages') + self.listUptodatePackagesAct.setStatusTip(self.tr( + 'List all installed, up-to-date packages with versions')) + self.listUptodatePackagesAct.setWhatsThis(self.tr( + """<b>List Up-to-date Packages</b>""" + """<p>This lists all the installed, up-to-date packages together""" + """ with their versions.</p>""" + )) + self.listUptodatePackagesAct.triggered.connect( + self.__listUptodatePackages) + self.actions.append(self.listUptodatePackagesAct) + + self.listOutdatedPackagesAct = E5Action( + self.tr('List Outdated Packages'), + self.tr('List &Outdated Packages...'), + 0, 0, + self, 'pip_list_outdated_packages') + self.listOutdatedPackagesAct.setStatusTip(self.tr( + 'List all installed, outdated packages with versions')) + self.listOutdatedPackagesAct.setWhatsThis(self.tr( + """<b>List Up-to-date Packages</b>""" + """<p>This lists all the installed, outdated packages together""" + """ with their current and latest versions.</p>""" + )) + self.listOutdatedPackagesAct.triggered.connect( + self.__listOutdatedPackages) + self.actions.append(self.listOutdatedPackagesAct) + + ############################################## + ## Actions for installing packages + ############################################## + + self.installPackagesAct = E5Action( + self.tr('Install Packages'), + self.tr('&Install Packages'), + 0, 0, + self, 'pip_install_packages') + self.installPackagesAct.setStatusTip(self.tr( + 'Install packages according to user input')) + self.installPackagesAct.setWhatsThis(self.tr( + """<b>Install Packages</b>""" + """<p>This installs packages according to user input.</p>""" + )) + self.installPackagesAct.triggered.connect(self.__installPackages) + self.actions.append(self.installPackagesAct) + + self.installLocalPackageAct = E5Action( + self.tr('Install Local Package'), + self.tr('Install Local Package'), + 0, 0, + self, 'pip_install_local_package') + self.installLocalPackageAct.setStatusTip(self.tr( + 'Install a package from local storage')) + self.installLocalPackageAct.setWhatsThis(self.tr( + """<b>Install Local Package</b>""" + """<p>This installs a package available on local storage.</p>""" + )) + self.installLocalPackageAct.triggered.connect( + self.__installLocalPackage) + self.actions.append(self.installLocalPackageAct) + + self.installRequirementsAct = E5Action( + self.tr('Install Requirements'), + self.tr('Install Requirements'), + 0, 0, + self, 'pip_install_requirements') + self.installRequirementsAct.setStatusTip(self.tr( + 'Install packages according to a requirements file')) + self.installRequirementsAct.setWhatsThis(self.tr( + """<b>Install Requirements</b>""" + """<p>This installs packages according to a requirements""" + """ file.</p>""" + )) + self.installRequirementsAct.triggered.connect( + self.__installRequirements) + self.actions.append(self.installRequirementsAct) + + self.installPipAct = E5Action( + self.tr('Install Pip'), + self.tr('Install Pip'), + 0, 0, + self, 'pip_install_pip') + self.installPipAct.setStatusTip(self.tr( + 'Install the pip package itself')) + self.installPipAct.setWhatsThis(self.tr( + """<b>Install Pip</b>""" + """<p>This installs the pip package itself.</p>""" + )) + self.installPipAct.triggered.connect(self.__installPip) + self.actions.append(self.installPipAct) + + self.repairPipAct = E5Action( + self.tr('Repair Pip'), + self.tr('Repair Pip'), + 0, 0, + self, 'pip_repair_pip') + self.repairPipAct.setStatusTip(self.tr( + 'Repair the pip package')) + self.repairPipAct.setWhatsThis(self.tr( + """<b>Repair Pip</b>""" + """<p>This repairs the pip package by re-installing it.</p>""" + )) + self.repairPipAct.triggered.connect(self.__repairPip) + self.actions.append(self.repairPipAct) + + self.upgradePipAct = E5Action( + self.tr('Upgrade Pip'), + self.tr('Upgrade &Pip'), + 0, 0, + self, 'pip_upgrade_pip') + self.upgradePipAct.setStatusTip(self.tr( + 'Upgrade the pip package itself')) + self.upgradePipAct.setWhatsThis(self.tr( + """<b>Upgrade Pip</b>""" + """<p>This upgrades the pip package itself.</p>""" + )) + self.upgradePipAct.triggered.connect(self.upgradePip) + self.actions.append(self.upgradePipAct) + + self.upgradePackagesAct = E5Action( + self.tr('Upgrade Packages'), + self.tr('&Upgrade Packages'), + 0, 0, + self, 'pip_upgrade_packages') + self.upgradePackagesAct.setStatusTip(self.tr( + 'Upgrade packages according to user input')) + self.upgradePackagesAct.setWhatsThis(self.tr( + """<b>Upgrade Packages</b>""" + """<p>This upgrades packages according to user input.</p>""" + )) + self.upgradePackagesAct.triggered.connect(self.__upgradePackages) + self.actions.append(self.upgradePackagesAct) + + ############################################## + ## Actions for uninstalling packages + ############################################## + + self.uninstallPackagesAct = E5Action( + self.tr('Uninstall Packages'), + self.tr('Uninstall Packages'), + 0, 0, + self, 'pip_uninstall_packages') + self.uninstallPackagesAct.setStatusTip(self.tr( + 'Uninstall packages according to user input')) + self.uninstallPackagesAct.setWhatsThis(self.tr( + """<b>Uninstall Packages</b>""" + """<p>This uninstalls packages according to user input.</p>""" + )) + self.uninstallPackagesAct.triggered.connect(self.__uninstallPackages) + self.actions.append(self.uninstallPackagesAct) + + self.uninstallRequirementsAct = E5Action( + self.tr('Uninstall Requirements'), + self.tr('Uninstall Requirements'), + 0, 0, + self, 'pip_uninstall_requirements') + self.uninstallRequirementsAct.setStatusTip(self.tr( + 'Uninstall packages according to a requirements file')) + self.uninstallRequirementsAct.setWhatsThis(self.tr( + """<b>Uninstall Requirements</b>""" + """<p>This uninstalls packages according to a requirements""" + """ file.</p>""" + )) + self.uninstallRequirementsAct.triggered.connect( + self.__uninstallRequirements) + self.actions.append(self.uninstallRequirementsAct) + + ############################################## + ## Actions for generating requirements files + ############################################## + + self.generateRequirementsAct = E5Action( + self.tr('Generate Requirements'), + self.tr('&Generate Requirements...'), + 0, 0, + self, 'pip_generate_requirements') + self.generateRequirementsAct.setStatusTip(self.tr( + 'Generate the contents of a requirements file')) + self.generateRequirementsAct.setWhatsThis(self.tr( + """<b>Generate Requirements</b>""" + """<p>This generates the contents of a requirements file.</p>""" + )) + self.generateRequirementsAct.triggered.connect( + self.__generateRequirements) + self.actions.append(self.generateRequirementsAct) + + ############################################## + ## Actions for generating requirements files + ############################################## + + self.searchPyPIAct = E5Action( + self.tr('Search PyPI'), + self.tr('&Search PyPI...'), + 0, 0, + self, 'pip_search_pypi') + self.searchPyPIAct.setStatusTip(self.tr( + 'Open a dialog to search the Python Package Index')) + self.searchPyPIAct.setWhatsThis(self.tr( + """<b>Search PyPI</b>""" + """<p>This opens a dialog to search the Python Package""" + """ Index.</p>""" + )) + self.searchPyPIAct.triggered.connect(self.__searchPyPI) + self.actions.append(self.searchPyPIAct) + + ############################################## + ## Actions for editing configuration files + ############################################## + + self.editUserConfigAct = E5Action( + self.tr('Edit User Configuration'), + self.tr('Edit User Configuration...'), + 0, 0, + self, 'pip_edit_user_config') + self.editUserConfigAct.setStatusTip(self.tr( + 'Open the per user configuration file in an editor')) + self.editUserConfigAct.setWhatsThis(self.tr( + """<b>Edit User Configuration</b>""" + """<p>This opens the per user configuration file in an editor.""" + """</p>""" + )) + self.editUserConfigAct.triggered.connect(self.__editUserConfiguration) + self.actions.append(self.editUserConfigAct) + + self.editVirtualenvConfigAct = E5Action( + self.tr('Edit Current Virtualenv Configuration'), + self.tr('Edit Current Virtualenv Configuration...'), + 0, 0, + self, 'pip_edit_virtualenv_config') + self.editVirtualenvConfigAct.setStatusTip(self.tr( + 'Open the current virtualenv configuration file in an editor')) + self.editVirtualenvConfigAct.setWhatsThis(self.tr( + """<b>Edit Current Virtualenv Configuration</b>""" + """<p>This opens the current virtualenv configuration file in""" + """ an editor. </p>""" + )) + self.editVirtualenvConfigAct.triggered.connect( + self.__editVirtualenvConfiguration) + self.actions.append(self.editVirtualenvConfigAct) + + self.pipConfigAct = E5Action( + self.tr('Configure'), + self.tr('Configure...'), + 0, 0, self, 'pip_configure') + self.pipConfigAct.setStatusTip(self.tr( + 'Show the configuration dialog with the Python Package Management' + ' page selected' + )) + self.pipConfigAct.setWhatsThis(self.tr( + """<b>Configure</b>""" + """<p>Show the configuration dialog with the Python Package""" + """ Management page selected.</p>""" + )) + self.pipConfigAct.triggered.connect(self.__pipConfigure) + self.actions.append(self.pipConfigAct) + + def initMenu(self): + """ + Public slot to initialize the menu. + + @return the menu generated + @rtype QMenu + """ + self.__menus = {} # clear menus references + + menu = QMenu(self.tr('P&ython Package Management'), self.__ui) + menu.setTearOffEnabled(True) + menu.setIcon(UI.PixmapCache.getIcon("pypi.png")) + + menu.addAction(self.selectEnvironmentAct) + menu.addSeparator() + menu.addAction(self.listPackagesAct) + menu.addAction(self.listUptodatePackagesAct) + menu.addAction(self.listOutdatedPackagesAct) + menu.addSeparator() + menu.addAction(self.installPipAct) + menu.addSeparator() + menu.addAction(self.installPackagesAct) + menu.addAction(self.installLocalPackageAct) + menu.addAction(self.installRequirementsAct) + menu.addSeparator() + menu.addAction(self.upgradePipAct) + menu.addAction(self.upgradePackagesAct) + menu.addSeparator() + menu.addAction(self.uninstallPackagesAct) + menu.addAction(self.uninstallRequirementsAct) + menu.addSeparator() + menu.addAction(self.generateRequirementsAct) + menu.addSeparator() + menu.addAction(self.searchPyPIAct) + menu.addSeparator() + menu.addAction(self.repairPipAct) + menu.addSeparator() + menu.addAction(self.editUserConfigAct) + menu.addAction(self.editVirtualenvConfigAct) + menu.addSeparator() + menu.addAction(self.pipConfigAct) + + self.__menus["main"] = menu + + menu.aboutToShow.connect(self.__aboutToShowMenu) + + return menu + + def __aboutToShowMenu(self): + """ + Private slot to set the action enabled status. + """ + enable = bool(self.__plugin.getPreferences("CurrentEnvironment")) + for act in self.actions: + if act not in [self.selectEnvironmentAct, + self.installPipAct, + self.editUserConfigAct, + self.editVirtualenvConfigAct, + self.pipConfigAct]: + act.setEnabled(enable) + + def getMenu(self, name): + """ + Public method to get a reference to the requested menu. + + @param name name of the menu + @type str + @return reference to the menu or None, if no + menu with the given name exists + @rtype QMenu or None + """ + if name in self.__menus: + return self.__menus[name] + else: + return None + + def getMenuNames(self): + """ + Public method to get the names of all menus. + + @return menu names + @rtype list of str + """ + return list(self.__menus.keys()) + + def __handleTearOffMenu(self, venvName): + """ + Private slot to handle a change of the selected virtual environment. + + @param venvName logical name of the virtual environment + @type str + """ + if self.__menus["main"].isTearOffMenuVisible(): + # determine, if torn off menu needs to be refreshed + enabled = self.listPackagesAct.isEnabled() + if ((bool(venvName) and not enabled) or + (not bool(venvName) and enabled)): + self.__menus["main"].hideTearOffMenu() + + ########################################################################## + ## Methods below implement some utility functions + ########################################################################## + + def runProcess(self, args, interpreter): + """ + Public method to execute the current pip with the given arguments. + + The selected pip executable is called with the given arguments and + waited for its end. + + @param args list of command line arguments + @type list of str + @param interpreter path of the Python interpreter to be used + @type str + @return tuple containing a flag indicating success and the output + of the process + @rtype tuple of (bool, str) + """ + ioEncoding = Preferences.getSystem("IOEncoding") + + process = QProcess() + process.start(interpreter, args) + procStarted = process.waitForStarted() + if procStarted: + finished = process.waitForFinished(30000) + if finished: + if process.exitCode() == 0: + output = str(process.readAllStandardOutput(), ioEncoding, + 'replace') + return True, output + else: + return (False, + self.tr("python exited with an error ({0}).") + .format(process.exitCode())) + else: + process.terminate() + process.waitForFinished(2000) + process.kill() + process.waitForFinished(3000) + return False, self.tr("python did not finish within" + " 30 seconds.") + + return False, self.tr("python could not be started.") + + def __getUserConfig(self): + """ + Private method to get the name of the user configuration file. + + @return path of the user configuration file + @rtype str + """ + # Unix: ~/.config/pip/pip.conf + # OS X: ~/Library/Application Support/pip/pip.conf + # Windows: %APPDATA%\pip\pip.ini + # Environment: $PIP_CONFIG_FILE + + try: + return os.environ["PIP_CONFIG_FILE"] + except KeyError: + pass + + if Globals.isWindowsPlatform(): + config = os.path.join(os.environ["APPDATA"], "pip", "pip.ini") + elif Globals.isMacPlatform(): + config = os.path.expanduser( + "~/Library/Application Support/pip/pip.conf") + else: + config = os.path.expanduser("~/.config/pip/pip.conf") + + return config + + def __getVirtualenvConfig(self): + """ + Private method to get the name of the virtualenv configuration file. + + @return path of the virtualenv configuration file + @rtype str + """ + # Unix, OS X: $VIRTUAL_ENV/pip.conf + # Windows: %VIRTUAL_ENV%\pip.ini + + if Globals.isWindowsPlatform(): + pip = "pip.ini" + else: + pip = "pip.conf" + try: + venvDirectory = os.environ["VIRTUAL_ENV"] + except KeyError: + venvName = self.__plugin.getPreferences("CurrentEnvironment") + if not venvName: + self.__selectPipVirtualenv() + venvName = self.__plugin.getPreferences("CurrentEnvironment") + if self.__virtualenvManager.isGlobalEnvironment(venvName): + venvDirectory = self.__getUserConfig() + else: + venvDirectory = \ + self.__virtualenvManager.getVirtualenvDirectory(venvName) + + return os.path.join(venvDirectory, pip) + + def getDefaultEnvironmentString(self): + """ + Public method to get the string for the default environment. + + @return string for the default environment + @rtype str + """ + return self.tr("<standard>") + + def getProjectEnvironmentString(self): + """ + Public method to get the string for the project environment. + + @return string for the project environment + @rtype str + """ + if self.__project.isOpen(): + return self.tr("<project>") + else: + return "" + + def getVirtualenvInterpreter(self, venvName): + """ + Public method to get the interpreter for a virtual environment. + + @param venvName logical name for the virtual environment + @type str + @return interpreter path + @rtype str + """ + if venvName == self.getDefaultEnvironmentString(): + venvName = self.__plugin.getPreferences("CurrentEnvironment") + elif venvName == self.getProjectEnvironmentString(): + venvName = self.__project.getDebugProperty("VIRTUALENV") + if not venvName: + # fall back to standard if not defined + venvName = self.__plugin.getPreferences("CurrentEnvironment") + + interpreter = self.__virtualenvManager.getVirtualenvInterpreter( + venvName) + if not interpreter: + E5MessageBox.critical( + None, + self.tr("Interpreter for Virtual Environment"), + self.tr("""No interpreter configured for the selected""" + """ virtual environment.""")) + + return interpreter + + def getVirtualenvNames(self): + """ + Public method to get a sorted list of virtual environment names. + + @return sorted list of virtual environment names + @rtype list of str + """ + return sorted(self.__virtualenvManager.getVirtualenvNames()) + + ########################################################################## + ## Methods below implement the individual menu entries + ########################################################################## + + def __selectPipVirtualenv(self): + """ + Private method to select the virtual environment to be used. + """ + environments = self.getVirtualenvNames() + if environments: + currentEnvironment = self.__plugin.getPreferences( + "CurrentEnvironment") + try: + index = environments.index(currentEnvironment) + except ValueError: + index = 0 + environment, ok = QInputDialog.getItem( + None, + self.tr("Virtual Environment for pip"), + self.tr("Select the virtual environment to be used:"), + environments, index, False) + + if ok and environment: + self.__plugin.setPreferences("CurrentEnvironment", + environment) + else: + E5MessageBox.warning( + None, + self.tr("Virtual Environment for pip"), + self.tr("""No virtual environments have been configured yet.""" + """ Please use the Virtualenv Manager to do that.""")) + + def __listPackages(self): + """ + Private slot to list all installed packages. + """ + from .PipListDialog import PipListDialog + self.__listDialog = PipListDialog( + self, "list", self.__plugin.getPreferences("PipSearchIndex"), + self.tr("Installed Packages")) + self.__listDialog.show() + self.__listDialog.start() + + def __listUptodatePackages(self): + """ + Private slot to list all installed, up-to-date packages. + """ + from .PipListDialog import PipListDialog + self.__listUptodateDialog = PipListDialog( + self, "uptodate", self.__plugin.getPreferences("PipSearchIndex"), + self.tr("Up-to-date Packages")) + self.__listUptodateDialog.show() + self.__listUptodateDialog.start() + + def __listOutdatedPackages(self): + """ + Private slot to list all installed, up-to-date packages. + """ + from .PipListDialog import PipListDialog + self.__listOutdatedDialog = PipListDialog( + self, "outdated", self.__plugin.getPreferences("PipSearchIndex"), + self.tr("Outdated Packages")) + self.__listOutdatedDialog.show() + self.__listOutdatedDialog.start() + + def __editUserConfiguration(self): + """ + Private slot to edit the user configuration. + """ + self.__editConfiguration() + + def __editVirtualenvConfiguration(self): + """ + Private slot to edit the current virtualenv configuration. + """ + self.__editConfiguration(virtualenv=True) + + def __editConfiguration(self, virtualenv=False): + """ + Private method to edit a configuration. + + @param virtualenv flag indicating to edit the current virtualenv + configuration file + @type bool + """ + from QScintilla.MiniEditor import MiniEditor + if virtualenv: + cfgFile = self.__getVirtualenvConfig() + else: + cfgFile = self.__getUserConfig() + cfgDir = os.path.dirname(cfgFile) + if not cfgDir: + E5MessageBox.critical( + None, + self.tr("Edit Configuration"), + self.tr("""No valid configuration path determined.""" + """ Is a virtual environment selected? Aborting""")) + return + + try: + if not os.path.isdir(cfgDir): + os.makedirs(cfgDir) + except OSError: + E5MessageBox.critical( + None, + self.tr("Edit Configuration"), + self.tr("""No valid configuration path determined.""" + """ Is a virtual environment selected? Aborting""")) + return + + if not os.path.exists(cfgFile): + try: + f = open(cfgFile, "w") + f.write("[global]\n") + f.close() + except (IOError, OSError): + # ignore these + pass + + # check, if the destination is writeable + if not os.access(cfgFile, os.W_OK): + E5MessageBox.critical( + None, + self.tr("Edit Configuration"), + self.tr("""No valid configuartion path determined.""" + """ Is a virtual environment selected? Aborting""")) + return + + self.__editor = MiniEditor(cfgFile, "Properties") + self.__editor.show() + + def __installPip(self, userSite=False): + """ + Private slot to install pip. + + @param userSite flag indicating an install to the user install + directory + @type bool + """ + from .PipSelectionDialog import PipSelectionDialog + dlg = PipSelectionDialog(self) + if dlg.exec_() != QDialog.Accepted: + return + + venvName, userSite = dlg.getData() + interpreter = self.getVirtualenvInterpreter(venvName) + if not interpreter: + return + + dia = PipDialog(self.tr('Install PIP')) + if userSite: + commands = [(interpreter, ["-m", "ensurepip", "--user"])] + else: + commands = [(interpreter, ["-m", "ensurepip"])] + if self.__plugin.getPreferences("PipSearchIndex"): + indexUrl = \ + self.__plugin.getPreferences("PipSearchIndex") + "/simple" + args = ["-m", "pip", "install", "--index-url", indexUrl, + "--upgrade"] + else: + args = ["-m", "pip", "install", "--upgrade"] + if userSite: + args.append("--user") + args.append("pip") + commands.append((interpreter, args[:])) + + res = dia.startProcesses(commands) + if res: + dia.exec_() + + @pyqtSlot() + def upgradePip(self, venvName="", userSite=False): + """ + Public method to upgrade pip itself. + + @param venvName name of the virtual environment to be used + @type str + @param userSite flag indicating an install to the user install + directory + @type bool + @return flag indicating a successful execution + @rtype bool + """ + # Upgrading pip needs to be treated specially because + # it must be done using the python executable + + if not venvName: + from .PipSelectionDialog import PipSelectionDialog + dlg = PipSelectionDialog(self) + if dlg.exec_() != QDialog.Accepted: + return + + venvName, userSite = dlg.getData() + + interpreter = self.getVirtualenvInterpreter(venvName) + if not interpreter: + return + + if self.__plugin.getPreferences("PipSearchIndex"): + indexUrl = \ + self.__plugin.getPreferences("PipSearchIndex") + "/simple" + args = ["-m", "pip", "install", "--index-url", indexUrl, + "--upgrade"] + else: + args = ["-m", "pip", "install", "--upgrade"] + if userSite: + args.append("--user") + args.append("pip") + + dia = PipDialog(self.tr('Upgrade PIP')) + res = dia.startProcess(interpreter, args) + if res: + dia.exec_() + return res + + @pyqtSlot() + def __repairPip(self): + """ + Private method to repair the pip installation. + + @return flag indicating a successful execution + @rtype bool + """ + from .PipSelectionDialog import PipSelectionDialog + dlg = PipSelectionDialog(self) + if dlg.exec_() != QDialog.Accepted: + return False + + venvName, userSite = dlg.getData() + interpreter = self.getVirtualenvInterpreter(venvName) + if not interpreter: + return + + # python -m pip install --ignore-installed pip + if self.__plugin.getPreferences("PipSearchIndex"): + indexUrl = \ + self.__plugin.getPreferences("PipSearchIndex") + "/simple" + args = ["-m", "pip", "install", "--index-url", indexUrl, + "--ignore-installed"] + else: + args = ["-m", "pip", "install", "--ignore-installed"] + if userSite: + args.append("--user") + args.append("pip") + + dia = PipDialog(self.tr('Repair PIP')) + res = dia.startProcess(interpreter, args) + if res: + dia.exec_() + + def __checkUpgradePyQt(self, packages): + """ + Private method to check, if an upgrade of PyQt packages is attempted. + + @param packages list of packages to upgrade + @type list of str + @return flag indicating to abort the upgrade attempt + @rtype bool + """ + pyqtPackages = [p for p in packages + if p.lower() in ["pyqt5", "qscintilla", "sip"]] + + if bool(pyqtPackages): + abort = not E5MessageBox.yesNo( + None, + self.tr("Upgrade Packages"), + self.tr( + """You are trying to upgrade PyQt packages. This will""" + """ not work for the current instance of Python ({0}).""" + """ Do you want to continue?""").format(sys.executable), + icon=E5MessageBox.Critical) + else: + abort = False + + return abort + + def upgradePackages(self, packages, venvName="", userSite=False): + """ + Public method to upgrade the given list of packages. + + @param packages list of packages to upgrade + @type list of str + @param venvName name of the virtual environment to be used + @type str + @param userSite flag indicating an install to the user install + directory + @type bool + @return flag indicating a successful execution + @rtype bool + """ + if self.__checkUpgradePyQt(packages): + return False + + if not venvName: + venvName = self.__plugin.getPreferences("CurrentEnvironment") + interpreter = self.getVirtualenvInterpreter(venvName) + if not interpreter: + return + + if self.__plugin.getPreferences("PipSearchIndex"): + indexUrl = \ + self.__plugin.getPreferences("PipSearchIndex") + "/simple" + args = ["-m", "pip", "install", "--index-url", indexUrl, + "--upgrade"] + else: + args = ["-m", "pip", "install", "--upgrade"] + if userSite: + args.append("--user") + args += packages + dia = PipDialog(self.tr('Upgrade Packages')) + res = dia.startProcess(interpreter, args) + if res: + dia.exec_() + return res + + def __upgradePackages(self): + """ + Private slot to upgrade packages to be given by the user. + """ + from .PipPackagesInputDialog import PipPackagesInputDialog + dlg = PipPackagesInputDialog(self, self.tr("Upgrade Packages")) + if dlg.exec_() == QDialog.Accepted: + venvName, packages, user = dlg.getData() + if packages: + self.upgradePackages(packages, venvName=venvName, + userSite=user) + + def installPackages(self, packages, venvName="", userSite=False): + """ + Public method to install the given list of packages. + + @param packages list of packages to install + @type list of str + @param venvName name of the virtual environment to be used + @type str + @param userSite flag indicating an install to the user install + directory + @type bool + """ + if not venvName: + venvName = self.__plugin.getPreferences("CurrentEnvironment") + interpreter = self.getVirtualenvInterpreter(venvName) + if not interpreter: + return + + if self.__plugin.getPreferences("PipSearchIndex"): + indexUrl = \ + self.__plugin.getPreferences("PipSearchIndex") + "/simple" + args = ["-m", "pip", "install", "--index-url", indexUrl] + else: + args = ["-m", "pip", "install"] + if userSite: + args.append("--user") + args += packages + dia = PipDialog(self.tr('Install Packages')) + res = dia.startProcess(interpreter, args) + if res: + dia.exec_() + + def __installPackages(self): + """ + Private slot to install packages to be given by the user. + """ + from .PipPackagesInputDialog import PipPackagesInputDialog + dlg = PipPackagesInputDialog( + self, self.tr("Install Packages")) + if dlg.exec_() == QDialog.Accepted: + venvName, packages, user = dlg.getData() + if packages: + self.installPackages(packages, venvName=venvName, + userSite=user) + + def __installLocalPackage(self): + """ + Private slot to install a package available on local storage. + """ + from .PipFileSelectionDialog import PipFileSelectionDialog + dlg = PipFileSelectionDialog(self, "package") + if dlg.exec_() == QDialog.Accepted: + venvName, package, user = dlg.getData() + if package and os.path.exists(package): + self.installPackages([package], venvName=venvName, + userSite=user) + + def __installRequirements(self): + """ + Private slot to install packages as given in a requirements file. + """ + from .PipFileSelectionDialog import PipFileSelectionDialog + dlg = PipFileSelectionDialog(self, "requirements") + if dlg.exec_() == QDialog.Accepted: + venvName, requirements, user = dlg.getData() + if requirements and os.path.exists(requirements): + interpreter = self.getVirtualenvInterpreter(venvName) + if not interpreter: + return + if self.__plugin.getPreferences("PipSearchIndex"): + indexUrl = \ + self.__plugin.getPreferences("PipSearchIndex") + \ + "/simple" + args = ["-m", "pip", "install", "--index-url", indexUrl] + else: + args = ["-m", "pip", "install"] + if user: + args.append("--user") + args += ["--requirement", requirements] + dia = PipDialog(self.tr('Install Packages from Requirements')) + res = dia.startProcess(interpreter, args) + if res: + dia.exec_() + + def uninstallPackages(self, packages, venvName=""): + """ + Public method to uninstall the given list of packages. + + @param packages list of packages to uninstall + @type list of str + @param venvName name of the virtual environment to be used + @type str + @return flag indicating a successful execution + @rtype bool + """ + res = False + if packages: + from UI.DeleteFilesConfirmationDialog import \ + DeleteFilesConfirmationDialog + dlg = DeleteFilesConfirmationDialog( + self.parent(), + self.tr("Uninstall Packages"), + self.tr( + "Do you really want to uninstall these packages?"), + packages) + if dlg.exec_() == QDialog.Accepted: + if not venvName: + venvName = self.__plugin.getPreferences( + "CurrentEnvironment") + interpreter = self.getVirtualenvInterpreter(venvName) + if not interpreter: + return + args = ["-m", "pip", "uninstall", "--yes"] + packages + dia = PipDialog(self.tr('Uninstall Packages')) + res = dia.startProcess(interpreter, args) + if res: + dia.exec_() + return res + + def __uninstallPackages(self): + """ + Private slot to uninstall packages to be given by the user. + """ + from .PipPackagesInputDialog import PipPackagesInputDialog + dlg = PipPackagesInputDialog( + self, self.tr("Uninstall Packages"), install=False) + if dlg.exec_() == QDialog.Accepted: + venvName, packages, _user = dlg.getData() + if packages: + self.uninstallPackages(packages, venvName=venvName) + + def __uninstallRequirements(self): + """ + Private slot to uninstall packages as given in a requirements file. + """ + from .PipFileSelectionDialog import PipFileSelectionDialog + dlg = PipFileSelectionDialog(self, "requirements", + install=False) + if dlg.exec_() == QDialog.Accepted: + venvName, requirements, _user = dlg.getData() + if requirements and os.path.exists(requirements): + try: + f = open(requirements, "r") + reqs = f.read().splitlines() + f.close() + except (OSError, IOError): + return + + from UI.DeleteFilesConfirmationDialog import \ + DeleteFilesConfirmationDialog + dlg = DeleteFilesConfirmationDialog( + self.parent(), + self.tr("Uninstall Packages"), + self.tr( + "Do you really want to uninstall these packages?"), + reqs) + if dlg.exec_() == QDialog.Accepted: + if not venvName: + venvName = self.__plugin.getPreferences( + "CurrentEnvironment") + interpreter = self.getVirtualenvInterpreter(venvName) + if not interpreter: + return + args = ["-m", "pip", "uninstall", "--requirement", + requirements] + dia = PipDialog( + self.tr('Uninstall Packages from Requirements')) + res = dia.startProcess(interpreter, args) + if res: + dia.exec_() + + def __generateRequirements(self): + """ + Private slot to generate the contents for a requirements file. + """ + from .PipFreezeDialog import PipFreezeDialog + self.__freezeDialog = PipFreezeDialog(self) + self.__freezeDialog.show() + self.__freezeDialog.start() + + def __searchPyPI(self): + """ + Private slot to search the Python Package Index. + """ + from .PipSearchDialog import PipSearchDialog + + if self.__plugin.getPreferences("PipSearchIndex"): + indexUrl = self.__plugin.getPreferences("PipSearchIndex") + "/pypi" + else: + indexUrl = DefaultIndexUrlXml + + self.__searchDialog = PipSearchDialog(self, indexUrl) + self.__searchDialog.show() + + def __pipConfigure(self): + """ + Private slot to open the configuration page. + """ + e5App().getObject("UserInterface").showPreferences("pipPage")