Mon, 19 Sep 2022 17:54:33 +0200
Reformatted the code iaw. Black styling.
--- a/PluginMetricsRadon.epj Mon Sep 19 17:43:37 2022 +0200 +++ b/PluginMetricsRadon.epj Mon Sep 19 17:54:33 2022 +0200 @@ -1,19 +1,21 @@ { "header": { "comment": "eric project file for project PluginMetricsRadon", - "copyright": "Copyright (C) 2021 Detlev Offenbach, detlev@die-offenbachs.de" + "copyright": "Copyright (C) 2022 Detlev Offenbach, detlev@die-offenbachs.de" }, "project": { "AUTHOR": "Detlev Offenbach", "CHECKERSPARMS": { "Pep8Checker": { "AnnotationsChecker": { + "AllowStarArgAny": false, "AllowUntypedDefs": false, "AllowUntypedNested": false, "DispatchDecorators": [ "singledispatch", "singledispatchmethod" ], + "ForceFutureAnnotations": false, "MaximumComplexity": 3, "MaximumLength": 7, "MinimumCoverage": 75, @@ -62,17 +64,22 @@ "DocstringType": "eric", "EnabledCheckerCategories": "C, D, E, M, N, S, Y, W", "ExcludeFiles": "*/Ui_*.py, */*_rc.py", - "ExcludeMessages": "C101,E265,E266,E305,E402,M201,M301,M302,M303,M304,M305,M306,M307,M308,M311,M312,M313,M314,M315,M321,M701,M702,M811,M834,N802,N803,N807,N808,N821,W293,W504,Y119,Y401,Y402", + "ExcludeMessages": "C101,D243,E265,E266,E305,E402,M201,M301,M302,M303,M304,M305,M306,M307,M308,M311,M312,M313,M314,M315,M321,M701,M702,M811,M834,N802,N803,N807,N808,N821,W293,W503,Y119,Y401,Y402", "FixCodes": "", "FixIssues": false, "FutureChecker": "", "HangClosing": false, + "ImportsChecker": { + "ApplicationPackageNames": [], + "BanRelativeImports": "", + "BannedModules": [] + }, "IncludeMessages": "", "LineComplexity": 25, "LineComplexityScore": 10, "MaxCodeComplexity": 10, - "MaxDocLineLength": 79, - "MaxLineLength": 79, + "MaxDocLineLength": 88, + "MaxLineLength": 88, "NoFixCodes": "E501", "RepeatMessages": true, "SecurityChecker": { @@ -131,6 +138,7 @@ } }, "EMAIL": "detlev@die-offenbachs.de", + "EMBEDDED_VENV": false, "EOL": 1, "FILETYPES": { "*.epj": "OTHERS", @@ -166,6 +174,7 @@ }, "INTERFACES": [], "LEXERASSOCS": {}, + "LICENSE": "", "MAINSCRIPT": "PluginMetricsRadon.py", "MAKEPARAMS": { "MakeEnabled": false, @@ -180,12 +189,28 @@ ".hgignore", "ChangeLog", "PKGLIST", + "PluginMetricsRadon.epj", "PluginMetricsRadon.zip", "RadonMetrics/Documentation/LICENSE.GPL3", - "RadonMetrics/Documentation/source", - "PluginMetricsRadon.epj" + "RadonMetrics/Documentation/source" ], "OTHERTOOLSPARMS": { + "Black": { + "exclude": "/(\\.direnv|\\.eggs|\\.git|\\.hg|\\.mypy_cache|\\.nox|\\.tox|\\.venv|venv|\\.svn|_build|buck-out|build|dist|__pypackages__)/", + "extend-exclude": "", + "force-exclude": "", + "line-length": 88, + "skip-magic-trailing-comma": false, + "skip-string-normalization": false, + "source": "project", + "target-version": [ + "py37", + "py38", + "py39", + "py310", + "py311" + ] + }, "RadonCodeMetrics": { "ExcludeFiles": "*/radon/*", "MinimumRank": "C" @@ -217,6 +242,7 @@ "SPELLEXCLUDES": "", "SPELLLANGUAGE": "en_US", "SPELLWORDS": "", + "TESTING_FRAMEWORK": "", "TRANSLATIONEXCEPTIONS": [], "TRANSLATIONPATTERN": "RadonMetrics/i18n/radon_%language%.ts", "TRANSLATIONS": [
--- a/PluginMetricsRadon.py Mon Sep 19 17:43:37 2022 +0200 +++ b/PluginMetricsRadon.py Mon Sep 19 17:54:33 2022 +0200 @@ -49,7 +49,7 @@ class RadonMetricsPlugin(QObject): """ Class implementing the radon code metrics plug-in. - + @signal metricsDone(str, dict) emitted when the code metrics were determined for a file @signal maintainabilityIndexDone(str, dict) emitted when the @@ -59,54 +59,67 @@ @signal error(str, str, str) emitted in case of an error @signal batchFinished(str) emitted when a code metrics batch is done """ + metricsDone = pyqtSignal(str, dict) maintainabilityIndexDone = pyqtSignal(str, dict) complexityDone = pyqtSignal(str, dict) error = pyqtSignal(str, str, str) batchFinished = pyqtSignal(str) - + def __init__(self, ui): """ Constructor - + @param ui reference to the user interface object @type UserInterface """ super().__init__(ui) self.__ui = ui self.__initialize() - + self.backgroundService = ericApp().getObject("BackgroundService") - + path = os.path.join(os.path.dirname(__file__), packageName) - + # raw code metrics calculation self.backgroundService.serviceConnect( - 'radon_raw', 'Python3', path, 'CodeMetricsCalculator', + "radon_raw", + "Python3", + path, + "CodeMetricsCalculator", lambda fn, res: self.metricsCalculationDone("raw", fn, res), onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy3( - "raw", fx, lang, fn, msg), - onBatchDone=lambda fx, lang: self.batchJobDone( - "raw", fx, lang)) - + "raw", fx, lang, fn, msg + ), + onBatchDone=lambda fx, lang: self.batchJobDone("raw", fx, lang), + ) + # maintainability index calculation self.backgroundService.serviceConnect( - 'radon_mi', 'Python3', path, 'MaintainabilityIndexCalculator', + "radon_mi", + "Python3", + path, + "MaintainabilityIndexCalculator", lambda fn, res: self.metricsCalculationDone("mi", fn, res), onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy3( - "mi", fx, lang, fn, msg), - onBatchDone=lambda fx, lang: self.batchJobDone( - "mi", fx, lang)) - + "mi", fx, lang, fn, msg + ), + onBatchDone=lambda fx, lang: self.batchJobDone("mi", fx, lang), + ) + # cyclomatic complexity self.backgroundService.serviceConnect( - 'radon_cc', 'Python3', path, 'CyclomaticComplexityCalculator', + "radon_cc", + "Python3", + path, + "CyclomaticComplexityCalculator", lambda fn, res: self.metricsCalculationDone("cc", fn, res), onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy3( - "cc", fx, lang, fn, msg), - onBatchDone=lambda fx, lang: self.batchJobDone( - "cc", fx, lang)) - + "cc", fx, lang, fn, msg + ), + onBatchDone=lambda fx, lang: self.batchJobDone("cc", fx, lang), + ) + self.queuedBatches = { "raw": [], "mi": [], @@ -117,14 +130,14 @@ "mi": True, "cc": True, } - + self.__translator = None self.__loadTranslator() - + def __serviceError(self, type_, fn, msg): """ Private slot handling service errors. - + @param type_ type of the calculated metrics @type str, one of ["raw", "mi", "cc"] @param fn file name @@ -133,11 +146,11 @@ @type str """ self.error.emit(type_, fn, msg) - + def serviceErrorPy3(self, type_, fx, lang, fn, msg): """ Public slot handling service errors for Python 3. - + @param type_ type of the calculated metrics @type str, one of ["raw", "mi", "cc"] @param fx service name @@ -149,17 +162,17 @@ @param msg message text @type str """ - if fx in ['radon_' + type_, 'batch_radon_' + type_]: - if fx == 'radon_' + type_: + if fx in ["radon_" + type_, "batch_radon_" + type_]: + if fx == "radon_" + type_: self.__serviceError(type_, fn, msg) else: self.__serviceError(type_, self.tr("Python 3 batch job"), msg) self.batchJobDone(type_, fx, lang) - + def batchJobDone(self, type_, fx, lang): """ Public slot handling the completion of a batch job. - + @param type_ type of the calculated metrics @type str, one of ["raw", "mi", "cc"] @param fx service name @@ -167,21 +180,18 @@ @param lang language @type str """ - if fx in ['radon_' + type_, 'batch_radon_' + type_]: + if fx in ["radon_" + type_, "batch_radon_" + type_]: if lang in self.queuedBatches[type_]: self.queuedBatches[type_].remove(lang) # prevent sending the signal multiple times - if ( - len(self.queuedBatches[type_]) == 0 and - not self.batchesFinished[type_] - ): + if len(self.queuedBatches[type_]) == 0 and not self.batchesFinished[type_]: self.batchFinished.emit(type_) self.batchesFinished[type_] = True - + def metricsCalculationDone(self, type_, filename, result): """ Public slot to dispatch the result. - + @param type_ type of the calculated metrics @type str, one of ["raw", "mi", "cc"] @param filename name of the file the results belong to @@ -199,10 +209,9 @@ self.error.emit( type_, filename, - self.tr("Unknown metrics result received ({0}).").format( - type_) + self.tr("Unknown metrics result received ({0}).").format(type_), ) - + def __initialize(self): """ Private slot to (re)initialize the plugin. @@ -212,14 +221,14 @@ self.__projectCCDialog = None self.__projectMetricsActs = [] self.__projectSeparatorActs = [] - + self.__projectBrowserRawMetricsDialog = None self.__projectBrowserMIDialog = None self.__projectBrowserCCDialog = None self.__projectBrowserMenu = None self.__projectBrowserMetricsActs = [] self.__projectBrowserSeparatorActs = [] - + self.__editors = [] self.__editorRawMetricsDialog = None self.__editorMIDialog = None @@ -241,16 +250,15 @@ @type str """ if lang is None: - lang = 'Python{0}'.format(determinePythonVersion(filename, source)) - if lang == 'Python3': - self.backgroundService.enqueueRequest( - 'radon_raw', lang, filename, [source]) + lang = "Python{0}".format(determinePythonVersion(filename, source)) + if lang == "Python3": + self.backgroundService.enqueueRequest("radon_raw", lang, filename, [source]) def rawMetricsBatch(self, argumentsList): """ Public method to prepare raw code metrics calculation on multiple Python source files. - + @param argumentsList list of arguments tuples with each tuple containing filename and source @type (str, str) @@ -259,22 +267,23 @@ "Python3": [], } for filename, source in argumentsList: - lang = 'Python{0}'.format(determinePythonVersion(filename, source)) - if lang == 'Python3': + lang = "Python{0}".format(determinePythonVersion(filename, source)) + if lang == "Python3": data[lang].append((filename, source)) - + self.queuedBatches["raw"] = [] if data[lang]: - self.queuedBatches["raw"].append('Python3') + self.queuedBatches["raw"].append("Python3") self.backgroundService.enqueueRequest( - 'batch_radon_raw', 'Python3', "", data['Python3']) + "batch_radon_raw", "Python3", "", data["Python3"] + ) self.batchesFinished["raw"] = False - + def cancelRawMetricsBatch(self): """ Public method to cancel all batch jobs. """ - self.backgroundService.requestCancel('batch_radon_raw', 'Python3') + self.backgroundService.requestCancel("batch_radon_raw", "Python3") def maintainabilityIndex(self, lang, filename, source): """ @@ -290,16 +299,15 @@ @type str """ if lang is None: - lang = 'Python{0}'.format(determinePythonVersion(filename, source)) - if lang == 'Python3': - self.backgroundService.enqueueRequest( - 'radon_mi', lang, filename, [source]) + lang = "Python{0}".format(determinePythonVersion(filename, source)) + if lang == "Python3": + self.backgroundService.enqueueRequest("radon_mi", lang, filename, [source]) def maintainabilityIndexBatch(self, argumentsList): """ Public method to prepare maintainability index calculation on multiple Python source files. - + @param argumentsList list of arguments tuples with each tuple containing filename and source @type (str, str) @@ -308,23 +316,24 @@ "Python3": [], } for filename, source in argumentsList: - lang = 'Python{0}'.format(determinePythonVersion(filename, source)) - if lang == 'Python3': + lang = "Python{0}".format(determinePythonVersion(filename, source)) + if lang == "Python3": data[lang].append((filename, source)) - + self.queuedBatches["mi"] = [] - if data['Python3']: - self.queuedBatches["mi"].append('Python3') + if data["Python3"]: + self.queuedBatches["mi"].append("Python3") self.backgroundService.enqueueRequest( - 'batch_radon_mi', 'Python3', "", data['Python3']) + "batch_radon_mi", "Python3", "", data["Python3"] + ) self.batchesFinished["mi"] = False - + def cancelMaintainabilityIndexBatch(self): """ Public method to cancel all batch jobs. """ - self.backgroundService.requestCancel('batch_radon_mi', 'Python3') - + self.backgroundService.requestCancel("batch_radon_mi", "Python3") + def cyclomaticComplexity(self, lang, filename, source): """ Public method to prepare cyclomatic complexity calculation on one @@ -339,16 +348,15 @@ @type str """ if lang is None: - lang = 'Python{0}'.format(determinePythonVersion(filename, source)) - if lang == 'Python3': - self.backgroundService.enqueueRequest( - 'radon_cc', lang, filename, [source]) + lang = "Python{0}".format(determinePythonVersion(filename, source)) + if lang == "Python3": + self.backgroundService.enqueueRequest("radon_cc", lang, filename, [source]) def cyclomaticComplexityBatch(self, argumentsList): """ Public method to prepare cyclomatic complexity calculation on multiple Python source files. - + @param argumentsList list of arguments tuples with each tuple containing filename and source @type (str, str) @@ -357,40 +365,41 @@ "Python3": [], } for filename, source in argumentsList: - lang = 'Python{0}'.format(determinePythonVersion(filename, source)) - if lang == 'Python3': + lang = "Python{0}".format(determinePythonVersion(filename, source)) + if lang == "Python3": data[lang].append((filename, source)) - + self.queuedBatches["raw"] = [] - if data['Python3']: - self.queuedBatches["cc"].append('Python3') + if data["Python3"]: + self.queuedBatches["cc"].append("Python3") self.backgroundService.enqueueRequest( - 'batch_radon_cc', 'Python3', "", data['Python3']) + "batch_radon_cc", "Python3", "", data["Python3"] + ) self.batchesFinished["cc"] = False - + def cancelComplexityBatch(self): """ Public method to cancel all batch jobs. """ - self.backgroundService.requestCancel('batch_radon_cc', 'Python3') + self.backgroundService.requestCancel("batch_radon_cc", "Python3") def activate(self): """ Public method to activate this plug-in. - + @return tuple of None and activation status @rtype (None, bool) """ global error - error = "" # clear previous error - + error = "" # clear previous error + # Project menu actions menu = ericApp().getObject("Project").getMenu("Show") if menu: if not menu.isEmpty(): act = menu.addSeparator() self.__projectSeparatorActs.append(act) - + # header action act = QAction(self.tr("Radon"), self) font = act.font() @@ -399,60 +408,78 @@ act.triggered.connect(self.__showRadonVersion) menu.addAction(act) self.__projectMetricsActs.append(act) - + act = EricAction( - self.tr('Code Metrics'), - self.tr('Code &Metrics...'), 0, 0, - self, 'project_show_radon_raw') - act.setStatusTip( - self.tr('Show raw code metrics.')) - act.setWhatsThis(self.tr( - """<b>Code Metrics...</b>""" - """<p>This calculates raw code metrics of Python files""" - """ and shows the amount of lines of code, logical lines""" - """ of code, source lines of code, comment lines,""" - """ multi-line strings and blank lines.</p>""" - )) + self.tr("Code Metrics"), + self.tr("Code &Metrics..."), + 0, + 0, + self, + "project_show_radon_raw", + ) + act.setStatusTip(self.tr("Show raw code metrics.")) + act.setWhatsThis( + self.tr( + """<b>Code Metrics...</b>""" + """<p>This calculates raw code metrics of Python files""" + """ and shows the amount of lines of code, logical lines""" + """ of code, source lines of code, comment lines,""" + """ multi-line strings and blank lines.</p>""" + ) + ) act.triggered.connect(self.__projectRawMetrics) menu.addAction(act) self.__projectMetricsActs.append(act) - + act = EricAction( - self.tr('Maintainability Index'), - self.tr('Maintainability &Index...'), 0, 0, - self, 'project_show_radon_mi') + self.tr("Maintainability Index"), + self.tr("Maintainability &Index..."), + 0, + 0, + self, + "project_show_radon_mi", + ) act.setStatusTip( - self.tr('Show the maintainability index for Python files.')) - act.setWhatsThis(self.tr( - """<b>Maintainability Index...</b>""" - """<p>This calculates the maintainability index of Python""" - """ files and shows it together with a ranking.</p>""" - )) + self.tr("Show the maintainability index for Python files.") + ) + act.setWhatsThis( + self.tr( + """<b>Maintainability Index...</b>""" + """<p>This calculates the maintainability index of Python""" + """ files and shows it together with a ranking.</p>""" + ) + ) act.triggered.connect(self.__projectMaintainabilityIndex) menu.addAction(act) self.__projectMetricsActs.append(act) - + act = EricAction( - self.tr('Cyclomatic Complexity'), - self.tr('Cyclomatic &Complexity...'), 0, 0, - self, 'project_show_radon_cc') + self.tr("Cyclomatic Complexity"), + self.tr("Cyclomatic &Complexity..."), + 0, + 0, + self, + "project_show_radon_cc", + ) act.setStatusTip( - self.tr('Show the cyclomatic complexity for Python files.')) - act.setWhatsThis(self.tr( - """<b>Cyclomatic Complexity...</b>""" - """<p>This calculates the cyclomatic complexity of Python""" - """ files and shows it together with a ranking.</p>""" - )) + self.tr("Show the cyclomatic complexity for Python files.") + ) + act.setWhatsThis( + self.tr( + """<b>Cyclomatic Complexity...</b>""" + """<p>This calculates the cyclomatic complexity of Python""" + """ files and shows it together with a ranking.</p>""" + ) + ) act.triggered.connect(self.__projectCyclomaticComplexity) menu.addAction(act) self.__projectMetricsActs.append(act) - + act = menu.addSeparator() self.__projectSeparatorActs.append(act) - - ericApp().getObject("Project").addEricActions( - self.__projectMetricsActs[1:]) - + + ericApp().getObject("Project").addEricActions(self.__projectMetricsActs[1:]) + # Editor menu actions (one separator each above and below) act = QAction(self) act.setSeparator(True) @@ -460,7 +487,7 @@ act = QAction(self) act.setSeparator(True) self.__editorSeparatorActs.append(act) - + # header action act = QAction(self.tr("Radon"), self) font = act.font() @@ -468,81 +495,90 @@ act.setFont(font) act.triggered.connect(self.__showRadonVersion) self.__editorMetricsActs.append(act) - + act = EricAction( - self.tr('Code Metrics'), - self.tr('Code &Metrics...'), 0, 0, - self, "") - act.setStatusTip( - self.tr('Show raw code metrics.')) - act.setWhatsThis(self.tr( - """<b>Code Metrics...</b>""" - """<p>This calculates raw code metrics of Python files""" - """ and shows the amount of lines of code, logical lines""" - """ of code, source lines of code, comment lines,""" - """ multi-line strings and blank lines.</p>""" - )) + self.tr("Code Metrics"), self.tr("Code &Metrics..."), 0, 0, self, "" + ) + act.setStatusTip(self.tr("Show raw code metrics.")) + act.setWhatsThis( + self.tr( + """<b>Code Metrics...</b>""" + """<p>This calculates raw code metrics of Python files""" + """ and shows the amount of lines of code, logical lines""" + """ of code, source lines of code, comment lines,""" + """ multi-line strings and blank lines.</p>""" + ) + ) act.triggered.connect(self.__editorRawMetrics) self.__editorMetricsActs.append(act) - + act = EricAction( - self.tr('Maintainability Index'), - self.tr('Maintainability &Index...'), 0, 0, - self, "") - act.setStatusTip( - self.tr('Show the maintainability index for Python files.')) - act.setWhatsThis(self.tr( - """<b>Maintainability Index...</b>""" - """<p>This calculates the maintainability index of Python""" - """ files and shows it together with a ranking.</p>""" - )) + self.tr("Maintainability Index"), + self.tr("Maintainability &Index..."), + 0, + 0, + self, + "", + ) + act.setStatusTip(self.tr("Show the maintainability index for Python files.")) + act.setWhatsThis( + self.tr( + """<b>Maintainability Index...</b>""" + """<p>This calculates the maintainability index of Python""" + """ files and shows it together with a ranking.</p>""" + ) + ) act.triggered.connect(self.__editorMaintainabilityIndex) self.__editorMetricsActs.append(act) - + act = EricAction( - self.tr('Cyclomatic Complexity'), - self.tr('Cyclomatic &Complexity...'), 0, 0, - self, '') - act.setStatusTip( - self.tr('Show the cyclomatic complexity for Python files.')) - act.setWhatsThis(self.tr( - """<b>Cyclomatic Complexity...</b>""" - """<p>This calculates the cyclomatic complexity of Python""" - """ files and shows it together with a ranking.</p>""" - )) + self.tr("Cyclomatic Complexity"), + self.tr("Cyclomatic &Complexity..."), + 0, + 0, + self, + "", + ) + act.setStatusTip(self.tr("Show the cyclomatic complexity for Python files.")) + act.setWhatsThis( + self.tr( + """<b>Cyclomatic Complexity...</b>""" + """<p>This calculates the cyclomatic complexity of Python""" + """ files and shows it together with a ranking.</p>""" + ) + ) act.triggered.connect(self.__editorCyclomaticComplexity) self.__editorMetricsActs.append(act) - + ericApp().getObject("Project").showMenu.connect(self.__projectShowMenu) - ericApp().getObject("Project").projectClosed.connect( - self.__projectClosed) + ericApp().getObject("Project").projectClosed.connect(self.__projectClosed) ericApp().getObject("ProjectBrowser").getProjectBrowser( - "sources").showMenu.connect(self.__projectBrowserShowMenu) - ericApp().getObject("ViewManager").editorOpenedEd.connect( - self.__editorOpened) - ericApp().getObject("ViewManager").editorClosedEd.connect( - self.__editorClosed) - + "sources" + ).showMenu.connect(self.__projectBrowserShowMenu) + ericApp().getObject("ViewManager").editorOpenedEd.connect(self.__editorOpened) + ericApp().getObject("ViewManager").editorClosedEd.connect(self.__editorClosed) + for editor in ericApp().getObject("ViewManager").getOpenEditors(): self.__editorOpened(editor) - + return None, True - + def deactivate(self): """ Public method to deactivate this plug-in. """ - ericApp().getObject("Project").showMenu.disconnect( - self.__projectShowMenu) - ericApp().getObject("Project").projectClosed.disconnect( - self.__projectClosed) + ericApp().getObject("Project").showMenu.disconnect(self.__projectShowMenu) + ericApp().getObject("Project").projectClosed.disconnect(self.__projectClosed) ericApp().getObject("ProjectBrowser").getProjectBrowser( - "sources").showMenu.disconnect(self.__projectBrowserShowMenu) + "sources" + ).showMenu.disconnect(self.__projectBrowserShowMenu) ericApp().getObject("ViewManager").editorOpenedEd.disconnect( - self.__editorOpened) + self.__editorOpened + ) ericApp().getObject("ViewManager").editorClosedEd.disconnect( - self.__editorClosed) - + self.__editorClosed + ) + menu = ericApp().getObject("Project").getMenu("Show") if menu: for sep in self.__projectSeparatorActs: @@ -550,14 +586,15 @@ for act in self.__projectMetricsActs: menu.removeAction(act) ericApp().getObject("Project").removeEricActions( - self.__projectMetricsActs[1:]) - + self.__projectMetricsActs[1:] + ) + if self.__projectBrowserMenu: for sep in self.__projectBrowserSeparatorActs: self.__projectBrowserMenu.removeAction(sep) for act in self.__projectBrowserMetricsActs: self.__projectBrowserMenu.removeAction(act) - + for editor in self.__editors: editor.showMenu.disconnect(self.__editorShowMenu) menu = editor.getMenu("Show") @@ -566,9 +603,9 @@ menu.removeAction(sep) for act in self.__editorMetricsActs: menu.removeAction(act) - + self.__initialize() - + def __loadTranslator(self): """ Private method to load the translation file. @@ -577,7 +614,8 @@ loc = self.__ui.getLocale() if loc and loc != "C": locale_dir = os.path.join( - os.path.dirname(__file__), "RadonMetrics", "i18n") + os.path.dirname(__file__), "RadonMetrics", "i18n" + ) translation = "radon_{0}".format(loc) translator = QTranslator(None) loaded = translator.load(translation, locale_dir) @@ -585,15 +623,17 @@ self.__translator = translator ericApp().installTranslator(self.__translator) else: - print("Warning: translation file '{0}' could not be" - " loaded.".format(translation)) + 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 @type str @param menu reference to the menu @@ -602,30 +642,29 @@ if menuName == "Show": for act in self.__projectMetricsActs[1:]: act.setEnabled( - ericApp().getObject("Project").getProjectLanguage() == - "Python3") - + ericApp().getObject("Project").getProjectLanguage() == "Python3" + ) + def __projectBrowserShowMenu(self, menuName, menu): """ Private slot called, when the the project browser context menu or a submenu is about to be shown. - + @param menuName name of the menu to be shown @type str @param menu reference to the menu @type QMenu """ if ( - menuName == "Show" and - ericApp().getObject("Project").getProjectLanguage() == - "Python3" and - self.__projectBrowserMenu is None + menuName == "Show" + and ericApp().getObject("Project").getProjectLanguage() == "Python3" + and self.__projectBrowserMenu is None ): self.__projectBrowserMenu = menu - + act = menu.addSeparator() self.__projectBrowserSeparatorActs.append(act) - + # header action act = QAction(self.tr("Radon"), self) font = act.font() @@ -634,65 +673,77 @@ act.triggered.connect(self.__showRadonVersion) menu.addAction(act) self.__projectBrowserMetricsActs.append(act) - + act = EricAction( - self.tr('Code Metrics'), - self.tr('Code &Metrics...'), 0, 0, - self, '') - act.setStatusTip(self.tr( - 'Show raw code metrics.')) - act.setWhatsThis(self.tr( - """<b>Code Metrics...</b>""" - """<p>This calculates raw code metrics of Python files""" - """ and shows the amount of lines of code, logical lines""" - """ of code, source lines of code, comment lines,""" - """ multi-line strings and blank lines.</p>""" - )) + self.tr("Code Metrics"), self.tr("Code &Metrics..."), 0, 0, self, "" + ) + act.setStatusTip(self.tr("Show raw code metrics.")) + act.setWhatsThis( + self.tr( + """<b>Code Metrics...</b>""" + """<p>This calculates raw code metrics of Python files""" + """ and shows the amount of lines of code, logical lines""" + """ of code, source lines of code, comment lines,""" + """ multi-line strings and blank lines.</p>""" + ) + ) act.triggered.connect(self.__projectBrowserRawMetrics) menu.addAction(act) self.__projectBrowserMetricsActs.append(act) - + act = EricAction( - self.tr('Maintainability Index'), - self.tr('Maintainability &Index...'), 0, 0, - self, '') - act.setStatusTip(self.tr( - 'Show the maintainability index for Python files.')) - act.setWhatsThis(self.tr( - """<b>Maintainability Index...</b>""" - """<p>This calculates the maintainability index of""" - """ Python files and shows it together with a ranking.""" - """</p>""" - )) - act.triggered.connect( - self.__projectBrowserMaintainabilityIndex) + self.tr("Maintainability Index"), + self.tr("Maintainability &Index..."), + 0, + 0, + self, + "", + ) + act.setStatusTip( + self.tr("Show the maintainability index for Python files.") + ) + act.setWhatsThis( + self.tr( + """<b>Maintainability Index...</b>""" + """<p>This calculates the maintainability index of""" + """ Python files and shows it together with a ranking.""" + """</p>""" + ) + ) + act.triggered.connect(self.__projectBrowserMaintainabilityIndex) menu.addAction(act) self.__projectBrowserMetricsActs.append(act) - + act = EricAction( - self.tr('Cyclomatic Complexity'), - self.tr('Cyclomatic &Complexity...'), 0, 0, - self, '') - act.setStatusTip(self.tr( - 'Show the cyclomatic complexity for Python files.')) - act.setWhatsThis(self.tr( - """<b>Cyclomatic Complexity...</b>""" - """<p>This calculates the cyclomatic complexity of""" - """ Python files and shows it together with a ranking.""" - """</p>""" - )) - act.triggered.connect( - self.__projectBrowserCyclomaticComplexity) + self.tr("Cyclomatic Complexity"), + self.tr("Cyclomatic &Complexity..."), + 0, + 0, + self, + "", + ) + act.setStatusTip( + self.tr("Show the cyclomatic complexity for Python files.") + ) + act.setWhatsThis( + self.tr( + """<b>Cyclomatic Complexity...</b>""" + """<p>This calculates the cyclomatic complexity of""" + """ Python files and shows it together with a ranking.""" + """</p>""" + ) + ) + act.triggered.connect(self.__projectBrowserCyclomaticComplexity) menu.addAction(act) self.__projectBrowserMetricsActs.append(act) - + act = menu.addSeparator() self.__projectBrowserSeparatorActs.append(act) - + def __editorOpened(self, editor): """ Private slot called, when a new editor was opened. - + @param editor reference to the new editor @type Editor """ @@ -704,21 +755,21 @@ editor.showMenu.connect(self.__editorShowMenu) editor.editorRenamed.connect(lambda: self.__editorRenamed(editor)) self.__editors.append(editor) - + def __editorClosed(self, editor): """ Private slot called, when an editor was closed. - + @param editor reference to the editor @type Editor """ with contextlib.suppress(ValueError): self.__editors.remove(editor) - + def __editorRenamed(self, editor): """ Private slot called, when an editor was renamed. - + @param editor reference to the renamed editor @type Editor """ @@ -727,12 +778,12 @@ menu.addAction(self.__editorSeparatorActs[0]) menu.addActions(self.__editorMetricsActs) menu.addAction(self.__editorSeparatorActs[1]) - + 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 @type str @param menu reference to the menu @@ -744,11 +795,11 @@ enable = editor.isPyFile() for act in self.__editorMetricsActs: act.setEnabled(enable) - + ################################################################## ## Raw code metrics calculations ################################################################## - + def __projectRawMetrics(self): """ Private slot used to calculate raw code metrics for the project. @@ -761,21 +812,20 @@ for file in project.getSources() if file.endswith(tuple(Preferences.getPython("Python3Extensions"))) ] - + if self.__projectRawMetricsDialog is None: from RadonMetrics.RawMetricsDialog import RawMetricsDialog + self.__projectRawMetricsDialog = RawMetricsDialog(self) self.__projectRawMetricsDialog.show() self.__projectRawMetricsDialog.prepare(files, project) - + def __projectBrowserRawMetrics(self): """ Private method to handle the code metrics context menu action of the project sources browser. """ - browser = ( - ericApp().getObject("ProjectBrowser").getProjectBrowser("sources") - ) + browser = ericApp().getObject("ProjectBrowser").getProjectBrowser("sources") if browser.getSelectedItemsCount([ProjectBrowserFileItem]) > 1: fn = [] for itm in browser.getSelectedItems([ProjectBrowserFileItem]): @@ -786,13 +836,14 @@ fn = itm.fileName() except AttributeError: fn = itm.dirName() - + if self.__projectBrowserRawMetricsDialog is None: from RadonMetrics.RawMetricsDialog import RawMetricsDialog + self.__projectBrowserRawMetricsDialog = RawMetricsDialog(self) self.__projectBrowserRawMetricsDialog.show() self.__projectBrowserRawMetricsDialog.start(fn) - + def __editorRawMetrics(self): """ Private slot to handle the raw code metrics action of the editor show @@ -800,20 +851,21 @@ """ editor = ericApp().getObject("ViewManager").activeWindow() if ( - editor is not None and - editor.checkDirty() and - editor.getFileName() is not None + editor is not None + and editor.checkDirty() + and editor.getFileName() is not None ): if self.__editorRawMetricsDialog is None: from RadonMetrics.RawMetricsDialog import RawMetricsDialog + self.__editorRawMetricsDialog = RawMetricsDialog(self) self.__editorRawMetricsDialog.show() self.__editorRawMetricsDialog.start(editor.getFileName()) - + ################################################################## ## Maintainability index calculations ################################################################## - + def __projectMaintainabilityIndex(self): """ Private slot used to calculate the maintainability indexes for the @@ -827,22 +879,22 @@ for file in project.getSources() if file.endswith(tuple(Preferences.getPython("Python3Extensions"))) ] - + if self.__projectMIDialog is None: from RadonMetrics.MaintainabilityIndexDialog import ( - MaintainabilityIndexDialog + MaintainabilityIndexDialog, ) + self.__projectMIDialog = MaintainabilityIndexDialog(self) self.__projectMIDialog.show() self.__projectMIDialog.prepare(files, project) - + def __projectBrowserMaintainabilityIndex(self): """ Private method to handle the maintainability index context menu action of the project sources browser. """ - browser = ericApp().getObject("ProjectBrowser").getProjectBrowser( - "sources") + browser = ericApp().getObject("ProjectBrowser").getProjectBrowser("sources") if browser.getSelectedItemsCount([ProjectBrowserFileItem]) > 1: fn = [] for itm in browser.getSelectedItems([ProjectBrowserFileItem]): @@ -853,15 +905,16 @@ fn = itm.fileName() except AttributeError: fn = itm.dirName() - + if self.__projectBrowserMIDialog is None: from RadonMetrics.MaintainabilityIndexDialog import ( - MaintainabilityIndexDialog + MaintainabilityIndexDialog, ) + self.__projectBrowserMIDialog = MaintainabilityIndexDialog(self) self.__projectBrowserMIDialog.show() self.__projectBrowserMIDialog.start(fn) - + def __editorMaintainabilityIndex(self): """ Private slot to handle the maintainability index action of the editor @@ -869,22 +922,23 @@ """ editor = ericApp().getObject("ViewManager").activeWindow() if ( - editor is not None and - editor.checkDirty() and - editor.getFileName() is not None + editor is not None + and editor.checkDirty() + and editor.getFileName() is not None ): if self.__editorMIDialog is None: from RadonMetrics.MaintainabilityIndexDialog import ( - MaintainabilityIndexDialog + MaintainabilityIndexDialog, ) + self.__editorMIDialog = MaintainabilityIndexDialog(self) self.__editorMIDialog.show() self.__editorMIDialog.start(editor.getFileName()) - + ################################################################## ## Cyclomatic complexity calculations ################################################################## - + def __projectCyclomaticComplexity(self): """ Private slot used to calculate the cyclomatic complexity for the @@ -898,22 +952,22 @@ for file in project.getSources() if file.endswith(tuple(Preferences.getPython("Python3Extensions"))) ] - + if self.__projectCCDialog is None: from RadonMetrics.CyclomaticComplexityDialog import ( - CyclomaticComplexityDialog + CyclomaticComplexityDialog, ) + self.__projectCCDialog = CyclomaticComplexityDialog(self) self.__projectCCDialog.show() self.__projectCCDialog.prepare(files, project) - + def __projectBrowserCyclomaticComplexity(self): """ Private method to handle the cyclomatic complexity context menu action of the project sources browser. """ - browser = ericApp().getObject("ProjectBrowser").getProjectBrowser( - "sources") + browser = ericApp().getObject("ProjectBrowser").getProjectBrowser("sources") if browser.getSelectedItemsCount([ProjectBrowserFileItem]) > 1: fn = [] for itm in browser.getSelectedItems([ProjectBrowserFileItem]): @@ -924,16 +978,18 @@ fn = itm.fileName() except AttributeError: fn = itm.dirName() - + if self.__projectBrowserCCDialog is None: from RadonMetrics.CyclomaticComplexityDialog import ( - CyclomaticComplexityDialog + CyclomaticComplexityDialog, ) + self.__projectBrowserCCDialog = CyclomaticComplexityDialog( - self, isSingle=True) + self, isSingle=True + ) self.__projectBrowserCCDialog.show() self.__projectBrowserCCDialog.start(fn) - + def __editorCyclomaticComplexity(self): """ Private slot to handle the cyclomatic complexity action of the editor @@ -941,28 +997,29 @@ """ editor = ericApp().getObject("ViewManager").activeWindow() if ( - editor is not None and - editor.checkDirty() and - editor.getFileName() is not None + editor is not None + and editor.checkDirty() + and editor.getFileName() is not None ): if self.__editorCCDialog is None: from RadonMetrics.CyclomaticComplexityDialog import ( - CyclomaticComplexityDialog + CyclomaticComplexityDialog, ) - self.__editorCCDialog = CyclomaticComplexityDialog( - self, isSingle=True) + + self.__editorCCDialog = CyclomaticComplexityDialog(self, isSingle=True) self.__editorCCDialog.show() self.__editorCCDialog.start(editor.getFileName()) - + ################################################################## ## Radon info display ################################################################## - + def __showRadonVersion(self): """ Private slot to show the version number of the used radon library. """ from radon import __version__ + EricMessageBox.information( None, self.tr("Radon"), @@ -978,12 +1035,13 @@ """<li><b>McCabe's complexity</b>, i.e. cyclomatic""" """ complexity</li>""" """</ul></p>""" - ).format(__version__)) - + ).format(__version__), + ) + ################################################################## ## Project handling methods ################################################################## - + def __projectClosed(self): """ Private slot to handle closing a project. @@ -997,18 +1055,20 @@ def installDependencies(pipInstall): """ Function to install dependencies of this plug-in. - + @param pipInstall function to be called with a list of package names. @type function """ try: from radon import __version__ as radon_version import Globals + if Globals.versionToTuple(radon_version) < (4, 5, 0): # force an upgrade pipInstall(["radon>=4.5.0"]) except ImportError: pipInstall(["radon>=4.5.0"]) + # # eflag: noqa = M801
--- a/RadonMetrics/CodeMetricsCalculator.py Mon Sep 19 17:43:37 2022 +0200 +++ b/RadonMetrics/CodeMetricsCalculator.py Mon Sep 19 17:54:33 2022 +0200 @@ -14,7 +14,7 @@ def initService(): """ Initialize the service and return the entry point. - + @return the entry point for the background client (function) """ return rawCodeMetrics @@ -23,7 +23,7 @@ def initBatchService(): """ Initialize the batch service and return the entry point. - + @return the entry point for the background client (function) """ return batchRawCodeMetrics @@ -32,7 +32,7 @@ def rawCodeMetrics(file, text=""): """ Private function to calculate the raw code metrics of one file. - + @param file source filename @type str @param text source text @@ -46,7 +46,7 @@ def batchRawCodeMetrics(argumentsList, send, fx, cancelled, maxProcesses=0): """ Module function to calculate the raw code metrics for a batch of files. - + @param argumentsList list of arguments tuples as given for rawCodeMetrics @type list @param send reference to send function @@ -80,9 +80,8 @@ # Start worker processes workers = [ - multiprocessing.Process( - target=workerTask, args=(taskQueue, doneQueue) - ) for _ in range(NumberOfProcesses) + multiprocessing.Process(target=workerTask, args=(taskQueue, doneQueue)) + for _ in range(NumberOfProcesses) ] for worker in workers: worker.start() @@ -92,7 +91,7 @@ for i in range(len(argumentsList)): resultSent = False wasCancelled = False - + while not resultSent: try: # get result (waiting max. 3 seconds and send it to frontend @@ -104,18 +103,18 @@ if cancelled(): wasCancelled = True break - + if wasCancelled or cancelled(): # just exit the loop ignoring the results of queued tasks break - + if i < endIndex: taskQueue.put(argumentsList[i + initialTasks]) # Tell child processes to stop for _ in range(NumberOfProcesses): - taskQueue.put('STOP') - + taskQueue.put("STOP") + for worker in workers: worker.join() worker.close() @@ -125,13 +124,13 @@ """ Module function acting as the parallel worker for the raw code metrics calculation. - + @param inputQueue input queue @type multiprocessing.Queue @param outputQueue output queue @type multiprocessing.Queue """ - for filename, source in iter(inputQueue.get, 'STOP'): + for filename, source in iter(inputQueue.get, "STOP"): result = __rawCodeMetrics(filename, source) outputQueue.put((filename, result)) @@ -139,7 +138,7 @@ def __rawCodeMetrics(file, text=""): """ Private function to calculate the raw code metrics for one Python file. - + @param file source filename @type str @param text source text @@ -148,18 +147,19 @@ @rtype (tuple of dict) """ from radon.raw import analyze + try: res = __raw2Dict(analyze(text)) except Exception as err: res = {"error": str(err)} - return (res, ) + return (res,) def __raw2Dict(obj): """ Private function to convert an object holding raw analysis results into a dictionary. - + @param obj object as returned from analyze() @type radon.raw.Module @return conversion result
--- a/RadonMetrics/CyclomaticComplexityCalculator.py Mon Sep 19 17:43:37 2022 +0200 +++ b/RadonMetrics/CyclomaticComplexityCalculator.py Mon Sep 19 17:54:33 2022 +0200 @@ -14,7 +14,7 @@ def initService(): """ Initialize the service and return the entry point. - + @return the entry point for the background client (function) """ return cyclomaticComplexity @@ -23,7 +23,7 @@ def initBatchService(): """ Initialize the batch service and return the entry point. - + @return the entry point for the background client (function) """ return batchCyclomaticComplexity @@ -32,7 +32,7 @@ def cyclomaticComplexity(file, text=""): """ Private function to calculate the cyclomatic complexity of one file. - + @param file source filename @type str @param text source text @@ -43,12 +43,11 @@ return __cyclomaticComplexity(file, text) -def batchCyclomaticComplexity(argumentsList, send, fx, cancelled, - maxProcesses=0): +def batchCyclomaticComplexity(argumentsList, send, fx, cancelled, maxProcesses=0): """ Module function to calculate the cyclomatic complexity for a batch of files. - + @param argumentsList list of arguments tuples as given for cyclomaticComplexity @type list @@ -83,9 +82,8 @@ # Start worker processes workers = [ - multiprocessing.Process( - target=workerTask, args=(taskQueue, doneQueue) - ) for _ in range(NumberOfProcesses) + multiprocessing.Process(target=workerTask, args=(taskQueue, doneQueue)) + for _ in range(NumberOfProcesses) ] for worker in workers: worker.start() @@ -95,7 +93,7 @@ for i in range(len(argumentsList)): resultSent = False wasCancelled = False - + while not resultSent: try: # get result (waiting max. 3 seconds and send it to frontend @@ -107,18 +105,18 @@ if cancelled(): wasCancelled = True break - + if wasCancelled or cancelled(): # just exit the loop ignoring the results of queued tasks break - + if i < endIndex: taskQueue.put(argumentsList[i + initialTasks]) # Tell child processes to stop for _ in range(NumberOfProcesses): - taskQueue.put('STOP') - + taskQueue.put("STOP") + for worker in workers: worker.join() worker.close() @@ -128,13 +126,13 @@ """ Module function acting as the parallel worker for the cyclomatic complexity calculation. - + @param inputQueue input queue @type multiprocessing.Queue @param outputQueue output queue @type multiprocessing.Queue """ - for filename, source in iter(inputQueue.get, 'STOP'): + for filename, source in iter(inputQueue.get, "STOP"): result = __cyclomaticComplexity(filename, source) outputQueue.put((filename, result)) @@ -143,7 +141,7 @@ """ Private function to calculate the cyclomatic complexity for one Python file. - + @param file source filename @type str @param text source text @@ -152,11 +150,10 @@ @rtype (tuple of dict) """ from radon.complexity import cc_visit, cc_rank - + try: cc = cc_visit(text) - res = {"result": [v for v in map(__cc2Dict, cc) - if v["type"] != "method"]} + res = {"result": [v for v in map(__cc2Dict, cc) if v["type"] != "method"]} totalCC = 0 rankSummary = { "A": 0, @@ -174,14 +171,14 @@ res["summary"] = rankSummary except Exception as err: res = {"error": str(err)} - return (res, ) + return (res,) def __cc2Dict(obj): """ Private function to convert an object holding cyclomatic complexity results into a dictionary. - + @param obj object as returned from cc_visit() @type radon.visitors.Function @return conversion result @@ -189,18 +186,18 @@ """ from radon.complexity import cc_rank from radon.visitors import Function - + result = { - 'type': __getType(obj), - 'rank': cc_rank(obj.complexity), + "type": __getType(obj), + "rank": cc_rank(obj.complexity), } - attrs = set(Function._fields) - {'is_method', 'closures'} + attrs = set(Function._fields) - {"is_method", "closures"} attrs.add("fullname") for attr in attrs: v = getattr(obj, attr, None) if v is not None: result[attr] = v - for key in ('methods', 'closures'): + for key in ("methods", "closures"): if hasattr(obj, key): result[key] = list(map(__cc2Dict, getattr(obj, key))) return result @@ -209,18 +206,18 @@ def __getType(obj): """ Private function to get the type of an object as a string. - + @param obj object to be analyzed @type radon.visitors.Function or radon.visitors.Class @return type string for the object @rtype str, one of ["method", "function", "class"] """ from radon.visitors import Function - + if isinstance(obj, Function): if obj.is_method: - return 'method' + return "method" else: - return 'function' + return "function" else: - return 'class' + return "class"
--- a/RadonMetrics/CyclomaticComplexityDialog.py Mon Sep 19 17:43:37 2022 +0200 +++ b/RadonMetrics/CyclomaticComplexityDialog.py Mon Sep 19 17:54:33 2022 +0200 @@ -14,8 +14,13 @@ from PyQt6.QtCore import pyqtSlot, Qt, QTimer, QLocale from PyQt6.QtGui import QColor from PyQt6.QtWidgets import ( - QDialog, QDialogButtonBox, QAbstractButton, QHeaderView, QTreeWidgetItem, - QApplication, QMenu + QDialog, + QDialogButtonBox, + QAbstractButton, + QHeaderView, + QTreeWidgetItem, + QApplication, + QMenu, ) from .Ui_CyclomaticComplexityDialog import Ui_CyclomaticComplexityDialog @@ -30,13 +35,14 @@ Class implementing a dialog to show the cyclomatic complexity (McCabe complexity). """ + FilePathRole = Qt.ItemDataRole.UserRole + 1 LineNumberRole = Qt.ItemDataRole.UserRole + 2 - + def __init__(self, radonService, isSingle=False, parent=None): """ Constructor - + @param radonService reference to the service @type RadonMetricsPlugin @param isSingle flag indicating a single file dialog @@ -47,70 +53,73 @@ super().__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.WindowType.Window) - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setDefault(True) - + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) + self.resultList.headerItem().setText(self.resultList.columnCount(), "") - + self.rankComboBox.addItems(["A", "B", "C", "D", "E", "F"]) self.rankComboBox.setCurrentIndex(self.rankComboBox.findText("D")) self.__minimumRank = "D" - + self.radonService = radonService self.radonService.complexityDone.connect(self.__processResult) self.radonService.error.connect(self.__processError) self.radonService.batchFinished.connect(self.__batchFinished) - + self.__isSingle = isSingle self.cancelled = False - + self.__project = ericApp().getObject("Project") self.__locale = QLocale() self.__finished = True self.__errorItem = None - + self.__fileList = [] self.filterFrame.setVisible(False) - - self.explanationLabel.setText(self.tr( - "<table>" - "<tr><td colspan=3><b>Ranking:</b></td></tr>" - "<tr><td><b>A</b></td><td>1 - 5</td>" - "<td>(low risk - simple block)</td></tr>" - "<tr><td><b>B</b></td><td>6 - 10</td>" - "<td>(low risk - well structured and stable block)</td></tr>" - "<tr><td><b>C</b></td><td>11 - 20</td>" - "<td>(moderate risk - slightly complex block)</td></tr>" - "<tr><td><b>D</b></td><td>21 - 30</td>" - "<td>(more than moderate risk - more complex block)</td></tr>" - "<tr><td><b>E</b></td><td>31 - 40</td>" - "<td>(high risk - complex block, alarming)</td></tr>" - "<tr><td><b>F</b></td><td>> 40</td>" - "<td>(very high risk - error-prone, unstable block)</td></tr>" - "</table>" - )) - self.typeLabel.setText(self.tr( - "<table>" - "<tr><td colspan=2><b>Type:</b></td></tr>" - "<tr><td><b>C</b></td><td>Class</td></tr>" - "<tr><td><b>F</b></td><td>Function</td></tr>" - "<tr><td><b>M</b></td><td>Method</td></tr>" - "</table>" - )) - + + self.explanationLabel.setText( + self.tr( + "<table>" + "<tr><td colspan=3><b>Ranking:</b></td></tr>" + "<tr><td><b>A</b></td><td>1 - 5</td>" + "<td>(low risk - simple block)</td></tr>" + "<tr><td><b>B</b></td><td>6 - 10</td>" + "<td>(low risk - well structured and stable block)</td></tr>" + "<tr><td><b>C</b></td><td>11 - 20</td>" + "<td>(moderate risk - slightly complex block)</td></tr>" + "<tr><td><b>D</b></td><td>21 - 30</td>" + "<td>(more than moderate risk - more complex block)</td></tr>" + "<tr><td><b>E</b></td><td>31 - 40</td>" + "<td>(high risk - complex block, alarming)</td></tr>" + "<tr><td><b>F</b></td><td>> 40</td>" + "<td>(very high risk - error-prone, unstable block)</td></tr>" + "</table>" + ) + ) + self.typeLabel.setText( + self.tr( + "<table>" + "<tr><td colspan=2><b>Type:</b></td></tr>" + "<tr><td><b>C</b></td><td>Class</td></tr>" + "<tr><td><b>F</b></td><td>Function</td></tr>" + "<tr><td><b>M</b></td><td>Method</td></tr>" + "</table>" + ) + ) + self.__mappedType = { "class": "C", "function": "F", "method": "M", } - + try: usesDarkPalette = ericApp().usesDarkPalette() except AttributeError: from PyQt6.QtGui import QPalette + palette = ericApp().palette() lightness = palette.color(QPalette.Window).lightness() usesDarkPalette = lightness <= 128 @@ -142,56 +151,54 @@ "E": QColor("#ff0000"), "F": QColor("#ff0000"), } - + self.__menu = QMenu(self) - self.__menu.addAction(self.tr("Collapse all"), - self.__resultCollapse) + self.__menu.addAction(self.tr("Collapse all"), self.__resultCollapse) self.__menu.addAction(self.tr("Expand all"), self.__resultExpand) - self.resultList.setContextMenuPolicy( - Qt.ContextMenuPolicy.CustomContextMenu) - self.resultList.customContextMenuRequested.connect( - self.__showContextMenu) - + self.resultList.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) + self.resultList.customContextMenuRequested.connect(self.__showContextMenu) + def __resizeResultColumns(self): """ Private method to resize the list columns. """ - self.resultList.header().resizeSections( - QHeaderView.ResizeMode.ResizeToContents) + self.resultList.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents) self.resultList.header().setStretchLastSection(True) - + def __createFileItem(self, filename): """ Private method to create a new file item in the result list. - + @param filename name of the file @type str @return reference to the created item @rtype QTreeWidgetItem """ - itm = QTreeWidgetItem( - [self.__project.getRelativePath(filename)]) + itm = QTreeWidgetItem([self.__project.getRelativePath(filename)]) itm.setData(0, self.FilePathRole, filename) itm.setData(0, self.LineNumberRole, 1) return itm - + def __createResultItem(self, parentItem, values): """ Private slot to create a new item in the result list. - + @param parentItem reference to the parent item @type QTreeWidgetItem @param values values to be displayed @type dict """ if values["rank"] >= self.__minimumRank: - itm = QTreeWidgetItem(parentItem, [ - self.__mappedType[values["type"]], - values["fullname"], - "{0:3}".format(values["complexity"]), - values["rank"], - "{0:6}".format(values["lineno"]), - ]) + itm = QTreeWidgetItem( + parentItem, + [ + self.__mappedType[values["type"]], + values["fullname"], + "{0:3}".format(values["complexity"]), + values["rank"], + "{0:6}".format(values["lineno"]), + ], + ) itm.setTextAlignment(2, Qt.AlignmentFlag.AlignRight) itm.setTextAlignment(3, Qt.AlignmentFlag.AlignHCenter) itm.setTextAlignment(4, Qt.AlignmentFlag.AlignRight) @@ -199,44 +206,41 @@ itm.setBackground(3, self.__rankColors[values["rank"]]) if values["type"] in self.__typeColors: itm.setForeground(0, self.__typeColors[values["type"]]) - itm.setData(0, self.FilePathRole, - parentItem.data(0, self.FilePathRole)) + itm.setData(0, self.FilePathRole, parentItem.data(0, self.FilePathRole)) itm.setData(0, self.LineNumberRole, values["lineno"]) - + if "methods" in values: for method in values["methods"]: self.__createResultItem(parentItem, method) - + if "closures" in values and values["closures"]: for closure in values["closures"]: self.__createResultItem(parentItem, closure) - + def __createErrorItem(self, filename, message): """ Private slot to create a new error item in the result list. - + @param filename name of the file @type str @param message error message @type str """ if self.__errorItem is None: - self.__errorItem = QTreeWidgetItem(self.resultList, [ - self.tr("Errors")]) + self.__errorItem = QTreeWidgetItem(self.resultList, [self.tr("Errors")]) self.__errorItem.setExpanded(True) self.__errorItem.setForeground(0, Qt.GlobalColor.red) - - msg = "{0} ({1})".format(self.__project.getRelativePath(filename), - message) + + msg = "{0} ({1})".format(self.__project.getRelativePath(filename), message) if not self.resultList.findItems(msg, Qt.MatchFlag.MatchExactly): itm = QTreeWidgetItem(self.__errorItem, [msg]) itm.setForeground(0, Qt.GlobalColor.red) itm.setFirstColumnSpanned(True) - + def prepare(self, fileList, project): """ Public method to prepare the dialog with a list of filenames. - + @param fileList list of filenames @type list of str @param project reference to the project object @@ -244,31 +248,28 @@ """ self.__fileList = fileList[:] self.__project = project - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setDefault(True) - + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) + self.filterFrame.setVisible(True) - - self.__data = self.__project.getData( - "OTHERTOOLSPARMS", "RadonCodeMetrics") + + self.__data = self.__project.getData("OTHERTOOLSPARMS", "RadonCodeMetrics") if self.__data is None or "ExcludeFiles" not in self.__data: self.__data = {"ExcludeFiles": ""} if "MinimumRank" not in self.__data: self.__data["MinimumRank"] = "D" self.excludeFilesEdit.setText(self.__data["ExcludeFiles"]) self.__minimumRank = self.__data["MinimumRank"] - self.rankComboBox.setCurrentIndex(self.rankComboBox.findText( - self.__minimumRank)) - + self.rankComboBox.setCurrentIndex( + self.rankComboBox.findText(self.__minimumRank) + ) + def start(self, fn, minRank="D"): """ Public slot to start the cyclomatic complexity determination. - + @param fn file or list of files or directory to show the cyclomatic complexity for @type str or list of str @@ -280,24 +281,20 @@ self.resultList.clear() self.summaryLabel.clear() QApplication.processEvents() - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setEnabled(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setDefault(True) + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(True) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) self.rankComboBox.setEnabled(False) QApplication.processEvents() - + if isinstance(fn, list): self.files = fn elif os.path.isdir(fn): self.files = [] extensions = set(Preferences.getPython("Python3Extensions")) for ext in extensions: - self.files.extend( - Utilities.direntries(fn, True, '*{0}'.format(ext), 0)) + self.files.extend(Utilities.direntries(fn, True, "*{0}".format(ext), 0)) else: self.files = [fn] self.files.sort() @@ -307,7 +304,7 @@ self.files.remove(f) if self.__isSingle: self.__fileList = self.files[:] - + self.__summary = { "A": 0, "B": 0, @@ -318,18 +315,18 @@ } self.__ccSum = 0 self.__ccCount = 0 - + self.__minimumRank = self.rankComboBox.currentText() - + if len(self.files) > 0: # disable updates of the list for speed self.resultList.setUpdatesEnabled(False) self.resultList.setSortingEnabled(False) - + self.checkProgress.setMaximum(len(self.files)) self.checkProgress.setVisible(len(self.files) > 1) QApplication.processEvents() - + # now go through all the files self.progress = 0 if len(self.files) == 1: @@ -338,14 +335,14 @@ else: self.__batch = True self.cyclomaticComplexityBatch() - - def cyclomaticComplexity(self, codestring=''): + + def cyclomaticComplexity(self, codestring=""): """ Public method to start a cyclomatic complexity calculation for one Python file. - + The results are reported to the __processResult slot. - + @param codestring optional sourcestring @type str """ @@ -354,14 +351,14 @@ self.checkProgress.setValue(1) self.__finish() return - + self.filename = self.files.pop(0) self.checkProgress.setValue(self.progress) QApplication.processEvents() - + if self.cancelled: return - + try: self.source = Utilities.readEncodedFile(self.filename)[0] self.source = Utilities.normalizeCode(self.source) @@ -373,42 +370,41 @@ return self.__finished = False - self.radonService.cyclomaticComplexity( - None, self.filename, self.source) + self.radonService.cyclomaticComplexity(None, self.filename, self.source) def cyclomaticComplexityBatch(self): """ Public method to start a cyclomatic complexity calculation batch job. - + The results are reported to the __processResult slot. """ self.__lastFileItem = None - + argumentsList = [] for progress, filename in enumerate(self.files, start=1): self.checkProgress.setValue(progress) QApplication.processEvents() - + try: source = Utilities.readEncodedFile(filename)[0] source = Utilities.normalizeCode(source) except (UnicodeError, OSError) as msg: self.__createErrorItem(filename, str(msg).rstrip()) continue - + argumentsList.append((filename, source)) - + # reset the progress bar to the checked files self.checkProgress.setValue(self.progress) QApplication.processEvents() - + self.__finished = False self.radonService.cyclomaticComplexityBatch(argumentsList) - + def __batchFinished(self, type_): """ Private slot handling the completion of a batch job. - + @param type_ type of the calculated metrics @type str, one of ["raw", "mi", "cc"] """ @@ -416,11 +412,11 @@ self.checkProgress.setMaximum(1) self.checkProgress.setValue(1) self.__finish() - + def __processError(self, type_, fn, msg): """ Private slot to process an error indication from the service. - + @param type_ type of the calculated metrics @type str, one of ["raw", "mi", "cc"] @param fn filename of the file @@ -430,12 +426,12 @@ """ if type_ == "cc": self.__createErrorItem(fn, msg) - + def __processResult(self, fn, result): """ Private slot called after perfoming a cyclomatic complexity calculation on one file. - + @param fn filename of the file @type str @param result result dict @@ -443,14 +439,14 @@ """ if self.__finished: return - + # Check if it's the requested file, otherwise ignore signal if not # in batch mode if not self.__batch and fn != self.filename: return - + QApplication.processEvents() - + if "error" in result: self.__createErrorItem(fn, result["error"]) else: @@ -462,105 +458,108 @@ self.resultList.addTopLevelItem(fitm) fitm.setExpanded(True) fitm.setFirstColumnSpanned(True) - + self.__ccCount += result["count"] self.__ccSum += result["total_cc"] for rank in result["summary"]: self.__summary[rank] += result["summary"][rank] - + self.progress += 1 - + self.checkProgress.setValue(self.progress) QApplication.processEvents() - + if not self.__batch: self.cyclomaticComplexity() - + def __finish(self): """ Private slot called when the action or the user pressed the button. """ from radon.complexity import cc_rank - + if not self.__finished: self.__finished = True - + # re-enable updates of the list self.resultList.setSortingEnabled(True) self.resultList.sortItems(0, Qt.SortOrder.AscendingOrder) self.resultList.sortItems(1, Qt.SortOrder.AscendingOrder) self.resultList.setUpdatesEnabled(True) - + self.cancelled = True - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setDefault(True) + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled( + True + ) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled( + False + ) + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault( + True + ) self.rankComboBox.setEnabled(True) - + self.resultList.header().resizeSections( - QHeaderView.ResizeMode.ResizeToContents) + QHeaderView.ResizeMode.ResizeToContents + ) self.resultList.header().setStretchLastSection(True) self.resultList.header().setSectionResizeMode( - QHeaderView.ResizeMode.Interactive) - + QHeaderView.ResizeMode.Interactive + ) + averageCC = float(self.__ccSum) / (self.__ccCount or 1) - - self.summaryLabel.setText(self.tr( - "<b>Summary:</b><br/>" - "{0} blocks (classes, functions, methods) analyzed.<br/>" - "Average complexity: {7} ({8})" - "<table>" - "<tr><td width=30><b>A</b></td>" - "<td align='right'>{1} blocks</td></tr>" - "<tr><td width=30><b>B</b></td>" - "<td align='right'>{2} blocks</td></tr>" - "<tr><td width=30><b>C</b></td>" - "<td align='right'>{3} blocks</td></tr>" - "<tr><td width=30><b>D</b></td>" - "<td align='right'>{4} blocks</td></tr>" - "<tr><td width=30><b>E</b></td>" - "<td align='right'>{5} blocks</td></tr>" - "<tr><td width=30><b>F</b></td>" - "<td align='right'>{6} blocks</td></tr>" - "</table>" - ).format( - self.__locale.toString(self.__ccCount), - self.__locale.toString(self.__summary["A"]), - self.__locale.toString(self.__summary["B"]), - self.__locale.toString(self.__summary["C"]), - self.__locale.toString(self.__summary["D"]), - self.__locale.toString(self.__summary["E"]), - self.__locale.toString(self.__summary["F"]), - cc_rank(averageCC), - self.__locale.toString(averageCC, "f", 1) - )) - + + self.summaryLabel.setText( + self.tr( + "<b>Summary:</b><br/>" + "{0} blocks (classes, functions, methods) analyzed.<br/>" + "Average complexity: {7} ({8})" + "<table>" + "<tr><td width=30><b>A</b></td>" + "<td align='right'>{1} blocks</td></tr>" + "<tr><td width=30><b>B</b></td>" + "<td align='right'>{2} blocks</td></tr>" + "<tr><td width=30><b>C</b></td>" + "<td align='right'>{3} blocks</td></tr>" + "<tr><td width=30><b>D</b></td>" + "<td align='right'>{4} blocks</td></tr>" + "<tr><td width=30><b>E</b></td>" + "<td align='right'>{5} blocks</td></tr>" + "<tr><td width=30><b>F</b></td>" + "<td align='right'>{6} blocks</td></tr>" + "</table>" + ).format( + self.__locale.toString(self.__ccCount), + self.__locale.toString(self.__summary["A"]), + self.__locale.toString(self.__summary["B"]), + self.__locale.toString(self.__summary["C"]), + self.__locale.toString(self.__summary["D"]), + self.__locale.toString(self.__summary["E"]), + self.__locale.toString(self.__summary["F"]), + cc_rank(averageCC), + self.__locale.toString(averageCC, "f", 1), + ) + ) + self.checkProgress.setVisible(False) - + @pyqtSlot(QAbstractButton) def on_buttonBox_clicked(self, button): """ Private slot called by a button of the button box clicked. - + @param button button that was clicked @type QAbstractButton """ - if button == self.buttonBox.button( - QDialogButtonBox.StandardButton.Close - ): + if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close): self.close() - elif button == self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel - ): + elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel): if self.__batch: self.radonService.cancelComplexityBatch() QTimer.singleShot(1000, self.__finish) else: self.__finish() - + @pyqtSlot() def on_startButton_clicked(self): """ @@ -568,70 +567,67 @@ """ fileList = self.__fileList[:] dataChanged = False - + filterString = self.excludeFilesEdit.text() if ( - "ExcludeFiles" not in self.__data or - filterString != self.__data["ExcludeFiles"] + "ExcludeFiles" not in self.__data + or filterString != self.__data["ExcludeFiles"] ): self.__data["ExcludeFiles"] = filterString dataChanged = True - filterList = [f.strip() for f in filterString.split(",") - if f.strip()] + filterList = [f.strip() for f in filterString.split(",") if f.strip()] if filterList: for fileFilter in filterList: - fileList = [f for f in fileList - if not fnmatch.fnmatch(f, fileFilter)] - + fileList = [f for f in fileList if not fnmatch.fnmatch(f, fileFilter)] + minimumRank = self.rankComboBox.currentText() if ( - "MinimumRank" not in self.__data or - minimumRank != self.__data["MinimumRank"] + "MinimumRank" not in self.__data + or minimumRank != self.__data["MinimumRank"] ): self.__data["MinimumRank"] = minimumRank dataChanged = True - + if dataChanged: - self.__project.setData( - "OTHERTOOLSPARMS", "RadonCodeMetrics", self.__data) - + self.__project.setData("OTHERTOOLSPARMS", "RadonCodeMetrics", self.__data) + self.start(fileList) - + def __showContextMenu(self, coord): """ Private slot to show the context menu of the resultlist. - + @param coord the position of the mouse pointer (QPoint) """ if self.resultList.topLevelItemCount() > 0: self.__menu.popup(self.mapToGlobal(coord)) - + def __resultCollapse(self): """ Private slot to collapse all entries of the resultlist. """ for index in range(self.resultList.topLevelItemCount()): self.resultList.topLevelItem(index).setExpanded(False) - + def __resultExpand(self): """ Private slot to expand all entries of the resultlist. """ for index in range(self.resultList.topLevelItemCount()): self.resultList.topLevelItem(index).setExpanded(True) - + def clear(self): """ Public method to clear all results. """ self.resultList.clear() self.summaryLabel.clear() - + @pyqtSlot(QTreeWidgetItem, int) def on_resultList_itemActivated(self, item, column): """ Private slot to handle the activation of a result item. - + @param item reference to the activated item @type QTreeWidgetItem @param column activated column @@ -642,12 +638,12 @@ if filename: vm = ericApp().getObject("ViewManager") vm.openSourceFile(filename, lineno) - + @pyqtSlot(str) def on_rankComboBox_activated(self, rank): """ Private slot to handle the selection of a minimum rank. - + @param rank selected minimum rank @type str """
--- a/RadonMetrics/MaintainabilityIndexCalculator.py Mon Sep 19 17:43:37 2022 +0200 +++ b/RadonMetrics/MaintainabilityIndexCalculator.py Mon Sep 19 17:54:33 2022 +0200 @@ -14,7 +14,7 @@ def initService(): """ Initialize the service and return the entry point. - + @return the entry point for the background client (function) """ return maintainabilityIndex @@ -23,7 +23,7 @@ def initBatchService(): """ Initialize the batch service and return the entry point. - + @return the entry point for the background client (function) """ return batchMaintainabilityIndex @@ -32,7 +32,7 @@ def maintainabilityIndex(file, text=""): """ Private function to calculate the maintainability index of one file. - + @param file source filename @type str @param text source text @@ -43,12 +43,11 @@ return __maintainabilityIndex(file, text) -def batchMaintainabilityIndex(argumentsList, send, fx, cancelled, - maxProcesses=0): +def batchMaintainabilityIndex(argumentsList, send, fx, cancelled, maxProcesses=0): """ Module function to calculate the maintainability index for a batch of files. - + @param argumentsList list of arguments tuples as given for maintainabilityIndex @type list @@ -83,9 +82,8 @@ # Start worker processes workers = [ - multiprocessing.Process( - target=workerTask, args=(taskQueue, doneQueue) - ) for _ in range(NumberOfProcesses) + multiprocessing.Process(target=workerTask, args=(taskQueue, doneQueue)) + for _ in range(NumberOfProcesses) ] for worker in workers: worker.start() @@ -95,7 +93,7 @@ for i in range(len(argumentsList)): resultSent = False wasCancelled = False - + while not resultSent: try: # get result (waiting max. 3 seconds and send it to frontend @@ -107,18 +105,18 @@ if cancelled(): wasCancelled = True break - + if wasCancelled or cancelled(): # just exit the loop ignoring the results of queued tasks break - + if i < endIndex: taskQueue.put(argumentsList[i + initialTasks]) # Tell child processes to stop for _ in range(NumberOfProcesses): - taskQueue.put('STOP') - + taskQueue.put("STOP") + for worker in workers: worker.join() worker.close() @@ -128,13 +126,13 @@ """ Module function acting as the parallel worker for the maintainability index calculation. - + @param inputQueue input queue @type multiprocessing.Queue @param outputQueue output queue @type multiprocessing.Queue """ - for filename, source in iter(inputQueue.get, 'STOP'): + for filename, source in iter(inputQueue.get, "STOP"): result = __maintainabilityIndex(filename, source) outputQueue.put((filename, result)) @@ -143,7 +141,7 @@ """ Private function to calculate the maintainability index for one Python file. - + @param file source filename @type str @param text source text @@ -152,11 +150,11 @@ @rtype (tuple of dict) """ from radon.metrics import mi_visit, mi_rank - + try: mi = mi_visit(text, True) rank = mi_rank(mi) res = {"mi": mi, "rank": rank} except Exception as err: res = {"error": str(err)} - return (res, ) + return (res,)
--- a/RadonMetrics/MaintainabilityIndexDialog.py Mon Sep 19 17:43:37 2022 +0200 +++ b/RadonMetrics/MaintainabilityIndexDialog.py Mon Sep 19 17:54:33 2022 +0200 @@ -13,8 +13,12 @@ from PyQt6.QtCore import pyqtSlot, Qt, QTimer, QLocale from PyQt6.QtGui import QColor from PyQt6.QtWidgets import ( - QDialog, QDialogButtonBox, QAbstractButton, QHeaderView, QTreeWidgetItem, - QApplication + QDialog, + QDialogButtonBox, + QAbstractButton, + QHeaderView, + QTreeWidgetItem, + QApplication, ) from .Ui_MaintainabilityIndexDialog import Ui_MaintainabilityIndexDialog @@ -28,12 +32,13 @@ """ Class implementing a dialog to show maintainability indexes. """ + FilePathRole = Qt.ItemDataRole.UserRole + 1 - + def __init__(self, radonService, parent=None): """ Constructor - + @param radonService reference to the service @type RadonMetricsPlugin @param parent reference to the parent widget @@ -42,44 +47,44 @@ super().__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.WindowType.Window) - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setDefault(True) - + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) + self.resultList.headerItem().setText(self.resultList.columnCount(), "") - + self.radonService = radonService - self.radonService.maintainabilityIndexDone.connect( - self.__processResult) + self.radonService.maintainabilityIndexDone.connect(self.__processResult) self.radonService.error.connect(self.__processError) self.radonService.batchFinished.connect(self.__batchFinished) - + self.cancelled = False - + self.__project = ericApp().getObject("Project") self.__locale = QLocale() self.__finished = True self.__errorItem = None - + self.__fileList = [] self.filterFrame.setVisible(False) - - self.explanationLabel.setText(self.tr( - "<table>" - "<tr><td><b>Rank</b></td><td><b>MI Score</b></td>" - "<td><b>Maintainability</b></td></tr>" - "<tr><td><b>A</b></td><td>100 - 20</td><td>very high</td></tr>" - "<tr><td><b>B</b></td><td>19 - 10</td><td>medium</td></tr>" - "<tr><td><b>C</b></td><td>9 - 0</td><td>extremely low</td></tr>" - "</table>" - )) - + + self.explanationLabel.setText( + self.tr( + "<table>" + "<tr><td><b>Rank</b></td><td><b>MI Score</b></td>" + "<td><b>Maintainability</b></td></tr>" + "<tr><td><b>A</b></td><td>100 - 20</td><td>very high</td></tr>" + "<tr><td><b>B</b></td><td>19 - 10</td><td>medium</td></tr>" + "<tr><td><b>C</b></td><td>9 - 0</td><td>extremely low</td></tr>" + "</table>" + ) + ) + try: usesDarkPalette = ericApp().usesDarkPalette() except AttributeError: from PyQt6.QtGui import QPalette + palette = ericApp().palette() lightness = palette.color(QPalette.Window).lightness() usesDarkPalette = lightness <= 128 @@ -95,19 +100,20 @@ "B": QColor("#ffff00"), "C": QColor("#ff0000"), } - + def __resizeResultColumns(self): """ Private method to resize the list columns. """ self.resultList.header().resizeSections( - QHeaderView.ResizeMode.ResizeMode.ResizeToContents) + QHeaderView.ResizeMode.ResizeMode.ResizeToContents + ) self.resultList.header().setStretchLastSection(True) - + def __createResultItem(self, filename, values): """ Private slot to create a new item in the result list. - + @param filename name of the file @type str @param values values to be displayed @@ -115,8 +121,9 @@ """ data = [self.__project.getRelativePath(filename)] try: - data.append("{0:>6}".format( - self.__locale.toString(float(values["mi"]), "f", 2))) + data.append( + "{0:>6}".format(self.__locale.toString(float(values["mi"]), "f", 2)) + ) except ValueError: data.append(values["mi"]) data.append(values["rank"]) @@ -126,36 +133,34 @@ if values["rank"] in self.__rankColors: itm.setBackground(2, self.__rankColors[values["rank"]]) itm.setData(0, self.FilePathRole, filename) - + if values["rank"] in self.__summary: self.__summary[values["rank"]] += 1 - + def __createErrorItem(self, filename, message): """ Private slot to create a new error item in the result list. - + @param filename name of the file @type str @param message error message @type str """ if self.__errorItem is None: - self.__errorItem = QTreeWidgetItem(self.resultList, [ - self.tr("Errors")]) + self.__errorItem = QTreeWidgetItem(self.resultList, [self.tr("Errors")]) self.__errorItem.setExpanded(True) self.__errorItem.setForeground(0, Qt.GlobalColor.red) - - msg = "{0} ({1})".format(self.__project.getRelativePath(filename), - message) + + msg = "{0} ({1})".format(self.__project.getRelativePath(filename), message) if not self.resultList.findItems(msg, Qt.MatchFlag.MatchExactly): itm = QTreeWidgetItem(self.__errorItem, [msg]) itm.setForeground(0, Qt.GlobalColor.red) itm.setFirstColumnSpanned(True) - + def prepare(self, fileList, project): """ Public method to prepare the dialog with a list of filenames. - + @param fileList list of filenames @type list of str @param project reference to the project object @@ -163,26 +168,22 @@ """ self.__fileList = fileList[:] self.__project = project - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setDefault(True) - + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) + self.filterFrame.setVisible(True) - - self.__data = self.__project.getData( - "OTHERTOOLSPARMS", "RadonCodeMetrics") + + self.__data = self.__project.getData("OTHERTOOLSPARMS", "RadonCodeMetrics") if self.__data is None or "ExcludeFiles" not in self.__data: self.__data = {"ExcludeFiles": ""} self.excludeFilesEdit.setText(self.__data["ExcludeFiles"]) - + def start(self, fn): """ Public slot to start the maintainability index determination. - + @param fn file or list of files or directory to show the maintainability index for @type str or list of str @@ -192,23 +193,19 @@ self.summaryLabel.clear() self.cancelled = False QApplication.processEvents() - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setEnabled(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setDefault(True) + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(True) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) QApplication.processEvents() - + if isinstance(fn, list): self.files = fn elif os.path.isdir(fn): self.files = [] extensions = set(Preferences.getPython("Python3Extensions")) for ext in extensions: - self.files.extend( - Utilities.direntries(fn, True, '*{0}'.format(ext), 0)) + self.files.extend(Utilities.direntries(fn, True, "*{0}".format(ext), 0)) else: self.files = [fn] self.files.sort() @@ -216,22 +213,22 @@ for f in self.files[:]: if not os.path.exists(f): self.files.remove(f) - + self.__summary = { "A": 0, "B": 0, "C": 0, } - + if len(self.files) > 0: # disable updates of the list for speed self.resultList.setUpdatesEnabled(False) self.resultList.setSortingEnabled(False) - + self.checkProgress.setMaximum(len(self.files)) self.checkProgress.setVisible(len(self.files) > 1) QApplication.processEvents() - + # now go through all the files self.progress = 0 if len(self.files) == 1: @@ -240,14 +237,14 @@ else: self.__batch = True self.maintainabilityIndexBatch() - - def maintainabilityIndex(self, codestring=''): + + def maintainabilityIndex(self, codestring=""): """ Public method to start a maintainability index calculation for one Python file. - + The results are reported to the __processResult slot. - + @param codestring optional sourcestring @type str """ @@ -256,14 +253,14 @@ self.checkProgress.setValue(1) self.__finish() return - + self.filename = self.files.pop(0) self.checkProgress.setValue(self.progress) QApplication.processEvents() - + if self.cancelled: return - + try: self.source = Utilities.readEncodedFile(self.filename)[0] self.source = Utilities.normalizeCode(self.source) @@ -275,42 +272,41 @@ return self.__finished = False - self.radonService.maintainabilityIndex( - None, self.filename, self.source) + self.radonService.maintainabilityIndex(None, self.filename, self.source) def maintainabilityIndexBatch(self): """ Public method to start a maintainability index calculation batch job. - + The results are reported to the __processResult slot. """ self.__lastFileItem = None - + argumentsList = [] for progress, filename in enumerate(self.files, start=1): self.checkProgress.setValue(progress) QApplication.processEvents() - + try: source = Utilities.readEncodedFile(filename)[0] source = Utilities.normalizeCode(source) except (UnicodeError, OSError) as msg: self.__createErrorItem(filename, str(msg).rstrip()) continue - + argumentsList.append((filename, source)) - + # reset the progress bar to the checked files self.checkProgress.setValue(self.progress) QApplication.processEvents() - + self.__finished = False self.radonService.maintainabilityIndexBatch(argumentsList) - + def __batchFinished(self, type_): """ Private slot handling the completion of a batch job. - + @param type_ type of the calculated metrics @type str, one of ["raw", "mi", "cc"] """ @@ -318,11 +314,11 @@ self.checkProgress.setMaximum(1) self.checkProgress.setValue(1) self.__finish() - + def __processError(self, type_, fn, msg): """ Private slot to process an error indication from the service. - + @param type_ type of the calculated metrics @type str, one of ["raw", "mi", "cc"] @param fn filename of the file @@ -332,12 +328,12 @@ """ if type_ == "mi": self.__createErrorItem(fn, msg) - + def __processResult(self, fn, result): """ Private slot called after perfoming a maintainability index calculation on one file. - + @param fn filename of the file @type str @param result result dict @@ -345,124 +341,125 @@ """ if self.__finished: return - + # Check if it's the requested file, otherwise ignore signal if not # in batch mode if not self.__batch and fn != self.filename: return - + QApplication.processEvents() - + if "error" in result: self.__createErrorItem(fn, result["error"]) else: self.__createResultItem(fn, result) - + self.progress += 1 - + self.checkProgress.setValue(self.progress) QApplication.processEvents() - + if not self.__batch: self.maintainabilityIndex() - + def __finish(self): """ Private slot called when the action or the user pressed the button. """ if not self.__finished: self.__finished = True - + # reenable updates of the list self.resultList.setSortingEnabled(True) self.resultList.sortItems(0, Qt.SortOrder.AscendingOrder) self.resultList.setUpdatesEnabled(True) - + self.cancelled = True - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setDefault(True) - + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled( + True + ) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled( + False + ) + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault( + True + ) + self.resultList.header().resizeSections( - QHeaderView.ResizeMode.ResizeToContents) + QHeaderView.ResizeMode.ResizeToContents + ) self.resultList.header().setStretchLastSection(True) self.resultList.header().setSectionResizeMode( - QHeaderView.ResizeMode.Interactive) - - self.summaryLabel.setText(self.tr( - "<table>" - "<tr><td colspan=2><b>Summary:</b></td></tr>" - "<tr><td><b>A</b></td><td align='right'>{0} files</td></tr>" - "<tr><td><b>B</b></td><td align='right'>{1} files</td></tr>" - "<tr><td><b>C</b></td><td align='right'>{2} files</td></tr>" - "</table>" - ).format(self.__locale.toString(self.__summary["A"]), - self.__locale.toString(self.__summary["B"]), - self.__locale.toString(self.__summary["C"])) + QHeaderView.ResizeMode.Interactive ) - + + self.summaryLabel.setText( + self.tr( + "<table>" + "<tr><td colspan=2><b>Summary:</b></td></tr>" + "<tr><td><b>A</b></td><td align='right'>{0} files</td></tr>" + "<tr><td><b>B</b></td><td align='right'>{1} files</td></tr>" + "<tr><td><b>C</b></td><td align='right'>{2} files</td></tr>" + "</table>" + ).format( + self.__locale.toString(self.__summary["A"]), + self.__locale.toString(self.__summary["B"]), + self.__locale.toString(self.__summary["C"]), + ) + ) + self.checkProgress.setVisible(False) - + @pyqtSlot(QAbstractButton) def on_buttonBox_clicked(self, button): """ Private slot called by a button of the button box clicked. - + @param button button that was clicked @type QAbstractButton """ - if button == self.buttonBox.button( - QDialogButtonBox.StandardButton.Close - ): + if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close): self.close() - elif button == self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel - ): + elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel): if self.__batch: self.radonService.cancelMaintainabilityIndexBatch() QTimer.singleShot(1000, self.__finish) else: self.__finish() - + @pyqtSlot() def on_startButton_clicked(self): """ Private slot to start a maintainability index run. """ fileList = self.__fileList[:] - + filterString = self.excludeFilesEdit.text() if ( - "ExcludeFiles" not in self.__data or - filterString != self.__data["ExcludeFiles"] + "ExcludeFiles" not in self.__data + or filterString != self.__data["ExcludeFiles"] ): self.__data["ExcludeFiles"] = filterString - self.__project.setData( - "OTHERTOOLSPARMS", "RadonCodeMetrics", self.__data) - filterList = [f.strip() for f in filterString.split(",") - if f.strip()] + self.__project.setData("OTHERTOOLSPARMS", "RadonCodeMetrics", self.__data) + filterList = [f.strip() for f in filterString.split(",") if f.strip()] if filterList: for fileFilter in filterList: - fileList = [f for f in fileList - if not fnmatch.fnmatch(f, fileFilter)] - + fileList = [f for f in fileList if not fnmatch.fnmatch(f, fileFilter)] + self.start(fileList) - + def clear(self): """ Public method to clear all results. """ self.resultList.clear() self.summaryLabel.clear() - + @pyqtSlot(QTreeWidgetItem, int) def on_resultList_itemActivated(self, item, column): """ Private slot to handle the activation of a result item. - + @param item reference to the activated item @type QTreeWidgetItem @param column activated column
--- a/RadonMetrics/RawMetricsDialog.py Mon Sep 19 17:43:37 2022 +0200 +++ b/RadonMetrics/RawMetricsDialog.py Mon Sep 19 17:54:33 2022 +0200 @@ -12,8 +12,12 @@ from PyQt6.QtCore import pyqtSlot, Qt, QTimer, QLocale from PyQt6.QtWidgets import ( - QDialog, QDialogButtonBox, QAbstractButton, QHeaderView, QTreeWidgetItem, - QApplication + QDialog, + QDialogButtonBox, + QAbstractButton, + QHeaderView, + QTreeWidgetItem, + QApplication, ) from .Ui_RawMetricsDialog import Ui_RawMetricsDialog @@ -28,12 +32,13 @@ """ Class implementing a dialog to show raw code metrics. """ + FilePathRole = Qt.ItemDataRole.UserRole + 1 - + def __init__(self, radonService, parent=None): """ Constructor - + @param radonService reference to the service @type RadonMetricsPlugin @param parent reference to the parent widget @@ -42,67 +47,65 @@ super().__init__(parent) self.setupUi(self) self.setWindowFlags(Qt.WindowType.Window) - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setDefault(True) - - self.summaryList.headerItem().setText( - self.summaryList.columnCount(), "") + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) + + self.summaryList.headerItem().setText(self.summaryList.columnCount(), "") self.summaryList.header().resizeSection(0, 200) self.summaryList.header().resizeSection(1, 100) - + self.resultList.headerItem().setText(self.resultList.columnCount(), "") - + self.radonService = radonService self.radonService.metricsDone.connect(self.__processResult) self.radonService.error.connect(self.__processError) self.radonService.batchFinished.connect(self.__batchFinished) - + self.cancelled = False - + self.__project = ericApp().getObject("Project") self.__locale = QLocale() self.__finished = True self.__errorItem = None - + self.__fileList = [] self.filterFrame.setVisible(False) - - self.explanationLabel.setText(self.tr( - "<table>" - "<tr><td><b>LOC</b></td>" - "<td>Lines of code</td></tr>" - "<tr><td><b>SLOC</b></td><td>Source lines of code</td></tr>" - "<tr><td><b>LLOC</b></td><td>Logical lines of code</td></tr>" - "<tr><td><b>Comments</b></td><td>Comment lines</td></tr>" - "<tr><td><b>Empty Comments</b></td><td>Comment lines not" - " containing code</td></tr>" - "<tr><td><b>Multi</b></td>" - "<td>Lines in multi line strings</td></tr>" - "<tr><td><b>Empty</b></td><td>Blank lines</td></tr>" - "<tr><td colspan=2><b>Comment Statistics:</b></td</tr>" - "<tr><td><b>C % L</b></td><td>Comments to lines ratio</td></tr>" - "<tr><td><b>C % S</b></td>" - "<td>Comments to source lines ratio</td></tr>" - "<tr><td><b>C + M % L</b></td>" - "<td>Comments plus multi line strings to lines ratio</td></tr>" - "</table>" - )) - + + self.explanationLabel.setText( + self.tr( + "<table>" + "<tr><td><b>LOC</b></td>" + "<td>Lines of code</td></tr>" + "<tr><td><b>SLOC</b></td><td>Source lines of code</td></tr>" + "<tr><td><b>LLOC</b></td><td>Logical lines of code</td></tr>" + "<tr><td><b>Comments</b></td><td>Comment lines</td></tr>" + "<tr><td><b>Empty Comments</b></td><td>Comment lines not" + " containing code</td></tr>" + "<tr><td><b>Multi</b></td>" + "<td>Lines in multi line strings</td></tr>" + "<tr><td><b>Empty</b></td><td>Blank lines</td></tr>" + "<tr><td colspan=2><b>Comment Statistics:</b></td</tr>" + "<tr><td><b>C % L</b></td><td>Comments to lines ratio</td></tr>" + "<tr><td><b>C % S</b></td>" + "<td>Comments to source lines ratio</td></tr>" + "<tr><td><b>C + M % L</b></td>" + "<td>Comments plus multi line strings to lines ratio</td></tr>" + "</table>" + ) + ) + def __resizeResultColumns(self): """ Private method to resize the list columns. """ - self.resultList.header().resizeSections( - QHeaderView.ResizeMode.ResizeToContents) + self.resultList.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents) self.resultList.header().setStretchLastSection(True) - + def __createResultItem(self, filename, values): """ Private slot to create a new item in the result list. - + @param filename name of the file @type str @param values values to be displayed @@ -114,47 +117,54 @@ data.append("{0:5}".format(int(value))) except ValueError: data.append(value) - data.append("{0:3.0%}".format(min( - values["comments"] / (float(values["loc"]) or 1), - 1.0))) - data.append("{0:3.0%}".format(min( - values["comments"] / (float(values["sloc"]) or 1), - 1.0))) - data.append("{0:3.0%}".format(min( - (values["comments"] + values["multi"]) / - (float(values["loc"]) or 1), - 1.0))) + data.append( + "{0:3.0%}".format( + min(values["comments"] / (float(values["loc"]) or 1), 1.0) + ) + ) + data.append( + "{0:3.0%}".format( + min(values["comments"] / (float(values["sloc"]) or 1), 1.0) + ) + ) + data.append( + "{0:3.0%}".format( + min( + (values["comments"] + values["multi"]) + / (float(values["loc"]) or 1), + 1.0, + ) + ) + ) itm = QTreeWidgetItem(self.resultList, data) for col in range(1, 10): itm.setTextAlignment(col, Qt.AlignmentFlag.AlignRight) itm.setData(0, self.FilePathRole, filename) - + def __createErrorItem(self, filename, message): """ Private slot to create a new error item in the result list. - + @param filename name of the file @type str @param message error message @type str """ if self.__errorItem is None: - self.__errorItem = QTreeWidgetItem(self.resultList, [ - self.tr("Errors")]) + self.__errorItem = QTreeWidgetItem(self.resultList, [self.tr("Errors")]) self.__errorItem.setExpanded(True) self.__errorItem.setForeground(0, Qt.GlobalColor.red) - - msg = "{0} ({1})".format(self.__project.getRelativePath(filename), - message) + + msg = "{0} ({1})".format(self.__project.getRelativePath(filename), message) if not self.resultList.findItems(msg, Qt.MatchFlag.MatchExactly): itm = QTreeWidgetItem(self.__errorItem, [msg]) itm.setForeground(0, Qt.GlobalColor.red) itm.setFirstColumnSpanned(True) - + def prepare(self, fileList, project): """ Public method to prepare the dialog with a list of filenames. - + @param fileList list of filenames @type list of str @param project reference to the project object @@ -162,26 +172,22 @@ """ self.__fileList = fileList[:] self.__project = project - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setDefault(True) - + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) + self.filterFrame.setVisible(True) - - self.__data = self.__project.getData( - "OTHERTOOLSPARMS", "RadonCodeMetrics") + + self.__data = self.__project.getData("OTHERTOOLSPARMS", "RadonCodeMetrics") if self.__data is None or "ExcludeFiles" not in self.__data: self.__data = {"ExcludeFiles": ""} self.excludeFilesEdit.setText(self.__data["ExcludeFiles"]) - + def start(self, fn): """ Public slot to start the code metrics determination. - + @param fn file or list of files or directory to show the code metrics for @type str or list of str @@ -191,23 +197,19 @@ self.resultList.clear() self.summaryList.clear() QApplication.processEvents() - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setEnabled(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setDefault(True) + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(True) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) QApplication.processEvents() - + if isinstance(fn, list): self.files = fn elif os.path.isdir(fn): self.files = [] extensions = set(Preferences.getPython("Python3Extensions")) for ext in extensions: - self.files.extend( - Utilities.direntries(fn, True, '*{0}'.format(ext), 0)) + self.files.extend(Utilities.direntries(fn, True, "*{0}".format(ext), 0)) else: self.files = [fn] self.files.sort() @@ -215,21 +217,28 @@ for f in self.files[:]: if not os.path.exists(f): self.files.remove(f) - + self.__summary = {"files": 0} - for key in ['loc', 'lloc', 'sloc', 'comments', 'multi', - 'single_comments', 'blank']: + for key in [ + "loc", + "lloc", + "sloc", + "comments", + "multi", + "single_comments", + "blank", + ]: self.__summary[key] = 0 - + if len(self.files) > 0: # disable updates of the list for speed self.resultList.setUpdatesEnabled(False) self.resultList.setSortingEnabled(False) - + self.checkProgress.setMaximum(len(self.files)) self.checkProgress.setVisible(len(self.files) > 1) QApplication.processEvents() - + # now go through all the files self.progress = 0 if len(self.files) == 1: @@ -238,13 +247,13 @@ else: self.__batch = True self.rawMetricsBatch() - - def rawMetrics(self, codestring=''): + + def rawMetrics(self, codestring=""): """ Public method to start a code metrics calculation for one Python file. - + The results are reported to the __processResult slot. - + @param codestring optional sourcestring @type str """ @@ -253,14 +262,14 @@ self.checkProgress.setValue(1) self.__finish() return - + self.filename = self.files.pop(0) self.checkProgress.setValue(self.progress) QApplication.processEvents() - + if self.cancelled: return - + try: self.source = Utilities.readEncodedFile(self.filename)[0] self.source = Utilities.normalizeCode(self.source) @@ -272,42 +281,41 @@ return self.__finished = False - self.radonService.rawMetrics( - None, self.filename, self.source) + self.radonService.rawMetrics(None, self.filename, self.source) def rawMetricsBatch(self): """ Public method to start a code metrics calculation batch job. - + The results are reported to the __processResult slot. """ self.__lastFileItem = None - + argumentsList = [] for progress, filename in enumerate(self.files, start=1): self.checkProgress.setValue(progress) QApplication.processEvents() - + try: source = Utilities.readEncodedFile(filename)[0] source = Utilities.normalizeCode(source) except (UnicodeError, OSError) as msg: self.__createErrorItem(filename, str(msg).rstrip()) continue - + argumentsList.append((filename, source)) - + # reset the progress bar to the checked files self.checkProgress.setValue(self.progress) QApplication.processEvents() - + self.__finished = False self.radonService.rawMetricsBatch(argumentsList) - + def __batchFinished(self, type_): """ Private slot handling the completion of a batch job. - + @param type_ type of the calculated metrics @type str, one of ["raw", "mi", "cc"] """ @@ -315,11 +323,11 @@ self.checkProgress.setMaximum(1) self.checkProgress.setValue(1) self.__finish() - + def __processError(self, type_, fn, msg): """ Private slot to process an error indication from the service. - + @param type_ type of the calculated metrics @type str, one of ["raw", "mi", "cc"] @param fn filename of the file @@ -329,12 +337,12 @@ """ if type_ == "raw": self.__createErrorItem(fn, msg) - + def __processResult(self, fn, result): """ Private slot called after perfoming a code metrics calculation on one file. - + @param fn filename of the file @type str @param result result dict @@ -342,195 +350,214 @@ """ if self.__finished: return - + # Check if it's the requested file, otherwise ignore signal if not # in batch mode if not self.__batch and fn != self.filename: return - + QApplication.processEvents() - + if "error" in result: self.__createErrorItem(fn, result["error"]) else: self.__createResultItem(fn, result) - + self.progress += 1 - + self.checkProgress.setValue(self.progress) QApplication.processEvents() - + if not self.__batch: self.rawMetrics() - + def __getValues(self, result): """ Private method to extract the code metric values. - + @param result result dict @type dict @return list of values suitable for display @rtype list of str """ v = [] - for key in ['loc', 'sloc', 'lloc', 'comments', 'multi', - 'single_comments', 'blank']: + for key in [ + "loc", + "sloc", + "lloc", + "comments", + "multi", + "single_comments", + "blank", + ]: val = result.get(key, -1) if val >= 0: v.append(self.__locale.toString(val)) else: - v.append('') + v.append("") self.__summary[key] += int(val) self.__summary["files"] += 1 return v - + def __finish(self): """ Private slot called when the action or the user pressed the button. """ if not self.__finished: self.__finished = True - + # reenable updates of the list self.resultList.setSortingEnabled(True) self.resultList.sortItems(0, Qt.SortOrder.AscendingOrder) self.resultList.setUpdatesEnabled(True) - + self.__createSummary() - + self.cancelled = True - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setDefault(True) - + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled( + True + ) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled( + False + ) + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault( + True + ) + self.resultList.header().resizeSections( - QHeaderView.ResizeMode.ResizeToContents) + QHeaderView.ResizeMode.ResizeToContents + ) self.resultList.header().setStretchLastSection(True) self.resultList.header().setSectionResizeMode( - QHeaderView.ResizeMode.Interactive) - + QHeaderView.ResizeMode.Interactive + ) + self.checkProgress.setVisible(False) - + def __createSummary(self): """ Private method to create the code metrics summary. """ self.__createSummaryItem( - self.tr("Files"), self.__locale.toString(self.__summary["files"])) + self.tr("Files"), self.__locale.toString(self.__summary["files"]) + ) self.__createSummaryItem( - self.tr("LOC"), self.__locale.toString(self.__summary["loc"])) + self.tr("LOC"), self.__locale.toString(self.__summary["loc"]) + ) self.__createSummaryItem( - self.tr("SLOC"), self.__locale.toString(self.__summary["sloc"])) + self.tr("SLOC"), self.__locale.toString(self.__summary["sloc"]) + ) self.__createSummaryItem( - self.tr("LLOC"), self.__locale.toString(self.__summary["lloc"])) + self.tr("LLOC"), self.__locale.toString(self.__summary["lloc"]) + ) self.__createSummaryItem( - self.tr("Comment Lines"), - self.__locale.toString(self.__summary["comments"])) + self.tr("Comment Lines"), self.__locale.toString(self.__summary["comments"]) + ) self.__createSummaryItem( self.tr("Empty Comments"), - self.__locale.toString(self.__summary["single_comments"])) + self.__locale.toString(self.__summary["single_comments"]), + ) self.__createSummaryItem( self.tr("Multiline Strings"), - self.__locale.toString(self.__summary["multi"])) + self.__locale.toString(self.__summary["multi"]), + ) self.__createSummaryItem( - self.tr("Empty Lines"), - self.__locale.toString(self.__summary["blank"])) + self.tr("Empty Lines"), self.__locale.toString(self.__summary["blank"]) + ) self.__createSummaryItem( self.tr("C % L"), - "{0:3.0%}".format(min( - self.__summary["comments"] / ( - float(self.__summary["loc"]) or 1), - 1.0)) + "{0:3.0%}".format( + min( + self.__summary["comments"] / (float(self.__summary["loc"]) or 1), + 1.0, + ) + ), ) self.__createSummaryItem( self.tr("C % S"), - "{0:3.0%}".format(min( - self.__summary["comments"] / ( - float(self.__summary["sloc"]) or 1), - 1.0)) + "{0:3.0%}".format( + min( + self.__summary["comments"] / (float(self.__summary["sloc"]) or 1), + 1.0, + ) + ), ) self.__createSummaryItem( self.tr("C + M % L"), - "{0:3.0%}".format(min( - (self.__summary["comments"] + self.__summary["multi"]) / ( - float(self.__summary["loc"]) or 1), - 1.0)) + "{0:3.0%}".format( + min( + (self.__summary["comments"] + self.__summary["multi"]) + / (float(self.__summary["loc"]) or 1), + 1.0, + ) + ), ) - + self.summaryList.header().resizeSections( - QHeaderView.ResizeMode.ResizeToContents) + QHeaderView.ResizeMode.ResizeToContents + ) self.summaryList.header().setStretchLastSection(True) - + def __createSummaryItem(self, col0, col1): """ Private slot to create a new item in the summary list. - + @param col0 string for column 0 (string) @param col1 string for column 1 (string) """ itm = QTreeWidgetItem(self.summaryList, [col0, col1]) itm.setTextAlignment(1, Qt.AlignmentFlag.AlignRight) - + @pyqtSlot(QAbstractButton) def on_buttonBox_clicked(self, button): """ Private slot called by a button of the button box clicked. - + @param button button that was clicked @type QAbstractButton """ - if button == self.buttonBox.button( - QDialogButtonBox.StandardButton.Close - ): + if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close): self.close() - elif button == self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel - ): + elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel): if self.__batch: self.radonService.cancelRawMetricsBatch() QTimer.singleShot(1000, self.__finish) else: self.__finish() - + @pyqtSlot() def on_startButton_clicked(self): """ Private slot to start a code metrics run. """ fileList = self.__fileList[:] - + filterString = self.excludeFilesEdit.text() if ( - "ExcludeFiles" not in self.__data or - filterString != self.__data["ExcludeFiles"] + "ExcludeFiles" not in self.__data + or filterString != self.__data["ExcludeFiles"] ): self.__data["ExcludeFiles"] = filterString - self.__project.setData( - "OTHERTOOLSPARMS", "RadonCodeMetrics", self.__data) - filterList = [f.strip() for f in filterString.split(",") - if f.strip()] + self.__project.setData("OTHERTOOLSPARMS", "RadonCodeMetrics", self.__data) + filterList = [f.strip() for f in filterString.split(",") if f.strip()] if filterList: for fileFilter in filterList: - fileList = [f for f in fileList - if not fnmatch.fnmatch(f, fileFilter)] - + fileList = [f for f in fileList if not fnmatch.fnmatch(f, fileFilter)] + self.start(fileList) - + def clear(self): """ Public method to clear all results. """ self.resultList.clear() self.summaryList.clear() - + @pyqtSlot(QTreeWidgetItem, int) def on_resultList_itemActivated(self, item, column): """ Private slot to handle the activation of a result item. - + @param item reference to the activated item @type QTreeWidgetItem @param column activated column