Plugins/PluginCodeStyleChecker.py

Sat, 22 Mar 2014 09:12:19 +0100

author
T.Rzepka <Tobias.Rzepka@gmail.com>
date
Sat, 22 Mar 2014 09:12:19 +0100
branch
BgService
changeset 3413
5e63f809732a
parent 3145
a9de05d4a22f
child 3442
927186c0d409
permissions
-rw-r--r--

Code style checker: Translations extracted and refactored.

# -*- coding: utf-8 -*-

# Copyright (c) 2011 - 2013 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the code style checker plug-in.
"""

from __future__ import unicode_literals

import os

from PyQt4.QtCore import QObject, pyqtSignal
from PyQt4.QtGui import QApplication

from E5Gui.E5Application import e5App
from E5Gui.E5Action import E5Action

from Utilities import determinePythonVersion
import Preferences

# Start-Of-Header
name = "Code Style Checker Plugin"
author = "Detlev Offenbach <detlev@die-offenbachs.de>"
autoactivate = True
deactivateable = True
version = "5.4.0"
className = "CodeStyleCheckerPlugin"
packageName = "__core__"
shortDescription = "Show the Python Code Style Checker dialog."
longDescription = """This plugin implements the Python Code Style""" \
    """ Checker dialog. A PEP-8 checker is used to check Python source""" \
    """ files for compliance to the code style conventions given in PEP-8.""" \
    """ A PEP-257 checker is used to check Python source files for""" \
    """ compliance to docstring conventions given in PEP-257 and an""" \
    """ eric5 variant is used to check against eric conventions."""
pyqtApi = 2
# End-Of-Header


error = ""


class CodeStyleCheckerPlugin(QObject):
    """
    Class implementing the code style checker plug-in.
    
    @signal styleChecked(str, dict, int, list) emited when the style check was
        done.
    """
    styleChecked = pyqtSignal(str, dict, int, list)
    
    def __init__(self, ui):
        """
        Constructor
        
        @param ui reference to the user interface object (UI.UserInterface)
        """
        super(CodeStyleCheckerPlugin, self).__init__(ui)
        self.__ui = ui
        self.__initialize()
        
        self.backgroundService = e5App().getObject("BackgroundService")
        
        path = os.path.join(
            os.path.dirname(__file__), 'CheckerPlugins', 'CodeStyleChecker')
        for lang in ['Python2', 'Python3']:
            self.backgroundService.serviceConnect(
                'style', lang, path, 'CodeStyleChecker',
                self.__translateStyleCheck,
                lambda fx, fn, ver, msg: self.styleChecked.emit(
                    fn, {}, 0, [[1, 1, '---- ' + msg, False, False]]))

    def __initialize(self):
        """
        Private slot to (re)initialize the plugin.
        """
        self.__projectAct = None
        self.__projectCodeStyleCheckerDialog = None
        
        self.__projectBrowserAct = None
        self.__projectBrowserMenu = None
        self.__projectBrowserCodeStyleCheckerDialog = None
        
        self.__editors = []
        self.__editorAct = None
        self.__editorCodeStyleCheckerDialog = None

    def styleCheck(self, lang, filename, source, args):
        """
        Method to prepare a style check on one Python source file in another
        task.

        @param lang language of the file or None to determine by internal
            algorithm (str or None)
        @param filename source filename (string)
        @param source string containing the code to check (string)
        @param args arguments used by the codeStyleCheck function (list of
            excludeMessages (str), includeMessages (str), repeatMessages
            (bool), fixCodes (str), noFixCodes (str), fixIssues (bool),
            maxLineLength (int), hangClosing (bool), docType (str), errors
            (list of str), eol (str), encoding (str))
        """
        if lang is None:
            lang = 'Python{0}'.format(determinePythonVersion(filename, source))
        if lang not in ['Python2', 'Python3']:
            return
        
        data = [source, args]
        self.backgroundService.enqueueRequest('style', lang, filename, data)
    
    def __translateStyleCheck(self, fn, codeStyleCheckerStats, results):
        """
        Privat slot called after perfoming a style check on one file.
        
        @param fn filename of the just checked file (str)
        @param codeStyleCheckerStats stats of style and name check (dict)
        @param results tuple for each found violation of style (tuple of
            lineno (int), position (int), text (str), fixed (bool),
            autofixing (bool), fixedMsg (str))
        """
        from CheckerPlugins.CodeStyleChecker.translations import \
            getTranslatedMessage
            
        fixes = 0
        for result in results:
            msg = getTranslatedMessage(result[2])
        
            fixedMsg = result.pop()
            if fixedMsg:
                fixes += 1
                trFixedMsg = getTranslatedMessage(fixedMsg)
                
                msg += "\n" + QApplication.translate(
                    'CodeStyleCheckerDialog', "Fix: {0}").format(trFixedMsg)
            
            result[2] = msg
        self.styleChecked.emit(fn, codeStyleCheckerStats, fixes, results)

    def activate(self):
        """
        Public method to activate this plugin.
        
        @return tuple of None and activation status (boolean)
        """
        menu = e5App().getObject("Project").getMenu("Checks")
        if menu:
            self.__projectAct = E5Action(
                self.trUtf8('Check Code Style'),
                self.trUtf8('&Code Style...'), 0, 0,
                self, 'project_check_pep8')
            self.__projectAct.setStatusTip(
                self.trUtf8('Check code style.'))
            self.__projectAct.setWhatsThis(self.trUtf8(
                """<b>Check Code Style...</b>"""
                """<p>This checks Python files for compliance to the"""
                """ code style conventions given in various PEPs.</p>"""
            ))
            self.__projectAct.triggered[()].connect(
                self.__projectCodeStyleCheck)
            e5App().getObject("Project").addE5Actions([self.__projectAct])
            menu.addAction(self.__projectAct)
        
        self.__editorAct = E5Action(
            self.trUtf8('Check Code Style'),
            self.trUtf8('&Code Style...'), 0, 0,
            self, "")
        self.__editorAct.setWhatsThis(self.trUtf8(
            """<b>Check Code Style...</b>"""
            """<p>This checks Python files for compliance to the"""
            """ code style conventions given in various PEPs.</p>"""
        ))
        self.__editorAct.triggered[()].connect(self.__editorCodeStyleCheck)
        
        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)
        
        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:
            menu.removeAction(self.__projectAct)
        
        if self.__projectBrowserMenu:
            if self.__projectBrowserAct:
                self.__projectBrowserMenu.removeAction(
                    self.__projectBrowserAct)
        
        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 __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" and self.__projectAct is not None:
            self.__projectAct.setEnabled(
                e5App().getObject("Project").getProjectLanguage() in
                ["Python3", "Python2", "Python"])
    
    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() in \
                ["Python3", "Python2", "Python"]:
            self.__projectBrowserMenu = menu
            if self.__projectBrowserAct is None:
                self.__projectBrowserAct = E5Action(
                    self.trUtf8('Check Code Style'),
                    self.trUtf8('&Code Style...'), 0, 0,
                    self, "")
                self.__projectBrowserAct.setWhatsThis(self.trUtf8(
                    """<b>Check Code Style...</b>"""
                    """<p>This checks Python files for compliance to the"""
                    """ code style conventions given in various PEPs.</p>"""
                ))
                self.__projectBrowserAct.triggered[()].connect(
                    self.__projectBrowserCodeStyleCheck)
            if not self.__projectBrowserAct in menu.actions():
                menu.addAction(self.__projectBrowserAct)
    
    def __projectCodeStyleCheck(self):
        """
        Public slot used to check the project files for code style.
        """
        project = e5App().getObject("Project")
        project.saveAllScripts()
        ppath = project.getProjectPath()
        files = [os.path.join(ppath, file)
                 for file in project.pdata["SOURCES"]
                 if file.endswith(
                     tuple(Preferences.getPython("Python3Extensions")) +
                     tuple(Preferences.getPython("PythonExtensions")))]
        
        from CheckerPlugins.CodeStyleChecker.CodeStyleCheckerDialog import \
            CodeStyleCheckerDialog
        self.__projectCodeStyleCheckerDialog = CodeStyleCheckerDialog(self)
        self.__projectCodeStyleCheckerDialog.show()
        self.__projectCodeStyleCheckerDialog.prepare(files, project)
    
    def __projectBrowserCodeStyleCheck(self):
        """
        Private method to handle the code style check context menu action of
        the project sources browser.
        """
        browser = e5App().getObject("ProjectBrowser")\
            .getProjectBrowser("sources")
        itm = browser.model().item(browser.currentIndex())
        try:
            fn = itm.fileName()
            isDir = False
        except AttributeError:
            fn = itm.dirName()
            isDir = True
        
        from CheckerPlugins.CodeStyleChecker.CodeStyleCheckerDialog import \
            CodeStyleCheckerDialog
        self.__projectBrowserCodeStyleCheckerDialog = CodeStyleCheckerDialog(
            self)
        self.__projectBrowserCodeStyleCheckerDialog.show()
        if isDir:
            self.__projectBrowserCodeStyleCheckerDialog.start(
                fn, save=True)
        else:
            self.__projectBrowserCodeStyleCheckerDialog.start(
                fn, save=True, repeat=True)
    
    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
        """
        if menuName == "Checks":
            if not self.__editorAct in menu.actions():
                menu.addAction(self.__editorAct)
            self.__editorAct.setEnabled(
                editor.isPy3File() or editor.isPy2File())
    
    def __editorCodeStyleCheck(self):
        """
        Private slot to handle the code style check context menu action
        of the editors.
        """
        editor = e5App().getObject("ViewManager").activeWindow()
        if editor is not None:
            if editor.checkDirty() and editor.getFileName() is not None:
                from CheckerPlugins.CodeStyleChecker.CodeStyleCheckerDialog \
                    import CodeStyleCheckerDialog
                self.__editorCodeStyleCheckerDialog = CodeStyleCheckerDialog(
                    self)
                self.__editorCodeStyleCheckerDialog.show()
                self.__editorCodeStyleCheckerDialog.start(
                    editor.getFileName(),
                    save=True,
                    repeat=True)

eric ide

mercurial