VultureChecker/VultureCheckerDialog.py

branch
eric7
changeset 90
6393ee6e7993
parent 89
58860f9f3046
child 95
1c4dadf3ce89
equal deleted inserted replaced
89:58860f9f3046 90:6393ee6e7993
11 import fnmatch 11 import fnmatch
12 import contextlib 12 import contextlib
13 13
14 from PyQt6.QtCore import pyqtSlot, Qt, QTimer 14 from PyQt6.QtCore import pyqtSlot, Qt, QTimer
15 from PyQt6.QtWidgets import ( 15 from PyQt6.QtWidgets import (
16 QDialog, QDialogButtonBox, QAbstractButton, QHeaderView, QTreeWidgetItem, 16 QDialog,
17 QApplication, QMenu 17 QDialogButtonBox,
18 QAbstractButton,
19 QHeaderView,
20 QTreeWidgetItem,
21 QApplication,
22 QMenu,
18 ) 23 )
19 24
20 from .Ui_VultureCheckerDialog import Ui_VultureCheckerDialog 25 from .Ui_VultureCheckerDialog import Ui_VultureCheckerDialog
21 26
22 from EricWidgets.EricApplication import ericApp 27 from EricWidgets.EricApplication import ericApp
27 32
28 class VultureItem: 33 class VultureItem:
29 """ 34 """
30 Class to hold the name, type, confidence and location of defined code. 35 Class to hold the name, type, confidence and location of defined code.
31 """ 36 """
32 def __init__(self, name, typ, filename, firstLineno, lastLineno, 37
33 confidence): 38 def __init__(self, name, typ, filename, firstLineno, lastLineno, confidence):
34 """ 39 """
35 Constructor 40 Constructor
36 41
37 @param name item name 42 @param name item name
38 @type str 43 @type str
39 @param typ item type 44 @param typ item type
40 @type str 45 @type str
41 @param filename name of the file containing item 46 @param filename name of the file containing item
57 62
58 class VultureCheckerDialog(QDialog, Ui_VultureCheckerDialog): 63 class VultureCheckerDialog(QDialog, Ui_VultureCheckerDialog):
59 """ 64 """
60 Class implementing a dialog to show the vulture check results. 65 Class implementing a dialog to show the vulture check results.
61 """ 66 """
67
62 FilePathRole = Qt.ItemDataRole.UserRole + 1 68 FilePathRole = Qt.ItemDataRole.UserRole + 1
63 TypeRole = Qt.ItemDataRole.UserRole + 2 69 TypeRole = Qt.ItemDataRole.UserRole + 2
64 70
65 def __init__(self, vultureService, parent=None): 71 def __init__(self, vultureService, parent=None):
66 """ 72 """
67 Constructor 73 Constructor
68 74
69 @param vultureService reference to the service 75 @param vultureService reference to the service
70 @type VulturePlugin 76 @type VulturePlugin
71 @param parent reference to the parent widget 77 @param parent reference to the parent widget
72 @type QWidget 78 @type QWidget
73 """ 79 """
74 super().__init__(parent) 80 super().__init__(parent)
75 self.setupUi(self) 81 self.setupUi(self)
76 self.setWindowFlags(Qt.WindowType.Window) 82 self.setWindowFlags(Qt.WindowType.Window)
77 83
78 self.buttonBox.button( 84 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False)
79 QDialogButtonBox.StandardButton.Close).setEnabled(False) 85 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True)
80 self.buttonBox.button( 86
81 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
82
83 self.resultList.headerItem().setText(self.resultList.columnCount(), "") 87 self.resultList.headerItem().setText(self.resultList.columnCount(), "")
84 88
85 self.__menu = QMenu(self) 89 self.__menu = QMenu(self)
86 self.__whiteListAct = self.__menu.addAction( 90 self.__whiteListAct = self.__menu.addAction(
87 self.tr("Add to Whitelist"), self.__whiteList) 91 self.tr("Add to Whitelist"), self.__whiteList
92 )
88 self.__menu.addSeparator() 93 self.__menu.addSeparator()
89 self.__menu.addAction( 94 self.__menu.addAction(self.tr("Edit Whitelist"), self.__editWhiteList)
90 self.tr("Edit Whitelist"), self.__editWhiteList)
91 self.__menu.addSeparator() 95 self.__menu.addSeparator()
92 self.__collapseAct = self.__menu.addAction( 96 self.__collapseAct = self.__menu.addAction(
93 self.tr("Collapse all"), self.__resultCollapse) 97 self.tr("Collapse all"), self.__resultCollapse
98 )
94 self.__expandAct = self.__menu.addAction( 99 self.__expandAct = self.__menu.addAction(
95 self.tr("Expand all"), self.__resultExpand) 100 self.tr("Expand all"), self.__resultExpand
96 self.resultList.customContextMenuRequested.connect( 101 )
97 self.__showContextMenu) 102 self.resultList.customContextMenuRequested.connect(self.__showContextMenu)
98 103
99 self.vultureService = vultureService 104 self.vultureService = vultureService
100 self.vultureService.analysisDone.connect(self.__processResult) 105 self.vultureService.analysisDone.connect(self.__processResult)
101 self.vultureService.error.connect(self.__processError) 106 self.vultureService.error.connect(self.__processError)
102 self.vultureService.batchFinished.connect(self.__batchFinished) 107 self.vultureService.batchFinished.connect(self.__batchFinished)
103 108
104 self.cancelled = False 109 self.cancelled = False
105 110
106 self.__project = ericApp().getObject("Project") 111 self.__project = ericApp().getObject("Project")
107 self.__finished = True 112 self.__finished = True
108 self.__errorItem = None 113 self.__errorItem = None
109 self.__data = None 114 self.__data = None
110 self.__slotsAreUsed = True 115 self.__slotsAreUsed = True
111 116
112 self.__fileList = [] 117 self.__fileList = []
113 self.filterFrame.setVisible(False) 118 self.filterFrame.setVisible(False)
114 119
115 self.__translatedTypes = { 120 self.__translatedTypes = {
116 "property": self.tr("Property"), 121 "property": self.tr("Property"),
117 "function": self.tr("Function"), 122 "function": self.tr("Function"),
118 "method": self.tr("Method"), 123 "method": self.tr("Method"),
119 "attribute": self.tr("Attribute"), 124 "attribute": self.tr("Attribute"),
120 "variable": self.tr("Variable"), 125 "variable": self.tr("Variable"),
121 "class": self.tr("Class"), 126 "class": self.tr("Class"),
122 "import": self.tr("Import"), 127 "import": self.tr("Import"),
123 } 128 }
124 129
125 def __createErrorItem(self, filename, message): 130 def __createErrorItem(self, filename, message):
126 """ 131 """
127 Private slot to create a new error item in the result list. 132 Private slot to create a new error item in the result list.
128 133
129 @param filename name of the file 134 @param filename name of the file
130 @type str 135 @type str
131 @param message error message 136 @param message error message
132 @type str 137 @type str
133 """ 138 """
134 if self.__errorItem is None: 139 if self.__errorItem is None:
135 self.__errorItem = QTreeWidgetItem(self.resultList, [ 140 self.__errorItem = QTreeWidgetItem(self.resultList, [self.tr("Errors")])
136 self.tr("Errors")])
137 self.__errorItem.setExpanded(True) 141 self.__errorItem.setExpanded(True)
138 self.__errorItem.setForeground(0, Qt.GlobalColor.red) 142 self.__errorItem.setForeground(0, Qt.GlobalColor.red)
139 143
140 msg = "{0} ({1})".format(self.__project.getRelativePath(filename), 144 msg = "{0} ({1})".format(self.__project.getRelativePath(filename), message)
141 message)
142 if not self.resultList.findItems(msg, Qt.MatchFlag.MatchExactly): 145 if not self.resultList.findItems(msg, Qt.MatchFlag.MatchExactly):
143 itm = QTreeWidgetItem(self.__errorItem, [msg]) 146 itm = QTreeWidgetItem(self.__errorItem, [msg])
144 itm.setForeground(0, Qt.GlobalColor.red) 147 itm.setForeground(0, Qt.GlobalColor.red)
145 itm.setFirstColumnSpanned(True) 148 itm.setFirstColumnSpanned(True)
146 149
147 def prepare(self, fileList, project): 150 def prepare(self, fileList, project):
148 """ 151 """
149 Public method to prepare the dialog with a list of filenames. 152 Public method to prepare the dialog with a list of filenames.
150 153
151 @param fileList list of filenames 154 @param fileList list of filenames
152 @type list of str 155 @type list of str
153 @param project reference to the project object 156 @param project reference to the project object
154 @type Project 157 @type Project
155 """ 158 """
156 self.__fileList = fileList[:] 159 self.__fileList = fileList[:]
157 self.__project = project 160 self.__project = project
158 161
159 self.buttonBox.button( 162 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True)
160 QDialogButtonBox.StandardButton.Close).setEnabled(True) 163 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
161 self.buttonBox.button( 164 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True)
162 QDialogButtonBox.StandardButton.Cancel).setEnabled(False) 165
163 self.buttonBox.button(
164 QDialogButtonBox.StandardButton.Close).setDefault(True)
165
166 self.filterFrame.setVisible(True) 166 self.filterFrame.setVisible(True)
167 167
168 self.__data = self.__project.getData( 168 self.__data = self.__project.getData("CHECKERSPARMS", "Vulture")
169 "CHECKERSPARMS", "Vulture")
170 if self.__data is None: 169 if self.__data is None:
171 self.__data = {} 170 self.__data = {}
172 if "ExcludeFiles" not in self.__data: 171 if "ExcludeFiles" not in self.__data:
173 self.__data["ExcludeFiles"] = "" 172 self.__data["ExcludeFiles"] = ""
174 if "WhiteLists" not in self.__data: 173 if "WhiteLists" not in self.__data:
186 } 185 }
187 if "method" not in self.__data["WhiteLists"]: 186 if "method" not in self.__data["WhiteLists"]:
188 self.__data["WhiteLists"]["method"] = [] 187 self.__data["WhiteLists"]["method"] = []
189 if "import" not in self.__data["WhiteLists"]: 188 if "import" not in self.__data["WhiteLists"]:
190 self.__data["WhiteLists"]["import"] = [] 189 self.__data["WhiteLists"]["import"] = []
191 190
192 self.excludeFilesEdit.setText(self.__data["ExcludeFiles"]) 191 self.excludeFilesEdit.setText(self.__data["ExcludeFiles"])
193 192
194 def start(self, fn): 193 def start(self, fn):
195 """ 194 """
196 Public slot to start the code metrics determination. 195 Public slot to start the code metrics determination.
197 196
198 @param fn file or list of files or directory to show 197 @param fn file or list of files or directory to show
199 the code metrics for 198 the code metrics for
200 @type str or list of str 199 @type str or list of str
201 """ 200 """
202 self.cancelled = False 201 self.cancelled = False
203 self.__errorItem = None 202 self.__errorItem = None
204 self.resultList.clear() 203 self.resultList.clear()
205 204
206 self.buttonBox.button( 205 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False)
207 QDialogButtonBox.StandardButton.Close).setEnabled(False) 206 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(True)
208 self.buttonBox.button( 207 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True)
209 QDialogButtonBox.StandardButton.Cancel).setEnabled(True)
210 self.buttonBox.button(
211 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
212 QApplication.processEvents() 208 QApplication.processEvents()
213 209
214 self.__prepareResultLists() 210 self.__prepareResultLists()
215 211
216 if isinstance(fn, list): 212 if isinstance(fn, list):
217 self.files = fn 213 self.files = fn
218 elif os.path.isdir(fn): 214 elif os.path.isdir(fn):
219 self.files = [] 215 self.files = []
220 extensions = set(Preferences.getPython("Python3Extensions")) 216 extensions = set(Preferences.getPython("Python3Extensions"))
221 for ext in extensions: 217 for ext in extensions:
222 self.files.extend( 218 self.files.extend(Utilities.direntries(fn, True, "*{0}".format(ext), 0))
223 Utilities.direntries(fn, True, '*{0}'.format(ext), 0))
224 else: 219 else:
225 self.files = [fn] 220 self.files = [fn]
226 self.files.sort() 221 self.files.sort()
227 # check for missing files 222 # check for missing files
228 for f in self.files[:]: 223 for f in self.files[:]:
229 if not os.path.exists(f): 224 if not os.path.exists(f):
230 self.files.remove(f) 225 self.files.remove(f)
231 226
232 if len(self.files) > 0: 227 if len(self.files) > 0:
233 # disable updates of the list for speed 228 # disable updates of the list for speed
234 self.resultList.setUpdatesEnabled(False) 229 self.resultList.setUpdatesEnabled(False)
235 self.resultList.setSortingEnabled(False) 230 self.resultList.setSortingEnabled(False)
236 231
237 self.checkProgress.setMaximum(len(self.files)) 232 self.checkProgress.setMaximum(len(self.files))
238 self.checkProgress.setVisible(len(self.files) > 1) 233 self.checkProgress.setVisible(len(self.files) > 1)
239 QApplication.processEvents() 234 QApplication.processEvents()
240 235
241 # now go through all the files 236 # now go through all the files
242 self.progress = 0 237 self.progress = 0
243 if len(self.files) == 1: 238 if len(self.files) == 1:
244 self.__batch = False 239 self.__batch = False
245 self.vultureCheck() 240 self.vultureCheck()
246 else: 241 else:
247 self.__batch = True 242 self.__batch = True
248 self.vultureCheckBatch() 243 self.vultureCheckBatch()
249 244
250 def vultureCheck(self): 245 def vultureCheck(self):
251 """ 246 """
252 Public method to start a vulture check for one Python file. 247 Public method to start a vulture check for one Python file.
253 248
254 The results are reported to the __processResult slot. 249 The results are reported to the __processResult slot.
255 """ 250 """
256 if not self.files: 251 if not self.files:
257 self.checkProgress.setMaximum(1) 252 self.checkProgress.setMaximum(1)
258 self.checkProgress.setValue(1) 253 self.checkProgress.setValue(1)
259 self.__finish() 254 self.__finish()
260 return 255 return
261 256
262 self.filename = self.files.pop(0) 257 self.filename = self.files.pop(0)
263 self.checkProgress.setValue(self.progress) 258 self.checkProgress.setValue(self.progress)
264 QApplication.processEvents() 259 QApplication.processEvents()
265 260
266 if self.cancelled: 261 if self.cancelled:
267 return 262 return
268 263
269 try: 264 try:
270 self.source = Utilities.readEncodedFile(self.filename)[0] 265 self.source = Utilities.readEncodedFile(self.filename)[0]
271 self.source = Utilities.normalizeCode(self.source) 266 self.source = Utilities.normalizeCode(self.source)
272 except (UnicodeError, OSError) as msg: 267 except (UnicodeError, OSError) as msg:
273 self.__createErrorItem(self.filename, str(msg).rstrip()) 268 self.__createErrorItem(self.filename, str(msg).rstrip())
275 # Continue with next file 270 # Continue with next file
276 self.vultureCheck() 271 self.vultureCheck()
277 return 272 return
278 273
279 self.__finished = False 274 self.__finished = False
280 self.vultureService.vultureCheck( 275 self.vultureService.vultureCheck(None, self.filename, self.source)
281 None, self.filename, self.source)
282 276
283 def vultureCheckBatch(self): 277 def vultureCheckBatch(self):
284 """ 278 """
285 Public method to start a vulture check batch job. 279 Public method to start a vulture check batch job.
286 280
287 The results are reported to the __processResult slot. 281 The results are reported to the __processResult slot.
288 """ 282 """
289 argumentsList = [] 283 argumentsList = []
290 for progress, filename in enumerate(self.files, start=1): 284 for progress, filename in enumerate(self.files, start=1):
291 self.checkProgress.setValue(progress) 285 self.checkProgress.setValue(progress)
292 QApplication.processEvents() 286 QApplication.processEvents()
293 287
294 try: 288 try:
295 source = Utilities.readEncodedFile(filename)[0] 289 source = Utilities.readEncodedFile(filename)[0]
296 source = Utilities.normalizeCode(source) 290 source = Utilities.normalizeCode(source)
297 except (UnicodeError, OSError) as msg: 291 except (UnicodeError, OSError) as msg:
298 self.__createErrorItem(filename, str(msg).rstrip()) 292 self.__createErrorItem(filename, str(msg).rstrip())
299 continue 293 continue
300 294
301 argumentsList.append((filename, source)) 295 argumentsList.append((filename, source))
302 296
303 # reset the progress bar to the checked files 297 # reset the progress bar to the checked files
304 self.checkProgress.setValue(self.progress) 298 self.checkProgress.setValue(self.progress)
305 QApplication.processEvents() 299 QApplication.processEvents()
306 300
307 self.__finished = False 301 self.__finished = False
308 self.vultureService.vultureCheckBatch(argumentsList) 302 self.vultureService.vultureCheckBatch(argumentsList)
309 303
310 def __batchFinished(self): 304 def __batchFinished(self):
311 """ 305 """
312 Private slot handling the completion of a batch job. 306 Private slot handling the completion of a batch job.
313 """ 307 """
314 self.checkProgress.setMaximum(1) 308 self.checkProgress.setMaximum(1)
315 self.checkProgress.setValue(1) 309 self.checkProgress.setValue(1)
316 self.__finish() 310 self.__finish()
317 311
318 def __processError(self, fn, msg): 312 def __processError(self, fn, msg):
319 """ 313 """
320 Private slot to process an error indication from the service. 314 Private slot to process an error indication from the service.
321 315
322 @param fn filename of the file 316 @param fn filename of the file
323 @type str 317 @type str
324 @param msg error message 318 @param msg error message
325 @type str 319 @type str
326 """ 320 """
327 self.__createErrorItem(fn, msg) 321 self.__createErrorItem(fn, msg)
328 322
329 def __processResult(self, fn, result): 323 def __processResult(self, fn, result):
330 """ 324 """
331 Private slot called after performing a vulture analysis on one file. 325 Private slot called after performing a vulture analysis on one file.
332 326
333 @param fn filename of the file 327 @param fn filename of the file
334 @type str 328 @type str
335 @param result result dict 329 @param result result dict
336 @type dict 330 @type dict
337 """ 331 """
338 if self.__finished: 332 if self.__finished:
339 return 333 return
340 334
341 # Check if it's the requested file, otherwise ignore signal if not 335 # Check if it's the requested file, otherwise ignore signal if not
342 # in batch mode 336 # in batch mode
343 if not self.__batch and fn != self.filename: 337 if not self.__batch and fn != self.filename:
344 return 338 return
345 339
346 if "error" in result: 340 if "error" in result:
347 self.__createErrorItem(fn, result["error"]) 341 self.__createErrorItem(fn, result["error"])
348 else: 342 else:
349 self.__storeResult(result) 343 self.__storeResult(result)
350 344
351 self.progress += 1 345 self.progress += 1
352 346
353 self.checkProgress.setValue(self.progress) 347 self.checkProgress.setValue(self.progress)
354 QApplication.processEvents() 348 QApplication.processEvents()
355 349
356 if not self.__batch: 350 if not self.__batch:
357 self.vultureCheck() 351 self.vultureCheck()
358 352
359 def __finish(self): 353 def __finish(self):
360 """ 354 """
361 Private slot called when the action finished or the user pressed the 355 Private slot called when the action finished or the user pressed the
362 cancel button. 356 cancel button.
363 """ 357 """
364 if not self.__finished: 358 if not self.__finished:
365 self.__finished = True 359 self.__finished = True
366 360
367 if not self.cancelled: 361 if not self.cancelled:
368 self.__createResultItems() 362 self.__createResultItems()
369 363
370 # reenable updates of the list 364 # reenable updates of the list
371 self.resultList.setSortingEnabled(True) 365 self.resultList.setSortingEnabled(True)
372 self.resultList.sortItems(0, Qt.SortOrder.AscendingOrder) 366 self.resultList.sortItems(0, Qt.SortOrder.AscendingOrder)
373 self.resultList.setUpdatesEnabled(True) 367 self.resultList.setUpdatesEnabled(True)
374 368
375 self.cancelled = True 369 self.cancelled = True
376 self.buttonBox.button( 370 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(
377 QDialogButtonBox.StandardButton.Close).setEnabled(True) 371 True
378 self.buttonBox.button( 372 )
379 QDialogButtonBox.StandardButton.Cancel).setEnabled(False) 373 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(
380 self.buttonBox.button( 374 False
381 QDialogButtonBox.StandardButton.Close).setDefault(True) 375 )
382 376 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(
377 True
378 )
379
383 self.resultList.header().resizeSections( 380 self.resultList.header().resizeSections(
384 QHeaderView.ResizeMode.ResizeToContents) 381 QHeaderView.ResizeMode.ResizeToContents
382 )
385 self.resultList.header().setStretchLastSection(True) 383 self.resultList.header().setStretchLastSection(True)
386 self.resultList.header().setSectionResizeMode( 384 self.resultList.header().setSectionResizeMode(
387 QHeaderView.ResizeMode.Interactive) 385 QHeaderView.ResizeMode.Interactive
388 386 )
387
389 self.checkProgress.setVisible(False) 388 self.checkProgress.setVisible(False)
390 389
391 if self.resultList.topLevelItemCount() == 0: 390 if self.resultList.topLevelItemCount() == 0:
392 itm = QTreeWidgetItem(self.resultList, 391 itm = QTreeWidgetItem(
393 [self.tr("No unused code found.")]) 392 self.resultList, [self.tr("No unused code found.")]
393 )
394 itm.setFirstColumnSpanned(True) 394 itm.setFirstColumnSpanned(True)
395 395
396 @pyqtSlot(QAbstractButton) 396 @pyqtSlot(QAbstractButton)
397 def on_buttonBox_clicked(self, button): 397 def on_buttonBox_clicked(self, button):
398 """ 398 """
399 Private slot called by a button of the button box clicked. 399 Private slot called by a button of the button box clicked.
400 400
401 @param button button that was clicked 401 @param button button that was clicked
402 @type QAbstractButton 402 @type QAbstractButton
403 """ 403 """
404 if button == self.buttonBox.button( 404 if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close):
405 QDialogButtonBox.StandardButton.Close
406 ):
407 self.close() 405 self.close()
408 elif button == self.buttonBox.button( 406 elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel):
409 QDialogButtonBox.StandardButton.Cancel
410 ):
411 self.cancelled = True 407 self.cancelled = True
412 if self.__batch: 408 if self.__batch:
413 self.vultureService.cancelVultureCheckBatch() 409 self.vultureService.cancelVultureCheckBatch()
414 QTimer.singleShot(1000, self.__finish) 410 QTimer.singleShot(1000, self.__finish)
415 else: 411 else:
416 self.__finish() 412 self.__finish()
417 413
418 @pyqtSlot() 414 @pyqtSlot()
419 def on_startButton_clicked(self): 415 def on_startButton_clicked(self):
420 """ 416 """
421 Private slot to start a code metrics run. 417 Private slot to start a code metrics run.
422 """ 418 """
423 fileList = self.__fileList[:] 419 fileList = self.__fileList[:]
424 420
425 filterString = self.excludeFilesEdit.text() 421 filterString = self.excludeFilesEdit.text()
426 if filterString != self.__data["ExcludeFiles"]: 422 if filterString != self.__data["ExcludeFiles"]:
427 self.__data["ExcludeFiles"] = filterString 423 self.__data["ExcludeFiles"] = filterString
428 self.__project.setData( 424 self.__project.setData("CHECKERSPARMS", "Vulture", self.__data)
429 "CHECKERSPARMS", "Vulture", self.__data) 425 filterList = [f.strip() for f in filterString.split(",") if f.strip()]
430 filterList = [f.strip() for f in filterString.split(",")
431 if f.strip()]
432 if filterList: 426 if filterList:
433 for fileFilter in filterList: 427 for fileFilter in filterList:
434 fileList = [f for f in fileList 428 fileList = [f for f in fileList if not fnmatch.fnmatch(f, fileFilter)]
435 if not fnmatch.fnmatch(f, fileFilter)] 429
436
437 self.start(fileList) 430 self.start(fileList)
438 431
439 def clear(self): 432 def clear(self):
440 """ 433 """
441 Public method to clear all results. 434 Public method to clear all results.
442 """ 435 """
443 self.resultList.clear() 436 self.resultList.clear()
444 437
445 @pyqtSlot(QTreeWidgetItem, int) 438 @pyqtSlot(QTreeWidgetItem, int)
446 def on_resultList_itemActivated(self, item, column): 439 def on_resultList_itemActivated(self, item, column):
447 """ 440 """
448 Private slot to handle the activation of a result item. 441 Private slot to handle the activation of a result item.
449 442
450 @param item reference to the activated item 443 @param item reference to the activated item
451 @type QTreeWidgetItem 444 @type QTreeWidgetItem
452 @param column column the item was activated in 445 @param column column the item was activated in
453 @type int 446 @type int
454 """ 447 """
459 except ValueError: 452 except ValueError:
460 lineno = 1 453 lineno = 1
461 if filename: 454 if filename:
462 vm = ericApp().getObject("ViewManager") 455 vm = ericApp().getObject("ViewManager")
463 vm.openSourceFile(filename, lineno) 456 vm.openSourceFile(filename, lineno)
464 457
465 def __prepareResultLists(self): 458 def __prepareResultLists(self):
466 """ 459 """
467 Private method to prepare the result lists. 460 Private method to prepare the result lists.
468 """ 461 """
469 self.__unusedAttrs = [] 462 self.__unusedAttrs = []
471 self.__unusedFuncs = [] 464 self.__unusedFuncs = []
472 self.__unusedMethods = [] 465 self.__unusedMethods = []
473 self.__unusedImports = [] 466 self.__unusedImports = []
474 self.__unusedProps = [] 467 self.__unusedProps = []
475 self.__unusedVars = [] 468 self.__unusedVars = []
476 469
477 def __storeResult(self, result): 470 def __storeResult(self, result):
478 """ 471 """
479 Private method to store the result of an analysis. 472 Private method to store the result of an analysis.
480 473
481 @param result result dictionary 474 @param result result dictionary
482 @type dict 475 @type dict
483 """ 476 """
484 self.__unusedAttrs.extend(self.__filteredList( 477 self.__unusedAttrs.extend(
485 [self.__dict2Item(d) for d in result["UnusedAttributes"]])) 478 self.__filteredList(
486 self.__unusedClasses.extend(self.__filteredList( 479 [self.__dict2Item(d) for d in result["UnusedAttributes"]]
487 [self.__dict2Item(d) for d in result["UnusedClasses"]])) 480 )
488 self.__unusedFuncs.extend(self.__filteredList( 481 )
489 [self.__dict2Item(d) for d in result["UnusedFunctions"]])) 482 self.__unusedClasses.extend(
490 self.__unusedMethods.extend(self.__filteredList( 483 self.__filteredList([self.__dict2Item(d) for d in result["UnusedClasses"]])
491 [self.__dict2Item(d) for d in result["UnusedMethods"]])) 484 )
492 self.__unusedImports.extend(self.__filteredList( 485 self.__unusedFuncs.extend(
493 [self.__dict2Item(d) for d in result["UnusedImports"]])) 486 self.__filteredList(
494 self.__unusedProps.extend(self.__filteredList( 487 [self.__dict2Item(d) for d in result["UnusedFunctions"]]
495 [self.__dict2Item(d) for d in result["UnusedProperties"]])) 488 )
496 self.__unusedVars.extend(self.__filteredList( 489 )
497 [self.__dict2Item(d) for d in result["UnusedVariables"]])) 490 self.__unusedMethods.extend(
498 491 self.__filteredList([self.__dict2Item(d) for d in result["UnusedMethods"]])
492 )
493 self.__unusedImports.extend(
494 self.__filteredList([self.__dict2Item(d) for d in result["UnusedImports"]])
495 )
496 self.__unusedProps.extend(
497 self.__filteredList(
498 [self.__dict2Item(d) for d in result["UnusedProperties"]]
499 )
500 )
501 self.__unusedVars.extend(
502 self.__filteredList(
503 [self.__dict2Item(d) for d in result["UnusedVariables"]]
504 )
505 )
506
499 def __dict2Item(self, d): 507 def __dict2Item(self, d):
500 """ 508 """
501 Private method to convert an item dictionary to a vulture item. 509 Private method to convert an item dictionary to a vulture item.
502 510
503 @param d item dictionary 511 @param d item dictionary
504 @type dict 512 @type dict
505 @return vulture item 513 @return vulture item
506 @rtype VultureItem 514 @rtype VultureItem
507 """ 515 """
508 return VultureItem(d["name"], d["type"], d["file"], d["first_line"], 516 return VultureItem(
509 d["last_line"], confidence=d["confidence"]) 517 d["name"],
510 518 d["type"],
519 d["file"],
520 d["first_line"],
521 d["last_line"],
522 confidence=d["confidence"],
523 )
524
511 def __filteredList(self, itemList): 525 def __filteredList(self, itemList):
512 """ 526 """
513 Private method to filter a list against the whitelist patterns 527 Private method to filter a list against the whitelist patterns
514 returning items not matching the whitelist. 528 returning items not matching the whitelist.
515 529
516 @param itemList list of items to be filtered 530 @param itemList list of items to be filtered
517 @type list of VultureItem 531 @type list of VultureItem
518 @return list of filtered items 532 @return list of filtered items
519 @rtype list of VultureItem 533 @rtype list of VultureItem
520 """ 534 """
521 filteredList = itemList 535 filteredList = itemList
522 for pattern in self.__data["WhiteLists"]["__patterns__"]: 536 for pattern in self.__data["WhiteLists"]["__patterns__"]:
523 filteredList = [item for item in filteredList 537 filteredList = [
524 if not fnmatch.fnmatchcase(item.name, pattern)] 538 item
525 return filteredList # __IGNORE_WARNING_M834__ 539 for item in filteredList
526 540 if not fnmatch.fnmatchcase(item.name, pattern)
541 ]
542 return filteredList # __IGNORE_WARNING_M834__
543
527 def __filterUnusedItems(self, unused, whitelistName): 544 def __filterUnusedItems(self, unused, whitelistName):
528 """ 545 """
529 Private method to get a list of unused items. 546 Private method to get a list of unused items.
530 547
531 @param unused list of unused items 548 @param unused list of unused items
532 @type list of VultureItem 549 @type list of VultureItem
533 @param whitelistName name of the whitelist to use as a filter 550 @param whitelistName name of the whitelist to use as a filter
534 @type str 551 @type str
535 @return list of unused items 552 @return list of unused items
536 @rtype list of VultureItem 553 @rtype list of VultureItem
537 """ 554 """
538 return [ 555 return [
539 item for item in set(unused) 556 item
557 for item in set(unused)
540 if item.name not in self.__data["WhiteLists"][whitelistName] 558 if item.name not in self.__data["WhiteLists"][whitelistName]
541 ] 559 ]
542 560
543 def __filterUnusedFunctions(self): 561 def __filterUnusedFunctions(self):
544 """ 562 """
545 Private method to get the list of unused functions. 563 Private method to get the list of unused functions.
546 564
547 @return list of unused functions 565 @return list of unused functions
548 @rtype list of VultureItem 566 @rtype list of VultureItem
549 """ 567 """
550 return self.__filterUnusedItems(self.__unusedFuncs, "function") 568 return self.__filterUnusedItems(self.__unusedFuncs, "function")
551 569
552 def __filterUnusedMethods(self): 570 def __filterUnusedMethods(self):
553 """ 571 """
554 Private method to get the list of unused methods. 572 Private method to get the list of unused methods.
555 573
556 @return list of unused methods 574 @return list of unused methods
557 @rtype list of VultureItem 575 @rtype list of VultureItem
558 """ 576 """
559 return self.__filterUnusedItems(self.__unusedMethods, "method") 577 return self.__filterUnusedItems(self.__unusedMethods, "method")
560 578
561 def __filterUnusedClasses(self): 579 def __filterUnusedClasses(self):
562 """ 580 """
563 Private method to get the list of unused classes. 581 Private method to get the list of unused classes.
564 582
565 @return list of unused classes 583 @return list of unused classes
566 @rtype list of VultureItem 584 @rtype list of VultureItem
567 """ 585 """
568 return self.__filterUnusedItems(self.__unusedClasses, "class") 586 return self.__filterUnusedItems(self.__unusedClasses, "class")
569 587
570 def __filterUnusedImports(self): 588 def __filterUnusedImports(self):
571 """ 589 """
572 Private method to get a list of unused imports. 590 Private method to get a list of unused imports.
573 591
574 @return list of unused imports 592 @return list of unused imports
575 @rtype list of VultureItem 593 @rtype list of VultureItem
576 """ 594 """
577 return self.__filterUnusedItems(self.__unusedImports, "import") 595 return self.__filterUnusedItems(self.__unusedImports, "import")
578 596
579 def __filterUnusedProperties(self): 597 def __filterUnusedProperties(self):
580 """ 598 """
581 Private method to get the list of unused properties. 599 Private method to get the list of unused properties.
582 600
583 @return list of unused properties 601 @return list of unused properties
584 @rtype list of VultureItem 602 @rtype list of VultureItem
585 """ 603 """
586 return self.__filterUnusedItems(self.__unusedProps, "property") 604 return self.__filterUnusedItems(self.__unusedProps, "property")
587 605
588 def __filterUnusedVariables(self): 606 def __filterUnusedVariables(self):
589 """ 607 """
590 Private method to get the list of unused variables. 608 Private method to get the list of unused variables.
591 609
592 @return list of unused variables 610 @return list of unused variables
593 @rtype list of VultureItem 611 @rtype list of VultureItem
594 """ 612 """
595 return self.__filterUnusedItems(self.__unusedVars, "variable") 613 return self.__filterUnusedItems(self.__unusedVars, "variable")
596 614
597 def __filterUnusedAttributes(self): 615 def __filterUnusedAttributes(self):
598 """ 616 """
599 Private method to get the list of unused attributes. 617 Private method to get the list of unused attributes.
600 618
601 @return list of unused attributes 619 @return list of unused attributes
602 @rtype list of VultureItem 620 @rtype list of VultureItem
603 """ 621 """
604 return self.__filterUnusedItems(self.__unusedAttrs, "attribute") 622 return self.__filterUnusedItems(self.__unusedAttrs, "attribute")
605 623
606 def __createResultItems(self): 624 def __createResultItems(self):
607 """ 625 """
608 Private method to populate the list with the analysis result. 626 Private method to populate the list with the analysis result.
609 """ 627 """
610 lastFileItem = None 628 lastFileItem = None
611 lastFileName = "" 629 lastFileName = ""
612 items = (self.__filterUnusedFunctions() + 630 items = (
613 self.__filterUnusedMethods() + 631 self.__filterUnusedFunctions()
614 self.__filterUnusedClasses() + 632 + self.__filterUnusedMethods()
615 self.__filterUnusedImports() + 633 + self.__filterUnusedClasses()
616 self.__filterUnusedProperties() + 634 + self.__filterUnusedImports()
617 self.__filterUnusedVariables() + 635 + self.__filterUnusedProperties()
618 self.__filterUnusedAttributes()) 636 + self.__filterUnusedVariables()
637 + self.__filterUnusedAttributes()
638 )
619 for item in sorted(items, key=lambda item: item.filename): 639 for item in sorted(items, key=lambda item: item.filename):
620 if lastFileItem is None or lastFileName != item.filename: 640 if lastFileItem is None or lastFileName != item.filename:
621 lastFileItem = self.__createFileItem(item.filename) 641 lastFileItem = self.__createFileItem(item.filename)
622 lastFileName = item.filename 642 lastFileName = item.filename
623 643
624 self.__createResultItem(lastFileItem, item) 644 self.__createResultItem(lastFileItem, item)
625 645
626 def __createResultItem(self, parent, item): 646 def __createResultItem(self, parent, item):
627 """ 647 """
628 Private method to create a result item. 648 Private method to create a result item.
629 649
630 @param parent reference to the parent item 650 @param parent reference to the parent item
631 @type QTreeWidgetItem 651 @type QTreeWidgetItem
632 @param item reference to the item 652 @param item reference to the item
633 @type VultureItem 653 @type VultureItem
634 """ 654 """
635 try: 655 try:
636 translatedType = self.__translatedTypes[item.typ] 656 translatedType = self.__translatedTypes[item.typ]
637 except KeyError: 657 except KeyError:
638 translatedType = item.typ 658 translatedType = item.typ
639 itm = QTreeWidgetItem(parent, [ 659 itm = QTreeWidgetItem(
640 "{0:6d}".format(item.first_lineno), item.name, 660 parent,
641 "{0:3d}%".format(item.confidence), translatedType]) 661 [
662 "{0:6d}".format(item.first_lineno),
663 item.name,
664 "{0:3d}%".format(item.confidence),
665 translatedType,
666 ],
667 )
642 itm.setData(0, self.FilePathRole, item.filename) 668 itm.setData(0, self.FilePathRole, item.filename)
643 itm.setData(0, self.TypeRole, item.typ) 669 itm.setData(0, self.TypeRole, item.typ)
644 itm.setTextAlignment(0, Qt.AlignmentFlag.AlignRight) # line no 670 itm.setTextAlignment(0, Qt.AlignmentFlag.AlignRight) # line no
645 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignRight) # confidence 671 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignRight) # confidence
646 672
647 def __createFileItem(self, filename): 673 def __createFileItem(self, filename):
648 """ 674 """
649 Private method to create a file item. 675 Private method to create a file item.
650 676
651 @param filename file name for the item 677 @param filename file name for the item
652 @type str 678 @type str
653 @return reference to the created item 679 @return reference to the created item
654 @rtype QTreeWidgetItem 680 @rtype QTreeWidgetItem
655 """ 681 """
656 itm = QTreeWidgetItem(self.resultList, [ 682 itm = QTreeWidgetItem(
657 self.__project.getRelativePath(filename)]) 683 self.resultList, [self.__project.getRelativePath(filename)]
684 )
658 itm.setData(0, self.FilePathRole, filename) 685 itm.setData(0, self.FilePathRole, filename)
659 itm.setExpanded(True) 686 itm.setExpanded(True)
660 itm.setFirstColumnSpanned(True) 687 itm.setFirstColumnSpanned(True)
661 688
662 return itm 689 return itm
663 690
664 def __showContextMenu(self, coord): 691 def __showContextMenu(self, coord):
665 """ 692 """
666 Private slot to show the context menu of the listview. 693 Private slot to show the context menu of the listview.
667 694
668 @param coord the position of the mouse pointer 695 @param coord the position of the mouse pointer
669 @type QPoint 696 @type QPoint
670 """ 697 """
671 topLevelPresent = self.resultList.topLevelItemCount() > 0 698 topLevelPresent = self.resultList.topLevelItemCount() > 0
672 self.__collapseAct.setEnabled(topLevelPresent) 699 self.__collapseAct.setEnabled(topLevelPresent)
673 self.__expandAct.setEnabled(topLevelPresent) 700 self.__expandAct.setEnabled(topLevelPresent)
674 701
675 self.__whiteListAct.setEnabled( 702 self.__whiteListAct.setEnabled(len(self.__getSelectedNonFileItems()) != 0)
676 len(self.__getSelectedNonFileItems()) != 0) 703
677
678 self.__menu.popup(self.resultList.mapToGlobal(coord)) 704 self.__menu.popup(self.resultList.mapToGlobal(coord))
679 705
680 def __resultCollapse(self): 706 def __resultCollapse(self):
681 """ 707 """
682 Private slot to collapse all entries of the resultlist. 708 Private slot to collapse all entries of the resultlist.
683 """ 709 """
684 for index in range(self.resultList.topLevelItemCount()): 710 for index in range(self.resultList.topLevelItemCount()):
685 self.resultList.topLevelItem(index).setExpanded(False) 711 self.resultList.topLevelItem(index).setExpanded(False)
686 712
687 def __resultExpand(self): 713 def __resultExpand(self):
688 """ 714 """
689 Private slot to expand all entries of the resultlist. 715 Private slot to expand all entries of the resultlist.
690 """ 716 """
691 for index in range(self.resultList.topLevelItemCount()): 717 for index in range(self.resultList.topLevelItemCount()):
692 self.resultList.topLevelItem(index).setExpanded(True) 718 self.resultList.topLevelItem(index).setExpanded(True)
693 719
694 def __getSelectedNonFileItems(self): 720 def __getSelectedNonFileItems(self):
695 """ 721 """
696 Private method to get a list of selected non file items. 722 Private method to get a list of selected non file items.
697 723
698 @return list of selected non file items 724 @return list of selected non file items
699 @rtype list of QTreeWidgetItem 725 @rtype list of QTreeWidgetItem
700 """ 726 """
701 return [i for i in self.resultList.selectedItems() 727 return [i for i in self.resultList.selectedItems() if i.parent() is not None]
702 if i.parent() is not None] 728
703
704 def __editWhiteList(self): 729 def __editWhiteList(self):
705 """ 730 """
706 Private slot to edit the whitelist. 731 Private slot to edit the whitelist.
707 """ 732 """
708 from .EditWhiteListDialog import EditWhiteListDialog 733 from .EditWhiteListDialog import EditWhiteListDialog
734
709 dlg = EditWhiteListDialog(self.__data["WhiteLists"]) 735 dlg = EditWhiteListDialog(self.__data["WhiteLists"])
710 if dlg.exec() == QDialog.DialogCode.Accepted: 736 if dlg.exec() == QDialog.DialogCode.Accepted:
711 whitelists = dlg.getWhiteLists() 737 whitelists = dlg.getWhiteLists()
712 self.__storeWhiteLists(whitelists) 738 self.__storeWhiteLists(whitelists)
713 739
714 def __whiteList(self): 740 def __whiteList(self):
715 """ 741 """
716 Private slot to add entries to the whitelist. 742 Private slot to add entries to the whitelist.
717 """ 743 """
718 whitelists = {} 744 whitelists = {}
725 pitm = itm.parent() 751 pitm = itm.parent()
726 pitm.removeChild(itm) 752 pitm.removeChild(itm)
727 del itm 753 del itm
728 if pitm.childCount() == 0: 754 if pitm.childCount() == 0:
729 self.resultList.takeTopLevelItem( 755 self.resultList.takeTopLevelItem(
730 self.resultList.indexOfTopLevelItem(pitm)) 756 self.resultList.indexOfTopLevelItem(pitm)
757 )
731 del pitm 758 del pitm
732 self.__storeWhiteLists(whitelists) 759 self.__storeWhiteLists(whitelists)
733 760
734 def __storeWhiteLists(self, whitelists): 761 def __storeWhiteLists(self, whitelists):
735 """ 762 """
736 Private method to store the new whitelists, if they have changed. 763 Private method to store the new whitelists, if they have changed.
737 764
738 @param whitelists dictionary of lists of whitelisted names 765 @param whitelists dictionary of lists of whitelisted names
739 @type dict of list of str 766 @type dict of list of str
740 """ 767 """
741 changed = False 768 changed = False
742 for key in whitelists: 769 for key in whitelists:
743 whitelist = list(set(whitelists[key])) 770 whitelist = list(set(whitelists[key]))
744 with contextlib.suppress(KeyError): 771 with contextlib.suppress(KeyError):
745 if sorted(whitelist) != sorted(self.__data["WhiteLists"][key]): 772 if sorted(whitelist) != sorted(self.__data["WhiteLists"][key]):
746 self.__data["WhiteLists"][key] = whitelist[:] 773 self.__data["WhiteLists"][key] = whitelist[:]
747 changed = True 774 changed = True
748 775
749 if changed: 776 if changed:
750 self.__project.setData("CHECKERSPARMS", "Vulture", self.__data) 777 self.__project.setData("CHECKERSPARMS", "Vulture", self.__data)

eric ide

mercurial