PluginPyLint.py

changeset 22
68beeb22dade
parent 20
8ca799e4b7e4
child 24
b01348dd84d5
--- 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):
         """

eric ide

mercurial