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 """ |
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 = {} |