VultureChecker/VultureCheckerDialog.py

changeset 69
3c2922b45a9f
parent 68
d7a6b7ea640d
child 79
47e46cd3bb23
equal deleted inserted replaced
68:d7a6b7ea640d 69:3c2922b45a9f
7 Module implementing a dialog to show the vulture check results. 7 Module implementing a dialog to show the vulture check results.
8 """ 8 """
9 9
10 import os 10 import os
11 import fnmatch 11 import fnmatch
12 import contextlib
12 13
13 from PyQt5.QtCore import pyqtSlot, qVersion, Qt, QTimer, QRegExp 14 from PyQt5.QtCore import pyqtSlot, qVersion, Qt, QTimer, QRegExp
14 from PyQt5.QtWidgets import ( 15 from PyQt5.QtWidgets import (
15 QDialog, QDialogButtonBox, QAbstractButton, QHeaderView, QTreeWidgetItem, 16 QDialog, QDialogButtonBox, QAbstractButton, QHeaderView, QTreeWidgetItem,
16 QApplication, QMenu 17 QApplication, QMenu
22 23
23 import Preferences 24 import Preferences
24 import Utilities 25 import Utilities
25 26
26 27
27 class VultureItem(object): 28 class VultureItem:
28 """ 29 """
29 Class to hold the name, type, confidence and location of defined code. 30 Class to hold the name, type, confidence and location of defined code.
30 """ 31 """
31 def __init__(self, name, typ, filename, firstLineno, lastLineno, 32 def __init__(self, name, typ, filename, firstLineno, lastLineno,
32 confidence): 33 confidence):
68 @param vultureService reference to the service 69 @param vultureService reference to the service
69 @type VulturePlugin 70 @type VulturePlugin
70 @param parent reference to the parent widget 71 @param parent reference to the parent widget
71 @type QWidget 72 @type QWidget
72 """ 73 """
73 super(VultureCheckerDialog, self).__init__(parent) 74 super().__init__(parent)
74 self.setupUi(self) 75 self.setupUi(self)
75 self.setWindowFlags(Qt.Window) 76 self.setWindowFlags(Qt.Window)
76 77
77 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) 78 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
78 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) 79 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
110 self.filterFrame.setVisible(False) 111 self.filterFrame.setVisible(False)
111 112
112 self.__translatedTypes = { 113 self.__translatedTypes = {
113 "property": self.tr("Property"), 114 "property": self.tr("Property"),
114 "function": self.tr("Function"), 115 "function": self.tr("Function"),
115 "slot": self.tr("Slot"), 116 "method": self.tr("Methode"),
116 "attribute": self.tr("Attribute"), 117 "attribute": self.tr("Attribute"),
117 "variable": self.tr("Variable"), 118 "variable": self.tr("Variable"),
118 "class": self.tr("Class"), 119 "class": self.tr("Class"),
119 "import": self.tr("Import"), 120 "import": self.tr("Import"),
120 } 121 }
167 self.__data["ExcludeFiles"] = "" 168 self.__data["ExcludeFiles"] = ""
168 if "WhiteLists" not in self.__data: 169 if "WhiteLists" not in self.__data:
169 self.__data["WhiteLists"] = { 170 self.__data["WhiteLists"] = {
170 "property": [], 171 "property": [],
171 "function": [], 172 "function": [],
172 "slot": [],
173 "attribute": [], 173 "attribute": [],
174 "variable": [], 174 "variable": [],
175 "class": [], 175 "class": [],
176 "import": [], 176 "import": [],
177 "__patterns__": [ 177 "__patterns__": [
178 "on_*", 178 "on_*",
179 "visit_*", 179 "visit_*",
180 ], 180 ],
181 } 181 }
182 if "method" not in self.__data["WhiteLists"]:
183 self.__data["WhiteLists"]["method"] = []
182 if "import" not in self.__data["WhiteLists"]: 184 if "import" not in self.__data["WhiteLists"]:
183 self.__data["WhiteLists"]["import"] = [] 185 self.__data["WhiteLists"]["import"] = []
184 if "SlotsAreUsed" not in self.__data:
185 self.__data["SlotsAreUsed"] = True
186 186
187 self.excludeFilesEdit.setText(self.__data["ExcludeFiles"]) 187 self.excludeFilesEdit.setText(self.__data["ExcludeFiles"])
188 self.slotsCheckBox.setChecked(self.__data["SlotsAreUsed"])
189 188
190 def start(self, fn): 189 def start(self, fn):
191 """ 190 """
192 Public slot to start the code metrics determination. 191 Public slot to start the code metrics determination.
193 192
283 Public method to start a vulture check batch job. 282 Public method to start a vulture check batch job.
284 283
285 The results are reported to the __processResult slot. 284 The results are reported to the __processResult slot.
286 """ 285 """
287 self.checkProgressLabel.setPath(self.tr("Preparing files...")) 286 self.checkProgressLabel.setPath(self.tr("Preparing files..."))
288 progress = 0
289 287
290 argumentsList = [] 288 argumentsList = []
291 for filename in self.files: 289 for progress, filename in enumerate(self.files, start=1):
292 progress += 1
293 self.checkProgress.setValue(progress) 290 self.checkProgress.setValue(progress)
294 QApplication.processEvents() 291 QApplication.processEvents()
295 292
296 try: 293 try:
297 source = Utilities.readEncodedFile(filename)[0] 294 source = Utilities.readEncodedFile(filename)[0]
436 if filterList: 433 if filterList:
437 for fileFilter in filterList: 434 for fileFilter in filterList:
438 fileList = [f for f in fileList 435 fileList = [f for f in fileList
439 if not fnmatch.fnmatch(f, fileFilter)] 436 if not fnmatch.fnmatch(f, fileFilter)]
440 437
441 self.__slotsAreUsed = self.slotsCheckBox.isChecked()
442 if self.__slotsAreUsed != self.__data["SlotsAreUsed"]:
443 self.__data["SlotsAreUsed"] = self.__slotsAreUsed
444 self.__project.setData(
445 "CHECKERSPARMS", "Vulture", self.__data)
446
447 self.start(fileList) 438 self.start(fileList)
448 439
449 def clear(self): 440 def clear(self):
450 """ 441 """
451 Public method to clear all results. 442 Public method to clear all results.
474 465
475 def __prepareResultLists(self): 466 def __prepareResultLists(self):
476 """ 467 """
477 Private method to prepare the result lists. 468 Private method to prepare the result lists.
478 """ 469 """
479 self.__definedAttrs = [] 470 self.__unusedAttrs = []
480 self.__definedClasses = [] 471 self.__unusedClasses = []
481 self.__definedFuncs = [] 472 self.__unusedFuncs = []
482 self.__definedImports = [] 473 self.__unusedMethods = []
483 self.__definedSlots = [] 474 self.__unusedImports = []
484 self.__definedProps = [] 475 self.__unusedProps = []
485 self.__definedVars = [] 476 self.__unusedVars = []
486
487 self.__usedAttrs = []
488 self.__usedNames = []
489 477
490 def __storeResult(self, result): 478 def __storeResult(self, result):
491 """ 479 """
492 Private method to store the result of an analysis. 480 Private method to store the result of an analysis.
493 481
494 @param result result dictionary 482 @param result result dictionary
495 @type dict 483 @type dict
496 """ 484 """
497 self.__definedAttrs.extend(self.__filteredList( 485 self.__unusedAttrs.extend(self.__filteredList(
498 [self.__dict2Item(d) for d in result["DefinedAttributes"]])) 486 [self.__dict2Item(d) for d in result["UnusedAttributes"]]))
499 self.__definedClasses.extend(self.__filteredList( 487 self.__unusedClasses.extend(self.__filteredList(
500 [self.__dict2Item(d) for d in result["DefinedClasses"]])) 488 [self.__dict2Item(d) for d in result["UnusedClasses"]]))
501 self.__definedFuncs.extend(self.__filteredList( 489 self.__unusedFuncs.extend(self.__filteredList(
502 [self.__dict2Item(d) for d in result["DefinedFunctions"]])) 490 [self.__dict2Item(d) for d in result["UnusedFunctions"]]))
503 self.__definedImports.extend(self.__filteredList( 491 self.__unusedMethods.extend(self.__filteredList(
504 [self.__dict2Item(d) for d in result["DefinedImports"]])) 492 [self.__dict2Item(d) for d in result["UnusedMethods"]]))
505 self.__definedSlots.extend(self.__filteredList( 493 self.__unusedImports.extend(self.__filteredList(
506 [self.__dict2Item(d) for d in result["DefinedSlots"]])) 494 [self.__dict2Item(d) for d in result["UnusedImports"]]))
507 self.__definedProps.extend(self.__filteredList( 495 self.__unusedProps.extend(self.__filteredList(
508 [self.__dict2Item(d) for d in result["DefinedProperties"]])) 496 [self.__dict2Item(d) for d in result["UnusedProperties"]]))
509 self.__definedVars.extend(self.__filteredList( 497 self.__unusedVars.extend(self.__filteredList(
510 [self.__dict2Item(d) for d in result["DefinedVariables"]])) 498 [self.__dict2Item(d) for d in result["UnusedVariables"]]))
511 self.__usedAttrs.extend(result["UsedAttributes"])
512 self.__usedNames.extend(result["UsedNames"])
513 499
514 def __dict2Item(self, d): 500 def __dict2Item(self, d):
515 """ 501 """
516 Private method to convert an item dictionary to a vulture item. 502 Private method to convert an item dictionary to a vulture item.
517 503
538 regExp = QRegExp(pattern, Qt.CaseSensitive, QRegExp.Wildcard) 524 regExp = QRegExp(pattern, Qt.CaseSensitive, QRegExp.Wildcard)
539 filteredList = [item for item in filteredList 525 filteredList = [item for item in filteredList
540 if not regExp.exactMatch(item.name)] 526 if not regExp.exactMatch(item.name)]
541 return filteredList # __IGNORE_WARNING_M834__ 527 return filteredList # __IGNORE_WARNING_M834__
542 528
543 def __getUnusedItems(self, defined, used): 529 def __filterUnusedItems(self, unused, whitelistName):
544 """ 530 """
545 Private method to get a list of unused items. 531 Private method to get a list of unused items.
546 532
547 @param defined list of defined items 533 @param unused list of unused items
548 @type list of VultureItem 534 @type list of VultureItem
549 @param used list of used names 535 @param whitelistName name of the whitelist to use as a filter
550 @type list of str 536 @type str
551 @return list of unused items 537 @return list of unused items
552 @rtype list of VultureItem 538 @rtype list of VultureItem
553 """ 539 """
554 return [item for item in set(defined) if item.name not in used] 540 return [
555 541 item for item in set(unused)
556 def __unusedFunctions(self): 542 if item.name not in self.__data["WhiteLists"][whitelistName]
543 ]
544
545 def __filterUnusedFunctions(self):
557 """ 546 """
558 Private method to get the list of unused functions. 547 Private method to get the list of unused functions.
559 548
560 @return list of unused functions 549 @return list of unused functions
561 @rtype list of VultureItem 550 @rtype list of VultureItem
562 """ 551 """
563 return self.__getUnusedItems( 552 return self.__filterUnusedItems(self.__unusedFuncs, "function")
564 self.__definedFuncs, 553
565 self.__usedAttrs + self.__usedNames + 554 def __filterUnusedMethods(self):
566 self.__data["WhiteLists"]["function"] 555 """
567 ) 556 Private method to get the list of unused methods.
568 557
569 def __unusedSlots(self): 558 @return list of unused methods
570 """ 559 @rtype list of VultureItem
571 Private method to get the list of unused PyQt/PySide slots. 560 """
572 561 return self.__filterUnusedItems(self.__unusedMethods, "method")
573 @return list of unused PyQt/PySide slots 562
574 @rtype list of VultureItem 563 def __filterUnusedClasses(self):
575 """
576 return self.__getUnusedItems(
577 self.__definedSlots,
578 self.__usedAttrs + self.__usedNames +
579 self.__data["WhiteLists"]["slot"]
580 )
581
582 def __unusedClasses(self):
583 """ 564 """
584 Private method to get the list of unused classes. 565 Private method to get the list of unused classes.
585 566
586 @return list of unused classes 567 @return list of unused classes
587 @rtype list of VultureItem 568 @rtype list of VultureItem
588 """ 569 """
589 return self.__getUnusedItems( 570 return self.__filterUnusedItems(self.__unusedClasses, "class")
590 self.__definedClasses, 571
591 self.__usedAttrs + self.__usedNames + 572 def __filterUnusedImports(self):
592 self.__data["WhiteLists"]["class"]
593 )
594
595 def __unusedImports(self):
596 """ 573 """
597 Private method to get a list of unused imports. 574 Private method to get a list of unused imports.
598 575
599 @return list of unused imports 576 @return list of unused imports
600 @rtype list of VultureItem 577 @rtype list of VultureItem
601 """ 578 """
602 return self.__getUnusedItems( 579 return self.__filterUnusedItems(self.__unusedImports, "import")
603 self.__definedClasses, 580
604 self.__usedAttrs + self.__usedNames + 581 def __filterUnusedProperties(self):
605 self.__data["WhiteLists"]["import"]
606 )
607
608 def __unusedProperties(self):
609 """ 582 """
610 Private method to get the list of unused properties. 583 Private method to get the list of unused properties.
611 584
612 @return list of unused properties 585 @return list of unused properties
613 @rtype list of VultureItem 586 @rtype list of VultureItem
614 """ 587 """
615 return self.__getUnusedItems( 588 return self.__filterUnusedItems(self.__unusedProps, "property")
616 self.__definedProps, 589
617 self.__usedAttrs + 590 def __filterUnusedVariables(self):
618 self.__data["WhiteLists"]["property"]
619 )
620
621 def __unusedVariables(self):
622 """ 591 """
623 Private method to get the list of unused variables. 592 Private method to get the list of unused variables.
624 593
625 @return list of unused variables 594 @return list of unused variables
626 @rtype list of VultureItem 595 @rtype list of VultureItem
627 """ 596 """
628 return self.__getUnusedItems( 597 return self.__filterUnusedItems(self.__unusedVars, "variable")
629 self.__definedVars, 598
630 self.__usedAttrs + self.__usedNames + 599 def __filterUnusedAttributes(self):
631 self.__data["WhiteLists"]["variable"]
632 )
633
634 def __unusedAttributes(self):
635 """ 600 """
636 Private method to get the list of unused attributes. 601 Private method to get the list of unused attributes.
637 602
638 @return list of unused attributes 603 @return list of unused attributes
639 @rtype list of VultureItem 604 @rtype list of VultureItem
640 """ 605 """
641 return self.__getUnusedItems( 606 return self.__filterUnusedItems(self.__unusedAttrs, "attribute")
642 self.__definedAttrs,
643 self.__usedAttrs +
644 self.__data["WhiteLists"]["attribute"]
645 )
646 607
647 def __createResultItems(self): 608 def __createResultItems(self):
648 """ 609 """
649 Private method to populate the list with the analysis result. 610 Private method to populate the list with the analysis result.
650 """ 611 """
651 lastFileItem = None 612 lastFileItem = None
652 lastFileName = "" 613 lastFileName = ""
653 items = (self.__unusedFunctions() + 614 items = (self.__filterUnusedFunctions() +
654 self.__unusedClasses() + 615 self.__filterUnusedMethods() +
655 self.__unusedImports() + 616 self.__filterUnusedClasses() +
656 self.__unusedProperties() + 617 self.__filterUnusedImports() +
657 self.__unusedVariables() + 618 self.__filterUnusedProperties() +
658 self.__unusedAttributes()) 619 self.__filterUnusedVariables() +
659 if not self.__slotsAreUsed: 620 self.__filterUnusedAttributes())
660 items += self.__unusedSlots()
661 for item in sorted(items, key=lambda item: item.filename): 621 for item in sorted(items, key=lambda item: item.filename):
662 if lastFileItem is None or lastFileName != item.filename: 622 if lastFileItem is None or lastFileName != item.filename:
663 lastFileItem = self.__createFileItem(item.filename) 623 lastFileItem = self.__createFileItem(item.filename)
664 lastFileName = item.filename 624 lastFileName = item.filename
665 625
759 """ 719 """
760 whitelists = {} 720 whitelists = {}
761 for key in self.__data["WhiteLists"]: 721 for key in self.__data["WhiteLists"]:
762 whitelists[key] = self.__data["WhiteLists"][key][:] 722 whitelists[key] = self.__data["WhiteLists"][key][:]
763 for itm in self.__getSelectedNonFileItems(): 723 for itm in self.__getSelectedNonFileItems():
764 try: 724 with contextlib.suppress(KeyError):
765 whitelists[itm.data(0, self.TypeRole)].append(itm.text(1)) 725 whitelists[itm.data(0, self.TypeRole)].append(itm.text(1))
766 except KeyError:
767 # ignore non-existing types
768 pass
769 # remove the item from the result list 726 # remove the item from the result list
770 pitm = itm.parent() 727 pitm = itm.parent()
771 pitm.removeChild(itm) 728 pitm.removeChild(itm)
772 del itm 729 del itm
773 if pitm.childCount() == 0: 730 if pitm.childCount() == 0:
784 @type dict of list of str 741 @type dict of list of str
785 """ 742 """
786 changed = False 743 changed = False
787 for key in whitelists: 744 for key in whitelists:
788 whitelist = list(set(whitelists[key])) 745 whitelist = list(set(whitelists[key]))
789 try: 746 with contextlib.suppress(KeyError):
790 if sorted(whitelist) != sorted(self.__data["WhiteLists"][key]): 747 if sorted(whitelist) != sorted(self.__data["WhiteLists"][key]):
791 self.__data["WhiteLists"][key] = whitelist[:] 748 self.__data["WhiteLists"][key] = whitelist[:]
792 changed = True 749 changed = True
793 except KeyError:
794 # ignore non-existing types
795 pass
796 750
797 if changed: 751 if changed:
798 self.__project.setData("CHECKERSPARMS", "Vulture", self.__data) 752 self.__project.setData("CHECKERSPARMS", "Vulture", self.__data)

eric ide

mercurial