5 |
5 |
6 """ |
6 """ |
7 Module implementing the PyLint plug-in. |
7 Module implementing the PyLint plug-in. |
8 """ |
8 """ |
9 |
9 |
|
10 from __future__ import unicode_literals # __IGNORE_WARNING__ |
|
11 try: |
|
12 str = unicode |
|
13 except (NameError): |
|
14 pass |
|
15 |
|
16 import re |
10 import os |
17 import os |
11 import sys |
|
12 import copy |
18 import copy |
13 |
19 import platform |
14 from PyQt4.QtCore import QObject, QTranslator, QCoreApplication |
20 |
|
21 from PyQt4.QtCore import QObject, QTranslator, QCoreApplication, QProcess |
15 from PyQt4.QtGui import QDialog |
22 from PyQt4.QtGui import QDialog |
16 |
23 |
17 from E5Gui.E5Application import e5App |
24 try: |
18 from E5Gui.E5Action import E5Action |
25 from E5Gui.E5Application import e5App |
19 from E5Gui import E5MessageBox |
26 from E5Gui.E5Action import E5Action |
20 |
27 from E5Gui import E5MessageBox |
|
28 error = "" |
|
29 except ImportError: |
|
30 error = QCoreApplication.translate("PyLintPlugin", |
|
31 """Your version of Eric5 is not supported.""" |
|
32 """ At least version 5.1.0 of Eric5 is needed.""") |
|
33 |
|
34 import Preferences |
21 import Utilities |
35 import Utilities |
22 |
36 |
23 # Start-of-Header |
37 # Start-of-Header |
24 name = "PyLint Plugin" |
38 name = "PyLint Plugin" |
25 author = "Detlev Offenbach <detlev@die-offenbachs.de>" |
39 author = "Detlev Offenbach <detlev@die-offenbachs.de>" |
26 autoactivate = True |
40 autoactivate = True |
27 deactivateable = True |
41 deactivateable = True |
28 version = "5.2.1" |
42 version = "5.3.0" |
29 className = "PyLintPlugin" |
43 className = "PyLintPlugin" |
30 packageName = "PyLint" |
44 packageName = "PyLint" |
31 shortDescription = "Show the PyLint dialogs." |
45 shortDescription = "Show the PyLint dialogs." |
32 longDescription = """This plug-in implements the PyLint dialogs.""" \ |
46 longDescription = """This plug-in implements the PyLint dialogs.""" \ |
33 """ PyLint is used to check Python source files according to various rules.""" |
47 """ PyLint is used to check Python source files according to various rules.""" |
34 needsRestart = False |
48 needsRestart = False |
35 pyqtApi = 2 |
49 pyqtApi = 2 |
36 # End-of-Header |
50 # End-of-Header |
37 |
51 |
38 error = "" |
52 exePy2 = [] |
39 |
53 exePy3 = [] |
40 |
54 |
41 def exeDisplayData(): |
55 def exeDisplayDataList(): |
42 """ |
56 """ |
43 Public method to support the display of some executable info. |
57 Public method to support the display of some executable info. |
44 |
58 |
45 @return dictionary containing the data to query the presence of |
59 @return dictionary containing the data to query the presence of |
46 the executable |
60 the executable |
47 """ |
61 """ |
|
62 dataList = [] |
48 data = { |
63 data = { |
49 "programEntry": True, |
64 "programEntry": True, |
50 "header": QCoreApplication.translate("PyLintPlugin", |
65 "header": QCoreApplication.translate("PyLintPlugin", |
51 "Checkers - Pylint"), |
66 "Checkers - Pylint"), |
52 "exe": 'dummypylint', |
67 "exe": 'dummypylint', |
54 "versionStartsWith": 'dummypylint', |
69 "versionStartsWith": 'dummypylint', |
55 "versionPosition": -1, |
70 "versionPosition": -1, |
56 "version": "", |
71 "version": "", |
57 "versionCleanup": (0, -1), |
72 "versionCleanup": (0, -1), |
58 } |
73 } |
59 exe = _findExecutable() |
74 if _checkProgram(): |
60 if exe: |
75 for exePath in (exePy2[0], exePy3[0]): |
61 data["exe"] = exe |
76 data["exe"] = exePath |
62 data["versionStartsWith"] = 'pylint' |
77 data["versionStartsWith"] = "pylint" |
63 |
78 dataList.append(data.copy()) |
64 return data |
79 else: |
65 |
80 dataList.append(data) |
66 |
81 return dataList |
67 def _findExecutable(): |
82 |
|
83 def __getProgramVersion(exe): |
|
84 """ |
|
85 Private method to generate a program entry. |
|
86 |
|
87 @param exe name of the executable program (string) |
|
88 @return version string of detected version (string) |
|
89 """ |
|
90 proc = QProcess() |
|
91 proc.setProcessChannelMode(QProcess.MergedChannels) |
|
92 proc.start(exe, ['--version']) |
|
93 finished = proc.waitForFinished(10000) |
|
94 if finished: |
|
95 output = str(proc.readAllStandardOutput(), |
|
96 Preferences.getSystem("IOEncoding"), |
|
97 'replace') |
|
98 versionRe = re.compile('^pylint', re.UNICODE) |
|
99 for line in output.splitlines(): |
|
100 if versionRe.search(line): |
|
101 version = line.split()[-1] |
|
102 version = version[:-1] |
|
103 break |
|
104 else: |
|
105 version = '0.0.0' |
|
106 return version |
|
107 |
|
108 def _findExecutable(majorVersion): |
68 """ |
109 """ |
69 Restricted function to determine the name and path of the executable. |
110 Restricted function to determine the name and path of the executable. |
70 |
111 |
|
112 @param majorVersion major python version of the executables (int) |
71 @return path name of the executable (string) |
113 @return path name of the executable (string) |
72 """ |
114 """ |
|
115 # Determine Python Version |
|
116 if majorVersion == 3: |
|
117 minorVersions = range(5) |
|
118 elif majorVersion == 2: |
|
119 minorVersions = range(5, 9) |
|
120 else: |
|
121 return [] |
|
122 |
|
123 executables = set() |
73 if Utilities.isWindowsPlatform(): |
124 if Utilities.isWindowsPlatform(): |
74 # |
125 # |
75 # Windows |
126 # Windows |
76 # |
127 # |
77 exe = 'pylint.bat' |
|
78 if Utilities.isinpath(exe): |
|
79 return exe |
|
80 try: |
128 try: |
81 # only since python 3.2 |
129 import winreg |
82 import sysconfig |
|
83 scripts = sysconfig.get_path('scripts', 'nt') |
|
84 return os.path.join(scripts, exe) |
|
85 except ImportError: |
130 except ImportError: |
|
131 import _winreg as winreg # __IGNORE_WARNING__ |
|
132 |
|
133 def getExePath(branch, access, versionStr): |
86 try: |
134 try: |
87 import winreg |
135 software = winreg.OpenKey(branch, 'Software', 0, access) |
88 except ImportError: |
136 python = winreg.OpenKey(software, 'Python', 0, access) |
89 # give up ... |
137 pcore = winreg.OpenKey(python, 'PythonCore', 0, access) |
|
138 version = winreg.OpenKey(pcore, versionStr, 0, access) |
|
139 installpath = winreg.QueryValue(version, 'InstallPath') |
|
140 exe = os.path.join(installpath, 'Scripts', 'pylint.bat') |
|
141 if os.access(exe, os.X_OK): |
|
142 return exe |
|
143 except WindowsError: # __IGNORE_WARNING__ |
90 return None |
144 return None |
|
145 return None |
|
146 |
|
147 for minorVersion in minorVersions: |
|
148 versionStr = '{0}.{1}'.format(majorVersion, minorVersion) |
|
149 exePath = getExePath(winreg.HKEY_CURRENT_USER, |
|
150 winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr) |
|
151 |
|
152 if exePath is not None: |
|
153 executables.add(exePath) |
|
154 exePath = getExePath(winreg.HKEY_LOCAL_MACHINE, |
|
155 winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr) |
91 |
156 |
92 def getExePath(branch): |
157 # Even on Intel 64-bit machines it's 'AMD64' |
93 version = str(sys.version_info.major) + '.' + \ |
158 if platform.machine() == 'AMD64': |
94 str(sys.version_info.minor) |
159 if exePath is not None: |
95 try: |
160 executables.add(exePath) |
96 software = winreg.OpenKey(branch, 'Software') |
161 exePath = getExePath(winreg.HKEY_CURRENT_USER, |
97 python = winreg.OpenKey(software, 'Python') |
162 winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr) |
98 pcore = winreg.OpenKey(python, 'PythonCore') |
163 |
99 version = winreg.OpenKey(pcore, version) |
164 if exePath is not None: |
100 installpath = winreg.QueryValue(version, 'InstallPath') |
165 executables.add(exePath) |
101 return os.path.join(installpath, 'Scripts', exe) |
166 exePath = getExePath(winreg.HKEY_LOCAL_MACHINE, |
102 except WindowsError: # __IGNORE_WARNING__ |
167 winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr) |
103 return None |
168 |
104 |
169 if exePath is not None: |
105 exePath = getExePath(winreg.HKEY_CURRENT_USER) |
170 executables.add(exePath) |
106 if not exePath: |
|
107 exePath = getExePath(winreg.HKEY_LOCAL_MACHINE) |
|
108 return exePath |
|
109 else: |
171 else: |
110 # |
172 # |
111 # Linux, Unix ... |
173 # Linux, Unix ... |
112 pylintScript = 'pylint' |
174 pylintScript = 'pylint' |
113 scriptSuffixes = ["", |
175 scriptSuffixes = ["", |
114 "-python{0}".format(sys.version[:1]), |
176 "-python{0}".format(majorVersion)] |
115 "-python{0}".format(sys.version[:3])] |
177 for minorVersion in minorVersions: |
|
178 scriptSuffixes.append( |
|
179 "-python{0}.{1}".format(majorVersion, minorVersion)) |
116 # There could be multiple pylint executables in the path |
180 # There could be multiple pylint executables in the path |
117 # e.g. for different python variants |
181 # e.g. for different python variants |
118 path = Utilities.getEnvironmentEntry('PATH') |
182 path = Utilities.getEnvironmentEntry('PATH') |
119 # environment variable not defined |
183 # environment variable not defined |
120 if path is None: |
184 if path is None: |
121 return None |
185 return [] |
122 |
186 |
123 # step 1: determine possible candidates |
187 # step 1: determine possible candidates |
124 exes = [] |
188 exes = [] |
125 dirs = path.split(os.pathsep) |
189 dirs = path.split(os.pathsep) |
126 for dir in dirs: |
190 for dir in dirs: |
127 for suffix in scriptSuffixes: |
191 for suffix in scriptSuffixes: |
128 exe = os.path.join(dir, pylintScript + suffix) |
192 exe = os.path.join(dir, pylintScript + suffix) |
129 if os.access(exe, os.X_OK): |
193 if os.access(exe, os.X_OK): |
130 exes.append(exe) |
194 exes.append(exe) |
131 |
195 |
132 # step 2: determine the Python 3 variant |
196 # step 2: determine the Python variant |
133 found = False |
|
134 if Utilities.isMacPlatform(): |
197 if Utilities.isMacPlatform(): |
135 checkStr = "Python.framework/Versions/3".lower() |
198 checkStrings = ["Python.framework/Versions/3".lower(), |
|
199 "python3"] |
136 else: |
200 else: |
137 checkStr = "python3" |
201 checkStrings = ["python3"] |
|
202 |
|
203 _exePy2 = set() |
|
204 _exePy3 = set() |
138 for exe in exes: |
205 for exe in exes: |
139 try: |
206 try: |
140 f = open(exe, "r") |
207 f = open(exe, "r") |
141 line0 = f.readline() |
208 line0 = f.readline() |
142 if checkStr in line0.lower(): |
209 for checkStr in checkStrings: |
143 found = True |
210 if checkStr in line0.lower(): |
|
211 _exePy3.add(exe) |
|
212 break |
|
213 else: |
|
214 _exePy2.add(exe) |
144 finally: |
215 finally: |
145 f.close() |
216 f.close() |
146 if found: |
217 |
147 return exe |
218 executables = _exePy3 if majorVersion == 3 else _exePy2 |
148 |
219 |
149 return None |
220 # Find the executable with the highest version number |
150 |
221 maxVersion = '0.0.0' |
|
222 maxExe = '' |
|
223 for executable in list(executables): |
|
224 version = __getProgramVersion(executable) |
|
225 if version > maxVersion: |
|
226 maxVersion = version |
|
227 maxExe = executable |
|
228 |
|
229 return maxExe, maxVersion |
151 |
230 |
152 def _checkProgram(): |
231 def _checkProgram(): |
153 """ |
232 """ |
154 Restricted function to check the availability of pylint. |
233 Restricted function to check the availability of pylint. |
155 |
234 |
156 @return flag indicating availability (boolean) |
235 @return flag indicating availability (boolean) |
157 """ |
236 """ |
158 global error |
237 global error, exePy2, exePy3 |
159 |
238 |
160 if _findExecutable() is None: |
239 exePy2 = _findExecutable(2) |
|
240 exePy3 = _findExecutable(3) |
|
241 if exePy2[0] == '' and exePy3[0] == '': |
161 error = QCoreApplication.translate("PyLintPlugin", |
242 error = QCoreApplication.translate("PyLintPlugin", |
162 "The pylint executable could not be found.") |
243 "The pylint executable could not be found.") |
|
244 return False |
|
245 elif exePy2[1] < '0.23.0' and exePy3[1] < '0.23.0': |
|
246 error = QCoreApplication.translate("PyLintPlugin", |
|
247 "PyLint version < 0.23.0.") |
163 return False |
248 return False |
164 else: |
249 else: |
165 return True |
250 return True |
166 |
251 |
167 |
252 |
391 @param mpName name of module or package to be checked (string) |
469 @param mpName name of module or package to be checked (string) |
392 @param forProject flag indicating a run for the project (boolean) |
470 @param forProject flag indicating a run for the project (boolean) |
393 """ |
471 """ |
394 if forEditor: |
472 if forEditor: |
395 parms = copy.deepcopy(self.__editorParms) |
473 parms = copy.deepcopy(self.__editorParms) |
|
474 editor = e5App().getObject("ViewManager").getOpenEditor(mpName) |
|
475 majorVersionStr = editor.getLanguage() |
396 else: |
476 else: |
397 parms = project.getData('CHECKERSPARMS', "PYLINT") |
477 parms = project.getData('CHECKERSPARMS', "PYLINT") |
398 exe = _findExecutable() |
478 majorVersionStr = project.getProjectLanguage() |
399 if exe is None: |
479 exe, version = {"Python": exePy2, "Python2": exePy2, |
|
480 "Python3": exePy3}.get(majorVersionStr) |
|
481 if exe == '': |
400 E5MessageBox.critical(None, |
482 E5MessageBox.critical(None, |
401 self.trUtf8("pylint"), |
483 self.trUtf8("pylint"), |
402 self.trUtf8("""The pylint executable could not be found.""")) |
484 self.trUtf8("""The pylint executable could not be found.""")) |
403 return |
485 return |
|
486 elif version < '0.23.0': |
|
487 E5MessageBox.critical(None, |
|
488 self.trUtf8("pylint"), |
|
489 self.trUtf8("PyLint version < 0.23.0.")) |
|
490 return |
404 |
491 |
405 from PyLint.PyLintConfigDialog import PyLintConfigDialog |
492 from PyLint.PyLintConfigDialog import PyLintConfigDialog |
406 dlg = PyLintConfigDialog(project.getProjectPath(), exe, parms) |
493 dlg = PyLintConfigDialog(project.getProjectPath(), exe, parms, version) |
407 if dlg.exec_() == QDialog.Accepted: |
494 if dlg.exec_() == QDialog.Accepted: |
408 args, parms = dlg.generateParameters() |
495 args, parms = dlg.generateParameters() |
409 self.__editorParms = copy.deepcopy(parms) |
496 self.__editorParms = copy.deepcopy(parms) |
410 if not forEditor: |
497 if not forEditor: |
411 project.setData('CHECKERSPARMS', "PYLINT", parms) |
498 project.setData('CHECKERSPARMS', "PYLINT", parms) |
412 |
499 |
413 # now do the call |
500 # now do the call |
414 from PyLint.PyLintExecDialog import PyLintExecDialog |
501 from PyLint.PyLintExecDialog import PyLintExecDialog |
415 dlg2 = PyLintExecDialog() |
502 dlg2 = PyLintExecDialog() |
416 try: |
503 reportFile = parms.get('reportFile', None) |
417 reportFile = parms['reportFile'] |
|
418 except KeyError: |
|
419 reportFile = None |
|
420 res = dlg2.start(args, mpName, reportFile, project.getProjectPath()) |
504 res = dlg2.start(args, mpName, reportFile, project.getProjectPath()) |
421 if res: |
505 if res: |
422 dlg2.show() |
506 dlg2.show() |
423 if forProject: |
507 if forProject: |
424 self.__pylintPDialog = dlg2 |
508 self.__pylintPDialog = dlg2 |