PipInterface/Pip.py

branch
pypi
changeset 6782
390a45748883
parent 6766
c722fcfb5f62
child 6785
058d63c537a4
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")

eric ide

mercurial