PluginMetricsRadon.py

changeset 13
22bc345844e7
parent 12
32a3c9d62e90
child 18
58ce8a433422
--- a/PluginMetricsRadon.py	Sat Sep 19 11:54:33 2015 +0200
+++ b/PluginMetricsRadon.py	Sat Sep 19 18:24:07 2015 +0200
@@ -34,7 +34,10 @@
 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."""
+    """ 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
 pyqtApi = 2
@@ -110,6 +113,22 @@
                 onBatchDone=lambda fx, lang: self.batchJobDone(
                     "mi", fx, lang))
             
+            # cyclomatic complexity
+            self.backgroundService.serviceConnect(
+                'radon_cc', 'Python2', path, 'CyclomaticComplexityCalculator',
+                lambda fn, res: self.metricsCalculationDone("cc", fn, res),
+                onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy2(
+                    "cc", fx, lang, fn, msg),
+                onBatchDone=lambda fx, lang: self.batchJobDone(
+                    "c", fx, lang))
+            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.hasBatch = True
         except TypeError:
             # backward compatibility for eric 6.0
@@ -137,6 +156,18 @@
                 onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy3(
                     "mi", fx, lang, fn, msg))
             
+            # cyclomatic complexity
+            self.backgroundService.serviceConnect(
+                'radon_cc', 'Python2', path, 'CyclomaticComplexityCalculator',
+                lambda fn, res: self.metricsCalculationDone("cc", fn, res),
+                onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy2(
+                    "cc", fx, lang, fn, msg))
+            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))
+            
             self.hasBatch = False
         
         self.queuedBatches = {
@@ -263,11 +294,13 @@
         """
         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 = []
@@ -275,6 +308,7 @@
         self.__editors = []
         self.__editorRawMetricsDialog = None
         self.__editorMIDialog = None
+        self.__editorCCDialog = None
         self.__editorMetricsActs = []
         self.__editorSeparatorActs = []
 
@@ -390,6 +424,62 @@
         for lang in ['Python2', 'Python3']:
             self.backgroundService.requestCancel('batch_radon_mi', lang)
     
+    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 = 'Python{0}'.format(determinePythonVersion(filename, source))
+        if lang not in ['Python2', 'Python3']:
+            return
+        
+        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 = {
+            "Python2": [],
+            "Python3": [],
+        }
+        for filename, source in argumentsList:
+            lang = 'Python{0}'.format(determinePythonVersion(filename, source))
+            if lang not in ['Python2', 'Python3']:
+                continue
+            else:
+                data[lang].append((filename, source))
+        
+        self.queuedBatches["raw"] = []
+        for lang in ['Python2', 'Python3']:
+            if data[lang]:
+                self.queuedBatches["cc"].append(lang)
+                self.backgroundService.enqueueRequest('batch_radon_cc', lang,
+                                                      "", data[lang])
+                self.batchesFinished["cc"] = False
+    
+    def cancelComplexityBatch(self):
+        """
+        Public method to cancel all batch jobs.
+        """
+        for lang in ['Python2', 'Python3']:
+            self.backgroundService.requestCancel('batch_radon_cc', lang)
+
     def activate(self):
         """
         Public method to activate this plug-in.
@@ -448,6 +538,21 @@
             menu.addAction(act)
             self.__projectMetricsActs.append(act)
             
+            act = E5Action(
+                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)
             
@@ -468,7 +573,6 @@
         font.setBold(True)
         act.setFont(font)
         act.triggered.connect(self.__showRadonVersion)
-        menu.addAction(act)
         self.__editorMetricsActs.append(act)
         
         act = E5Action(
@@ -501,6 +605,20 @@
         act.triggered.connect(self.__editorMaintainabilityIndex)
         self.__editorMetricsActs.append(act)
         
+        act = E5Action(
+            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)
+        
         e5App().getObject("Project").showMenu.connect(self.__projectShowMenu)
         e5App().getObject("ProjectBrowser").getProjectBrowser("sources")\
             .showMenu.connect(self.__projectBrowserShowMenu)
@@ -618,9 +736,9 @@
                 act = E5Action(
                     self.tr('Code Metrics'),
                     self.tr('Code &Metrics...'), 0, 0,
-                        self, '')
-                act.setStatusTip(
-                    self.tr('Show raw code metrics.'))
+                    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"""
@@ -635,10 +753,9 @@
                 act = E5Action(
                     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.'))
+                    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"""
@@ -650,6 +767,23 @@
                 menu.addAction(act)
                 self.__projectBrowserMetricsActs.append(act)
                 
+                act = E5Action(
+                    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)
     
@@ -813,6 +947,75 @@
                 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 = e5App().getObject("Project")
+        project.saveAllScripts()
+        ppath = project.getProjectPath()
+        files = [os.path.join(ppath, file)
+                 for file in project.pdata["SOURCES"]
+                 if file.endswith(
+                     tuple(Preferences.getPython("Python3Extensions")) +
+                     tuple(Preferences.getPython("PythonExtensions")))]
+        
+        if self.__projectCCDialog is None:
+            from RadonMetrics.CyclomaticComplexityDialog import \
+                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 = e5App().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 \
+                CyclomaticComplexityDialog
+            self.__projectBrowserCCDialog = CyclomaticComplexityDialog(self)
+        self.__projectBrowserCCDialog.show()
+        self.__projectBrowserCCDialog.start(fn)
+    
+    def __editorCyclomaticComplexity(self):
+        """
+        Private slot to handle the cyclomatic complexity action of the editor
+        show menu.
+        """
+        editor = e5App().getObject("ViewManager").activeWindow()
+        if editor is not None:
+            if editor.checkDirty() and editor.getFileName() is not None:
+                if self.__editorCCDialog is None:
+                    from RadonMetrics.CyclomaticComplexityDialog import \
+                        CyclomaticComplexityDialog
+                    self.__editorCCDialog = CyclomaticComplexityDialog(self)
+                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.
@@ -834,4 +1037,3 @@
                 """ complexity</li>"""
                 """</ul></p>"""
             ).format(__version__))
-        

eric ide

mercurial