Mon, 02 Apr 2018 12:04:18 +0200
Merged with default branch to prepare new release.
# -*- coding: utf-8 -*- # Copyright (c) 2015 - 2018 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the pip interface plug-in. """ from __future__ import unicode_literals import os import platform from PyQt5.QtCore import pyqtSignal, QObject, QCoreApplication, QProcess from E5Gui.E5Application import e5App import Preferences import Utilities import UI.Info # Start-Of-Header name = "pip Interface Plug-in" author = "Detlev Offenbach <detlev@die-offenbachs.de>" autoactivate = True deactivateable = True version = UI.Info.VersionOnly className = "PipInterfacePlugin" packageName = "__core__" shortDescription = "Plug-in implementing a simple GUI for the pip command." longDescription = ( """Plug-in implementing a simple GUI for the pip command.""" ) needsRestart = False pyqtApi = 2 python2Compatible = True # End-Of-Header error = "" pipPluginObject = None def exeDisplayDataList(): """ Module function to support the display of some executable info. @return list of dictionaries containing the data to query the presence of the executable """ global pipPluginObject dataList = [] data = { "programEntry": True, "header": QCoreApplication.translate( "PipInterfacePlugin", "Package Management - pip"), "exe": "dummyExe", "versionCommand": "--version", "versionStartsWith": "dummyExe", "versionPosition": 1, "version": "", "versionCleanup": None, } if pipPluginObject is not None: executables = pipPluginObject.getPreferences("PipExecutables") if not executables: executables = ["pip3", "pip2", "pip"] for exePath in executables: data["exe"] = exePath data["versionStartsWith"] = "pip" dataList.append(data.copy()) return dataList def createPipPage(configDlg): """ Module function to create the pip configuration page. @param configDlg reference to the configuration dialog @return reference to the configuration page """ global pipPluginObject from UiExtensionPlugins.PipInterface.ConfigurationPage.PipPage import \ PipPage page = PipPage(pipPluginObject) return page def getConfigData(): """ Module function returning data as required by the configuration dialog. @return dictionary containing the relevant data """ return { "pipPage": [ QCoreApplication.translate( "PipInterfacePlugin", "Python Package Management"), "preferences-python.png", createPipPage, None, None ], } def prepareUninstall(): """ Module function to prepare for an un-installation. """ Preferences.Prefs.settings.remove(PipInterfacePlugin.PreferencesKey) def _findDefaultExecutables(majorVersion): """ Restricted function to determine the name and path of the executables. @param majorVersion major python version of the executables (int) @return path names of the executables (list of string) """ # Determine Python Version if majorVersion == 3: minorVersions = range(10) elif majorVersion == 2: minorVersions = range(6, 8) else: return [] executables = set() if Utilities.isWindowsPlatform(): # # Windows # try: import winreg except ImportError: import _winreg as winreg # __IGNORE_WARNING__ def getExePath(branch, access, versionStr): exes = [] try: software = winreg.OpenKey(branch, 'Software', 0, access) python = winreg.OpenKey(software, 'Python', 0, access) pcore = winreg.OpenKey(python, 'PythonCore', 0, access) version = winreg.OpenKey(pcore, versionStr, 0, access) installpath = winreg.QueryValue(version, 'InstallPath') exe = os.path.join(installpath, 'Scripts', 'pip.exe') if os.access(exe, os.X_OK): exes.append(exe) except (WindowsError, OSError): # __IGNORE_WARNING__ pass return exes versionSuffixes = ["", "-32", "-64"] for minorVersion in minorVersions: for versionSuffix in versionSuffixes: versionStr = '{0}.{1}{2}'.format(majorVersion, minorVersion, versionSuffix) exePaths = getExePath( winreg.HKEY_CURRENT_USER, winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr) for exePath in exePaths: executables.add(exePath) exePaths = getExePath( winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr) for exePath in exePaths: executables.add(exePath) # Even on Intel 64-bit machines it's 'AMD64' if platform.machine() == 'AMD64': exePaths = getExePath( winreg.HKEY_CURRENT_USER, winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr) for exePath in exePaths: executables.add(exePath) exePath = getExePath( winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr) for exePath in exePaths: executables.add(exePath) else: # # Linux, Unix ... pipScript = 'pip' scriptSuffixes = ["", "{0}".format(majorVersion), "-{0}".format(majorVersion), ] for minorVersion in minorVersions: scriptSuffixes.append( "{0}.{1}".format(majorVersion, minorVersion)) scriptSuffixes.append( "-{0}.{1}".format(majorVersion, minorVersion)) # There could be multiple pip executables in the path # e.g. for different python variants path = Utilities.getEnvironmentEntry('PATH') # environment variable not defined if path is None: return [] # step 1: determine possible candidates exes = [] dirs = path.split(os.pathsep) for directory in dirs: for suffix in scriptSuffixes: exe = os.path.join(directory, pipScript + suffix) if os.access(exe, os.X_OK): exes.append(exe) # step 2: determine the Python variant _exePy2 = set() _exePy3 = set() versionArgs = ["-c", "import sys; print(sys.version_info[0])"] for exe in exes: try: f = open(exe, "r") line0 = f.readline() program = line0.replace("#!", "").strip() process = QProcess() process.start(program, versionArgs) process.waitForFinished(5000) # get a QByteArray of the output versionBytes = process.readAllStandardOutput() versionStr = str(versionBytes, encoding='utf-8').strip() if versionStr == "3": _exePy3.add(exe) elif versionStr == "2": _exePy2.add(exe) finally: f.close() executables = _exePy3 if majorVersion == 3 else _exePy2 return list(executables) class PipInterfacePlugin(QObject): """ Class implementing the pip interface plug-in. @signal currentPipChanged(exe) emitted to signal a change of the current pip executable """ PreferencesKey = "PipPlugin" currentPipChanged = pyqtSignal(str) def __init__(self, ui): """ Constructor @param ui reference to the user interface object (UI.UserInterface) """ super(PipInterfacePlugin, self).__init__(ui) self.__ui = ui self.__initialize() self.__defaults = { "PipExecutables": [], "CurrentPipExecutable": "", "PipSearchIndex": "", # used by the search command } def __initialize(self): """ Private slot to (re)initialize the plugin. """ self.__object = None self.__mainAct = None self.__mainMenu = None def activate(self): """ Public method to activate this plugin. @return tuple of None and activation status (boolean) """ global error error = "" # clear previous error global pipPluginObject pipPluginObject = self from UiExtensionPlugins.PipInterface.Pip import Pip self.__object = Pip(self, self.__ui) self.__object.initActions() e5App().registerPluginObject("PipGui", self.__object) menu = self.__ui.getMenu("extras") self.__mainMenu = self.__object.initMenu() self.__mainAct = menu.addMenu(self.__mainMenu) if self.getPreferences("PipExecutables"): # remove all executables, that don't exist anymore executables = [] for executable in self.getPreferences("PipExecutables"): if os.access(executable, os.X_OK): executables.append(executable) self.setPreferences("PipExecutables", executables) current = self.getPreferences("CurrentPipExecutable") if current not in executables: current = "" self.setPreferences("CurrentPipExecutable", current) else: # load the list with default executables self.setPreferences("PipExecutables", self.getDefaultPipExecutables()) return None, True def deactivate(self): """ Public method to deactivate this plugin. """ e5App().unregisterPluginObject("PipGui") menu = self.__ui.getMenu("extras") menu.removeAction(self.__mainAct) self.__mainAct = None self.__initialize() def getPreferences(self, key): """ Public method to retrieve the various refactoring settings. @param key the key of the value to get @return the requested refactoring setting """ if key in ["PipExecutables"]: return Preferences.toList(Preferences.Prefs.settings.value( self.PreferencesKey + "/" + key, self.__defaults[key])) else: return Preferences.Prefs.settings.value( self.PreferencesKey + "/" + key, self.__defaults[key]) def setPreferences(self, key, value): """ Public method to store the various refactoring settings. @param key the key of the setting to be set (string) @param value the value to be set """ Preferences.Prefs.settings.setValue( self.PreferencesKey + "/" + key, value) if key == "CurrentPipExecutable": self.currentPipChanged.emit(value) def getMenu(self, name): """ Public method to get a reference to the requested menu. @param name name of the menu (string) @return reference to the menu (QMenu) or None, if no menu with the given name exists """ if self.__object is not None: return self.__object.getMenu(name) else: return None def getMenuNames(self): """ Public method to get the names of all menus. @return menu names (list of string) """ if self.__object is not None: return list(self.__menus.keys()) else: return [] def getDefaultPipExecutables(self): """ Public method to get the default list of pip executables. @return list of pip executables (list of string) """ return _findDefaultExecutables(2) + _findDefaultExecutables(3) # # eflag: noqa = M801