Tue, 09 Jul 2024 14:24:16 +0200
Added the Ui_*.py files and redid the distribution archive.
# -*- coding: utf-8 -*- # Copyright (c) 2015 - 2024 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the radon code metrics plug-in. """ import contextlib import os from PyQt6.QtCore import QObject, QTranslator, pyqtSignal from PyQt6.QtGui import QAction from eric7 import Globals, Preferences from eric7.EricGui.EricAction import EricAction from eric7.EricWidgets import EricMessageBox from eric7.EricWidgets.EricApplication import ericApp from eric7.Project.ProjectBrowserModel import ProjectBrowserFileItem from eric7.SystemUtilities import FileSystemUtilities # Start-Of-Header name = "Radon Metrics Plugin" author = "Detlev Offenbach <detlev@die-offenbachs.de>" autoactivate = True deactivateable = True version = "10.3.1" className = "RadonMetricsPlugin" packageName = "RadonMetrics" shortDescription = "Code metrics plugin using radon package" longDescription = ( """This plug-in implements dialogs to show various code metrics. These""" """ are determined using the radon code metrics package. 'Raw code""" """ metrics', 'Maintainability Index' and 'McCabe Complexity' can be""" """ requested through different dialogs for one file or the whole""" """ project.""" ) needsRestart = False hasCompiledForms = True pyqtApi = 2 # End-Of-Header error = "" 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 maintainability index was determined for a file @signal complexityDone(str, dict) emitted when the cyclomatic complexity was determined for a file @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", 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), ) # maintainability index calculation self.backgroundService.serviceConnect( "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), ) # cyclomatic complexity self.backgroundService.serviceConnect( "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), ) self.queuedBatches = { "raw": [], "mi": [], "cc": [], } self.batchesFinished = { "raw": True, "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 @type str @param msg message text @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 @type str @param lang language @type str @param fn file name @type str @param msg message text @type str """ 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 @type str @param lang language @type str """ 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_]: 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 @type str @param result result dictionary @type dict """ if type_ == "raw": self.metricsDone.emit(filename, result) elif type_ == "mi": self.maintainabilityIndexDone.emit(filename, result) elif type_ == "cc": self.complexityDone.emit(filename, result) else: self.error.emit( type_, filename, self.tr("Unknown metrics result received ({0}).").format(type_), ) def __initialize(self): """ Private slot to (re)initialize the plugin. """ self.__projectRawMetricsDialog = None self.__projectMIDialog = None 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 self.__editorCCDialog = None self.__editorMetricsActs = [] self.__editorSeparatorActs = [] def rawMetrics(self, lang, filename, source): """ Public method to prepare raw code metrics calculation on one Python source file. @param lang language of the file or None to determine by internal algorithm @type str or None @param filename source filename @type str @param source string containing the code @type str """ if lang is None: lang = "Python3" 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) """ data = { "Python3": [], } for filename, source in argumentsList: data["Python3"].append((filename, source)) self.queuedBatches["raw"] = [] if data["Python3"]: self.queuedBatches["raw"].append("Python3") self.backgroundService.enqueueRequest( "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") def maintainabilityIndex(self, lang, filename, source): """ Public method to prepare maintainability index calculation on one Python source file. @param lang language of the file or None to determine by internal algorithm @type str or None @param filename source filename @type str @param source string containing the code @type str """ if lang is None: lang = "Python3" 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) """ data = { "Python3": [], } for filename, source in argumentsList: data["Python3"].append((filename, source)) self.queuedBatches["mi"] = [] if data["Python3"]: self.queuedBatches["mi"].append("Python3") self.backgroundService.enqueueRequest( "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") def cyclomaticComplexity(self, lang, filename, source): """ Public method to prepare cyclomatic complexity calculation on one Python source file. @param lang language of the file or None to determine by internal algorithm @type str or None @param filename source filename @type str @param source string containing the code @type str """ if lang is None: lang = "Python3" 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) """ data = { "Python3": [], } for filename, source in argumentsList: data["Python3"].append((filename, source)) self.queuedBatches["raw"] = [] if data["Python3"]: self.queuedBatches["cc"].append("Python3") self.backgroundService.enqueueRequest( "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") 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 # 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() font.setBold(True) act.setFont(font) 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>""" ) ) 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", ) 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.__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", ) 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.__projectCyclomaticComplexity) menu.addAction(act) self.__projectMetricsActs.append(act) act = menu.addSeparator() self.__projectSeparatorActs.append(act) ericApp().getObject("Project").addEricActions(self.__projectMetricsActs[1:]) # Editor menu actions (one separator each above and below) act = QAction(self) act.setSeparator(True) self.__editorSeparatorActs.append(act) act = QAction(self) act.setSeparator(True) self.__editorSeparatorActs.append(act) # header action act = QAction(self.tr("Radon"), self) font = act.font() font.setBold(True) 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>""" ) ) 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>""" ) ) 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>""" ) ) 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("ProjectBrowser").getProjectBrowser( "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("ProjectBrowser").getProjectBrowser( "sources" ).showMenu.disconnect(self.__projectBrowserShowMenu) ericApp().getObject("ViewManager").editorOpenedEd.disconnect( self.__editorOpened ) ericApp().getObject("ViewManager").editorClosedEd.disconnect( self.__editorClosed ) menu = ericApp().getObject("Project").getMenu("Show") if menu: for sep in self.__projectSeparatorActs: menu.removeAction(sep) for act in self.__projectMetricsActs: menu.removeAction(act) ericApp().getObject("Project").removeEricActions( 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") if menu is not None: for sep in self.__editorSeparatorActs: menu.removeAction(sep) for act in self.__editorMetricsActs: menu.removeAction(act) self.__initialize() def __loadTranslator(self): """ Private method to load the translation file. """ if self.__ui is not None: loc = self.__ui.getLocale() if loc and loc != "C": locale_dir = os.path.join( os.path.dirname(__file__), "RadonMetrics", "i18n" ) translation = "radon_{0}".format(loc) translator = QTranslator(None) loaded = translator.load(translation, locale_dir) if loaded: self.__translator = translator ericApp().installTranslator(self.__translator) else: print( "Warning: translation file '{0}' could not be" " loaded.".format(translation) ) print("Using default.") def __projectShowMenu( self, menuName, menu, # noqa: U100 ): """ 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 @type QMenu """ if menuName == "Show": for act in self.__projectMetricsActs[1:]: act.setEnabled( ericApp().getObject("Project").getProjectLanguage() == "Python3" and not FileSystemUtilities.isRemoteFileName( ericApp().getObject("Project").getProjectPath() ) ) 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 not FileSystemUtilities.isRemoteFileName( ericApp().getObject("Project").getProjectPath() ) 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() font.setBold(True) act.setFont(font) 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>""" ) ) 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) 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) 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 """ menu = editor.getMenu("Show") if menu is not None: menu.addAction(self.__editorSeparatorActs[0]) menu.addActions(self.__editorMetricsActs) menu.addAction(self.__editorSeparatorActs[1]) 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 """ menu = editor.getMenu("Show") if menu is not None: menu.addAction(self.__editorSeparatorActs[0]) menu.addActions(self.__editorMetricsActs) menu.addAction(self.__editorSeparatorActs[1]) def __editorShowMenu( self, menuName, menu, # noqa: U100 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 @type QMenu @param editor reference to the editor @type Editor """ if menuName == "Show": enable = editor.isPyFile() and not FileSystemUtilities.isRemoteFileName( editor.getFileName() ) 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. """ project = ericApp().getObject("Project") project.saveAllScripts() ppath = project.getProjectPath() files = [ os.path.join(ppath, file) for file in project.getSources() if file.endswith(tuple(Preferences.getPython("Python3Extensions"))) ] if self.__projectRawMetricsDialog is None: from RadonMetrics.RawMetricsDialog import RawMetricsDialog # noqa: I101 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") if browser.getSelectedItemsCount([ProjectBrowserFileItem]) > 1: fn = [] for itm in browser.getSelectedItems([ProjectBrowserFileItem]): fn.append(itm.fileName()) else: itm = browser.model().item(browser.currentIndex()) try: fn = itm.fileName() except AttributeError: fn = itm.dirName() if self.__projectBrowserRawMetricsDialog is None: from RadonMetrics.RawMetricsDialog import RawMetricsDialog # noqa: I101 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 menu. """ editor = ericApp().getObject("ViewManager").activeWindow() if ( editor is not None and editor.checkDirty() and editor.getFileName() is not None ): if self.__editorRawMetricsDialog is None: from RadonMetrics.RawMetricsDialog import RawMetricsDialog # noqa: I101 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 project. """ project = ericApp().getObject("Project") project.saveAllScripts() ppath = project.getProjectPath() files = [ os.path.join(ppath, file) for file in project.getSources() if file.endswith(tuple(Preferences.getPython("Python3Extensions"))) ] if self.__projectMIDialog is None: from RadonMetrics.MaintainabilityIndexDialog import ( # noqa: I101 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") if browser.getSelectedItemsCount([ProjectBrowserFileItem]) > 1: fn = [] for itm in browser.getSelectedItems([ProjectBrowserFileItem]): fn.append(itm.fileName()) else: itm = browser.model().item(browser.currentIndex()) try: fn = itm.fileName() except AttributeError: fn = itm.dirName() if self.__projectBrowserMIDialog is None: from RadonMetrics.MaintainabilityIndexDialog import ( # noqa: I101 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 show menu. """ editor = ericApp().getObject("ViewManager").activeWindow() if ( editor is not None and editor.checkDirty() and editor.getFileName() is not None ): if self.__editorMIDialog is None: from RadonMetrics.MaintainabilityIndexDialog import ( # noqa: I101 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 project. """ project = ericApp().getObject("Project") project.saveAllScripts() ppath = project.getProjectPath() files = [ os.path.join(ppath, file) for file in project.getSources() if file.endswith(tuple(Preferences.getPython("Python3Extensions"))) ] if self.__projectCCDialog is None: from RadonMetrics.CyclomaticComplexityDialog import ( # noqa: I101 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") if browser.getSelectedItemsCount([ProjectBrowserFileItem]) > 1: fn = [] for itm in browser.getSelectedItems([ProjectBrowserFileItem]): fn.append(itm.fileName()) else: itm = browser.model().item(browser.currentIndex()) try: fn = itm.fileName() except AttributeError: fn = itm.dirName() if self.__projectBrowserCCDialog is None: from RadonMetrics.CyclomaticComplexityDialog import ( # noqa: I101 CyclomaticComplexityDialog, ) self.__projectBrowserCCDialog = CyclomaticComplexityDialog( self, isSingle=True ) self.__projectBrowserCCDialog.show() self.__projectBrowserCCDialog.start(fn) def __editorCyclomaticComplexity(self): """ Private slot to handle the cyclomatic complexity action of the editor show menu. """ editor = ericApp().getObject("ViewManager").activeWindow() if ( editor is not None and editor.checkDirty() and editor.getFileName() is not None ): if self.__editorCCDialog is None: from RadonMetrics.CyclomaticComplexityDialog import ( # noqa: I101 CyclomaticComplexityDialog, ) 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__ # noqa: I102 EricMessageBox.information( None, self.tr("Radon"), self.tr( """<p><b>Radon Version {0}</b></p>""" """<p>Radon is a Python tool that computes various metrics""" """ from the source code. Radon can compute:""" """<ul>""" """<li><b>Raw</b> metrics (these include SLOC, comment""" """ lines, blank lines, multi line strings, ...)</li>""" """<li><b>Maintainability Index</b> (the one used in Visual""" """ Studio)</li>""" """<li><b>McCabe's complexity</b>, i.e. cyclomatic""" """ complexity</li>""" """</ul></p>""" ).format(__version__), ) ################################################################## ## Project handling methods ################################################################## def __projectClosed(self): """ Private slot to handle closing a project. """ self.__projectCCDialog and self.__projectCCDialog.clear() self.__projectMIDialog and self.__projectMIDialog.clear() if self.__projectRawMetricsDialog: self.__projectRawMetricsDialog.clear() 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 # noqa: I101, I102 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