Sat, 23 Feb 2013 15:12:21 +0100
Modified the code to do lazy import.
# -*- coding: utf-8 -*- # Copyright (c) 2007 - 2013 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the PyLint plug-in. """ import os import sys import copy from PyQt4.QtCore import QObject, QTranslator, QCoreApplication from PyQt4.QtGui import QDialog from E5Gui.E5Application import e5App from E5Gui.E5Action import E5Action from E5Gui import E5MessageBox import Utilities # Start-of-Header name = "PyLint Plugin" author = "Detlev Offenbach <detlev@die-offenbachs.de>" autoactivate = True deactivateable = True version = "5.2.0" className = "PyLintPlugin" packageName = "PyLint" shortDescription = "Show the PyLint dialogs." longDescription = """This plug-in implements the PyLint dialogs.""" \ """ PyLint is used to check Python source files according to various rules.""" needsRestart = False pyqtApi = 2 # End-of-Header error = "" def exeDisplayData(): """ Public method to support the display of some executable info. @return dictionary containing the data to query the presence of the executable """ data = { "programEntry": True, "header": QCoreApplication.translate("PyLintPlugin", "Checkers - Pylint"), "exe": 'dummypylint', "versionCommand": '--version', "versionStartsWith": 'dummypylint', "versionPosition": -1, "version": "", "versionCleanup": (0, -1), } exe = _findExecutable() if exe: data["exe"] = exe data["versionStartsWith"] = 'pylint' return data def _findExecutable(): """ Restricted function to determine the name and path of the executable. @return path name of the executable (string) """ 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) except ImportError: try: import winreg except ImportError: # give up ... return None 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 else: # # Linux, Unix ... pylintScript = 'pylint' scriptSuffixes = ["", "-python{0}".format(sys.version[:1]), "-python{0}".format(sys.version[:3])] # 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 # step 1: determine possible candidates exes = [] dirs = path.split(os.pathsep) for dir in dirs: for suffix in scriptSuffixes: exe = os.path.join(dir, pylintScript + suffix) if os.access(exe, os.X_OK): exes.append(exe) # step 2: determine the Python 3 variant found = False if Utilities.isMacPlatform(): checkStr = "Python.framework/Versions/3".lower() else: checkStr = "python3" for exe in exes: try: f = open(exe, "r") line0 = f.readline() if checkStr in line0.lower(): found = True finally: f.close() if found: return exe return None def _checkProgram(): """ Restricted function to check the availability of pylint. @return flag indicating availability (boolean) """ global error if _findExecutable() is None: error = QCoreApplication.translate("PyLintPlugin", "The pylint executable could not be found.") return False else: return True class PyLintPlugin(QObject): """ Class implementing the PyLint plug-in. """ def __init__(self, ui): """ Constructor @param ui reference to the user interface object (UI.UserInterface) """ QObject.__init__(self, ui) self.__ui = ui self.__initialize() self.__translator = None self.__loadTranslator() def __initialize(self): """ Private slot to (re)initialize the plugin. """ self.__projectAct = None self.__projectShowAct = None self.__pylintPDialog = None self.__projectBrowserAct = None self.__projectBrowserShowAct = None self.__projectBrowserMenu = None self.__pylintPsbDialog = None self.__editors = [] self.__editorAct = None self.__editorPylintDialog = None self.__editorParms = None def activate(self): """ Public method to activate this plugin. @return tuple of None and activation status (boolean) """ global error # 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'), self.trUtf8('Run &PyLint...'), 0, 0, self, 'project_check_pylint') self.__projectAct.setStatusTip( self.trUtf8('Check project, packages or modules with pylint.')) self.__projectAct.setWhatsThis(self.trUtf8( """<b>Run PyLint...</b>""" """<p>This checks the project, packages or modules using pylint.</p>""" )) self.__projectAct.triggered[()].connect(self.__projectPylint) e5App().getObject("Project").addE5Actions([self.__projectAct]) menu.addAction(self.__projectAct) self.__projectShowAct = E5Action(self.trUtf8('Show PyLint Dialog'), self.trUtf8('Show Py&Lint Dialog...'), 0, 0, self, 'project_check_pylintshow') self.__projectShowAct.setStatusTip( self.trUtf8('Show the PyLint dialog with the results of the last run.')) self.__projectShowAct.setWhatsThis(self.trUtf8( """<b>Show PyLint Dialog...</b>""" """<p>This shows the PyLint dialog with the results""" """ of the last run.</p>""" )) self.__projectShowAct.triggered[()].connect(self.__projectPylintShow) e5App().getObject("Project").addE5Actions([self.__projectShowAct]) menu.addAction(self.__projectShowAct) self.__editorAct = E5Action(self.trUtf8('Run PyLint'), self.trUtf8('Run &PyLint...'), 0, 0, self, "") self.__editorAct.setWhatsThis(self.trUtf8( """<b>Run PyLint...</b>""" """<p>This checks the loaded module using pylint.</p>""" )) self.__editorAct.triggered[()].connect(self.__editorPylint) e5App().getObject("Project").showMenu.connect(self.__projectShowMenu) e5App().getObject("ProjectBrowser").getProjectBrowser("sources")\ .showMenu.connect(self.__projectBrowserShowMenu) e5App().getObject("ViewManager").editorOpenedEd.connect(self.__editorOpened) e5App().getObject("ViewManager").editorClosedEd.connect(self.__editorClosed) for editor in e5App().getObject("ViewManager").getOpenEditors(): self.__editorOpened(editor) error = "" return None, True def deactivate(self): """ Public method to deactivate this plugin. """ e5App().getObject("Project").showMenu.disconnect(self.__projectShowMenu) e5App().getObject("ProjectBrowser").getProjectBrowser("sources")\ .showMenu.disconnect(self.__projectBrowserShowMenu) e5App().getObject("ViewManager").editorOpenedEd.disconnect(self.__editorOpened) e5App().getObject("ViewManager").editorClosedEd.disconnect(self.__editorClosed) menu = e5App().getObject("Project").getMenu("Checks") if menu: if self.__projectAct: menu.removeAction(self.__projectAct) e5App().getObject("Project").removeE5Actions([self.__projectAct]) if self.__projectShowAct: menu.removeAction(self.__projectShowAct) e5App().getObject("Project").removeE5Actions([self.__projectShowAct]) if self.__projectBrowserMenu: if self.__projectBrowserAct: self.__projectBrowserMenu.removeAction(self.__projectBrowserAct) if self.__projectBrowserShowAct: self.__projectBrowserMenu.removeAction(self.__projectBrowserShowAct) for editor in self.__editors: editor.showMenu.disconnect(self.__editorShowMenu) menu = editor.getMenu("Checks") if menu is not None: menu.removeAction(self.__editorAct) self.__initialize() def __loadTranslator(self): """ Private method to load the translation file. """ if self.__ui is not None: loc = self.__ui.getLocale() if loc and loc != "C": locale_dir = \ os.path.join(os.path.dirname(__file__), "PyLint", "i18n") translation = "pylint_{0}".format(loc) translator = QTranslator(None) loaded = translator.load(translation, locale_dir) if loaded: self.__translator = translator e5App().installTranslator(self.__translator) else: print("Warning: translation file '{0}' could not be loaded."\ .format(translation)) print("Using default.") def __projectShowMenu(self, menuName, menu): """ Private slot called, when the the project menu or a submenu is about to be shown. @param menuName name of the menu to be shown (string) @param menu reference to the menu (QMenu) """ if menuName == "Checks": if self.__projectAct is not None: self.__projectAct.setEnabled( e5App().getObject("Project").getProjectLanguage() == "Python3") if self.__projectShowAct is not None: self.__projectShowAct.setEnabled( e5App().getObject("Project").getProjectLanguage() == "Python3") self.__projectShowAct.setEnabled(self.__pylintPDialog is not None) def __projectBrowserShowMenu(self, menuName, menu): """ Private slot called, when the the project browser menu or a submenu is about to be shown. @param menuName name of the menu to be shown (string) @param menu reference to the menu (QMenu) """ if menuName == "Checks" and \ e5App().getObject("Project").getProjectLanguage() == "Python3": self.__projectBrowserMenu = menu if self.__projectBrowserAct is None: self.__projectBrowserAct = E5Action(self.trUtf8('Run PyLint'), self.trUtf8('Run &PyLint...'), 0, 0, self, '') self.__projectBrowserAct.setWhatsThis(self.trUtf8( """<b>Run PyLint...</b>""" """<p>This checks the project, packages or modules""" """ using pylint.</p>""" )) self.__projectBrowserAct.triggered[()].connect( self.__projectBrowserPylint) if self.__projectBrowserShowAct is None: self.__projectBrowserShowAct = \ E5Action(self.trUtf8('Show PyLint Dialog'), self.trUtf8('Show Py&Lint Dialog...'), 0, 0, self, '') self.__projectBrowserShowAct.setWhatsThis(self.trUtf8( """<b>Show PyLint Dialog...</b>""" """<p>This shows the PyLint dialog with the results""" """ of the last run.</p>""" )) self.__projectBrowserShowAct.triggered[()].connect( self.__projectBrowserPylintShow) if not self.__projectBrowserAct in menu.actions(): menu.addAction(self.__projectBrowserAct) if not self.__projectBrowserShowAct in menu.actions(): menu.addAction(self.__projectBrowserShowAct) self.__projectBrowserShowAct.setEnabled(self.__pylintPsbDialog is not None) def __pyLint(self, project, mpName, forProject, forEditor=False): """ Private method used to perform a PyLint run. @param project reference to the Project object @param mpName name of module or package to be checked (string) @param forProject flag indicating a run for the project (boolean) """ if forEditor: parms = copy.deepcopy(self.__editorParms) else: parms = project.getData('CHECKERSPARMS', "PYLINT") exe = _findExecutable() if exe is None: E5MessageBox.critical(None, self.trUtf8("pylint"), self.trUtf8("""The pylint executable could not be found.""")) return from PyLint.PyLintConfigDialog import PyLintConfigDialog dlg = PyLintConfigDialog(project.getProjectPath(), exe, parms) if dlg.exec_() == QDialog.Accepted: args, parms = dlg.generateParameters() self.__editorParms = copy.deepcopy(parms) if not forEditor: project.setData('CHECKERSPARMS', "PYLINT", parms) # now do the call from PyLint.PyLintExecDialog import PyLintExecDialog dlg2 = PyLintExecDialog() try: reportFile = parms['reportFile'] except KeyError: reportFile = None res = dlg2.start(args, mpName, reportFile, project.getProjectPath()) if res: dlg2.show() if forProject: self.__pylintPDialog = dlg2 elif forEditor: self.__editorPylintDialog = dlg2 else: self.__pylintPsbDialog = dlg2 def __projectPylint(self): """ Public slot used to check the project files with Pylint. """ project = e5App().getObject("Project") project.saveAllScripts() self.__pyLint(project, project.getProjectPath(), True) def __projectPylintShow(self): """ Public slot to show the PyLint dialog with the results of the last run. """ if self.__pylintPDialog is not None: self.__pylintPDialog.show() def __projectBrowserPylint(self): """ Private method to handle the Pylint context menu action of the project sources browser. """ project = e5App().getObject("Project") browser = e5App().getObject("ProjectBrowser").getProjectBrowser("sources") itm = browser.model().item(browser.currentIndex()) try: fn = itm.fileName() except AttributeError: fn = itm.dirName() self.__pyLint(project, fn, False) def __projectBrowserPylintShow(self): """ Public slot to show the PyLint dialog with the results of the last run. """ if self.__pylintPsbDialog is not None: self.__pylintPsbDialog.show() def __editorOpened(self, editor): """ Private slot called, when a new editor was opened. @param editor reference to the new editor (QScintilla.Editor) """ menu = editor.getMenu("Checks") if menu is not None: menu.addAction(self.__editorAct) editor.showMenu.connect(self.__editorShowMenu) self.__editors.append(editor) def __editorClosed(self, editor): """ Private slot called, when an editor was closed. @param editor reference to the editor (QScintilla.Editor) """ try: self.__editors.remove(editor) except ValueError: pass def __editorShowMenu(self, menuName, menu, editor): """ Private slot called, when the the editor context menu or a submenu is about to be shown. @param menuName name of the menu to be shown (string) @param menu reference to the menu (QMenu) @param editor reference to the editor (QScintilla.Editor) """ if menuName == "Checks": if not self.__editorAct in menu.actions(): menu.addAction(self.__editorAct) self.__editorAct.setEnabled(editor.isPy3File()) def __editorPylint(self): """ Private slot to handle the Pylint context menu action of the editors. """ editor = e5App().getObject("ViewManager").activeWindow() if editor is not None: if not editor.checkDirty(): return fn = editor.getFileName() project = e5App().getObject("Project") self.__pyLint(project, fn, False, True)