PluginMetricsRadon.py

changeset 13
22bc345844e7
parent 12
32a3c9d62e90
child 18
58ce8a433422
equal deleted inserted replaced
12:32a3c9d62e90 13:22bc345844e7
32 className = "RadonMetricsPlugin" 32 className = "RadonMetricsPlugin"
33 packageName = "RadonMetrics" 33 packageName = "RadonMetrics"
34 shortDescription = "Code metrics plugin using radon package" 34 shortDescription = "Code metrics plugin using radon package"
35 longDescription = ( 35 longDescription = (
36 """This plug-in implements dialogs to show various code metrics. These""" 36 """This plug-in implements dialogs to show various code metrics. These"""
37 """ are determined using the radon code metrics package.""" 37 """ are determined using the radon code metrics package. 'Raw code"""
38 """ metrics', 'Maintainability Index' and 'McCabe Complexity' can be"""
39 """ requested through different dialogs for one file or the whole"""
40 """ project."""
38 ) 41 )
39 needsRestart = False 42 needsRestart = False
40 pyqtApi = 2 43 pyqtApi = 2
41 python2Compatible = True 44 python2Compatible = True
42 # End-Of-Header 45 # End-Of-Header
108 onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy3( 111 onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy3(
109 "mi", fx, lang, fn, msg), 112 "mi", fx, lang, fn, msg),
110 onBatchDone=lambda fx, lang: self.batchJobDone( 113 onBatchDone=lambda fx, lang: self.batchJobDone(
111 "mi", fx, lang)) 114 "mi", fx, lang))
112 115
116 # cyclomatic complexity
117 self.backgroundService.serviceConnect(
118 'radon_cc', 'Python2', path, 'CyclomaticComplexityCalculator',
119 lambda fn, res: self.metricsCalculationDone("cc", fn, res),
120 onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy2(
121 "cc", fx, lang, fn, msg),
122 onBatchDone=lambda fx, lang: self.batchJobDone(
123 "c", fx, lang))
124 self.backgroundService.serviceConnect(
125 'radon_cc', 'Python3', path, 'CyclomaticComplexityCalculator',
126 lambda fn, res: self.metricsCalculationDone("cc", fn, res),
127 onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy3(
128 "cc", fx, lang, fn, msg),
129 onBatchDone=lambda fx, lang: self.batchJobDone(
130 "cc", fx, lang))
131
113 self.hasBatch = True 132 self.hasBatch = True
114 except TypeError: 133 except TypeError:
115 # backward compatibility for eric 6.0 134 # backward compatibility for eric 6.0
116 # raw code metrics calculation 135 # raw code metrics calculation
117 self.backgroundService.serviceConnect( 136 self.backgroundService.serviceConnect(
134 self.backgroundService.serviceConnect( 153 self.backgroundService.serviceConnect(
135 'radon_mi', 'Python3', path, 'MaintainabilityIndexCalculator', 154 'radon_mi', 'Python3', path, 'MaintainabilityIndexCalculator',
136 lambda fn, res: self.metricsCalculationDone("mi", fn, res), 155 lambda fn, res: self.metricsCalculationDone("mi", fn, res),
137 onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy3( 156 onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy3(
138 "mi", fx, lang, fn, msg)) 157 "mi", fx, lang, fn, msg))
158
159 # cyclomatic complexity
160 self.backgroundService.serviceConnect(
161 'radon_cc', 'Python2', path, 'CyclomaticComplexityCalculator',
162 lambda fn, res: self.metricsCalculationDone("cc", fn, res),
163 onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy2(
164 "cc", fx, lang, fn, msg))
165 self.backgroundService.serviceConnect(
166 'radon_cc', 'Python3', path, 'CyclomaticComplexityCalculator',
167 lambda fn, res: self.metricsCalculationDone("cc", fn, res),
168 onErrorCallback=lambda fx, lang, fn, msg: self.serviceErrorPy3(
169 "cc", fx, lang, fn, msg))
139 170
140 self.hasBatch = False 171 self.hasBatch = False
141 172
142 self.queuedBatches = { 173 self.queuedBatches = {
143 "raw": [], 174 "raw": [],
261 """ 292 """
262 Private slot to (re)initialize the plugin. 293 Private slot to (re)initialize the plugin.
263 """ 294 """
264 self.__projectRawMetricsDialog = None 295 self.__projectRawMetricsDialog = None
265 self.__projectMIDialog = None 296 self.__projectMIDialog = None
297 self.__projectCCDialog = None
266 self.__projectMetricsActs = [] 298 self.__projectMetricsActs = []
267 self.__projectSeparatorActs = [] 299 self.__projectSeparatorActs = []
268 300
269 self.__projectBrowserRawMetricsDialog = None 301 self.__projectBrowserRawMetricsDialog = None
270 self.__projectBrowserMIDialog = None 302 self.__projectBrowserMIDialog = None
303 self.__projectBrowserCCDialog = None
271 self.__projectBrowserMenu = None 304 self.__projectBrowserMenu = None
272 self.__projectBrowserMetricsActs = [] 305 self.__projectBrowserMetricsActs = []
273 self.__projectBrowserSeparatorActs = [] 306 self.__projectBrowserSeparatorActs = []
274 307
275 self.__editors = [] 308 self.__editors = []
276 self.__editorRawMetricsDialog = None 309 self.__editorRawMetricsDialog = None
277 self.__editorMIDialog = None 310 self.__editorMIDialog = None
311 self.__editorCCDialog = None
278 self.__editorMetricsActs = [] 312 self.__editorMetricsActs = []
279 self.__editorSeparatorActs = [] 313 self.__editorSeparatorActs = []
280 314
281 def rawMetrics(self, lang, filename, source): 315 def rawMetrics(self, lang, filename, source):
282 """ 316 """
388 Public method to cancel all batch jobs. 422 Public method to cancel all batch jobs.
389 """ 423 """
390 for lang in ['Python2', 'Python3']: 424 for lang in ['Python2', 'Python3']:
391 self.backgroundService.requestCancel('batch_radon_mi', lang) 425 self.backgroundService.requestCancel('batch_radon_mi', lang)
392 426
427 def cyclomaticComplexity(self, lang, filename, source):
428 """
429 Public method to prepare cyclomatic complexity calculation on one
430 Python source file.
431
432 @param lang language of the file or None to determine by internal
433 algorithm
434 @type str or None
435 @param filename source filename
436 @type str
437 @param source string containing the code
438 @type str
439 """
440 if lang is None:
441 lang = 'Python{0}'.format(determinePythonVersion(filename, source))
442 if lang not in ['Python2', 'Python3']:
443 return
444
445 self.backgroundService.enqueueRequest(
446 'radon_cc', lang, filename, [source])
447
448 def cyclomaticComplexityBatch(self, argumentsList):
449 """
450 Public method to prepare cyclomatic complexity calculation on multiple
451 Python source files.
452
453 @param argumentsList list of arguments tuples with each tuple
454 containing filename and source
455 @type (str, str)
456 """
457 data = {
458 "Python2": [],
459 "Python3": [],
460 }
461 for filename, source in argumentsList:
462 lang = 'Python{0}'.format(determinePythonVersion(filename, source))
463 if lang not in ['Python2', 'Python3']:
464 continue
465 else:
466 data[lang].append((filename, source))
467
468 self.queuedBatches["raw"] = []
469 for lang in ['Python2', 'Python3']:
470 if data[lang]:
471 self.queuedBatches["cc"].append(lang)
472 self.backgroundService.enqueueRequest('batch_radon_cc', lang,
473 "", data[lang])
474 self.batchesFinished["cc"] = False
475
476 def cancelComplexityBatch(self):
477 """
478 Public method to cancel all batch jobs.
479 """
480 for lang in ['Python2', 'Python3']:
481 self.backgroundService.requestCancel('batch_radon_cc', lang)
482
393 def activate(self): 483 def activate(self):
394 """ 484 """
395 Public method to activate this plug-in. 485 Public method to activate this plug-in.
396 486
397 @return tuple of None and activation status 487 @return tuple of None and activation status
446 )) 536 ))
447 act.triggered.connect(self.__projectMaintainabilityIndex) 537 act.triggered.connect(self.__projectMaintainabilityIndex)
448 menu.addAction(act) 538 menu.addAction(act)
449 self.__projectMetricsActs.append(act) 539 self.__projectMetricsActs.append(act)
450 540
541 act = E5Action(
542 self.tr('Cyclomatic Complexity'),
543 self.tr('Cyclomatic &Complexity...'), 0, 0,
544 self, 'project_show_radon_cc')
545 act.setStatusTip(
546 self.tr('Show the cyclomatic complexity for Python files.'))
547 act.setWhatsThis(self.tr(
548 """<b>Cyclomatic Complexity...</b>"""
549 """<p>This calculates the cyclomatic complexity of Python"""
550 """ files and shows it together with a ranking.</p>"""
551 ))
552 act.triggered.connect(self.__projectCyclomaticComplexity)
553 menu.addAction(act)
554 self.__projectMetricsActs.append(act)
555
451 act = menu.addSeparator() 556 act = menu.addSeparator()
452 self.__projectSeparatorActs.append(act) 557 self.__projectSeparatorActs.append(act)
453 558
454 e5App().getObject("Project").addE5Actions( 559 e5App().getObject("Project").addE5Actions(
455 self.__projectMetricsActs[1:]) 560 self.__projectMetricsActs[1:])
466 act = QAction(self.tr("Radon"), self) 571 act = QAction(self.tr("Radon"), self)
467 font = act.font() 572 font = act.font()
468 font.setBold(True) 573 font.setBold(True)
469 act.setFont(font) 574 act.setFont(font)
470 act.triggered.connect(self.__showRadonVersion) 575 act.triggered.connect(self.__showRadonVersion)
471 menu.addAction(act)
472 self.__editorMetricsActs.append(act) 576 self.__editorMetricsActs.append(act)
473 577
474 act = E5Action( 578 act = E5Action(
475 self.tr('Code Metrics'), 579 self.tr('Code Metrics'),
476 self.tr('Code &Metrics...'), 0, 0, 580 self.tr('Code &Metrics...'), 0, 0,
497 """<b>Maintainability Index...</b>""" 601 """<b>Maintainability Index...</b>"""
498 """<p>This calculates the maintainability index of Python""" 602 """<p>This calculates the maintainability index of Python"""
499 """ files and shows it together with a ranking.</p>""" 603 """ files and shows it together with a ranking.</p>"""
500 )) 604 ))
501 act.triggered.connect(self.__editorMaintainabilityIndex) 605 act.triggered.connect(self.__editorMaintainabilityIndex)
606 self.__editorMetricsActs.append(act)
607
608 act = E5Action(
609 self.tr('Cyclomatic Complexity'),
610 self.tr('Cyclomatic &Complexity...'), 0, 0,
611 self, '')
612 act.setStatusTip(
613 self.tr('Show the cyclomatic complexity for Python files.'))
614 act.setWhatsThis(self.tr(
615 """<b>Cyclomatic Complexity...</b>"""
616 """<p>This calculates the cyclomatic complexity of Python"""
617 """ files and shows it together with a ranking.</p>"""
618 ))
619 act.triggered.connect(self.__editorCyclomaticComplexity)
502 self.__editorMetricsActs.append(act) 620 self.__editorMetricsActs.append(act)
503 621
504 e5App().getObject("Project").showMenu.connect(self.__projectShowMenu) 622 e5App().getObject("Project").showMenu.connect(self.__projectShowMenu)
505 e5App().getObject("ProjectBrowser").getProjectBrowser("sources")\ 623 e5App().getObject("ProjectBrowser").getProjectBrowser("sources")\
506 .showMenu.connect(self.__projectBrowserShowMenu) 624 .showMenu.connect(self.__projectBrowserShowMenu)
616 self.__projectBrowserMetricsActs.append(act) 734 self.__projectBrowserMetricsActs.append(act)
617 735
618 act = E5Action( 736 act = E5Action(
619 self.tr('Code Metrics'), 737 self.tr('Code Metrics'),
620 self.tr('Code &Metrics...'), 0, 0, 738 self.tr('Code &Metrics...'), 0, 0,
621 self, '') 739 self, '')
622 act.setStatusTip( 740 act.setStatusTip(self.tr(
623 self.tr('Show raw code metrics.')) 741 'Show raw code metrics.'))
624 act.setWhatsThis(self.tr( 742 act.setWhatsThis(self.tr(
625 """<b>Code Metrics...</b>""" 743 """<b>Code Metrics...</b>"""
626 """<p>This calculates raw code metrics of Python files""" 744 """<p>This calculates raw code metrics of Python files"""
627 """ and shows the amount of lines of code, logical lines""" 745 """ and shows the amount of lines of code, logical lines"""
628 """ of code, source lines of code, comment lines,""" 746 """ of code, source lines of code, comment lines,"""
633 self.__projectBrowserMetricsActs.append(act) 751 self.__projectBrowserMetricsActs.append(act)
634 752
635 act = E5Action( 753 act = E5Action(
636 self.tr('Maintainability Index'), 754 self.tr('Maintainability Index'),
637 self.tr('Maintainability &Index...'), 0, 0, 755 self.tr('Maintainability &Index...'), 0, 0,
638 self, 'project_show_radon_mi') 756 self, '')
639 act.setStatusTip( 757 act.setStatusTip(self.tr(
640 self.tr('Show the maintainability index for Python' 758 'Show the maintainability index for Python files.'))
641 ' files.'))
642 act.setWhatsThis(self.tr( 759 act.setWhatsThis(self.tr(
643 """<b>Maintainability Index...</b>""" 760 """<b>Maintainability Index...</b>"""
644 """<p>This calculates the maintainability index of""" 761 """<p>This calculates the maintainability index of"""
645 """ Python files and shows it together with a ranking.""" 762 """ Python files and shows it together with a ranking."""
646 """</p>""" 763 """</p>"""
647 )) 764 ))
648 act.triggered.connect( 765 act.triggered.connect(
649 self.__projectBrowserMaintainabilityIndex) 766 self.__projectBrowserMaintainabilityIndex)
767 menu.addAction(act)
768 self.__projectBrowserMetricsActs.append(act)
769
770 act = E5Action(
771 self.tr('Cyclomatic Complexity'),
772 self.tr('Cyclomatic &Complexity...'), 0, 0,
773 self, '')
774 act.setStatusTip(self.tr(
775 'Show the cyclomatic complexity for Python files.'))
776 act.setWhatsThis(self.tr(
777 """<b>Cyclomatic Complexity...</b>"""
778 """<p>This calculates the cyclomatic complexity of"""
779 """ Python files and shows it together with a ranking."""
780 """</p>"""
781 ))
782 act.triggered.connect(
783 self.__projectBrowserCyclomaticComplexity)
650 menu.addAction(act) 784 menu.addAction(act)
651 self.__projectBrowserMetricsActs.append(act) 785 self.__projectBrowserMetricsActs.append(act)
652 786
653 act = menu.addSeparator() 787 act = menu.addSeparator()
654 self.__projectBrowserSeparatorActs.append(act) 788 self.__projectBrowserSeparatorActs.append(act)
810 from RadonMetrics.MaintainabilityIndexDialog import \ 944 from RadonMetrics.MaintainabilityIndexDialog import \
811 MaintainabilityIndexDialog 945 MaintainabilityIndexDialog
812 self.__editorMIDialog = MaintainabilityIndexDialog(self) 946 self.__editorMIDialog = MaintainabilityIndexDialog(self)
813 self.__editorMIDialog.show() 947 self.__editorMIDialog.show()
814 self.__editorMIDialog.start(editor.getFileName()) 948 self.__editorMIDialog.start(editor.getFileName())
949
950 ##################################################################
951 ## Cyclomatic complexity calculations
952 ##################################################################
953
954 def __projectCyclomaticComplexity(self):
955 """
956 Private slot used to calculate the cyclomatic complexity for the
957 project.
958 """
959 project = e5App().getObject("Project")
960 project.saveAllScripts()
961 ppath = project.getProjectPath()
962 files = [os.path.join(ppath, file)
963 for file in project.pdata["SOURCES"]
964 if file.endswith(
965 tuple(Preferences.getPython("Python3Extensions")) +
966 tuple(Preferences.getPython("PythonExtensions")))]
967
968 if self.__projectCCDialog is None:
969 from RadonMetrics.CyclomaticComplexityDialog import \
970 CyclomaticComplexityDialog
971 self.__projectCCDialog = CyclomaticComplexityDialog(self)
972 self.__projectCCDialog.show()
973 self.__projectCCDialog.prepare(files, project)
974
975 def __projectBrowserCyclomaticComplexity(self):
976 """
977 Private method to handle the cyclomatic complexity context menu action
978 of the project sources browser.
979 """
980 browser = e5App().getObject("ProjectBrowser").getProjectBrowser(
981 "sources")
982 if browser.getSelectedItemsCount([ProjectBrowserFileItem]) > 1:
983 fn = []
984 for itm in browser.getSelectedItems([ProjectBrowserFileItem]):
985 fn.append(itm.fileName())
986 else:
987 itm = browser.model().item(browser.currentIndex())
988 try:
989 fn = itm.fileName()
990 except AttributeError:
991 fn = itm.dirName()
992
993 if self.__projectBrowserCCDialog is None:
994 from RadonMetrics.CyclomaticComplexityDialog import \
995 CyclomaticComplexityDialog
996 self.__projectBrowserCCDialog = CyclomaticComplexityDialog(self)
997 self.__projectBrowserCCDialog.show()
998 self.__projectBrowserCCDialog.start(fn)
999
1000 def __editorCyclomaticComplexity(self):
1001 """
1002 Private slot to handle the cyclomatic complexity action of the editor
1003 show menu.
1004 """
1005 editor = e5App().getObject("ViewManager").activeWindow()
1006 if editor is not None:
1007 if editor.checkDirty() and editor.getFileName() is not None:
1008 if self.__editorCCDialog is None:
1009 from RadonMetrics.CyclomaticComplexityDialog import \
1010 CyclomaticComplexityDialog
1011 self.__editorCCDialog = CyclomaticComplexityDialog(self)
1012 self.__editorCCDialog.show()
1013 self.__editorCCDialog.start(editor.getFileName())
1014
1015 ##################################################################
1016 ## Radon info display
1017 ##################################################################
815 1018
816 def __showRadonVersion(self): 1019 def __showRadonVersion(self):
817 """ 1020 """
818 Private slot to show the version number of the used radon library. 1021 Private slot to show the version number of the used radon library.
819 """ 1022 """
832 """ Studio)</li>""" 1035 """ Studio)</li>"""
833 """<li><b>McCabe's complexity</b>, i.e. cyclomatic""" 1036 """<li><b>McCabe's complexity</b>, i.e. cyclomatic"""
834 """ complexity</li>""" 1037 """ complexity</li>"""
835 """</ul></p>""" 1038 """</ul></p>"""
836 ).format(__version__)) 1039 ).format(__version__))
837

eric ide

mercurial