PyLint/PyLintConfigDialog.py

Sat, 24 Apr 2021 17:01:22 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 24 Apr 2021 17:01:22 +0200
changeset 95
50eba81e4a9f
parent 94
45d226917534
permissions
-rw-r--r--

- implemented some code simplifications

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

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

"""
Module implementing a dialog to configure the PyLint process.
"""

import os
import copy

from PyQt5.QtCore import pyqtSlot, QProcess
from PyQt5.QtWidgets import QDialog

from E5Gui.E5Application import e5App
from E5Gui import E5FileDialog, E5MessageBox
from E5Gui.E5Completers import E5FileCompleter

from .Ui_PyLintConfigDialog import Ui_PyLintConfigDialog

import Preferences
import Utilities


class PyLintConfigDialog(QDialog, Ui_PyLintConfigDialog):
    """
    Class implementing a dialog to configure the PyLint process.
    """
    def __init__(self, ppath, exe, parms, version):
        """
        Constructor
        
        @param ppath project path (string or QString)
            Used to set the default path for the rcfile selection dialog
        @param exe name of the pylint executable (string)
        @param parms parameters to set in the dialog
        @param version pylint version (string)
        """
        super().__init__(None)
        self.setupUi(self)
        
        self.version = version
        self.pylintProc = None
        self.lint = exe
        
        self.__initializeDefaults()
        
        # get a copy of the defaults to store the user settings
        self.parameters = copy.deepcopy(self.defaults)
        
        # combine it with the values of parms
        if parms is not None:
            self.parameters.update(parms)
        
        self.configfileCompleter = E5FileCompleter(self.configfileEdit)
        self.reportfileCompleter = E5FileCompleter(self.reportfileEdit)
        
        # initialize general tab
        self.configfileEdit.setText(self.parameters['configFile'])
        self.txtOutputButton.setChecked(self.parameters['txtReport'])
        self.htmlOutputButton.setChecked(self.parameters['htmlReport'])
        self.dialogOutputButton.setChecked(self.parameters['dialogReport'])
        self.reportfileEdit.setText(self.parameters['reportFile'])
        
        # initialize checkers tab
        self.basicCheckBox.setChecked(self.parameters['enableBasic'])
        self.classesCheckBox.setChecked(self.parameters['enableClasses'])
        self.designCheckBox.setChecked(self.parameters['enableDesign'])
        self.exceptionsCheckBox.setChecked(self.parameters['enableExceptions'])
        self.formatCheckBox.setChecked(self.parameters['enableFormat'])
        self.importsCheckBox.setChecked(self.parameters['enableImports'])
        self.metricsCheckBox.setChecked(self.parameters['enableMetrics'])
        self.miscellaneousCheckBox.setChecked(
            self.parameters['enableMiscellaneous'])
        self.newstyleCheckBox.setChecked(self.parameters['enableNewstyle'])
        self.similaritiesCheckBox.setChecked(
            self.parameters['enableSimilarities'])
        self.typecheckCheckBox.setChecked(self.parameters['enableTypecheck'])
        self.variablesCheckBox.setChecked(self.parameters['enableVariables'])
        self.loggingCheckBox.setChecked(self.parameters['enableLogging'])
        self.stringFormatCheckBox.setChecked(
            self.parameters['enableStringFormat'])
        
        # initialize messages tab
        self.enabledMessagesEdit.setText(self.parameters['enabledMessages'])
        self.disabledMessagesEdit.setText(self.parameters['disabledMessages'])
        
        self.ppath = ppath

    def __initializeDefaults(self):
        """
        Private method to set the default values.
        
        These are needed later on to generate the commandline
        parameters.
        """
        self.defaults = {
            # general options
            'configFile': '',
            'reportFile': '',
            'txtReport': False,
            'htmlReport': True,
            'dialogReport': False,
            
            # enabled checkers
            'enableBasic': True,
            'enableClasses': True,
            'enableDesign': True,
            'enableExceptions': True,
            'enableFormat': False,
            'enableImports': False,
            'enableLogging': True,
            'enableMetrics': True,
            'enableMiscellaneous': True,
            'enableNewstyle': True,
            'enableSimilarities': True,
            'enableStringFormat': True,
            'enableTypecheck': True,
            'enableVariables': True,
            
            # messages
            'enabledMessages': '',
            'disabledMessages': '',
        }

    def generateParameters(self):
        """
        Public method that generates the commandline parameters.
        
        It generates a QStringList to be used
        to set the QProcess arguments for the pylint call and
        a list containing the non default parameters. The second
        list can be passed back upon object generation to overwrite
        the default settings.
        
        <b>Note</b>: The arguments list contains the name of the pylint
        executable as the first parameter.
        
        @return a tuple of the commandline parameters and non default
            parameters (list of strings, dictionary)
        """
        parms = {}
        args = []
        
        # 1. the program name
        args.append(self.lint)
        
        # 2. the commandline options
        # 2.1 general options
        if self.parameters['configFile'] != self.defaults['configFile']:
            parms['configFile'] = self.parameters['configFile']
            args.append('--rcfile={0}'.format(self.parameters['configFile']))
        parms['txtReport'] = self.parameters['txtReport']
        parms['htmlReport'] = self.parameters['htmlReport']
        parms['dialogReport'] = self.parameters['dialogReport']
        if self.parameters['htmlReport']:
            args.append('--output-format=html')
        elif self.parameters['dialogReport']:
            args.append('--output-format=parseable')
            args.append('--reports=n')
        else:
            args.append('--output-format=text')
        if self.parameters['reportFile'] != self.defaults['reportFile']:
            parms['reportFile'] = self.parameters['reportFile']
        
        # 2.2 checkers options
        parms['enableBasic'] = self.parameters['enableBasic']
        parms['enableClasses'] = self.parameters['enableClasses']
        parms['enableDesign'] = self.parameters['enableDesign']
        parms['enableExceptions'] = self.parameters['enableExceptions']
        parms['enableFormat'] = self.parameters['enableFormat']
        parms['enableImports'] = self.parameters['enableImports']
        parms['enableMetrics'] = self.parameters['enableMetrics']
        parms['enableMiscellaneous'] = self.parameters['enableMiscellaneous']
        parms['enableNewstyle'] = self.parameters['enableNewstyle']
        parms['enableSimilarities'] = self.parameters['enableSimilarities']
        parms['enableTypecheck'] = self.parameters['enableTypecheck']
        parms['enableVariables'] = self.parameters['enableVariables']
        parms['enableLogging'] = self.parameters['enableLogging']
        parms['enableStringFormat'] = self.parameters['enableStringFormat']
        
        checkers = []
        if self.parameters['enableBasic']:
            checkers.append('basic')
        if self.parameters['enableClasses']:
            checkers.append('classes')
        if self.parameters['enableDesign']:
            checkers.append('design')
        if self.parameters['enableExceptions']:
            checkers.append('exceptions')
        if self.parameters['enableFormat']:
            checkers.append('format')
        if self.parameters['enableImports']:
            checkers.append('imports')
        if self.parameters['enableMetrics']:
            checkers.append('metrics')
        if self.parameters['enableMiscellaneous']:
            checkers.append('miscellaneous')
        if self.parameters['enableNewstyle']:
            checkers.append('newstyle')
        if self.parameters['enableSimilarities']:
            checkers.append('similarities')
        if self.parameters['enableTypecheck']:
            checkers.append('typecheck')
        if self.parameters['enableVariables']:
            checkers.append('variables')
        if self.parameters['enableLogging']:
            checkers.append('logging')
        if self.parameters['enableStringFormat']:
            if self.version > '0.27.0':
                checkers.append('string')
            else:
                checkers.append('string_format')
            
        args.append('--disable=all')
        if checkers:
            args.append('--enable={0}'.format(','.join(checkers)))
        
        # 2.3 messages options
        parms['enabledMessages'] = self.parameters['enabledMessages']
        parms['disabledMessages'] = self.parameters['disabledMessages']
        if parms['enabledMessages']:
            args.append('--enable={0}'.format(parms['enabledMessages']))
        if parms['disabledMessages']:
            args.append('--disable={0}'.format(parms['disabledMessages']))
        
        return (args, parms)

    @pyqtSlot()
    def on_configfileButton_clicked(self):
        """
        Private slot to select the configuration file.
        
        It displays a file selection dialog to select the configuration file.
        """
        startWith = self.configfileEdit.text()
        if startWith == "":
            startWith = self.ppath
        config = E5FileDialog.getOpenFileName(
            self,
            self.tr("Select configuration file"),
            startWith,
            self.tr("Configuration Files (*.cfg *.cnf *.rc);;"
                    "All Files (*)"))
        if config:
            self.configfileEdit.setText(Utilities.toNativeSeparators(config))
    
    @pyqtSlot()
    def on_reportfileButton_clicked(self):
        """
        Private slot to select the report file.
        
        It displays a file selection dialog to select the report file.
        """
        report = E5FileDialog.getSaveFileName(
            self,
            self.tr("Select report file"),
            self.reportfileEdit.text(),
            None,
            None,
            E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
        
        if report:
            self.reportfileEdit.setText(Utilities.toNativeSeparators(report))
    
    def accept(self):
        """
        Public slot called by the Ok button.
        
        It saves the values in the parameters dictionary.
        """
        # get data of general tab
        self.parameters['configFile'] = self.configfileEdit.text()
        self.parameters['txtReport'] = self.txtOutputButton.isChecked()
        self.parameters['htmlReport'] = self.htmlOutputButton.isChecked()
        self.parameters['dialogReport'] = self.dialogOutputButton.isChecked()
        self.parameters['reportFile'] = self.reportfileEdit.text()
        
        # get data of checkers tab
        self.parameters['enableBasic'] = self.basicCheckBox.isChecked()
        self.parameters['enableClasses'] = self.classesCheckBox.isChecked()
        self.parameters['enableDesign'] = self.designCheckBox.isChecked()
        self.parameters['enableExceptions'] = (
            self.exceptionsCheckBox.isChecked())
        self.parameters['enableFormat'] = self.formatCheckBox.isChecked()
        self.parameters['enableImports'] = self.importsCheckBox.isChecked()
        self.parameters['enableMetrics'] = self.metricsCheckBox.isChecked()
        self.parameters['enableMiscellaneous'] = (
            self.miscellaneousCheckBox.isChecked())
        self.parameters['enableNewstyle'] = self.newstyleCheckBox.isChecked()
        self.parameters['enableSimilarities'] = (
            self.similaritiesCheckBox.isChecked())
        self.parameters['enableTypecheck'] = self.typecheckCheckBox.isChecked()
        self.parameters['enableVariables'] = self.variablesCheckBox.isChecked()
        self.parameters['enableLogging'] = self.loggingCheckBox.isChecked()
        self.parameters['enableStringFormat'] = (
            self.stringFormatCheckBox.isChecked())
        
        # get data of messages tab
        self.parameters['enabledMessages'] = ','.join(
            [m.strip() for m in self.enabledMessagesEdit.text().split(',')])
        self.parameters['disabledMessages'] = ','.join(
            [m.strip() for m in self.disabledMessagesEdit.text().split(',')])
        
        # call the accept slot of the base class
        super().accept()

    ###########################################################################
    ## Methods below are needed to generate a configuration file template
    ###########################################################################

    @pyqtSlot()
    def on_configButton_clicked(self):
        """
        Private slot to handle the generation of a sample configuration.
        """
        self.buf = ""
        self.pylintProc = QProcess()
        args = []
        
        self.__ioEncoding = Preferences.getSystem("IOEncoding")
        
        args.append('--generate-rcfile')
        
        self.pylintProc.readyReadStandardOutput.connect(self.__readStdout)
        self.pylintProc.readyReadStandardError.connect(self.__readStderr)
        self.pylintProc.finished.connect(self.__createConfigDone)
        
        self.pylintProc.start(self.lint, args)
        procStarted = self.pylintProc.waitForStarted()
        if procStarted:
            e5App().getObject("ViewManager").enableEditorsCheckFocusIn(False)
        else:
            E5MessageBox.critical(
                self,
                self.tr('Process Generation Error'),
                self.tr(
                    'Could not start {0}.<br>'
                    'Ensure that it is in the search path.'
                ).format(self.lint))
    
    def __createConfigDone(self, exitCode, exitStatus):
        """
        Private slot to handle the the finished signal of the pylint process.
        
        @param exitCode exit code of the process (integer)
        @param exitStatus exit status of the process (QProcess.ExitStatus)
        """
        vm = e5App().getObject("ViewManager")
        vm.enableEditorsCheckFocusIn(True)
        if exitStatus == QProcess.NormalExit and exitCode == 0:
            vm.newEditor()
            aw = vm.activeWindow()
            aw.insertAt(self.buf, 0, 0)
            aw.setLanguage('dummy.rc')
        self.reject()
    
    def __readStdout(self):
        """
        Private slot to handle the readyReadStandardOutput signal of the
        pylint process.
        """
        if self.pylintProc is None:
            return
        self.pylintProc.setReadChannel(QProcess.StandardOutput)
        
        while self.pylintProc and self.pylintProc.canReadLine():
            line = str(self.pylintProc.readLine(), self.__ioEncoding,
                       "replace").rstrip()
            self.buf += line + os.linesep
        
    def __readStderr(self):
        """
        Private slot to handle the readyReadStandardError signal of the
        pylint process.
        """
        if self.pylintProc is None:
            return
        self.pylintProc.setReadChannel(QProcess.StandardError)
        while self.pylintProc and self.pylintProc.canReadLine():
            s = 'pylint: ' + str(
                self.pylintProc.readLine(), self.__ioEncoding, "replace")
            e5App().getObject("UserInterface").appendStderr.emit(s)

eric ide

mercurial