37 # Start-of-Header |
38 # Start-of-Header |
38 name = "PyLint Plugin" |
39 name = "PyLint Plugin" |
39 author = "Detlev Offenbach <detlev@die-offenbachs.de>" |
40 author = "Detlev Offenbach <detlev@die-offenbachs.de>" |
40 autoactivate = True |
41 autoactivate = True |
41 deactivateable = True |
42 deactivateable = True |
42 version = "5.3.0" |
43 version = "5.3.1" |
43 className = "PyLintPlugin" |
44 className = "PyLintPlugin" |
44 packageName = "PyLint" |
45 packageName = "PyLint" |
45 shortDescription = "Show the PyLint dialogs." |
46 shortDescription = "Show the PyLint dialogs." |
46 longDescription = """This plug-in implements the PyLint dialogs.""" \ |
47 longDescription = """This plug-in implements the PyLint dialogs.""" \ |
47 """ PyLint is used to check Python source files according to various rules.""" |
48 """ PyLint is used to check Python source files according to various""" \ |
|
49 """ rules.""" |
48 needsRestart = False |
50 needsRestart = False |
49 pyqtApi = 2 |
51 pyqtApi = 2 |
50 # End-of-Header |
52 # End-of-Header |
51 |
53 |
52 exePy2 = [] |
54 exePy2 = [] |
53 exePy3 = [] |
55 exePy3 = [] |
54 |
56 |
|
57 |
55 def exeDisplayDataList(): |
58 def exeDisplayDataList(): |
56 """ |
59 """ |
57 Public method to support the display of some executable info. |
60 Public method to support the display of some executable info. |
58 |
61 |
59 @return dictionary containing the data to query the presence of |
62 @return dictionary containing the data to query the presence of |
60 the executable |
63 the executable |
61 """ |
64 """ |
62 dataList = [] |
65 dataList = [] |
63 data = { |
66 data = { |
64 "programEntry": True, |
67 "programEntry": True, |
65 "header": QCoreApplication.translate("PyLintPlugin", |
68 "header": QCoreApplication.translate( |
66 "Checkers - Pylint"), |
69 "PyLintPlugin", "Checkers - Pylint"), |
67 "exe": 'dummypylint', |
70 "exe": 'dummypylint', |
68 "versionCommand": '--version', |
71 "versionCommand": '--version', |
69 "versionStartsWith": 'dummypylint', |
72 "versionStartsWith": 'dummypylint', |
70 "versionPosition": -1, |
73 "versionPosition": -1, |
71 "version": "", |
74 "version": "", |
91 proc.setProcessChannelMode(QProcess.MergedChannels) |
95 proc.setProcessChannelMode(QProcess.MergedChannels) |
92 proc.start(exe, ['--version']) |
96 proc.start(exe, ['--version']) |
93 finished = proc.waitForFinished(10000) |
97 finished = proc.waitForFinished(10000) |
94 if finished: |
98 if finished: |
95 output = str(proc.readAllStandardOutput(), |
99 output = str(proc.readAllStandardOutput(), |
96 Preferences.getSystem("IOEncoding"), |
100 Preferences.getSystem("IOEncoding"), |
97 'replace') |
101 'replace') |
98 versionRe = re.compile('^pylint', re.UNICODE) |
102 versionRe = re.compile('^pylint', re.UNICODE) |
99 for line in output.splitlines(): |
103 for line in output.splitlines(): |
100 if versionRe.search(line): |
104 if versionRe.search(line): |
101 version = line.split()[-1] |
105 version = line.split()[-1] |
102 version = version[:-1] |
106 version = version[:-1] |
103 break |
107 break |
104 else: |
108 else: |
105 version = '0.0.0' |
109 version = '0.0.0' |
106 return version |
110 return version |
|
111 |
107 |
112 |
108 def _findExecutable(majorVersion): |
113 def _findExecutable(majorVersion): |
109 """ |
114 """ |
110 Restricted function to determine the name and path of the executable. |
115 Restricted function to determine the name and path of the executable. |
111 |
116 |
144 return None |
149 return None |
145 return None |
150 return None |
146 |
151 |
147 for minorVersion in minorVersions: |
152 for minorVersion in minorVersions: |
148 versionStr = '{0}.{1}'.format(majorVersion, minorVersion) |
153 versionStr = '{0}.{1}'.format(majorVersion, minorVersion) |
149 exePath = getExePath(winreg.HKEY_CURRENT_USER, |
154 exePath = getExePath( |
|
155 winreg.HKEY_CURRENT_USER, |
150 winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr) |
156 winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr) |
151 |
157 |
152 if exePath is not None: |
158 if exePath is not None: |
153 executables.add(exePath) |
159 executables.add(exePath) |
154 exePath = getExePath(winreg.HKEY_LOCAL_MACHINE, |
160 exePath = getExePath( |
|
161 winreg.HKEY_LOCAL_MACHINE, |
155 winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr) |
162 winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr) |
156 |
163 |
157 # Even on Intel 64-bit machines it's 'AMD64' |
164 # Even on Intel 64-bit machines it's 'AMD64' |
158 if platform.machine() == 'AMD64': |
165 if platform.machine() == 'AMD64': |
159 if exePath is not None: |
166 if exePath is not None: |
160 executables.add(exePath) |
167 executables.add(exePath) |
161 exePath = getExePath(winreg.HKEY_CURRENT_USER, |
168 exePath = getExePath( |
|
169 winreg.HKEY_CURRENT_USER, |
162 winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr) |
170 winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr) |
163 |
171 |
164 if exePath is not None: |
172 if exePath is not None: |
165 executables.add(exePath) |
173 executables.add(exePath) |
166 exePath = getExePath(winreg.HKEY_LOCAL_MACHINE, |
174 exePath = getExePath( |
|
175 winreg.HKEY_LOCAL_MACHINE, |
167 winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr) |
176 winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr) |
168 |
177 |
169 if exePath is not None: |
178 if exePath is not None: |
170 executables.add(exePath) |
179 executables.add(exePath) |
171 else: |
180 else: |
172 # |
181 # |
173 # Linux, Unix ... |
182 # Linux, Unix ... |
174 pylintScript = 'pylint' |
183 pylintScript = 'pylint' |
175 scriptSuffixes = ["", |
184 scriptSuffixes = ["", |
176 "-python{0}".format(majorVersion)] |
185 "-python{0}".format(majorVersion)] |
177 for minorVersion in minorVersions: |
186 for minorVersion in minorVersions: |
178 scriptSuffixes.append( |
187 scriptSuffixes.append( |
179 "-python{0}.{1}".format(majorVersion, minorVersion)) |
188 "-python{0}.{1}".format(majorVersion, minorVersion)) |
180 # There could be multiple pylint executables in the path |
189 # There could be multiple pylint executables in the path |
181 # e.g. for different python variants |
190 # e.g. for different python variants |
182 path = Utilities.getEnvironmentEntry('PATH') |
191 path = Utilities.getEnvironmentEntry('PATH') |
183 # environment variable not defined |
192 # environment variable not defined |
184 if path is None: |
193 if path is None: |
237 global error, exePy2, exePy3 |
247 global error, exePy2, exePy3 |
238 |
248 |
239 exePy2 = _findExecutable(2) |
249 exePy2 = _findExecutable(2) |
240 exePy3 = _findExecutable(3) |
250 exePy3 = _findExecutable(3) |
241 if exePy2[0] == '' and exePy3[0] == '': |
251 if exePy2[0] == '' and exePy3[0] == '': |
242 error = QCoreApplication.translate("PyLintPlugin", |
252 error = QCoreApplication.translate( |
243 "The pylint executable could not be found.") |
253 "PyLintPlugin", "The pylint executable could not be found.") |
244 return False |
254 return False |
245 elif exePy2[1] < '0.23.0' and exePy3[1] < '0.23.0': |
255 elif exePy2[1] < '0.23.0' and exePy3[1] < '0.23.0': |
246 error = QCoreApplication.translate("PyLintPlugin", |
256 error = QCoreApplication.translate( |
247 "PyLint version < 0.23.0.") |
257 "PyLintPlugin", "PyLint version < 0.23.0.") |
248 return False |
258 return False |
249 else: |
259 else: |
250 return True |
260 return True |
251 |
261 |
252 |
262 |
300 if not _checkProgram(): |
310 if not _checkProgram(): |
301 return None, False |
311 return None, False |
302 |
312 |
303 menu = e5App().getObject("Project").getMenu("Checks") |
313 menu = e5App().getObject("Project").getMenu("Checks") |
304 if menu: |
314 if menu: |
305 self.__projectAct = E5Action(self.trUtf8('Run PyLint'), |
315 self.__projectAct = E5Action( |
306 self.trUtf8('Run &PyLint...'), 0, 0, |
316 self.trUtf8('Run PyLint'), |
307 self, 'project_check_pylint') |
317 self.trUtf8('Run &PyLint...'), 0, 0, |
|
318 self, 'project_check_pylint') |
308 self.__projectAct.setStatusTip( |
319 self.__projectAct.setStatusTip( |
309 self.trUtf8('Check project, packages or modules with pylint.')) |
320 self.trUtf8('Check project, packages or modules with pylint.')) |
310 self.__projectAct.setWhatsThis(self.trUtf8( |
321 self.__projectAct.setWhatsThis(self.trUtf8( |
311 """<b>Run PyLint...</b>""" |
322 """<b>Run PyLint...</b>""" |
312 """<p>This checks the project, packages or modules using pylint.</p>""" |
323 """<p>This checks the project, packages or modules using""" |
|
324 """ pylint.</p>""" |
313 )) |
325 )) |
314 self.__projectAct.triggered[()].connect(self.__projectPylint) |
326 self.__projectAct.triggered[()].connect(self.__projectPylint) |
315 e5App().getObject("Project").addE5Actions([self.__projectAct]) |
327 e5App().getObject("Project").addE5Actions([self.__projectAct]) |
316 menu.addAction(self.__projectAct) |
328 menu.addAction(self.__projectAct) |
317 |
329 |
318 self.__projectShowAct = E5Action(self.trUtf8('Show PyLint Dialog'), |
330 self.__projectShowAct = E5Action( |
319 self.trUtf8('Show Py&Lint Dialog...'), 0, 0, |
331 self.trUtf8('Show PyLint Dialog'), |
320 self, 'project_check_pylintshow') |
332 self.trUtf8('Show Py&Lint Dialog...'), 0, 0, |
321 self.__projectShowAct.setStatusTip( |
333 self, 'project_check_pylintshow') |
322 self.trUtf8('Show the PyLint dialog with the results of the last run.')) |
334 self.__projectShowAct.setStatusTip(self.trUtf8( |
|
335 'Show the PyLint dialog with the results of the last run.')) |
323 self.__projectShowAct.setWhatsThis(self.trUtf8( |
336 self.__projectShowAct.setWhatsThis(self.trUtf8( |
324 """<b>Show PyLint Dialog...</b>""" |
337 """<b>Show PyLint Dialog...</b>""" |
325 """<p>This shows the PyLint dialog with the results""" |
338 """<p>This shows the PyLint dialog with the results""" |
326 """ of the last run.</p>""" |
339 """ of the last run.</p>""" |
327 )) |
340 )) |
328 self.__projectShowAct.triggered[()].connect(self.__projectPylintShow) |
341 self.__projectShowAct.triggered[()].connect( |
|
342 self.__projectPylintShow) |
329 e5App().getObject("Project").addE5Actions([self.__projectShowAct]) |
343 e5App().getObject("Project").addE5Actions([self.__projectShowAct]) |
330 menu.addAction(self.__projectShowAct) |
344 menu.addAction(self.__projectShowAct) |
331 |
345 |
332 self.__editorAct = E5Action(self.trUtf8('Run PyLint'), |
346 self.__editorAct = E5Action( |
333 self.trUtf8('Run &PyLint...'), 0, 0, |
347 self.trUtf8('Run PyLint'), |
334 self, "") |
348 self.trUtf8('Run &PyLint...'), 0, 0, |
|
349 self, "") |
335 self.__editorAct.setWhatsThis(self.trUtf8( |
350 self.__editorAct.setWhatsThis(self.trUtf8( |
336 """<b>Run PyLint...</b>""" |
351 """<b>Run PyLint...</b>""" |
337 """<p>This checks the loaded module using pylint.</p>""" |
352 """<p>This checks the loaded module using pylint.</p>""" |
338 )) |
353 )) |
339 self.__editorAct.triggered[()].connect(self.__editorPylint) |
354 self.__editorAct.triggered[()].connect(self.__editorPylint) |
340 |
355 |
341 e5App().getObject("Project").showMenu.connect(self.__projectShowMenu) |
356 e5App().getObject("Project").showMenu.connect(self.__projectShowMenu) |
342 e5App().getObject("ProjectBrowser").getProjectBrowser("sources")\ |
357 e5App().getObject("ProjectBrowser").getProjectBrowser("sources")\ |
343 .showMenu.connect(self.__projectBrowserShowMenu) |
358 .showMenu.connect(self.__projectBrowserShowMenu) |
344 e5App().getObject("ViewManager").editorOpenedEd.connect(self.__editorOpened) |
359 e5App().getObject("ViewManager").editorOpenedEd.connect( |
345 e5App().getObject("ViewManager").editorClosedEd.connect(self.__editorClosed) |
360 self.__editorOpened) |
|
361 e5App().getObject("ViewManager").editorClosedEd.connect( |
|
362 self.__editorClosed) |
346 |
363 |
347 for editor in e5App().getObject("ViewManager").getOpenEditors(): |
364 for editor in e5App().getObject("ViewManager").getOpenEditors(): |
348 self.__editorOpened(editor) |
365 self.__editorOpened(editor) |
349 |
366 |
350 error = "" |
367 error = "" |
352 |
369 |
353 def deactivate(self): |
370 def deactivate(self): |
354 """ |
371 """ |
355 Public method to deactivate this plugin. |
372 Public method to deactivate this plugin. |
356 """ |
373 """ |
357 e5App().getObject("Project").showMenu.disconnect(self.__projectShowMenu) |
374 e5App().getObject("Project").showMenu.disconnect( |
|
375 self.__projectShowMenu) |
358 e5App().getObject("ProjectBrowser").getProjectBrowser("sources")\ |
376 e5App().getObject("ProjectBrowser").getProjectBrowser("sources")\ |
359 .showMenu.disconnect(self.__projectBrowserShowMenu) |
377 .showMenu.disconnect(self.__projectBrowserShowMenu) |
360 e5App().getObject("ViewManager").editorOpenedEd.disconnect(self.__editorOpened) |
378 e5App().getObject("ViewManager").editorOpenedEd.disconnect( |
361 e5App().getObject("ViewManager").editorClosedEd.disconnect(self.__editorClosed) |
379 self.__editorOpened) |
|
380 e5App().getObject("ViewManager").editorClosedEd.disconnect( |
|
381 self.__editorClosed) |
362 |
382 |
363 menu = e5App().getObject("Project").getMenu("Checks") |
383 menu = e5App().getObject("Project").getMenu("Checks") |
364 if menu: |
384 if menu: |
365 if self.__projectAct: |
385 if self.__projectAct: |
366 menu.removeAction(self.__projectAct) |
386 menu.removeAction(self.__projectAct) |
367 e5App().getObject("Project").removeE5Actions([self.__projectAct]) |
387 e5App().getObject("Project").removeE5Actions( |
|
388 [self.__projectAct]) |
368 if self.__projectShowAct: |
389 if self.__projectShowAct: |
369 menu.removeAction(self.__projectShowAct) |
390 menu.removeAction(self.__projectShowAct) |
370 e5App().getObject("Project").removeE5Actions([self.__projectShowAct]) |
391 e5App().getObject("Project").removeE5Actions( |
|
392 [self.__projectShowAct]) |
371 |
393 |
372 if self.__projectBrowserMenu: |
394 if self.__projectBrowserMenu: |
373 if self.__projectBrowserAct: |
395 if self.__projectBrowserAct: |
374 self.__projectBrowserMenu.removeAction(self.__projectBrowserAct) |
396 self.__projectBrowserMenu.removeAction( |
|
397 self.__projectBrowserAct) |
375 if self.__projectBrowserShowAct: |
398 if self.__projectBrowserShowAct: |
376 self.__projectBrowserMenu.removeAction(self.__projectBrowserShowAct) |
399 self.__projectBrowserMenu.removeAction( |
|
400 self.__projectBrowserShowAct) |
377 |
401 |
378 for editor in self.__editors: |
402 for editor in self.__editors: |
379 editor.showMenu.disconnect(self.__editorShowMenu) |
403 editor.showMenu.disconnect(self.__editorShowMenu) |
380 menu = editor.getMenu("Checks") |
404 menu = editor.getMenu("Checks") |
381 if menu is not None: |
405 if menu is not None: |
397 loaded = translator.load(translation, locale_dir) |
421 loaded = translator.load(translation, locale_dir) |
398 if loaded: |
422 if loaded: |
399 self.__translator = translator |
423 self.__translator = translator |
400 e5App().installTranslator(self.__translator) |
424 e5App().installTranslator(self.__translator) |
401 else: |
425 else: |
402 print("Warning: translation file '{0}' could not be loaded."\ |
426 print("Warning: translation file '{0}' could not be" |
403 .format(translation)) |
427 " loaded.".format(translation)) |
404 print("Using default.") |
428 print("Using default.") |
405 |
429 |
406 def __projectShowMenu(self, menuName, menu): |
430 def __projectShowMenu(self, menuName, menu): |
407 """ |
431 """ |
408 Private slot called, when the the project menu or a submenu is |
432 Private slot called, when the the project menu or a submenu is |
426 |
450 |
427 @param menuName name of the menu to be shown (string) |
451 @param menuName name of the menu to be shown (string) |
428 @param menu reference to the menu (QMenu) |
452 @param menu reference to the menu (QMenu) |
429 """ |
453 """ |
430 if menuName == "Checks" and \ |
454 if menuName == "Checks" and \ |
431 e5App().getObject("Project").getProjectLanguage().startswith("Python"): |
455 e5App().getObject("Project").getProjectLanguage()\ |
|
456 .startswith("Python"): |
432 self.__projectBrowserMenu = menu |
457 self.__projectBrowserMenu = menu |
433 if self.__projectBrowserAct is None: |
458 if self.__projectBrowserAct is None: |
434 self.__projectBrowserAct = E5Action(self.trUtf8('Run PyLint'), |
459 self.__projectBrowserAct = E5Action( |
435 self.trUtf8('Run &PyLint...'), 0, 0, |
460 self.trUtf8('Run PyLint'), |
436 self, '') |
461 self.trUtf8('Run &PyLint...'), 0, 0, |
|
462 self, '') |
437 self.__projectBrowserAct.setWhatsThis(self.trUtf8( |
463 self.__projectBrowserAct.setWhatsThis(self.trUtf8( |
438 """<b>Run PyLint...</b>""" |
464 """<b>Run PyLint...</b>""" |
439 """<p>This checks the project, packages or modules""" |
465 """<p>This checks the project, packages or modules""" |
440 """ using pylint.</p>""" |
466 """ using pylint.</p>""" |
441 )) |
467 )) |
442 self.__projectBrowserAct.triggered[()].connect( |
468 self.__projectBrowserAct.triggered[()].connect( |
443 self.__projectBrowserPylint) |
469 self.__projectBrowserPylint) |
444 |
470 |
445 if self.__projectBrowserShowAct is None: |
471 if self.__projectBrowserShowAct is None: |
446 self.__projectBrowserShowAct = \ |
472 self.__projectBrowserShowAct = E5Action( |
447 E5Action(self.trUtf8('Show PyLint Dialog'), |
473 self.trUtf8('Show PyLint Dialog'), |
448 self.trUtf8('Show Py&Lint Dialog...'), 0, 0, |
474 self.trUtf8('Show Py&Lint Dialog...'), 0, 0, |
449 self, '') |
475 self, '') |
450 self.__projectBrowserShowAct.setWhatsThis(self.trUtf8( |
476 self.__projectBrowserShowAct.setWhatsThis(self.trUtf8( |
451 """<b>Show PyLint Dialog...</b>""" |
477 """<b>Show PyLint Dialog...</b>""" |
452 """<p>This shows the PyLint dialog with the results""" |
478 """<p>This shows the PyLint dialog with the results""" |
453 """ of the last run.</p>""" |
479 """ of the last run.</p>""" |
454 )) |
480 )) |
457 |
483 |
458 if not self.__projectBrowserAct in menu.actions(): |
484 if not self.__projectBrowserAct in menu.actions(): |
459 menu.addAction(self.__projectBrowserAct) |
485 menu.addAction(self.__projectBrowserAct) |
460 if not self.__projectBrowserShowAct in menu.actions(): |
486 if not self.__projectBrowserShowAct in menu.actions(): |
461 menu.addAction(self.__projectBrowserShowAct) |
487 menu.addAction(self.__projectBrowserShowAct) |
462 self.__projectBrowserShowAct.setEnabled(self.__pylintPsbDialog is not None) |
488 self.__projectBrowserShowAct.setEnabled( |
|
489 self.__pylintPsbDialog is not None) |
463 |
490 |
464 def __pyLint(self, project, mpName, forProject, forEditor=False): |
491 def __pyLint(self, project, mpName, forProject, forEditor=False): |
465 """ |
492 """ |
466 Private method used to perform a PyLint run. |
493 Private method used to perform a PyLint run. |
467 |
494 |
468 @param project reference to the Project object |
495 @param project reference to the Project object |
469 @param mpName name of module or package to be checked (string) |
496 @param mpName name of module or package to be checked (string) |
470 @param forProject flag indicating a run for the project (boolean) |
497 @param forProject flag indicating a run for the project (boolean) |
|
498 @param forEditor flag indicating a run for an editor (boolean) |
471 """ |
499 """ |
472 if forEditor: |
500 if forEditor: |
473 parms = copy.deepcopy(self.__editorParms) |
501 parms = copy.deepcopy(self.__editorParms) |
474 editor = e5App().getObject("ViewManager").getOpenEditor(mpName) |
502 editor = e5App().getObject("ViewManager").getOpenEditor(mpName) |
475 majorVersionStr = editor.getLanguage() |
503 majorVersionStr = editor.getLanguage() |
476 else: |
504 else: |
477 parms = project.getData('CHECKERSPARMS', "PYLINT") |
505 parms = project.getData('CHECKERSPARMS', "PYLINT") |
478 majorVersionStr = project.getProjectLanguage() |
506 majorVersionStr = project.getProjectLanguage() |
479 exe, version = {"Python": exePy2, "Python2": exePy2, |
507 exe, version = {"Python": exePy2, "Python2": exePy2, |
480 "Python3": exePy3}.get(majorVersionStr) |
508 "Python3": exePy3}.get(majorVersionStr) |
481 if exe == '': |
509 if exe == '': |
482 E5MessageBox.critical(None, |
510 E5MessageBox.critical( |
|
511 None, |
483 self.trUtf8("pylint"), |
512 self.trUtf8("pylint"), |
484 self.trUtf8("""The pylint executable could not be found.""")) |
513 self.trUtf8("""The pylint executable could not be found.""")) |
485 return |
514 return |
486 elif version < '0.23.0': |
515 elif version < '0.23.0': |
487 E5MessageBox.critical(None, |
516 E5MessageBox.critical( |
|
517 None, |
488 self.trUtf8("pylint"), |
518 self.trUtf8("pylint"), |
489 self.trUtf8("PyLint version < 0.23.0.")) |
519 self.trUtf8("PyLint version < 0.23.0.")) |
490 return |
520 return |
491 |
521 |
492 from PyLint.PyLintConfigDialog import PyLintConfigDialog |
522 from PyLint.PyLintConfigDialog import PyLintConfigDialog |