--- a/PluginPyLint.py Sun Apr 14 14:46:14 2013 +0200 +++ b/PluginPyLint.py Thu Sep 26 22:53:14 2013 +0200 @@ -7,17 +7,31 @@ Module implementing the PyLint plug-in. """ +from __future__ import unicode_literals # __IGNORE_WARNING__ +try: + str = unicode +except (NameError): + pass + +import re import os -import sys import copy +import platform -from PyQt4.QtCore import QObject, QTranslator, QCoreApplication +from PyQt4.QtCore import QObject, QTranslator, QCoreApplication, QProcess from PyQt4.QtGui import QDialog -from E5Gui.E5Application import e5App -from E5Gui.E5Action import E5Action -from E5Gui import E5MessageBox +try: + from E5Gui.E5Application import e5App + from E5Gui.E5Action import E5Action + from E5Gui import E5MessageBox + error = "" +except ImportError: + error = QCoreApplication.translate("PyLintPlugin", + """Your version of Eric5 is not supported.""" + """ At least version 5.1.0 of Eric5 is needed.""") +import Preferences import Utilities # Start-of-Header @@ -25,7 +39,7 @@ author = "Detlev Offenbach <detlev@die-offenbachs.de>" autoactivate = True deactivateable = True -version = "5.2.1" +version = "5.3.0" className = "PyLintPlugin" packageName = "PyLint" shortDescription = "Show the PyLint dialogs." @@ -35,16 +49,17 @@ pyqtApi = 2 # End-of-Header -error = "" +exePy2 = [] +exePy3 = [] - -def exeDisplayData(): +def exeDisplayDataList(): """ Public method to support the display of some executable info. @return dictionary containing the data to query the presence of the executable """ + dataList = [] data = { "programEntry": True, "header": QCoreApplication.translate("PyLintPlugin", @@ -56,69 +71,118 @@ "version": "", "versionCleanup": (0, -1), } - exe = _findExecutable() - if exe: - data["exe"] = exe - data["versionStartsWith"] = 'pylint' + if _checkProgram(): + for exePath in (exePy2[0], exePy3[0]): + data["exe"] = exePath + data["versionStartsWith"] = "pylint" + dataList.append(data.copy()) + else: + dataList.append(data) + return dataList + +def __getProgramVersion(exe): + """ + Private method to generate a program entry. - return data + @param exe name of the executable program (string) + @return version string of detected version (string) + """ + proc = QProcess() + proc.setProcessChannelMode(QProcess.MergedChannels) + proc.start(exe, ['--version']) + finished = proc.waitForFinished(10000) + if finished: + output = str(proc.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + 'replace') + versionRe = re.compile('^pylint', re.UNICODE) + for line in output.splitlines(): + if versionRe.search(line): + version = line.split()[-1] + version = version[:-1] + break + else: + version = '0.0.0' + return version - -def _findExecutable(): +def _findExecutable(majorVersion): """ Restricted function to determine the name and path of the executable. + @param majorVersion major python version of the executables (int) @return path name of the executable (string) """ + # Determine Python Version + if majorVersion == 3: + minorVersions = range(5) + elif majorVersion == 2: + minorVersions = range(5, 9) + else: + return [] + + executables = set() if Utilities.isWindowsPlatform(): # # Windows # - exe = 'pylint.bat' - if Utilities.isinpath(exe): - return exe try: - # only since python 3.2 - import sysconfig - scripts = sysconfig.get_path('scripts', 'nt') - return os.path.join(scripts, exe) + import winreg except ImportError: + import _winreg as winreg # __IGNORE_WARNING__ + + def getExePath(branch, access, versionStr): try: - import winreg - except ImportError: - # give up ... + 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', 'pylint.bat') + if os.access(exe, os.X_OK): + return exe + except WindowsError: # __IGNORE_WARNING__ return None + return None + + for minorVersion in minorVersions: + versionStr = '{0}.{1}'.format(majorVersion, minorVersion) + exePath = getExePath(winreg.HKEY_CURRENT_USER, + winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr) + + if exePath is not None: + executables.add(exePath) + exePath = getExePath(winreg.HKEY_LOCAL_MACHINE, + winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr) - def getExePath(branch): - version = str(sys.version_info.major) + '.' + \ - str(sys.version_info.minor) - try: - software = winreg.OpenKey(branch, 'Software') - python = winreg.OpenKey(software, 'Python') - pcore = winreg.OpenKey(python, 'PythonCore') - version = winreg.OpenKey(pcore, version) - installpath = winreg.QueryValue(version, 'InstallPath') - return os.path.join(installpath, 'Scripts', exe) - except WindowsError: # __IGNORE_WARNING__ - return None - - exePath = getExePath(winreg.HKEY_CURRENT_USER) - if not exePath: - exePath = getExePath(winreg.HKEY_LOCAL_MACHINE) - return exePath + # Even on Intel 64-bit machines it's 'AMD64' + if platform.machine() == 'AMD64': + if exePath is not None: + executables.add(exePath) + exePath = getExePath(winreg.HKEY_CURRENT_USER, + winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr) + + if exePath is not None: + executables.add(exePath) + exePath = getExePath(winreg.HKEY_LOCAL_MACHINE, + winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr) + + if exePath is not None: + executables.add(exePath) else: # # Linux, Unix ... pylintScript = 'pylint' scriptSuffixes = ["", - "-python{0}".format(sys.version[:1]), - "-python{0}".format(sys.version[:3])] + "-python{0}".format(majorVersion)] + for minorVersion in minorVersions: + scriptSuffixes.append( + "-python{0}.{1}".format(majorVersion, minorVersion)) # There could be multiple pylint executables in the path # e.g. for different python variants path = Utilities.getEnvironmentEntry('PATH') # environment variable not defined if path is None: - return None + return [] # step 1: determine possible candidates exes = [] @@ -129,25 +193,40 @@ if os.access(exe, os.X_OK): exes.append(exe) - # step 2: determine the Python 3 variant - found = False + # step 2: determine the Python variant if Utilities.isMacPlatform(): - checkStr = "Python.framework/Versions/3".lower() + checkStrings = ["Python.framework/Versions/3".lower(), + "python3"] else: - checkStr = "python3" + checkStrings = ["python3"] + + _exePy2 = set() + _exePy3 = set() for exe in exes: try: f = open(exe, "r") line0 = f.readline() - if checkStr in line0.lower(): - found = True + for checkStr in checkStrings: + if checkStr in line0.lower(): + _exePy3.add(exe) + break + else: + _exePy2.add(exe) finally: f.close() - if found: - return exe + + executables = _exePy3 if majorVersion == 3 else _exePy2 - return None + # Find the executable with the highest version number + maxVersion = '0.0.0' + maxExe = '' + for executable in list(executables): + version = __getProgramVersion(executable) + if version > maxVersion: + maxVersion = version + maxExe = executable + return maxExe, maxVersion def _checkProgram(): """ @@ -155,12 +234,18 @@ @return flag indicating availability (boolean) """ - global error + global error, exePy2, exePy3 - if _findExecutable() is None: + exePy2 = _findExecutable(2) + exePy3 = _findExecutable(3) + if exePy2[0] == '' and exePy3[0] == '': error = QCoreApplication.translate("PyLintPlugin", "The pylint executable could not be found.") return False + elif exePy2[1] < '0.23.0' and exePy3[1] < '0.23.0': + error = QCoreApplication.translate("PyLintPlugin", + "PyLint version < 0.23.0.") + return False else: return True @@ -208,19 +293,13 @@ """ global error + # There is already an error, don't activate + if error: + return None, False # pylint is only activated if it is available if not _checkProgram(): return None, False - try: - from pylint.__pkginfo__ import numversion - if numversion < (0, 23, 0): - error = self.trUtf8("PyLint version < 0.23.0.") - return None, False - except ImportError: - error = self.trUtf8("Cannot determine pylint version.") - return None, False - menu = e5App().getObject("Project").getMenu("Checks") if menu: self.__projectAct = E5Action(self.trUtf8('Run PyLint'), @@ -333,12 +412,11 @@ @param menu reference to the menu (QMenu) """ if menuName == "Checks": + lang = e5App().getObject("Project").getProjectLanguage() if self.__projectAct is not None: - self.__projectAct.setEnabled( - e5App().getObject("Project").getProjectLanguage() == "Python3") + self.__projectAct.setEnabled(lang.startswith("Python")) if self.__projectShowAct is not None: - self.__projectShowAct.setEnabled( - e5App().getObject("Project").getProjectLanguage() == "Python3") + self.__projectShowAct.setEnabled(lang.startswith("Python")) self.__projectShowAct.setEnabled(self.__pylintPDialog is not None) def __projectBrowserShowMenu(self, menuName, menu): @@ -350,7 +428,7 @@ @param menu reference to the menu (QMenu) """ if menuName == "Checks" and \ - e5App().getObject("Project").getProjectLanguage() == "Python3": + e5App().getObject("Project").getProjectLanguage().startswith("Python"): self.__projectBrowserMenu = menu if self.__projectBrowserAct is None: self.__projectBrowserAct = E5Action(self.trUtf8('Run PyLint'), @@ -393,17 +471,26 @@ """ if forEditor: parms = copy.deepcopy(self.__editorParms) + editor = e5App().getObject("ViewManager").getOpenEditor(mpName) + majorVersionStr = editor.getLanguage() else: parms = project.getData('CHECKERSPARMS', "PYLINT") - exe = _findExecutable() - if exe is None: + majorVersionStr = project.getProjectLanguage() + exe, version = {"Python": exePy2, "Python2": exePy2, + "Python3": exePy3}.get(majorVersionStr) + if exe == '': E5MessageBox.critical(None, self.trUtf8("pylint"), self.trUtf8("""The pylint executable could not be found.""")) return + elif version < '0.23.0': + E5MessageBox.critical(None, + self.trUtf8("pylint"), + self.trUtf8("PyLint version < 0.23.0.")) + return from PyLint.PyLintConfigDialog import PyLintConfigDialog - dlg = PyLintConfigDialog(project.getProjectPath(), exe, parms) + dlg = PyLintConfigDialog(project.getProjectPath(), exe, parms, version) if dlg.exec_() == QDialog.Accepted: args, parms = dlg.generateParameters() self.__editorParms = copy.deepcopy(parms) @@ -413,10 +500,7 @@ # now do the call from PyLint.PyLintExecDialog import PyLintExecDialog dlg2 = PyLintExecDialog() - try: - reportFile = parms['reportFile'] - except KeyError: - reportFile = None + reportFile = parms.get('reportFile', None) res = dlg2.start(args, mpName, reportFile, project.getProjectPath()) if res: dlg2.show() @@ -498,7 +582,7 @@ if menuName == "Checks": if not self.__editorAct in menu.actions(): menu.addAction(self.__editorAct) - self.__editorAct.setEnabled(editor.isPy3File()) + self.__editorAct.setEnabled(editor.isPy3File() or editor.isPy2File()) def __editorPylint(self): """