PyLintInterface/PyLintConfigDialog.py

branch
eric7
changeset 98
ab4aabca55ec
parent 95
50eba81e4a9f
child 101
98784d037491
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PyLintInterface/PyLintConfigDialog.py	Wed Jun 02 18:27:07 2021 +0200
@@ -0,0 +1,369 @@
+# -*- 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 PyQt6.QtCore import pyqtSlot, QProcess
+from PyQt6.QtWidgets import QDialog
+
+from EricWidgets.EricApplication import ericApp
+from EricWidgets import EricMessageBox
+from EricWidgets.EricPathPicker import EricPathPickerModes
+
+from .Ui_PyLintConfigDialog import Ui_PyLintConfigDialog
+
+import Preferences
+
+
+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; used to set the default path for the
+            rcfile picker
+        @type str
+        @param exe name of the pylint executable
+        @type str
+        @param parms parameters to set in the dialog
+        @type dict
+        @param version pylint version (unused)
+        @type str
+        """
+        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.configfilePicker.setWindowTitle(
+            self.tr("Select configuration file"))
+        self.configfilePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE)
+        self.configfilePicker.setFilters(self.tr(
+            "Configuration Files (*.cfg *.cnf *.rc);;"
+            "All Files (*)"
+        ))
+        self.configfilePicker.setDefaultDirectory(ppath)
+        
+        self.reportfilePicker.setWindowTitle(
+            self.tr("Select report file"))
+        self.reportfilePicker.setMode(EricPathPickerModes.SAVE_FILE_MODE)
+        self.reportfilePicker.setFilters(self.tr(
+            "HTML Files (*.html);;"
+            "Report Files (*.rpt);;"
+            "Text Files (*.txt);;"
+            "All Files (*)"
+        ))
+        
+        
+        # initialize general tab
+        self.configfilePicker.setText(self.parameters['configFile'])
+        self.txtOutputButton.setChecked(self.parameters['txtReport'])
+        self.htmlOutputButton.setChecked(self.parameters['htmlReport'])
+        self.dialogOutputButton.setChecked(self.parameters['dialogReport'])
+        self.reportfilePicker.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'])
+
+    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 list of strings 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
+        @rtype tuple of (list of str, dict)
+        """
+        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']:
+            checkers.append('string')
+            
+        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)
+
+    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.configfilePicker.text()
+        self.parameters['txtReport'] = self.txtOutputButton.isChecked()
+        self.parameters['htmlReport'] = self.htmlOutputButton.isChecked()
+        self.parameters['dialogReport'] = self.dialogOutputButton.isChecked()
+        self.parameters['reportFile'] = self.reportfilePicker.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:
+            ericApp().getObject("ViewManager").enableEditorsCheckFocusIn(False)
+        else:
+            EricMessageBox.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
+        @type int
+        @param exitStatus exit status of the process
+        @type QProcess.ExitStatus
+        """
+        vm = ericApp().getObject("ViewManager")
+        vm.enableEditorsCheckFocusIn(True)
+        if exitStatus == QProcess.ExitStatus.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.ProcessChannel.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.ProcessChannel.StandardError)
+        while self.__pylintProc and self.__pylintProc.canReadLine():
+            s = 'pylint: ' + str(
+                self.__pylintProc.readLine(), self.__ioEncoding, "replace")
+            ericApp().getObject("UserInterface").appendStderr.emit(s)

eric ide

mercurial