VultureChecker/VultureCheckerDialog.py

changeset 4
2a12ec365dd1
parent 3
0e548b994980
child 6
76c0f3ed7ac5
equal deleted inserted replaced
3:0e548b994980 4:2a12ec365dd1
15 pass 15 pass
16 16
17 import os 17 import os
18 import fnmatch 18 import fnmatch
19 19
20 from PyQt5.QtCore import pyqtSlot, qVersion, Qt, QTimer, QLocale 20 from PyQt5.QtCore import pyqtSlot, qVersion, Qt, QTimer, QRegExp
21 from PyQt5.QtWidgets import ( 21 from PyQt5.QtWidgets import (
22 QDialog, QDialogButtonBox, QAbstractButton, QHeaderView, QTreeWidgetItem, 22 QDialog, QDialogButtonBox, QAbstractButton, QHeaderView, QTreeWidgetItem,
23 QApplication 23 QApplication, QMenu
24 ) 24 )
25 25
26 from .Ui_VultureCheckerDialog import Ui_VultureCheckerDialog 26 from .Ui_VultureCheckerDialog import Ui_VultureCheckerDialog
27 27
28 from E5Gui.E5Application import e5App 28 from E5Gui.E5Application import e5App
31 import Utilities 31 import Utilities
32 32
33 from .vulture import Item 33 from .vulture import Item
34 34
35 35
36 # TODO: add a whitelist (incl. context menu to add an entry to the list)
37 class VultureCheckerDialog(QDialog, Ui_VultureCheckerDialog): 36 class VultureCheckerDialog(QDialog, Ui_VultureCheckerDialog):
38 """ 37 """
39 Class implementing a dialog to show the vulture check results. 38 Class implementing a dialog to show the vulture check results.
40 """ 39 """
41 FilePathRole = Qt.UserRole + 1 40 FilePathRole = Qt.UserRole + 1
41 TypeRole = Qt.UserRole + 2
42 42
43 def __init__(self, vultureService, parent=None): 43 def __init__(self, vultureService, parent=None):
44 """ 44 """
45 Constructor 45 Constructor
46 46
56 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) 56 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
57 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) 57 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
58 58
59 self.resultList.headerItem().setText(self.resultList.columnCount(), "") 59 self.resultList.headerItem().setText(self.resultList.columnCount(), "")
60 60
61 self.__menu = QMenu(self)
62 self.__whiteListAct = self.__menu.addAction(
63 self.tr("Add to Whitelist"), self.__whiteList)
64 self.__menu.addSeparator()
65 self.__menu.addAction(
66 self.tr("Edit Whitelist"), self.__editWhiteList)
67 self.__menu.addSeparator()
68 self.__collapseAct = self.__menu.addAction(
69 self.tr("Collapse all"), self.__resultCollapse)
70 self.__expandAct = self.__menu.addAction(
71 self.tr("Expand all"), self.__resultExpand)
72 self.resultList.customContextMenuRequested.connect(
73 self.__showContextMenu)
74
61 self.vultureService = vultureService 75 self.vultureService = vultureService
62 self.vultureService.analysisDone.connect(self.__processResult) 76 self.vultureService.analysisDone.connect(self.__processResult)
63 self.vultureService.error.connect(self.__processError) 77 self.vultureService.error.connect(self.__processError)
64 self.vultureService.batchFinished.connect(self.__batchFinished) 78 self.vultureService.batchFinished.connect(self.__batchFinished)
65 79
66 self.cancelled = False 80 self.cancelled = False
67 81
68 self.__project = e5App().getObject("Project") 82 self.__project = e5App().getObject("Project")
69 self.__locale = QLocale()
70 self.__finished = True 83 self.__finished = True
71 self.__errorItem = None 84 self.__errorItem = None
85 self.__data = None
72 86
73 self.__fileList = [] 87 self.__fileList = []
74 self.filterFrame.setVisible(False) 88 self.filterFrame.setVisible(False)
75 89
76 def __resizeResultColumns(self): 90 self.__translatedTypes = {
77 """ 91 "property": self.tr("Property"),
78 Private method to resize the list columns. 92 "function": self.tr("Function"),
79 """ 93 "attribute": self.tr("Attribute"),
80 self.resultList.header().resizeSections(QHeaderView.ResizeToContents) 94 "variable": self.tr("Variable"),
81 self.resultList.header().setStretchLastSection(True) 95 "class": self.tr("Class"),
96 }
82 97
83 def __createErrorItem(self, filename, message): 98 def __createErrorItem(self, filename, message):
84 """ 99 """
85 Private slot to create a new error item in the result list. 100 Private slot to create a new error item in the result list.
86 101
120 135
121 self.filterFrame.setVisible(True) 136 self.filterFrame.setVisible(True)
122 137
123 self.__data = self.__project.getData( 138 self.__data = self.__project.getData(
124 "CHECKERSPARMS", "Vulture") 139 "CHECKERSPARMS", "Vulture")
125 if self.__data is None or "ExcludeFiles" not in self.__data: 140 if self.__data is None:
126 self.__data = {"ExcludeFiles": ""} 141 self.__data = {}
142 if "ExcludeFiles" not in self.__data:
143 self.__data["ExcludeFiles"] = ""
144 if "WhiteLists" not in self.__data:
145 self.__data["WhiteLists"] = {
146 "property": [],
147 "function": [],
148 "attribute": [],
149 "variable": [],
150 "class": [],
151 "__patterns__": [
152 "on_*",
153 "visit_*",
154 ],
155 }
127 self.excludeFilesEdit.setText(self.__data["ExcludeFiles"]) 156 self.excludeFilesEdit.setText(self.__data["ExcludeFiles"])
128 157
129 def start(self, fn): 158 def start(self, fn):
130 """ 159 """
131 Public slot to start the code metrics determination. 160 Public slot to start the code metrics determination.
224 """ 253 """
225 Public method to start a vulture check batch job. 254 Public method to start a vulture check batch job.
226 255
227 The results are reported to the __processResult slot. 256 The results are reported to the __processResult slot.
228 """ 257 """
229 self.__lastFileItem = None
230
231 self.checkProgressLabel.setPath(self.tr("Preparing files...")) 258 self.checkProgressLabel.setPath(self.tr("Preparing files..."))
232 progress = 0 259 progress = 0
233 260
234 argumentsList = [] 261 argumentsList = []
235 for filename in self.files: 262 for filename in self.files:
362 Private slot to start a code metrics run. 389 Private slot to start a code metrics run.
363 """ 390 """
364 fileList = self.__fileList[:] 391 fileList = self.__fileList[:]
365 392
366 filterString = self.excludeFilesEdit.text() 393 filterString = self.excludeFilesEdit.text()
367 if "ExcludeFiles" not in self.__data or \ 394 if filterString != self.__data["ExcludeFiles"]:
368 filterString != self.__data["ExcludeFiles"]:
369 self.__data["ExcludeFiles"] = filterString 395 self.__data["ExcludeFiles"] = filterString
370 self.__project.setData( 396 self.__project.setData(
371 "CHECKERSPARMS", "Vulture", self.__data) 397 "CHECKERSPARMS", "Vulture", self.__data)
372 filterList = [f.strip() for f in filterString.split(",") 398 filterList = [f.strip() for f in filterString.split(",")
373 if f.strip()] 399 if f.strip()]
422 Private method to store the result of an analysis. 448 Private method to store the result of an analysis.
423 449
424 @param result result dictionary 450 @param result result dictionary
425 @type dict 451 @type dict
426 """ 452 """
427 self.__definedAttrs.extend( 453 self.__definedAttrs.extend(self.__filteredList(
428 [self.__dict2Item(d) for d in result["DefinedAttributes"]]) 454 [self.__dict2Item(d) for d in result["DefinedAttributes"]]))
429 self.__definedFuncs.extend( 455 self.__definedFuncs.extend(self.__filteredList(
430 [self.__dict2Item(d) for d in result["DefinedFunctions"]]) 456 [self.__dict2Item(d) for d in result["DefinedFunctions"]]))
431 self.__definedProps.extend( 457 self.__definedProps.extend(self.__filteredList(
432 [self.__dict2Item(d) for d in result["DefinedProperties"]]) 458 [self.__dict2Item(d) for d in result["DefinedProperties"]]))
433 self.__definedVars.extend( 459 self.__definedVars.extend(self.__filteredList(
434 [self.__dict2Item(d) for d in result["DefinedVariables"]]) 460 [self.__dict2Item(d) for d in result["DefinedVariables"]]))
435 self.__usedAttrs.extend( 461 self.__usedAttrs.extend(
436 [self.__dict2Item(d) for d in result["UsedAttributes"]]) 462 [self.__dict2Item(d) for d in result["UsedAttributes"]])
437 self.__usedVars.extend( 463 self.__usedVars.extend(
438 [self.__dict2Item(d) for d in result["UsedVariables"]]) 464 [self.__dict2Item(d) for d in result["UsedVariables"]])
439 self.__tupleAssignVars.extend( 465 self.__tupleAssignVars.extend(
450 @return vulture item 476 @return vulture item
451 @rtype vulture.Item 477 @rtype vulture.Item
452 """ 478 """
453 return Item(d["name"], d["type"], d["file"], d["line"]) 479 return Item(d["name"], d["type"], d["file"], d["line"])
454 480
481 def __filteredList(self, itemList):
482 """
483 Private method to filter a list against the whitelist patterns
484 returning items not matching the whitelist.
485
486 @param itemList list of items to be filtered
487 @type list of vulture.Item
488 @return list of filtered items
489 @rtype list of vulture.Item
490 """
491 filteredList = itemList
492 for pattern in self.__data["WhiteLists"]["__patterns__"]:
493 regExp = QRegExp(pattern, Qt.CaseSensitive, QRegExp.Wildcard)
494 filteredList = [name for name in filteredList
495 if not regExp.exactMatch(name)]
496 return filteredList
497
455 def __getUnusedItems(self, defined, used): 498 def __getUnusedItems(self, defined, used):
456 """ 499 """
457 Private method to get a list of unused items. 500 Private method to get a list of unused items.
458 501
459 @param defined list of defined items 502 @param defined list of defined items
472 @return list of unused functions 515 @return list of unused functions
473 @rtype list of vulture.Item 516 @rtype list of vulture.Item
474 """ 517 """
475 return self.__getUnusedItems( 518 return self.__getUnusedItems(
476 self.__definedFuncs, 519 self.__definedFuncs,
477 self.__usedAttrs + self.__usedVars + self.__namesImportedAsAliases) 520 self.__usedAttrs + self.__usedVars +
521 self.__namesImportedAsAliases +
522 self.__data["WhiteLists"]["function"] +
523 self.__data["WhiteLists"]["class"])
478 524
479 def __unusedProperties(self): 525 def __unusedProperties(self):
480 """ 526 """
481 Private method to get the list of unused properties. 527 Private method to get the list of unused properties.
482 528
483 @return list of unused properties 529 @return list of unused properties
484 @rtype list of vulture.Item 530 @rtype list of vulture.Item
485 """ 531 """
486 return self.__getUnusedItems(self.__definedProps, self.__usedAttrs) 532 return self.__getUnusedItems(
533 self.__definedProps,
534 self.__usedAttrs + self.__data["WhiteLists"]["property"])
487 535
488 def __unusedVariables(self): 536 def __unusedVariables(self):
489 """ 537 """
490 Private method to get the list of unused variables. 538 Private method to get the list of unused variables.
491 539
493 @rtype list of vulture.Item 541 @rtype list of vulture.Item
494 """ 542 """
495 return self.__getUnusedItems( 543 return self.__getUnusedItems(
496 self.__definedVars, 544 self.__definedVars,
497 self.__usedAttrs + self.__usedVars + self.__tupleAssignVars + 545 self.__usedAttrs + self.__usedVars + self.__tupleAssignVars +
498 self.__namesImportedAsAliases) 546 self.__namesImportedAsAliases +
547 self.__data["WhiteLists"]["variable"])
499 548
500 def __unusedAttributes(self): 549 def __unusedAttributes(self):
501 """ 550 """
502 Private method to get the list of unused attributes. 551 Private method to get the list of unused attributes.
503 552
504 @return list of unused attributes 553 @return list of unused attributes
505 @rtype list of vulture.Item 554 @rtype list of vulture.Item
506 """ 555 """
507 return self.__getUnusedItems(self.__definedAttrs, self.__usedAttrs) 556 return self.__getUnusedItems(
557 self.__definedAttrs,
558 self.__usedAttrs + self.__data["WhiteLists"]["attribute"])
508 559
509 def __createResultItems(self): 560 def __createResultItems(self):
510 """ 561 """
511 Private method to populate the list with the analysis result. 562 Private method to populate the list with the analysis result.
512 """ 563 """ # __IGNORE_WARNING__
513 def filename(item): 564 def filename(item):
514 return item.file 565 return item.file
515 566
516 lastFileItem = None 567 lastFileItem = None
517 lastFileName = "" 568 lastFileName = ""
518 for item in sorted(self.__unusedFunctions() + 569 for item in sorted(self.__unusedFunctions() +
519 self.__unusedProperties() + 570 self.__unusedProperties() +
520 self.__unusedVariables() + 571 self.__unusedVariables() +
521 self.__unusedAttributes(), 572 self.__unusedAttributes(),
522 key=filename): 573 key=filename):
523 if lastFileItem is None or lastFileName != item.file: 574 if lastFileItem is None or lastFileName != item.file:
524 lastFileItem = QTreeWidgetItem(self.resultList, [ 575 lastFileItem = self.__createFileItem(item.file)
525 self.__project.getRelativePath(item.file)])
526 lastFileItem.setData(0, self.FilePathRole, item.file)
527 lastFileItem.setExpanded(True)
528 lastFileItem.setFirstColumnSpanned(True)
529 lastFileName = item.file 576 lastFileName = item.file
530 577
531 itm = QTreeWidgetItem(lastFileItem, [ 578 self.__createResultItem(lastFileItem, item)
532 "{0:6d}".format(item.lineno), str(item), item.typ]) 579
533 itm.setData(0, self.FilePathRole, item.file) 580 def __createResultItem(self, parent, item):
581 """
582 Private method to create a result item.
583
584 @param parent reference to the parent item
585 @type QTreeWidgetItem
586 @param item reference to the item
587 @type vulture.Item
588 """
589 try:
590 translatedType = self.__translatedTypes[item.typ]
591 except KeyError:
592 translatedType = item.typ
593 itm = QTreeWidgetItem(parent, [
594 "{0:6d}".format(item.lineno), str(item), translatedType])
595 itm.setData(0, self.FilePathRole, item.file)
596 itm.setData(0, self.TypeRole, item.typ)
597 itm.setTextAlignment(0, Qt.Alignment(Qt.AlignRight))
598
599 def __createFileItem(self, filename):
600 """
601 Private method to create a file item.
602
603 @param filename file name for the item
604 @type str
605 """
606 itm = QTreeWidgetItem(self.resultList, [
607 self.__project.getRelativePath(filename)])
608 itm.setData(0, self.FilePathRole, filename)
609 itm.setExpanded(True)
610 itm.setFirstColumnSpanned(True)
611
612 return itm
613
614 def __showContextMenu(self, coord):
615 """
616 Private slot to show the context menu of the listview.
617
618 @param coord the position of the mouse pointer
619 @type QPoint
620 """
621 topLevelPresent = self.resultList.topLevelItemCount() > 0
622 self.__collapseAct.setEnabled(topLevelPresent)
623 self.__expandAct.setEnabled(topLevelPresent)
624
625 self.__whiteListAct.setEnabled(
626 len(self.__getSelectedNonFileItems()) != 0)
627
628 self.__menu.popup(self.resultList.mapToGlobal(coord))
629
630 def __resultCollapse(self):
631 """
632 Private slot to collapse all entries of the resultlist.
633 """
634 for index in range(self.resultList.topLevelItemCount()):
635 self.resultList.topLevelItem(index).setExpanded(False)
636
637 def __resultExpand(self):
638 """
639 Private slot to expand all entries of the resultlist.
640 """
641 for index in range(self.resultList.topLevelItemCount()):
642 self.resultList.topLevelItem(index).setExpanded(True)
643
644 def __getSelectedNonFileItems(self):
645 """
646 Private method to get a list of selected non file items.
647
648 @return list of selected non file items
649 @rtype list of QTreeWidgetItem
650 """
651 itmList = [i for i in self.resultList.selectedItems()
652 if i.parent() is not None]
653 return itmList
654
655 def __editWhiteList(self):
656 """
657 Private slot to edit the whitelist.
658 """
659 # TODO: add a whitelist edit dialog with "add" and "delete"
660 ## whitelist = dlg.getWhiteList()
661 ## self.__storeWhiteList(whitelist)
662
663 def __whiteList(self):
664 """
665 Private slot to add entries to the whitelist.
666 """
667 whitelists = {}
668 for key in self.__data["WhiteLists"]:
669 whitelists[key] = self.__data["WhiteLists"][key][:]
670 for itm in self.__getSelectedNonFileItems():
671 try:
672 whitelists[itm.data(0, self.TypeRole)].append(itm.text(1))
673 except KeyError:
674 # ignore non-existing types
675 pass
676 # remove the item from the result list
677 pitm = itm.parent()
678 pitm.removeChild(itm)
679 del itm
680 if pitm.childCount() == 0:
681 self.resultList.takeTopLevelItem(
682 self.resultList.indexOfTopLevelItem(pitm))
683 del pitm
684 self.__storeWhiteList(whitelists)
685
686 def __storeWhiteList(self, whitelists):
687 """
688 Private method to store the new whitelists, if they have changed.
689
690 @param whitelists dictionary of lists of whitelisted names
691 @type dict of list of str
692 """
693 changed = False
694 for key in whitelists:
695 whitelist = list(set(whitelists[key]))
696 try:
697 if sorted(whitelist) != sorted(self.__data["WhiteLists"][key]):
698 self.__data["WhiteLists"][key] = whitelist[:]
699 changed = True
700 except KeyError:
701 # ignore non-existing types
702 pass
703
704 if changed:
705 self.__project.setData("CHECKERSPARMS", "Vulture", self.__data)

eric ide

mercurial