|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2011 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a dialog to show the results of the code style check. |
|
8 """ |
|
9 |
|
10 import collections |
|
11 import copy |
|
12 import fnmatch |
|
13 import json |
|
14 import os |
|
15 import time |
|
16 |
|
17 from PyQt6.QtCore import pyqtSlot, Qt, QTimer, QCoreApplication |
|
18 from PyQt6.QtGui import QIcon |
|
19 from PyQt6.QtWidgets import ( |
|
20 QDialog, QTreeWidgetItem, QAbstractButton, QDialogButtonBox, QApplication, |
|
21 QListWidgetItem, QInputDialog, QLineEdit |
|
22 ) |
|
23 |
|
24 from EricWidgets.EricApplication import ericApp |
|
25 |
|
26 from .Ui_CodeStyleCheckerDialog import Ui_CodeStyleCheckerDialog |
|
27 |
|
28 import UI.PixmapCache |
|
29 import Preferences |
|
30 import Utilities |
|
31 |
|
32 from . import pycodestyle |
|
33 from . import CodeStyleCheckerUtilities |
|
34 |
|
35 from .Miscellaneous.MiscellaneousDefaults import ( |
|
36 MiscellaneousCheckerDefaultArgs |
|
37 ) |
|
38 from .Annotations.AnnotationsCheckerDefaults import ( |
|
39 AnnotationsCheckerDefaultArgs |
|
40 ) |
|
41 |
|
42 |
|
43 class CodeStyleCheckerDialog(QDialog, Ui_CodeStyleCheckerDialog): |
|
44 """ |
|
45 Class implementing a dialog to show the results of the code style check. |
|
46 """ |
|
47 filenameRole = Qt.ItemDataRole.UserRole + 1 |
|
48 lineRole = Qt.ItemDataRole.UserRole + 2 |
|
49 positionRole = Qt.ItemDataRole.UserRole + 3 |
|
50 messageRole = Qt.ItemDataRole.UserRole + 4 |
|
51 fixableRole = Qt.ItemDataRole.UserRole + 5 |
|
52 codeRole = Qt.ItemDataRole.UserRole + 6 |
|
53 ignoredRole = Qt.ItemDataRole.UserRole + 7 |
|
54 argsRole = Qt.ItemDataRole.UserRole + 8 |
|
55 |
|
56 availableFutures = [ |
|
57 'division', 'absolute_import', 'with_statement', |
|
58 'print_function', 'unicode_literals', 'generator_stop', |
|
59 'annotations'] |
|
60 |
|
61 cryptoBitSelectionsDsaRsa = [ |
|
62 "512", "1024", "2048", "4096", "8192", "16384", "32786", |
|
63 ] |
|
64 cryptoBitSelectionsEc = [ |
|
65 "160", "224", "256", "384", "512", |
|
66 ] |
|
67 |
|
68 checkCategories = { |
|
69 "A": QCoreApplication.translate( |
|
70 "CheckerCategories", |
|
71 "Annotations"), |
|
72 "C": QCoreApplication.translate( |
|
73 "CheckerCategories", |
|
74 "Code Complexity"), |
|
75 "D": QCoreApplication.translate( |
|
76 "CheckerCategories", |
|
77 "Documentation"), |
|
78 "E": QCoreApplication.translate( |
|
79 "CheckerCategories", |
|
80 "Errors"), |
|
81 "I": QCoreApplication.translate( |
|
82 "CheckerCategories", |
|
83 "Imports"), |
|
84 "M": QCoreApplication.translate( |
|
85 "CheckerCategories", |
|
86 "Miscellaneous"), |
|
87 "N": QCoreApplication.translate( |
|
88 "CheckerCategories", |
|
89 "Naming"), |
|
90 "P": QCoreApplication.translate( |
|
91 "CheckerCategories", |
|
92 "'pathlib' Usage"), |
|
93 "S": QCoreApplication.translate( |
|
94 "CheckerCategories", |
|
95 "Security"), |
|
96 "W": QCoreApplication.translate( |
|
97 "CheckerCategories", |
|
98 "Warnings"), |
|
99 "Y": QCoreApplication.translate( |
|
100 "CheckerCategories", |
|
101 "Simplify Code"), |
|
102 } |
|
103 |
|
104 noResults = 0 |
|
105 noFiles = 1 |
|
106 hasResults = 2 |
|
107 |
|
108 def __init__(self, styleCheckService, project=None, parent=None): |
|
109 """ |
|
110 Constructor |
|
111 |
|
112 @param styleCheckService reference to the service |
|
113 @type CodeStyleCheckService |
|
114 @param project reference to the project if called on project or project |
|
115 browser level |
|
116 @type Project |
|
117 @param parent reference to the parent widget |
|
118 @type QWidget |
|
119 """ |
|
120 super().__init__(parent) |
|
121 self.setupUi(self) |
|
122 self.setWindowFlags(Qt.WindowType.Window) |
|
123 |
|
124 self.__project = project |
|
125 |
|
126 self.optionsTabWidget.setCurrentIndex(0) |
|
127 |
|
128 self.excludeMessagesSelectButton.setIcon( |
|
129 UI.PixmapCache.getIcon("select")) |
|
130 self.includeMessagesSelectButton.setIcon( |
|
131 UI.PixmapCache.getIcon("select")) |
|
132 self.fixIssuesSelectButton.setIcon( |
|
133 UI.PixmapCache.getIcon("select")) |
|
134 self.noFixIssuesSelectButton.setIcon( |
|
135 UI.PixmapCache.getIcon("select")) |
|
136 |
|
137 self.docTypeComboBox.addItem(self.tr("PEP-257"), "pep257") |
|
138 self.docTypeComboBox.addItem(self.tr("Eric"), "eric") |
|
139 self.docTypeComboBox.addItem(self.tr("Eric (Blacked)"), "eric_black") |
|
140 |
|
141 for category, text in CodeStyleCheckerDialog.checkCategories.items(): |
|
142 itm = QListWidgetItem(text, self.categoriesList) |
|
143 itm.setData(Qt.ItemDataRole.UserRole, category) |
|
144 itm.setFlags(itm.flags() | Qt.ItemFlag.ItemIsUserCheckable) |
|
145 itm.setCheckState(Qt.CheckState.Unchecked) |
|
146 |
|
147 for future in CodeStyleCheckerDialog.availableFutures: |
|
148 itm = QListWidgetItem(future, self.futuresList) |
|
149 itm.setFlags(itm.flags() | Qt.ItemFlag.ItemIsUserCheckable) |
|
150 itm.setCheckState(Qt.CheckState.Unchecked) |
|
151 |
|
152 self.dsaHighRiskCombo.addItems( |
|
153 CodeStyleCheckerDialog.cryptoBitSelectionsDsaRsa) |
|
154 self.dsaMediumRiskCombo.addItems( |
|
155 CodeStyleCheckerDialog.cryptoBitSelectionsDsaRsa) |
|
156 self.rsaHighRiskCombo.addItems( |
|
157 CodeStyleCheckerDialog.cryptoBitSelectionsDsaRsa) |
|
158 self.rsaMediumRiskCombo.addItems( |
|
159 CodeStyleCheckerDialog.cryptoBitSelectionsDsaRsa) |
|
160 self.ecHighRiskCombo.addItems( |
|
161 CodeStyleCheckerDialog.cryptoBitSelectionsEc) |
|
162 self.ecMediumRiskCombo.addItems( |
|
163 CodeStyleCheckerDialog.cryptoBitSelectionsEc) |
|
164 |
|
165 self.statisticsButton.setEnabled(False) |
|
166 self.showButton.setEnabled(False) |
|
167 self.cancelButton.setEnabled(True) |
|
168 self.buttonBox.button( |
|
169 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
|
170 |
|
171 self.resultList.headerItem().setText(self.resultList.columnCount(), "") |
|
172 self.resultList.header().setSortIndicator( |
|
173 0, Qt.SortOrder.AscendingOrder) |
|
174 |
|
175 self.addBuiltinButton.setIcon(UI.PixmapCache.getIcon("plus")) |
|
176 self.deleteBuiltinButton.setIcon(UI.PixmapCache.getIcon("minus")) |
|
177 self.addWhitelistButton.setIcon(UI.PixmapCache.getIcon("plus")) |
|
178 self.deleteWhitelistButton.setIcon(UI.PixmapCache.getIcon("minus")) |
|
179 |
|
180 self.restartButton.setEnabled(False) |
|
181 self.fixButton.setEnabled(False) |
|
182 |
|
183 self.checkProgress.setVisible(False) |
|
184 self.checkProgressLabel.setVisible(False) |
|
185 self.checkProgressLabel.setMaximumWidth(600) |
|
186 |
|
187 self.styleCheckService = styleCheckService |
|
188 self.styleCheckService.styleChecked.connect(self.__processResult) |
|
189 self.styleCheckService.batchFinished.connect(self.__batchFinished) |
|
190 self.styleCheckService.error.connect(self.__processError) |
|
191 self.filename = None |
|
192 |
|
193 self.results = CodeStyleCheckerDialog.noResults |
|
194 self.cancelled = False |
|
195 self.__lastFileItem = None |
|
196 self.__batch = False |
|
197 self.__finished = True |
|
198 self.__errorItem = None |
|
199 self.__timenow = time.monotonic() |
|
200 |
|
201 self.__fileOrFileList = "" |
|
202 self.__forProject = False |
|
203 self.__data = {} |
|
204 self.__statistics = collections.defaultdict(self.__defaultStatistics) |
|
205 self.__onlyFixes = {} |
|
206 self.__noFixCodesList = [] |
|
207 self.__detectedCodes = [] |
|
208 |
|
209 self.on_loadDefaultButton_clicked() |
|
210 |
|
211 self.mainWidget.setCurrentWidget(self.configureTab) |
|
212 self.optionsTabWidget.setCurrentWidget(self.globalOptionsTab) |
|
213 |
|
214 def __defaultStatistics(self): |
|
215 """ |
|
216 Private method to return the default statistics entry. |
|
217 |
|
218 @return dictionary with default statistics entry |
|
219 @rtype dict |
|
220 """ |
|
221 return { |
|
222 "total": 0, |
|
223 "ignored": 0, |
|
224 } |
|
225 |
|
226 def __resort(self): |
|
227 """ |
|
228 Private method to resort the tree. |
|
229 """ |
|
230 self.resultList.sortItems(self.resultList.sortColumn(), |
|
231 self.resultList.header().sortIndicatorOrder() |
|
232 ) |
|
233 |
|
234 def __createErrorItem(self, filename, message): |
|
235 """ |
|
236 Private slot to create a new error item in the result list. |
|
237 |
|
238 @param filename name of the file |
|
239 @type str |
|
240 @param message error message |
|
241 @type str |
|
242 """ |
|
243 if self.__errorItem is None: |
|
244 self.__errorItem = QTreeWidgetItem(self.resultList, [ |
|
245 self.tr("Errors")]) |
|
246 self.__errorItem.setExpanded(True) |
|
247 self.__errorItem.setForeground(0, Qt.GlobalColor.red) |
|
248 |
|
249 msg = "{0} ({1})".format(self.__project.getRelativePath(filename), |
|
250 message) |
|
251 if not self.resultList.findItems(msg, Qt.MatchFlag.MatchExactly): |
|
252 itm = QTreeWidgetItem(self.__errorItem, [msg]) |
|
253 itm.setForeground(0, Qt.GlobalColor.red) |
|
254 itm.setFirstColumnSpanned(True) |
|
255 |
|
256 def __createFileErrorItem(self, filename, message): |
|
257 """ |
|
258 Private method to create an error entry for a given file. |
|
259 |
|
260 @param filename file name of the file |
|
261 @type str |
|
262 @param message error message text |
|
263 @type str |
|
264 """ |
|
265 result = { |
|
266 "file": filename, |
|
267 "line": 1, |
|
268 "offset": 1, |
|
269 "code": "", |
|
270 "args": [], |
|
271 "display": self.tr("Error: {0}").format(message).rstrip(), |
|
272 "fixed": False, |
|
273 "autofixing": False, |
|
274 "ignored": False, |
|
275 } |
|
276 self.__createResultItem(filename, result) |
|
277 |
|
278 def __createResultItem(self, filename, result): |
|
279 """ |
|
280 Private method to create an entry in the result list. |
|
281 |
|
282 @param filename file name of the file |
|
283 @type str |
|
284 @param result dictionary containing check result data |
|
285 @type dict |
|
286 @return reference to the created item |
|
287 @rtype QTreeWidgetItem |
|
288 """ |
|
289 from .CodeStyleFixer import FixableCodeStyleIssues |
|
290 |
|
291 if self.__lastFileItem is None: |
|
292 # It's a new file |
|
293 self.__lastFileItem = QTreeWidgetItem(self.resultList, [ |
|
294 self.__project.getRelativePath(filename)]) |
|
295 self.__lastFileItem.setFirstColumnSpanned(True) |
|
296 self.__lastFileItem.setExpanded(True) |
|
297 self.__lastFileItem.setData(0, self.filenameRole, filename) |
|
298 |
|
299 msgCode = result["code"].split(".", 1)[0] |
|
300 self.__detectedCodes.append(msgCode) |
|
301 |
|
302 fixable = False |
|
303 itm = QTreeWidgetItem( |
|
304 self.__lastFileItem, [ |
|
305 "{0:6}".format(result["line"]), |
|
306 msgCode, |
|
307 result["display"] |
|
308 ] |
|
309 ) |
|
310 |
|
311 CodeStyleCheckerUtilities.setItemIcon( |
|
312 itm, 1, msgCode, result.get("severity")) |
|
313 |
|
314 if result["fixed"]: |
|
315 itm.setIcon(0, UI.PixmapCache.getIcon("issueFixed")) |
|
316 elif ( |
|
317 msgCode in FixableCodeStyleIssues and |
|
318 not result["autofixing"] and |
|
319 msgCode not in self.__noFixCodesList |
|
320 ): |
|
321 itm.setIcon(0, UI.PixmapCache.getIcon("issueFixable")) |
|
322 fixable = True |
|
323 |
|
324 itm.setTextAlignment(0, Qt.AlignmentFlag.AlignRight) |
|
325 itm.setTextAlignment(1, Qt.AlignmentFlag.AlignHCenter) |
|
326 |
|
327 itm.setTextAlignment(0, Qt.AlignmentFlag.AlignVCenter) |
|
328 itm.setTextAlignment(1, Qt.AlignmentFlag.AlignVCenter) |
|
329 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignVCenter) |
|
330 |
|
331 itm.setData(0, self.filenameRole, filename) |
|
332 itm.setData(0, self.lineRole, int(result["line"])) |
|
333 itm.setData(0, self.positionRole, int(result["offset"])) |
|
334 itm.setData(0, self.messageRole, result["display"]) |
|
335 itm.setData(0, self.fixableRole, fixable) |
|
336 itm.setData(0, self.codeRole, msgCode) |
|
337 itm.setData(0, self.ignoredRole, result["ignored"]) |
|
338 itm.setData(0, self.argsRole, result["args"]) |
|
339 |
|
340 if result["ignored"]: |
|
341 font = itm.font(0) |
|
342 font.setItalic(True) |
|
343 for col in range(itm.columnCount()): |
|
344 itm.setFont(col, font) |
|
345 |
|
346 return itm |
|
347 |
|
348 def __modifyFixedResultItem(self, itm, result): |
|
349 """ |
|
350 Private method to modify a result list entry to show its |
|
351 positive fixed state. |
|
352 |
|
353 @param itm reference to the item to modify |
|
354 @type QTreeWidgetItem |
|
355 @param result dictionary containing check result data |
|
356 @type dict |
|
357 """ |
|
358 if result["fixed"]: |
|
359 itm.setText(2, result["display"]) |
|
360 itm.setIcon(0, UI.PixmapCache.getIcon("issueFixed")) |
|
361 |
|
362 itm.setData(0, self.messageRole, result["display"]) |
|
363 else: |
|
364 itm.setIcon(0, QIcon()) |
|
365 itm.setData(0, self.fixableRole, False) |
|
366 |
|
367 def __updateStatistics(self, statistics, fixer, ignoredErrors, securityOk): |
|
368 """ |
|
369 Private method to update the collected statistics. |
|
370 |
|
371 @param statistics dictionary of statistical data with |
|
372 message code as key and message count as value |
|
373 @type dict |
|
374 @param fixer reference to the code style fixer |
|
375 @type CodeStyleFixer |
|
376 @param ignoredErrors number of ignored errors |
|
377 @type int |
|
378 @param securityOk number of acknowledged security reports |
|
379 @type int |
|
380 """ |
|
381 self.__statistics["_FilesCount"] += 1 |
|
382 stats = [k for k in statistics if k[0].isupper()] |
|
383 if stats: |
|
384 self.__statistics["_FilesIssues"] += 1 |
|
385 for key in stats: |
|
386 self.__statistics[key]["total"] += statistics[key] |
|
387 for key in ignoredErrors: |
|
388 self.__statistics[key]["ignored"] += ignoredErrors[key] |
|
389 self.__statistics["_IssuesFixed"] += fixer |
|
390 self.__statistics["_SecurityOK"] += securityOk |
|
391 |
|
392 def __updateFixerStatistics(self, fixer): |
|
393 """ |
|
394 Private method to update the collected fixer related statistics. |
|
395 |
|
396 @param fixer reference to the code style fixer |
|
397 @type CodeStyleFixer |
|
398 """ |
|
399 self.__statistics["_IssuesFixed"] += fixer |
|
400 |
|
401 def __resetStatistics(self): |
|
402 """ |
|
403 Private slot to reset the statistics data. |
|
404 """ |
|
405 self.__statistics.clear() |
|
406 self.__statistics["_FilesCount"] = 0 |
|
407 self.__statistics["_FilesIssues"] = 0 |
|
408 self.__statistics["_IssuesFixed"] = 0 |
|
409 self.__statistics["_SecurityOK"] = 0 |
|
410 |
|
411 def __getBanRelativeImportsValue(self): |
|
412 """ |
|
413 Private method to get the value corresponding the selected button. |
|
414 |
|
415 @return value for the BanRelativeImports argument |
|
416 @rtype str |
|
417 """ |
|
418 if self.banParentsButton.isChecked(): |
|
419 return "parents" |
|
420 elif self.banAllButton.isChecked(): |
|
421 return "true" |
|
422 else: |
|
423 return "" |
|
424 |
|
425 def __setBanRelativeImports(self, value): |
|
426 """ |
|
427 Private method to set the button according to the ban relative imports |
|
428 setting. |
|
429 |
|
430 @param value value of the ban relative imports setting |
|
431 @type str |
|
432 """ |
|
433 if value == "parents": |
|
434 self.banParentsButton.setChecked(True) |
|
435 elif value == "true": |
|
436 self.banAllButton.setChecked(True) |
|
437 else: |
|
438 self.allowAllButton.setChecked(True) |
|
439 |
|
440 def prepare(self, fileList, project): |
|
441 """ |
|
442 Public method to prepare the dialog with a list of filenames. |
|
443 |
|
444 @param fileList list of filenames |
|
445 @type list of str |
|
446 @param project reference to the project object |
|
447 @type Project |
|
448 """ |
|
449 self.__fileOrFileList = fileList[:] |
|
450 self.__project = project |
|
451 self.__forProject = True |
|
452 |
|
453 self.buttonBox.button( |
|
454 QDialogButtonBox.StandardButton.Close).setEnabled(True) |
|
455 self.buttonBox.button( |
|
456 QDialogButtonBox.StandardButton.Close).setDefault(True) |
|
457 self.cancelButton.setEnabled(False) |
|
458 |
|
459 self.__data = self.__project.getData("CHECKERSPARMS", "Pep8Checker") |
|
460 if ( |
|
461 self.__data is None or |
|
462 len(self.__data) < 6 |
|
463 ): |
|
464 # initialize the data structure |
|
465 self.__data = { |
|
466 "ExcludeFiles": "", |
|
467 "ExcludeMessages": pycodestyle.DEFAULT_IGNORE, |
|
468 "IncludeMessages": "", |
|
469 "RepeatMessages": False, |
|
470 "FixCodes": "", |
|
471 "FixIssues": False, |
|
472 } |
|
473 if "EnabledCheckerCategories" not in self.__data: |
|
474 self.__data["EnabledCheckerCategories"] = ",".join( |
|
475 CodeStyleCheckerDialog.checkCategories.keys()) |
|
476 if "MaxLineLength" not in self.__data: |
|
477 self.__data["MaxLineLength"] = 88 |
|
478 # better code formatting than pycodestyle.MAX_LINE_LENGTH |
|
479 # see the Black tool |
|
480 if "MaxDocLineLength" not in self.__data: |
|
481 self.__data["MaxDocLineLength"] = 88 |
|
482 if "BlankLines" not in self.__data: |
|
483 self.__data["BlankLines"] = (2, 1) |
|
484 # top level, method |
|
485 if "HangClosing" not in self.__data: |
|
486 self.__data["HangClosing"] = False |
|
487 if "NoFixCodes" not in self.__data: |
|
488 self.__data["NoFixCodes"] = "E501" |
|
489 if "DocstringType" not in self.__data: |
|
490 self.__data["DocstringType"] = "pep257" |
|
491 if "ShowIgnored" not in self.__data: |
|
492 self.__data["ShowIgnored"] = False |
|
493 if "MaxCodeComplexity" not in self.__data: |
|
494 self.__data["MaxCodeComplexity"] = 10 |
|
495 if "LineComplexity" not in self.__data: |
|
496 self.__data["LineComplexity"] = 15 |
|
497 if "LineComplexityScore" not in self.__data: |
|
498 self.__data["LineComplexityScore"] = 10 |
|
499 if "ValidEncodings" not in self.__data: |
|
500 self.__data["ValidEncodings"] = ( |
|
501 MiscellaneousCheckerDefaultArgs["CodingChecker"] |
|
502 ) |
|
503 if ( |
|
504 "CopyrightMinFileSize" not in self.__data or |
|
505 "CopyrightAuthor" not in self.__data |
|
506 ): |
|
507 self.__data["CopyrightMinFileSize"] = ( |
|
508 MiscellaneousCheckerDefaultArgs[ |
|
509 "CopyrightChecker"]["MinFilesize"] |
|
510 ) |
|
511 self.__data["CopyrightAuthor"] = ( |
|
512 MiscellaneousCheckerDefaultArgs["CopyrightChecker"]["Author"] |
|
513 ) |
|
514 if "FutureChecker" not in self.__data: |
|
515 self.__data["FutureChecker"] = "" |
|
516 if "BuiltinsChecker" not in self.__data: |
|
517 self.__data["BuiltinsChecker"] = copy.deepcopy( |
|
518 MiscellaneousCheckerDefaultArgs["BuiltinsChecker"] |
|
519 ) |
|
520 |
|
521 if "CommentedCodeChecker" not in self.__data: |
|
522 self.__data["CommentedCodeChecker"] = copy.deepcopy( |
|
523 MiscellaneousCheckerDefaultArgs["CommentedCodeChecker"] |
|
524 ) |
|
525 if "WhiteList" not in self.__data["CommentedCodeChecker"]: |
|
526 self.__data["CommentedCodeChecker"]["WhiteList"] = ( |
|
527 MiscellaneousCheckerDefaultArgs[ |
|
528 "CommentedCodeChecker"]["WhiteList"][:] |
|
529 ) |
|
530 |
|
531 if "AnnotationsChecker" not in self.__data: |
|
532 self.__data["AnnotationsChecker"] = copy.deepcopy( |
|
533 AnnotationsCheckerDefaultArgs) |
|
534 else: |
|
535 # We are upgrading from an older data structure |
|
536 if "MaximumLength" not in self.__data["AnnotationsChecker"]: |
|
537 # MaximumLength is the sentinel for the first extension |
|
538 self.__data["AnnotationsChecker"].update({ |
|
539 "MaximumLength": |
|
540 AnnotationsCheckerDefaultArgs["MaximumLength"], |
|
541 "SuppressNoneReturning": |
|
542 AnnotationsCheckerDefaultArgs["SuppressNoneReturning"], |
|
543 "SuppressDummyArgs": |
|
544 AnnotationsCheckerDefaultArgs["SuppressDummyArgs"], |
|
545 "AllowUntypedDefs": |
|
546 AnnotationsCheckerDefaultArgs["AllowUntypedDefs"], |
|
547 "AllowUntypedNested": |
|
548 AnnotationsCheckerDefaultArgs["AllowUntypedNested"], |
|
549 "MypyInitReturn": |
|
550 AnnotationsCheckerDefaultArgs["MypyInitReturn"], |
|
551 "DispatchDecorators": |
|
552 AnnotationsCheckerDefaultArgs["DispatchDecorators"], |
|
553 "OverloadDecorators": |
|
554 AnnotationsCheckerDefaultArgs["OverloadDecorators"], |
|
555 }) |
|
556 |
|
557 if "SecurityChecker" not in self.__data: |
|
558 from .Security.SecurityDefaults import SecurityDefaults |
|
559 self.__data["SecurityChecker"] = { |
|
560 "HardcodedTmpDirectories": |
|
561 SecurityDefaults["hardcoded_tmp_directories"], |
|
562 "InsecureHashes": |
|
563 SecurityDefaults["insecure_hashes"], |
|
564 "InsecureSslProtocolVersions": |
|
565 SecurityDefaults["insecure_ssl_protocol_versions"], |
|
566 "WeakKeySizeDsaHigh": |
|
567 str(SecurityDefaults["weak_key_size_dsa_high"]), |
|
568 "WeakKeySizeDsaMedium": |
|
569 str(SecurityDefaults["weak_key_size_dsa_medium"]), |
|
570 "WeakKeySizeRsaHigh": |
|
571 str(SecurityDefaults["weak_key_size_rsa_high"]), |
|
572 "WeakKeySizeRsaMedium": |
|
573 str(SecurityDefaults["weak_key_size_rsa_medium"]), |
|
574 "WeakKeySizeEcHigh": |
|
575 str(SecurityDefaults["weak_key_size_ec_high"]), |
|
576 "WeakKeySizeEcMedium": |
|
577 str(SecurityDefaults["weak_key_size_ec_medium"]), |
|
578 "CheckTypedException": |
|
579 SecurityDefaults["check_typed_exception"], |
|
580 } |
|
581 |
|
582 if "ImportsChecker" not in self.__data: |
|
583 self.__data["ImportsChecker"] = { |
|
584 "ApplicationPackageNames": [], |
|
585 "BannedModules": [], |
|
586 "BanRelativeImports": "", |
|
587 } |
|
588 |
|
589 self.__initCategoriesList(self.__data["EnabledCheckerCategories"]) |
|
590 self.excludeFilesEdit.setText(self.__data["ExcludeFiles"]) |
|
591 self.excludeMessagesEdit.setText(self.__data["ExcludeMessages"]) |
|
592 self.includeMessagesEdit.setText(self.__data["IncludeMessages"]) |
|
593 self.repeatCheckBox.setChecked(self.__data["RepeatMessages"]) |
|
594 self.fixIssuesEdit.setText(self.__data["FixCodes"]) |
|
595 self.noFixIssuesEdit.setText(self.__data["NoFixCodes"]) |
|
596 self.fixIssuesCheckBox.setChecked(self.__data["FixIssues"]) |
|
597 self.ignoredCheckBox.setChecked(self.__data["ShowIgnored"]) |
|
598 self.lineLengthSpinBox.setValue(self.__data["MaxLineLength"]) |
|
599 self.docLineLengthSpinBox.setValue(self.__data["MaxDocLineLength"]) |
|
600 self.blankBeforeTopLevelSpinBox.setValue(self.__data["BlankLines"][0]) |
|
601 self.blankBeforeMethodSpinBox.setValue(self.__data["BlankLines"][1]) |
|
602 self.hangClosingCheckBox.setChecked(self.__data["HangClosing"]) |
|
603 self.docTypeComboBox.setCurrentIndex( |
|
604 self.docTypeComboBox.findData(self.__data["DocstringType"])) |
|
605 self.complexitySpinBox.setValue(self.__data["MaxCodeComplexity"]) |
|
606 self.lineComplexitySpinBox.setValue(self.__data["LineComplexity"]) |
|
607 self.lineComplexityScoreSpinBox.setValue( |
|
608 self.__data["LineComplexityScore"]) |
|
609 self.encodingsEdit.setText(self.__data["ValidEncodings"]) |
|
610 self.copyrightFileSizeSpinBox.setValue( |
|
611 self.__data["CopyrightMinFileSize"]) |
|
612 self.copyrightAuthorEdit.setText(self.__data["CopyrightAuthor"]) |
|
613 self.__initFuturesList(self.__data["FutureChecker"]) |
|
614 self.__initBuiltinsIgnoreList(self.__data["BuiltinsChecker"]) |
|
615 self.aggressiveCheckBox.setChecked( |
|
616 self.__data["CommentedCodeChecker"]["Aggressive"]) |
|
617 self.__initCommentedCodeCheckerWhiteList( |
|
618 self.__data["CommentedCodeChecker"]["WhiteList"]) |
|
619 |
|
620 # type annotations |
|
621 self.minAnnotationsCoverageSpinBox.setValue( |
|
622 self.__data["AnnotationsChecker"]["MinimumCoverage"]) |
|
623 self.maxAnnotationsComplexitySpinBox.setValue( |
|
624 self.__data["AnnotationsChecker"]["MaximumComplexity"]) |
|
625 self.maxAnnotationsLengthSpinBox.setValue( |
|
626 self.__data["AnnotationsChecker"]["MaximumLength"]) |
|
627 self.suppressNoneReturningCheckBox.setChecked( |
|
628 self.__data["AnnotationsChecker"]["SuppressNoneReturning"]) |
|
629 self.suppressDummyArgsCheckBox.setChecked( |
|
630 self.__data["AnnotationsChecker"]["SuppressDummyArgs"]) |
|
631 self.allowUntypedDefsCheckBox.setChecked( |
|
632 self.__data["AnnotationsChecker"]["AllowUntypedDefs"]) |
|
633 self.allowUntypedNestedCheckBox.setChecked( |
|
634 self.__data["AnnotationsChecker"]["AllowUntypedNested"]) |
|
635 self.mypyInitReturnCheckBox.setChecked( |
|
636 self.__data["AnnotationsChecker"]["MypyInitReturn"]) |
|
637 self.dispatchDecoratorEdit.setText( |
|
638 ", ".join( |
|
639 self.__data["AnnotationsChecker"]["DispatchDecorators"])) |
|
640 self.overloadDecoratorEdit.setText( |
|
641 ", ".join( |
|
642 self.__data["AnnotationsChecker"]["OverloadDecorators"])) |
|
643 |
|
644 # security |
|
645 self.tmpDirectoriesEdit.setPlainText("\n".join( |
|
646 self.__data["SecurityChecker"]["HardcodedTmpDirectories"])) |
|
647 self.hashesEdit.setText(", ".join( |
|
648 self.__data["SecurityChecker"]["InsecureHashes"])) |
|
649 self.insecureSslProtocolsEdit.setPlainText("\n".join( |
|
650 self.__data["SecurityChecker"]["InsecureSslProtocolVersions"])) |
|
651 self.dsaHighRiskCombo.setCurrentText( |
|
652 self.__data["SecurityChecker"]["WeakKeySizeDsaHigh"]) |
|
653 self.dsaMediumRiskCombo.setCurrentText( |
|
654 self.__data["SecurityChecker"]["WeakKeySizeDsaMedium"]) |
|
655 self.rsaHighRiskCombo.setCurrentText( |
|
656 self.__data["SecurityChecker"]["WeakKeySizeRsaHigh"]) |
|
657 self.rsaMediumRiskCombo.setCurrentText( |
|
658 self.__data["SecurityChecker"]["WeakKeySizeRsaMedium"]) |
|
659 self.ecHighRiskCombo.setCurrentText( |
|
660 self.__data["SecurityChecker"]["WeakKeySizeEcHigh"]) |
|
661 self.ecMediumRiskCombo.setCurrentText( |
|
662 self.__data["SecurityChecker"]["WeakKeySizeEcMedium"]) |
|
663 self.typedExceptionsCheckBox.setChecked( |
|
664 self.__data["SecurityChecker"]["CheckTypedException"]) |
|
665 |
|
666 self.__cleanupData() |
|
667 |
|
668 def __prepareProgress(self): |
|
669 """ |
|
670 Private method to prepare the progress tab for the next run. |
|
671 """ |
|
672 self.progressList.clear() |
|
673 if len(self.files) > 0: |
|
674 self.checkProgress.setMaximum(len(self.files)) |
|
675 self.checkProgressLabel.setVisible(len(self.files) > 1) |
|
676 self.checkProgress.setVisible(len(self.files) > 1) |
|
677 if len(self.files) > 1: |
|
678 if self.__project: |
|
679 self.progressList.addItems([ |
|
680 os.path.join("...", self.__project.getRelativePath(f)) |
|
681 for f in self.files |
|
682 ]) |
|
683 else: |
|
684 self.progressList.addItems(self.files) |
|
685 |
|
686 QApplication.processEvents() |
|
687 |
|
688 def start(self, fn, save=False, repeat=None): |
|
689 """ |
|
690 Public slot to start the code style check. |
|
691 |
|
692 @param fn file or list of files or directory to be checked |
|
693 @type str or list of str |
|
694 @param save flag indicating to save the given file/file list/directory |
|
695 @type bool |
|
696 @param repeat state of the repeat check box if it is not None |
|
697 @type None or bool |
|
698 """ |
|
699 if self.__project is None: |
|
700 self.__project = ericApp().getObject("Project") |
|
701 |
|
702 self.mainWidget.setCurrentWidget(self.progressTab) |
|
703 |
|
704 self.cancelled = False |
|
705 self.buttonBox.button( |
|
706 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
|
707 self.cancelButton.setEnabled(True) |
|
708 self.cancelButton.setDefault(True) |
|
709 self.statisticsButton.setEnabled(False) |
|
710 self.showButton.setEnabled(False) |
|
711 self.fixButton.setEnabled(False) |
|
712 self.startButton.setEnabled(False) |
|
713 self.restartButton.setEnabled(False) |
|
714 if repeat is not None: |
|
715 self.repeatCheckBox.setChecked(repeat) |
|
716 self.checkProgress.setVisible(True) |
|
717 QApplication.processEvents() |
|
718 |
|
719 if save: |
|
720 self.__fileOrFileList = fn |
|
721 |
|
722 if isinstance(fn, list): |
|
723 self.files = fn[:] |
|
724 elif os.path.isdir(fn): |
|
725 self.files = [] |
|
726 extensions = set(Preferences.getPython("Python3Extensions")) |
|
727 for ext in extensions: |
|
728 self.files.extend(Utilities.direntries( |
|
729 fn, True, '*{0}'.format(ext), 0)) |
|
730 else: |
|
731 self.files = [fn] |
|
732 |
|
733 # filter the list depending on the filter string |
|
734 if self.files: |
|
735 filterString = self.excludeFilesEdit.text() |
|
736 filterList = [f.strip() for f in filterString.split(",") |
|
737 if f.strip()] |
|
738 for fileFilter in filterList: |
|
739 self.files = [ |
|
740 f for f in self.files |
|
741 if not fnmatch.fnmatch(f, fileFilter.strip()) |
|
742 ] |
|
743 |
|
744 self.__errorItem = None |
|
745 self.__resetStatistics() |
|
746 self.__clearErrors(self.files) |
|
747 self.__cleanupData() |
|
748 self.__prepareProgress() |
|
749 |
|
750 if len(self.files) > 0: |
|
751 self.securityNoteLabel.setVisible( |
|
752 "S" in self.__getCategories(True, asList=True)) |
|
753 |
|
754 # extract the configuration values |
|
755 excludeMessages = self.__assembleExcludeMessages() |
|
756 includeMessages = self.includeMessagesEdit.text() |
|
757 repeatMessages = self.repeatCheckBox.isChecked() |
|
758 fixCodes = self.fixIssuesEdit.text() |
|
759 noFixCodes = self.noFixIssuesEdit.text() |
|
760 self.__noFixCodesList = [ |
|
761 c.strip() for c in noFixCodes.split(",") if c.strip() |
|
762 ] |
|
763 fixIssues = self.fixIssuesCheckBox.isChecked() and repeatMessages |
|
764 self.showIgnored = ( |
|
765 self.ignoredCheckBox.isChecked() and repeatMessages |
|
766 ) |
|
767 maxLineLength = self.lineLengthSpinBox.value() |
|
768 maxDocLineLength = self.docLineLengthSpinBox.value() |
|
769 blankLines = ( |
|
770 self.blankBeforeTopLevelSpinBox.value(), |
|
771 self.blankBeforeMethodSpinBox.value() |
|
772 ) |
|
773 hangClosing = self.hangClosingCheckBox.isChecked() |
|
774 docType = self.docTypeComboBox.itemData( |
|
775 self.docTypeComboBox.currentIndex()) |
|
776 codeComplexityArgs = { |
|
777 "McCabeComplexity": self.complexitySpinBox.value(), |
|
778 "LineComplexity": self.lineComplexitySpinBox.value(), |
|
779 "LineComplexityScore": self.lineComplexityScoreSpinBox.value(), |
|
780 } |
|
781 miscellaneousArgs = { |
|
782 "CodingChecker": self.encodingsEdit.text(), |
|
783 "CopyrightChecker": { |
|
784 "MinFilesize": self.copyrightFileSizeSpinBox.value(), |
|
785 "Author": self.copyrightAuthorEdit.text(), |
|
786 }, |
|
787 "FutureChecker": self.__getSelectedFutureImports(), |
|
788 "BuiltinsChecker": self.__getBuiltinsIgnoreList(), |
|
789 "CommentedCodeChecker": { |
|
790 "Aggressive": self.aggressiveCheckBox.isChecked(), |
|
791 "WhiteList": self.__getCommentedCodeCheckerWhiteList(), |
|
792 } |
|
793 } |
|
794 |
|
795 annotationArgs = { |
|
796 "MinimumCoverage": |
|
797 self.minAnnotationsCoverageSpinBox.value(), |
|
798 "MaximumComplexity": |
|
799 self.maxAnnotationsComplexitySpinBox.value(), |
|
800 "MaximumLength": |
|
801 self.maxAnnotationsLengthSpinBox.value(), |
|
802 "SuppressNoneReturning": |
|
803 self.suppressNoneReturningCheckBox.isChecked(), |
|
804 "SuppressDummyArgs": |
|
805 self.suppressDummyArgsCheckBox.isChecked(), |
|
806 "AllowUntypedDefs": |
|
807 self.allowUntypedDefsCheckBox.isChecked(), |
|
808 "AllowUntypedNested": |
|
809 self.allowUntypedNestedCheckBox.isChecked(), |
|
810 "MypyInitReturn": |
|
811 self.mypyInitReturnCheckBox.isChecked(), |
|
812 "DispatchDecorators": |
|
813 [d.strip() |
|
814 for d in self.dispatchDecoratorEdit.text().split(",")], |
|
815 "OverloadDecorators": |
|
816 [d.strip() |
|
817 for d in self.overloadDecoratorEdit.text().split(",")], |
|
818 } |
|
819 |
|
820 securityArgs = { |
|
821 "hardcoded_tmp_directories": [ |
|
822 t.strip() |
|
823 for t in self.tmpDirectoriesEdit.toPlainText().splitlines() |
|
824 ], |
|
825 "insecure_hashes": [ |
|
826 h.strip() |
|
827 for h in self.hashesEdit.text().split(",") |
|
828 ], |
|
829 "insecure_ssl_protocol_versions": [ |
|
830 p.strip() |
|
831 for p in self.insecureSslProtocolsEdit.toPlainText() |
|
832 .splitlines() |
|
833 ], |
|
834 "weak_key_size_dsa_high": |
|
835 int(self.dsaHighRiskCombo.currentText()), |
|
836 "weak_key_size_dsa_medium": |
|
837 int(self.dsaMediumRiskCombo.currentText()), |
|
838 "weak_key_size_rsa_high": |
|
839 int(self.rsaHighRiskCombo.currentText()), |
|
840 "weak_key_size_rsa_medium": |
|
841 int(self.rsaMediumRiskCombo.currentText()), |
|
842 "weak_key_size_ec_high": |
|
843 int(self.ecHighRiskCombo.currentText()), |
|
844 "weak_key_size_ec_medium": |
|
845 int(self.ecMediumRiskCombo.currentText()), |
|
846 "check_typed_exception": |
|
847 self.typedExceptionsCheckBox.isChecked(), |
|
848 } |
|
849 |
|
850 importsArgs = { |
|
851 "ApplicationPackageNames": |
|
852 sorted(self.appPackagesEdit.toPlainText().split()), |
|
853 "BannedModules": |
|
854 sorted(self.bannedModulesEdit.toPlainText().split()), |
|
855 "BanRelativeImports": self.__getBanRelativeImportsValue(), |
|
856 } |
|
857 |
|
858 self.__options = [excludeMessages, includeMessages, repeatMessages, |
|
859 fixCodes, noFixCodes, fixIssues, maxLineLength, |
|
860 maxDocLineLength, blankLines, hangClosing, |
|
861 docType, codeComplexityArgs, miscellaneousArgs, |
|
862 annotationArgs, securityArgs, importsArgs] |
|
863 |
|
864 # now go through all the files |
|
865 self.progress = 0 |
|
866 self.files.sort() |
|
867 self.__timenow = time.monotonic() |
|
868 |
|
869 if len(self.files) == 1: |
|
870 self.__batch = False |
|
871 self.mainWidget.setCurrentWidget(self.resultsTab) |
|
872 self.check() |
|
873 else: |
|
874 self.__batch = True |
|
875 self.checkBatch() |
|
876 else: |
|
877 self.results = CodeStyleCheckerDialog.noFiles |
|
878 self.__finished = False |
|
879 self.__finish() |
|
880 |
|
881 def __modifyOptions(self, source): |
|
882 """ |
|
883 Private method to modify the options based on eflag: entries. |
|
884 |
|
885 This method looks for comment lines like '# eflag: noqa = M601' |
|
886 at the end of the source in order to extend the list of excluded |
|
887 messages for one file only. |
|
888 |
|
889 @param source source text |
|
890 @type list of str or str |
|
891 @return list of checker options |
|
892 @rtype list |
|
893 """ |
|
894 options = self.__options[:] |
|
895 flags = Utilities.extractFlags(source) |
|
896 if "noqa" in flags and isinstance(flags["noqa"], str): |
|
897 excludeMessages = options[0].strip().rstrip(",") |
|
898 if excludeMessages: |
|
899 excludeMessages += "," |
|
900 excludeMessages += flags["noqa"] |
|
901 options[0] = excludeMessages |
|
902 return options |
|
903 |
|
904 def check(self, codestring=''): |
|
905 """ |
|
906 Public method to start a style check for one file. |
|
907 |
|
908 The results are reported to the __processResult slot. |
|
909 |
|
910 @param codestring optional sourcestring |
|
911 @type str |
|
912 """ |
|
913 if not self.files: |
|
914 self.checkProgressLabel.setPath("") |
|
915 self.checkProgress.setMaximum(1) |
|
916 self.checkProgress.setValue(1) |
|
917 self.__finish() |
|
918 return |
|
919 |
|
920 self.filename = self.files.pop(0) |
|
921 self.checkProgress.setValue(self.progress) |
|
922 self.checkProgressLabel.setPath(self.filename) |
|
923 QApplication.processEvents() |
|
924 |
|
925 if self.cancelled: |
|
926 self.__resort() |
|
927 return |
|
928 |
|
929 self.__lastFileItem = None |
|
930 |
|
931 if codestring: |
|
932 source = codestring.splitlines(True) |
|
933 encoding = Utilities.get_coding(source) |
|
934 else: |
|
935 try: |
|
936 source, encoding = Utilities.readEncodedFile( |
|
937 self.filename) |
|
938 source = source.splitlines(True) |
|
939 except (UnicodeError, OSError) as msg: |
|
940 self.results = CodeStyleCheckerDialog.hasResults |
|
941 self.__createFileErrorItem(self.filename, str(msg)) |
|
942 self.progress += 1 |
|
943 # Continue with next file |
|
944 self.check() |
|
945 return |
|
946 if encoding.endswith( |
|
947 ('-selected', '-default', '-guessed', '-ignore')): |
|
948 encoding = encoding.rsplit('-', 1)[0] |
|
949 |
|
950 options = self.__modifyOptions(source) |
|
951 |
|
952 errors = [] |
|
953 self.__itms = [] |
|
954 for error, itm in self.__onlyFixes.pop(self.filename, []): |
|
955 errors.append(error) |
|
956 self.__itms.append(itm) |
|
957 |
|
958 eol = self.__getEol(self.filename) |
|
959 args = options + [ |
|
960 errors, eol, encoding, Preferences.getEditor("CreateBackupFile") |
|
961 ] |
|
962 self.__finished = False |
|
963 self.styleCheckService.styleCheck( |
|
964 None, self.filename, source, args) |
|
965 |
|
966 def checkBatch(self): |
|
967 """ |
|
968 Public method to start a style check batch job. |
|
969 |
|
970 The results are reported to the __processResult slot. |
|
971 """ |
|
972 self.__lastFileItem = None |
|
973 |
|
974 self.checkProgressLabel.setPath(self.tr("Preparing files...")) |
|
975 |
|
976 argumentsList = [] |
|
977 for progress, filename in enumerate(self.files, start=1): |
|
978 self.checkProgress.setValue(progress) |
|
979 if time.monotonic() - self.__timenow > 0.01: |
|
980 QApplication.processEvents() |
|
981 self.__timenow = time.monotonic() |
|
982 |
|
983 try: |
|
984 source, encoding = Utilities.readEncodedFile( |
|
985 filename) |
|
986 source = source.splitlines(True) |
|
987 except (UnicodeError, OSError) as msg: |
|
988 self.results = CodeStyleCheckerDialog.hasResults |
|
989 self.__createFileErrorItem(filename, str(msg)) |
|
990 continue |
|
991 |
|
992 if encoding.endswith( |
|
993 ('-selected', '-default', '-guessed', '-ignore')): |
|
994 encoding = encoding.rsplit('-', 1)[0] |
|
995 |
|
996 options = self.__modifyOptions(source) |
|
997 |
|
998 errors = [] |
|
999 self.__itms = [] |
|
1000 for error, itm in self.__onlyFixes.pop(filename, []): |
|
1001 errors.append(error) |
|
1002 self.__itms.append(itm) |
|
1003 |
|
1004 eol = self.__getEol(filename) |
|
1005 args = options + [ |
|
1006 errors, eol, encoding, |
|
1007 Preferences.getEditor("CreateBackupFile") |
|
1008 ] |
|
1009 argumentsList.append((filename, source, args)) |
|
1010 |
|
1011 # reset the progress bar to the checked files |
|
1012 self.checkProgress.setValue(self.progress) |
|
1013 self.checkProgressLabel.setPath(self.tr("Transferring data...")) |
|
1014 QApplication.processEvents() |
|
1015 |
|
1016 self.__finished = False |
|
1017 self.styleCheckService.styleBatchCheck(argumentsList) |
|
1018 |
|
1019 def __batchFinished(self): |
|
1020 """ |
|
1021 Private slot handling the completion of a batch job. |
|
1022 """ |
|
1023 self.checkProgressLabel.setPath("") |
|
1024 self.checkProgress.setMaximum(1) |
|
1025 self.checkProgress.setValue(1) |
|
1026 self.__finish() |
|
1027 |
|
1028 def __processError(self, fn, msg): |
|
1029 """ |
|
1030 Private slot to process an error indication from the service. |
|
1031 |
|
1032 @param fn filename of the file |
|
1033 @type str |
|
1034 @param msg error message |
|
1035 @type str |
|
1036 """ |
|
1037 self.__createErrorItem(fn, msg) |
|
1038 |
|
1039 if not self.__batch: |
|
1040 self.check() |
|
1041 |
|
1042 def __processResult(self, fn, codeStyleCheckerStats, fixes, results): |
|
1043 """ |
|
1044 Private slot called after perfoming a style check on one file. |
|
1045 |
|
1046 @param fn filename of the just checked file |
|
1047 @type str |
|
1048 @param codeStyleCheckerStats stats of style and name check |
|
1049 @type dict |
|
1050 @param fixes number of applied fixes |
|
1051 @type int |
|
1052 @param results dictionary containing check result data |
|
1053 @type dict |
|
1054 """ |
|
1055 if self.__finished: |
|
1056 return |
|
1057 |
|
1058 # Check if it's the requested file, otherwise ignore signal if not |
|
1059 # in batch mode |
|
1060 if not self.__batch and fn != self.filename: |
|
1061 return |
|
1062 |
|
1063 # disable updates of the list for speed |
|
1064 self.resultList.setUpdatesEnabled(False) |
|
1065 self.resultList.setSortingEnabled(False) |
|
1066 |
|
1067 fixed = None |
|
1068 ignoredErrors = collections.defaultdict(int) |
|
1069 securityOk = 0 |
|
1070 if self.__itms: |
|
1071 for itm, result in zip(self.__itms, results): |
|
1072 self.__modifyFixedResultItem(itm, result) |
|
1073 self.__updateFixerStatistics(fixes) |
|
1074 else: |
|
1075 self.__lastFileItem = None |
|
1076 |
|
1077 for result in results: |
|
1078 if result["ignored"]: |
|
1079 ignoredErrors[result["code"]] += 1 |
|
1080 if self.showIgnored: |
|
1081 result["display"] = self.tr( |
|
1082 "{0} (ignored)" |
|
1083 ).format(result["display"]) |
|
1084 else: |
|
1085 continue |
|
1086 |
|
1087 elif result["securityOk"]: |
|
1088 securityOk += 1 |
|
1089 if result["code"].startswith("S"): |
|
1090 continue |
|
1091 |
|
1092 self.results = CodeStyleCheckerDialog.hasResults |
|
1093 self.__createResultItem(fn, result) |
|
1094 |
|
1095 self.__updateStatistics( |
|
1096 codeStyleCheckerStats, fixes, ignoredErrors, securityOk) |
|
1097 |
|
1098 if fixed: |
|
1099 vm = ericApp().getObject("ViewManager") |
|
1100 editor = vm.getOpenEditor(fn) |
|
1101 if editor: |
|
1102 editor.refresh() |
|
1103 |
|
1104 self.progress += 1 |
|
1105 |
|
1106 self.__resort() |
|
1107 # reenable updates of the list |
|
1108 self.resultList.setSortingEnabled(True) |
|
1109 self.resultList.setUpdatesEnabled(True) |
|
1110 |
|
1111 self.__updateProgress(fn) |
|
1112 |
|
1113 if not self.__batch: |
|
1114 self.check() |
|
1115 |
|
1116 def __updateProgress(self, fn): |
|
1117 """ |
|
1118 Private method to update the progress tab. |
|
1119 |
|
1120 @param fn filename of the just checked file |
|
1121 @type str |
|
1122 """ |
|
1123 if self.__project: |
|
1124 fn = os.path.join("...", self.__project.getRelativePath(fn)) |
|
1125 |
|
1126 self.checkProgress.setValue(self.progress) |
|
1127 self.checkProgressLabel.setPath(fn) |
|
1128 |
|
1129 # remove file from the list of jobs to do |
|
1130 fileItems = self.progressList.findItems(fn, Qt.MatchFlag.MatchExactly) |
|
1131 if fileItems: |
|
1132 row = self.progressList.row(fileItems[0]) |
|
1133 self.progressList.takeItem(row) |
|
1134 |
|
1135 if time.monotonic() - self.__timenow > 0.01: |
|
1136 QApplication.processEvents() |
|
1137 self.__timenow = time.monotonic() |
|
1138 |
|
1139 def __finish(self): |
|
1140 """ |
|
1141 Private slot called when the code style check finished or the user |
|
1142 pressed the cancel button. |
|
1143 """ |
|
1144 if not self.__finished: |
|
1145 self.__finished = True |
|
1146 |
|
1147 self.cancelled = True |
|
1148 self.buttonBox.button( |
|
1149 QDialogButtonBox.StandardButton.Close).setEnabled(True) |
|
1150 self.buttonBox.button( |
|
1151 QDialogButtonBox.StandardButton.Close).setDefault(True) |
|
1152 self.cancelButton.setEnabled(False) |
|
1153 self.statisticsButton.setEnabled(True) |
|
1154 self.showButton.setEnabled(True) |
|
1155 self.startButton.setEnabled(True) |
|
1156 self.restartButton.setEnabled(True) |
|
1157 |
|
1158 if self.results != CodeStyleCheckerDialog.hasResults: |
|
1159 if self.results == CodeStyleCheckerDialog.noResults: |
|
1160 QTreeWidgetItem( |
|
1161 self.resultList, [self.tr('No issues found.')]) |
|
1162 else: |
|
1163 QTreeWidgetItem( |
|
1164 self.resultList, |
|
1165 [self.tr('No files found (check your ignore list).')]) |
|
1166 QApplication.processEvents() |
|
1167 self.showButton.setEnabled(False) |
|
1168 else: |
|
1169 self.showButton.setEnabled(True) |
|
1170 for col in range(self.resultList.columnCount()): |
|
1171 self.resultList.resizeColumnToContents(col) |
|
1172 self.resultList.header().setStretchLastSection(True) |
|
1173 |
|
1174 if self.__detectedCodes: |
|
1175 self.filterComboBox.addItem("") |
|
1176 self.filterComboBox.addItems(sorted(set(self.__detectedCodes))) |
|
1177 self.filterComboBox.setEnabled(True) |
|
1178 self.filterButton.setEnabled(True) |
|
1179 |
|
1180 self.checkProgress.setVisible(False) |
|
1181 self.checkProgressLabel.setVisible(False) |
|
1182 |
|
1183 self.mainWidget.setCurrentWidget(self.resultsTab) |
|
1184 |
|
1185 def __getEol(self, fn): |
|
1186 """ |
|
1187 Private method to get the applicable eol string. |
|
1188 |
|
1189 @param fn filename where to determine the line ending |
|
1190 @type str |
|
1191 @return eol string |
|
1192 @rtype str |
|
1193 """ |
|
1194 eol = ( |
|
1195 self.__project.getEolString() |
|
1196 if self.__project.isOpen() and self.__project.isProjectFile(fn) |
|
1197 else Utilities.linesep() |
|
1198 ) |
|
1199 return eol |
|
1200 |
|
1201 @pyqtSlot() |
|
1202 def on_startButton_clicked(self): |
|
1203 """ |
|
1204 Private slot to start a code style check run. |
|
1205 """ |
|
1206 self.__cleanupData() |
|
1207 |
|
1208 if self.__forProject: |
|
1209 data = { |
|
1210 "EnabledCheckerCategories": self.__getCategories(True), |
|
1211 "ExcludeFiles": self.excludeFilesEdit.text(), |
|
1212 "ExcludeMessages": self.excludeMessagesEdit.text(), |
|
1213 "IncludeMessages": self.includeMessagesEdit.text(), |
|
1214 "RepeatMessages": self.repeatCheckBox.isChecked(), |
|
1215 "FixCodes": self.fixIssuesEdit.text(), |
|
1216 "NoFixCodes": self.noFixIssuesEdit.text(), |
|
1217 "FixIssues": self.fixIssuesCheckBox.isChecked(), |
|
1218 "ShowIgnored": self.ignoredCheckBox.isChecked(), |
|
1219 "MaxLineLength": self.lineLengthSpinBox.value(), |
|
1220 "MaxDocLineLength": self.docLineLengthSpinBox.value(), |
|
1221 "BlankLines": ( |
|
1222 self.blankBeforeTopLevelSpinBox.value(), |
|
1223 self.blankBeforeMethodSpinBox.value() |
|
1224 ), |
|
1225 "HangClosing": self.hangClosingCheckBox.isChecked(), |
|
1226 "DocstringType": self.docTypeComboBox.itemData( |
|
1227 self.docTypeComboBox.currentIndex()), |
|
1228 "MaxCodeComplexity": self.complexitySpinBox.value(), |
|
1229 "LineComplexity": self.lineComplexitySpinBox.value(), |
|
1230 "LineComplexityScore": self.lineComplexityScoreSpinBox.value(), |
|
1231 "ValidEncodings": self.encodingsEdit.text(), |
|
1232 "CopyrightMinFileSize": self.copyrightFileSizeSpinBox.value(), |
|
1233 "CopyrightAuthor": self.copyrightAuthorEdit.text(), |
|
1234 "FutureChecker": self.__getSelectedFutureImports(), |
|
1235 "BuiltinsChecker": self.__getBuiltinsIgnoreList(), |
|
1236 "CommentedCodeChecker": { |
|
1237 "Aggressive": self.aggressiveCheckBox.isChecked(), |
|
1238 "WhiteList": self.__getCommentedCodeCheckerWhiteList(), |
|
1239 }, |
|
1240 "AnnotationsChecker": { |
|
1241 "MinimumCoverage": |
|
1242 self.minAnnotationsCoverageSpinBox.value(), |
|
1243 "MaximumComplexity": |
|
1244 self.maxAnnotationsComplexitySpinBox.value(), |
|
1245 "MaximumLength": |
|
1246 self.maxAnnotationsLengthSpinBox.value(), |
|
1247 "SuppressNoneReturning": |
|
1248 self.suppressNoneReturningCheckBox.isChecked(), |
|
1249 "SuppressDummyArgs": |
|
1250 self.suppressDummyArgsCheckBox.isChecked(), |
|
1251 "AllowUntypedDefs": |
|
1252 self.allowUntypedDefsCheckBox.isChecked(), |
|
1253 "AllowUntypedNested": |
|
1254 self.allowUntypedNestedCheckBox.isChecked(), |
|
1255 "MypyInitReturn": |
|
1256 self.mypyInitReturnCheckBox.isChecked(), |
|
1257 "DispatchDecorators": |
|
1258 [d.strip() |
|
1259 for d in self.dispatchDecoratorEdit.text().split(",") |
|
1260 ], |
|
1261 "OverloadDecorators": |
|
1262 [d.strip() |
|
1263 for d in self.overloadDecoratorEdit.text().split(",") |
|
1264 ], |
|
1265 }, |
|
1266 "SecurityChecker": { |
|
1267 "HardcodedTmpDirectories": [ |
|
1268 t.strip() |
|
1269 for t in self.tmpDirectoriesEdit.toPlainText() |
|
1270 .splitlines() |
|
1271 ], |
|
1272 "InsecureHashes": [ |
|
1273 h.strip() |
|
1274 for h in self.hashesEdit.text().split(",") |
|
1275 ], |
|
1276 "InsecureSslProtocolVersions": [ |
|
1277 p.strip() |
|
1278 for p in self.insecureSslProtocolsEdit.toPlainText() |
|
1279 .splitlines() |
|
1280 ], |
|
1281 "WeakKeySizeDsaHigh": |
|
1282 self.dsaHighRiskCombo.currentText(), |
|
1283 "WeakKeySizeDsaMedium": |
|
1284 self.dsaMediumRiskCombo.currentText(), |
|
1285 "WeakKeySizeRsaHigh": |
|
1286 self.rsaHighRiskCombo.currentText(), |
|
1287 "WeakKeySizeRsaMedium": |
|
1288 self.rsaMediumRiskCombo.currentText(), |
|
1289 "WeakKeySizeEcHigh": |
|
1290 self.ecHighRiskCombo.currentText(), |
|
1291 "WeakKeySizeEcMedium": |
|
1292 self.ecMediumRiskCombo.currentText(), |
|
1293 "CheckTypedException": |
|
1294 self.typedExceptionsCheckBox.isChecked(), |
|
1295 }, |
|
1296 "ImportsChecker": { |
|
1297 "ApplicationPackageNames": |
|
1298 sorted(self.appPackagesEdit.toPlainText().split()), |
|
1299 "BannedModules": |
|
1300 sorted(self.bannedModulesEdit.toPlainText().split()), |
|
1301 "BanRelativeImports": self.__getBanRelativeImportsValue(), |
|
1302 }, |
|
1303 } |
|
1304 if ( |
|
1305 json.dumps(data, sort_keys=True) != |
|
1306 json.dumps(self.__data, sort_keys=True) |
|
1307 ): |
|
1308 self.__data = data |
|
1309 self.__project.setData("CHECKERSPARMS", "Pep8Checker", |
|
1310 self.__data) |
|
1311 |
|
1312 self.resultList.clear() |
|
1313 self.results = CodeStyleCheckerDialog.noResults |
|
1314 self.cancelled = False |
|
1315 self.__detectedCodes.clear() |
|
1316 self.filterComboBox.clear() |
|
1317 self.filterComboBox.setEnabled(False) |
|
1318 self.filterButton.setEnabled(False) |
|
1319 |
|
1320 self.start(self.__fileOrFileList) |
|
1321 |
|
1322 @pyqtSlot() |
|
1323 def on_restartButton_clicked(self): |
|
1324 """ |
|
1325 Private slot to restart a code style check run. |
|
1326 """ |
|
1327 self.on_startButton_clicked() |
|
1328 |
|
1329 def __selectCodes(self, edit, categories, showFixCodes): |
|
1330 """ |
|
1331 Private method to select message codes via a selection dialog. |
|
1332 |
|
1333 @param edit reference of the line edit to be populated |
|
1334 @type QLineEdit |
|
1335 @param categories list of message categories to omit |
|
1336 @type list of str |
|
1337 @param showFixCodes flag indicating to show a list of fixable |
|
1338 issues |
|
1339 @type bool |
|
1340 """ |
|
1341 from .CodeStyleCodeSelectionDialog import CodeStyleCodeSelectionDialog |
|
1342 dlg = CodeStyleCodeSelectionDialog(edit.text(), categories, |
|
1343 showFixCodes, self) |
|
1344 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
1345 edit.setText(dlg.getSelectedCodes()) |
|
1346 |
|
1347 @pyqtSlot() |
|
1348 def on_excludeMessagesSelectButton_clicked(self): |
|
1349 """ |
|
1350 Private slot to select the message codes to be excluded via a |
|
1351 selection dialog. |
|
1352 """ |
|
1353 self.__selectCodes(self.excludeMessagesEdit, |
|
1354 self.__getCategories(False, asList=True), |
|
1355 False) |
|
1356 |
|
1357 @pyqtSlot() |
|
1358 def on_includeMessagesSelectButton_clicked(self): |
|
1359 """ |
|
1360 Private slot to select the message codes to be included via a |
|
1361 selection dialog. |
|
1362 """ |
|
1363 self.__selectCodes(self.includeMessagesEdit, |
|
1364 self.__getCategories(True, asList=True), |
|
1365 False) |
|
1366 |
|
1367 @pyqtSlot() |
|
1368 def on_fixIssuesSelectButton_clicked(self): |
|
1369 """ |
|
1370 Private slot to select the issue codes to be fixed via a |
|
1371 selection dialog. |
|
1372 """ |
|
1373 self.__selectCodes(self.fixIssuesEdit, [], True) |
|
1374 |
|
1375 @pyqtSlot() |
|
1376 def on_noFixIssuesSelectButton_clicked(self): |
|
1377 """ |
|
1378 Private slot to select the issue codes not to be fixed via a |
|
1379 selection dialog. |
|
1380 """ |
|
1381 self.__selectCodes(self.noFixIssuesEdit, [], True) |
|
1382 |
|
1383 @pyqtSlot(QTreeWidgetItem, int) |
|
1384 def on_resultList_itemActivated(self, item, column): |
|
1385 """ |
|
1386 Private slot to handle the activation of an item. |
|
1387 |
|
1388 @param item reference to the activated item |
|
1389 @type QTreeWidgetItem |
|
1390 @param column column the item was activated in |
|
1391 @type int |
|
1392 """ |
|
1393 if self.results != CodeStyleCheckerDialog.hasResults: |
|
1394 return |
|
1395 |
|
1396 if item.parent(): |
|
1397 fn = os.path.abspath(item.data(0, self.filenameRole)) |
|
1398 lineno = item.data(0, self.lineRole) |
|
1399 position = item.data(0, self.positionRole) |
|
1400 message = item.data(0, self.messageRole) |
|
1401 code = item.data(0, self.codeRole) |
|
1402 |
|
1403 vm = ericApp().getObject("ViewManager") |
|
1404 vm.openSourceFile(fn, lineno=lineno, pos=position + 1) |
|
1405 editor = vm.getOpenEditor(fn) |
|
1406 |
|
1407 if code in ["E901", "E902"]: |
|
1408 editor.toggleSyntaxError(lineno, 0, True, message, True) |
|
1409 else: |
|
1410 editor.toggleWarning( |
|
1411 lineno, 0, True, message, warningType=editor.WarningStyle) |
|
1412 |
|
1413 editor.updateVerticalScrollBar() |
|
1414 |
|
1415 @pyqtSlot() |
|
1416 def on_resultList_itemSelectionChanged(self): |
|
1417 """ |
|
1418 Private slot to change the dialog state depending on the selection. |
|
1419 """ |
|
1420 self.fixButton.setEnabled(len(self.__getSelectedFixableItems()) > 0) |
|
1421 |
|
1422 @pyqtSlot() |
|
1423 def on_showButton_clicked(self): |
|
1424 """ |
|
1425 Private slot to handle the "Show" button press. |
|
1426 """ |
|
1427 vm = ericApp().getObject("ViewManager") |
|
1428 |
|
1429 selectedIndexes = [] |
|
1430 for index in range(self.resultList.topLevelItemCount()): |
|
1431 if self.resultList.topLevelItem(index).isSelected(): |
|
1432 selectedIndexes.append(index) |
|
1433 if len(selectedIndexes) == 0: |
|
1434 selectedIndexes = list(range(self.resultList.topLevelItemCount())) |
|
1435 for index in selectedIndexes: |
|
1436 itm = self.resultList.topLevelItem(index) |
|
1437 fn = os.path.abspath(itm.data(0, self.filenameRole)) |
|
1438 vm.openSourceFile(fn, 1) |
|
1439 editor = vm.getOpenEditor(fn) |
|
1440 editor.clearStyleWarnings() |
|
1441 for cindex in range(itm.childCount()): |
|
1442 citm = itm.child(cindex) |
|
1443 lineno = citm.data(0, self.lineRole) |
|
1444 message = citm.data(0, self.messageRole) |
|
1445 editor.toggleWarning( |
|
1446 lineno, 0, True, message, warningType=editor.WarningStyle) |
|
1447 |
|
1448 # go through the list again to clear warning markers for files, |
|
1449 # that are ok |
|
1450 openFiles = vm.getOpenFilenames() |
|
1451 errorFiles = [] |
|
1452 for index in range(self.resultList.topLevelItemCount()): |
|
1453 itm = self.resultList.topLevelItem(index) |
|
1454 errorFiles.append( |
|
1455 os.path.abspath(itm.data(0, self.filenameRole))) |
|
1456 for file in openFiles: |
|
1457 if file not in errorFiles: |
|
1458 editor = vm.getOpenEditor(file) |
|
1459 editor.clearStyleWarnings() |
|
1460 |
|
1461 editor = vm.activeWindow() |
|
1462 editor.updateVerticalScrollBar() |
|
1463 |
|
1464 @pyqtSlot() |
|
1465 def on_statisticsButton_clicked(self): |
|
1466 """ |
|
1467 Private slot to show the statistics dialog. |
|
1468 """ |
|
1469 from .CodeStyleStatisticsDialog import CodeStyleStatisticsDialog |
|
1470 dlg = CodeStyleStatisticsDialog(self.__statistics, self) |
|
1471 dlg.exec() |
|
1472 |
|
1473 @pyqtSlot() |
|
1474 def on_loadDefaultButton_clicked(self): |
|
1475 """ |
|
1476 Private slot to load the default configuration values. |
|
1477 """ |
|
1478 self.__initCategoriesList(Preferences.getSettings().value( |
|
1479 "PEP8/EnabledCheckerCategories", |
|
1480 ",".join(CodeStyleCheckerDialog.checkCategories.keys()))) |
|
1481 self.excludeFilesEdit.setText(Preferences.getSettings().value( |
|
1482 "PEP8/ExcludeFilePatterns", "")) |
|
1483 self.excludeMessagesEdit.setText(Preferences.getSettings().value( |
|
1484 "PEP8/ExcludeMessages", pycodestyle.DEFAULT_IGNORE)) |
|
1485 self.includeMessagesEdit.setText(Preferences.getSettings().value( |
|
1486 "PEP8/IncludeMessages", "")) |
|
1487 self.repeatCheckBox.setChecked(Preferences.toBool( |
|
1488 Preferences.getSettings().value("PEP8/RepeatMessages", False))) |
|
1489 self.fixIssuesEdit.setText(Preferences.getSettings().value( |
|
1490 "PEP8/FixCodes", "")) |
|
1491 self.noFixIssuesEdit.setText(Preferences.getSettings().value( |
|
1492 "PEP8/NoFixCodes", "E501")) |
|
1493 self.fixIssuesCheckBox.setChecked(Preferences.toBool( |
|
1494 Preferences.getSettings().value("PEP8/FixIssues", False))) |
|
1495 self.ignoredCheckBox.setChecked(Preferences.toBool( |
|
1496 Preferences.getSettings().value("PEP8/ShowIgnored", False))) |
|
1497 self.lineLengthSpinBox.setValue(int( |
|
1498 Preferences.getSettings().value("PEP8/MaxLineLength", 88))) |
|
1499 self.docLineLengthSpinBox.setValue(int( |
|
1500 Preferences.getSettings().value("PEP8/MaxDocLineLength", 88))) |
|
1501 self.blankBeforeTopLevelSpinBox.setValue( |
|
1502 int(Preferences.getSettings().value( |
|
1503 "PEP8/BlankLinesBeforeTopLevel", 2))) |
|
1504 self.blankBeforeMethodSpinBox.setValue( |
|
1505 int(Preferences.getSettings().value( |
|
1506 "PEP8/BlankLinesBeforeMethod", 1))) |
|
1507 self.hangClosingCheckBox.setChecked(Preferences.toBool( |
|
1508 Preferences.getSettings().value("PEP8/HangClosing", False))) |
|
1509 self.docTypeComboBox.setCurrentIndex(self.docTypeComboBox.findData( |
|
1510 Preferences.getSettings().value("PEP8/DocstringType", "pep257"))) |
|
1511 self.complexitySpinBox.setValue(int(Preferences.getSettings().value( |
|
1512 "PEP8/MaxCodeComplexity", 10))) |
|
1513 self.lineComplexitySpinBox.setValue( |
|
1514 int(Preferences.getSettings().value( |
|
1515 "PEP8/LineComplexity", 15))) |
|
1516 self.lineComplexityScoreSpinBox.setValue( |
|
1517 int(Preferences.getSettings().value( |
|
1518 "PEP8/LineComplexityScore", 10))) |
|
1519 self.encodingsEdit.setText(Preferences.getSettings().value( |
|
1520 "PEP8/ValidEncodings", |
|
1521 MiscellaneousCheckerDefaultArgs["CodingChecker"] |
|
1522 )) |
|
1523 self.copyrightFileSizeSpinBox.setValue(int( |
|
1524 Preferences.getSettings().value( |
|
1525 "PEP8/CopyrightMinFileSize", |
|
1526 MiscellaneousCheckerDefaultArgs[ |
|
1527 "CopyrightChecker"]["MinFilesize"] |
|
1528 ) |
|
1529 )) |
|
1530 self.copyrightAuthorEdit.setText( |
|
1531 Preferences.getSettings().value( |
|
1532 "PEP8/CopyrightAuthor", |
|
1533 MiscellaneousCheckerDefaultArgs["CopyrightChecker"]["Author"] |
|
1534 ) |
|
1535 ) |
|
1536 self.__initFuturesList( |
|
1537 Preferences.getSettings().value("PEP8/FutureChecker", "")) |
|
1538 self.__initBuiltinsIgnoreList(Preferences.toDict( |
|
1539 Preferences.getSettings().value( |
|
1540 "PEP8/BuiltinsChecker", |
|
1541 MiscellaneousCheckerDefaultArgs["BuiltinsChecker"] |
|
1542 )) |
|
1543 ) |
|
1544 self.aggressiveCheckBox.setChecked(Preferences.toBool( |
|
1545 Preferences.getSettings().value( |
|
1546 "PEP8/AggressiveSearch", |
|
1547 MiscellaneousCheckerDefaultArgs[ |
|
1548 "CommentedCodeChecker"]["Aggressive"] |
|
1549 ))) |
|
1550 self.__initCommentedCodeCheckerWhiteList(Preferences.toList( |
|
1551 Preferences.getSettings().value( |
|
1552 "PEP8/CommentedCodeWhitelist", |
|
1553 MiscellaneousCheckerDefaultArgs[ |
|
1554 "CommentedCodeChecker"]["WhiteList"] |
|
1555 ) |
|
1556 )) |
|
1557 |
|
1558 # Type Annotations Checker |
|
1559 self.minAnnotationsCoverageSpinBox.setValue(int( |
|
1560 Preferences.getSettings().value( |
|
1561 "PEP8/MinimumAnnotationsCoverage", |
|
1562 AnnotationsCheckerDefaultArgs["MinimumCoverage"]))) |
|
1563 self.maxAnnotationsComplexitySpinBox.setValue(int( |
|
1564 Preferences.getSettings().value( |
|
1565 "PEP8/MaximumAnnotationComplexity", |
|
1566 AnnotationsCheckerDefaultArgs["MaximumComplexity"]))) |
|
1567 self.maxAnnotationsLengthSpinBox.setValue(int( |
|
1568 Preferences.getSettings().value( |
|
1569 "PEP8/MaximumAnnotationLength", |
|
1570 AnnotationsCheckerDefaultArgs["MaximumLength"]))) |
|
1571 self.suppressNoneReturningCheckBox.setChecked(Preferences.toBool( |
|
1572 Preferences.getSettings().value( |
|
1573 "PEP8/SuppressNoneReturning", |
|
1574 AnnotationsCheckerDefaultArgs["SuppressNoneReturning"]))) |
|
1575 self.suppressDummyArgsCheckBox.setChecked(Preferences.toBool( |
|
1576 Preferences.getSettings().value( |
|
1577 "PEP8/SuppressDummyArgs", |
|
1578 AnnotationsCheckerDefaultArgs["SuppressDummyArgs"]))) |
|
1579 self.allowUntypedDefsCheckBox.setChecked(Preferences.toBool( |
|
1580 Preferences.getSettings().value( |
|
1581 "PEP8/AllowUntypedDefs", |
|
1582 AnnotationsCheckerDefaultArgs["AllowUntypedDefs"]))) |
|
1583 self.allowUntypedNestedCheckBox.setChecked(Preferences.toBool( |
|
1584 Preferences.getSettings().value( |
|
1585 "PEP8/AllowUntypedNested", |
|
1586 AnnotationsCheckerDefaultArgs["AllowUntypedNested"]))) |
|
1587 self.mypyInitReturnCheckBox.setChecked(Preferences.toBool( |
|
1588 Preferences.getSettings().value( |
|
1589 "PEP8/MypyInitReturn", |
|
1590 AnnotationsCheckerDefaultArgs["MypyInitReturn"]))) |
|
1591 self.dispatchDecoratorEdit.setText(", ".join(Preferences.toList( |
|
1592 Preferences.getSettings().value( |
|
1593 "PEP8/DispatchDecorators", |
|
1594 AnnotationsCheckerDefaultArgs["DispatchDecorators"])))) |
|
1595 self.overloadDecoratorEdit.setText(", ".join(Preferences.toList( |
|
1596 Preferences.getSettings().value( |
|
1597 "PEP8/OverloadDecorators", |
|
1598 AnnotationsCheckerDefaultArgs["OverloadDecorators"])))) |
|
1599 |
|
1600 # Security Checker |
|
1601 from .Security.SecurityDefaults import SecurityDefaults |
|
1602 self.tmpDirectoriesEdit.setPlainText("\n".join( |
|
1603 Preferences.toList(Preferences.getSettings().value( |
|
1604 "PEP8/HardcodedTmpDirectories", |
|
1605 SecurityDefaults["hardcoded_tmp_directories"])))) |
|
1606 self.hashesEdit.setText(", ".join( |
|
1607 Preferences.toList(Preferences.getSettings().value( |
|
1608 "PEP8/InsecureHashes", |
|
1609 SecurityDefaults["insecure_hashes"])))), |
|
1610 self.insecureSslProtocolsEdit.setPlainText("\n".join( |
|
1611 Preferences.toList(Preferences.getSettings().value( |
|
1612 "PEP8/InsecureSslProtocolVersions", |
|
1613 SecurityDefaults["insecure_ssl_protocol_versions"])))), |
|
1614 self.dsaHighRiskCombo.setCurrentText( |
|
1615 Preferences.getSettings().value( |
|
1616 "PEP8/WeakKeySizeDsaHigh", |
|
1617 str(SecurityDefaults["weak_key_size_dsa_high"]))) |
|
1618 self.dsaMediumRiskCombo.setCurrentText( |
|
1619 Preferences.getSettings().value( |
|
1620 "PEP8/WeakKeySizeDsaMedium", |
|
1621 str(SecurityDefaults["weak_key_size_dsa_medium"]))), |
|
1622 self.rsaHighRiskCombo.setCurrentText( |
|
1623 Preferences.getSettings().value( |
|
1624 "PEP8/WeakKeySizeRsaHigh", |
|
1625 str(SecurityDefaults["weak_key_size_rsa_high"]))), |
|
1626 self.rsaMediumRiskCombo.setCurrentText( |
|
1627 Preferences.getSettings().value( |
|
1628 "PEP8/WeakKeySizeRsaMedium", |
|
1629 str(SecurityDefaults["weak_key_size_rsa_medium"]))), |
|
1630 self.ecHighRiskCombo.setCurrentText( |
|
1631 Preferences.getSettings().value( |
|
1632 "PEP8/WeakKeySizeEcHigh", |
|
1633 str(SecurityDefaults["weak_key_size_ec_high"]))), |
|
1634 self.ecMediumRiskCombo.setCurrentText( |
|
1635 Preferences.getSettings().value( |
|
1636 "PEP8/WeakKeySizeEcMedium", |
|
1637 str(SecurityDefaults["weak_key_size_ec_medium"]))), |
|
1638 self.typedExceptionsCheckBox.setChecked(Preferences.toBool( |
|
1639 Preferences.getSettings().value( |
|
1640 "PEP8/CheckTypedException", |
|
1641 SecurityDefaults["check_typed_exception"]))), |
|
1642 |
|
1643 # Imports Checker |
|
1644 self.appPackagesEdit.setPlainText(" ".join( |
|
1645 sorted(Preferences.toList(Preferences.getSettings().value( |
|
1646 "PEP8/ApplicationPackageNames", []))))) |
|
1647 self.bannedModulesEdit.setPlainText(" ".join( |
|
1648 sorted(Preferences.toList(Preferences.getSettings().value( |
|
1649 "PEP8/BannedModules", []))))) |
|
1650 self.__setBanRelativeImports( |
|
1651 Preferences.getSettings().value( |
|
1652 "PEP8/BanRelativeImports", "")) |
|
1653 |
|
1654 self.__cleanupData() |
|
1655 |
|
1656 @pyqtSlot() |
|
1657 def on_storeDefaultButton_clicked(self): |
|
1658 """ |
|
1659 Private slot to store the current configuration values as |
|
1660 default values. |
|
1661 """ |
|
1662 Preferences.getSettings().setValue( |
|
1663 "PEP8/EnabledCheckerCategories", self.__getCategories(True)) |
|
1664 Preferences.getSettings().setValue( |
|
1665 "PEP8/ExcludeFilePatterns", self.excludeFilesEdit.text()) |
|
1666 Preferences.getSettings().setValue( |
|
1667 "PEP8/ExcludeMessages", self.excludeMessagesEdit.text()) |
|
1668 Preferences.getSettings().setValue( |
|
1669 "PEP8/IncludeMessages", self.includeMessagesEdit.text()) |
|
1670 Preferences.getSettings().setValue( |
|
1671 "PEP8/RepeatMessages", self.repeatCheckBox.isChecked()) |
|
1672 Preferences.getSettings().setValue( |
|
1673 "PEP8/FixCodes", self.fixIssuesEdit.text()) |
|
1674 Preferences.getSettings().setValue( |
|
1675 "PEP8/NoFixCodes", self.noFixIssuesEdit.text()) |
|
1676 Preferences.getSettings().setValue( |
|
1677 "PEP8/FixIssues", self.fixIssuesCheckBox.isChecked()) |
|
1678 Preferences.getSettings().setValue( |
|
1679 "PEP8/ShowIgnored", self.ignoredCheckBox.isChecked()) |
|
1680 Preferences.getSettings().setValue( |
|
1681 "PEP8/MaxLineLength", self.lineLengthSpinBox.value()) |
|
1682 Preferences.getSettings().setValue( |
|
1683 "PEP8/MaxDocLineLength", self.docLineLengthSpinBox.value()) |
|
1684 Preferences.getSettings().setValue( |
|
1685 "PEP8/BlankLinesBeforeTopLevel", |
|
1686 self.blankBeforeTopLevelSpinBox.value()) |
|
1687 Preferences.getSettings().setValue( |
|
1688 "PEP8/BlankLinesBeforeMethod", |
|
1689 self.blankBeforeMethodSpinBox.value()) |
|
1690 Preferences.getSettings().setValue( |
|
1691 "PEP8/HangClosing", self.hangClosingCheckBox.isChecked()) |
|
1692 Preferences.getSettings().setValue( |
|
1693 "PEP8/DocstringType", self.docTypeComboBox.itemData( |
|
1694 self.docTypeComboBox.currentIndex())) |
|
1695 Preferences.getSettings().setValue( |
|
1696 "PEP8/MaxCodeComplexity", self.complexitySpinBox.value()) |
|
1697 Preferences.getSettings().setValue( |
|
1698 "PEP8/LineComplexity", self.lineComplexitySpinBox.value()) |
|
1699 Preferences.getSettings().setValue( |
|
1700 "PEP8/LineComplexityScore", |
|
1701 self.lineComplexityScoreSpinBox.value()) |
|
1702 Preferences.getSettings().setValue( |
|
1703 "PEP8/ValidEncodings", self.encodingsEdit.text()) |
|
1704 Preferences.getSettings().setValue( |
|
1705 "PEP8/CopyrightMinFileSize", self.copyrightFileSizeSpinBox.value()) |
|
1706 Preferences.getSettings().setValue( |
|
1707 "PEP8/CopyrightAuthor", self.copyrightAuthorEdit.text()) |
|
1708 Preferences.getSettings().setValue( |
|
1709 "PEP8/FutureChecker", self.__getSelectedFutureImports()) |
|
1710 Preferences.getSettings().setValue( |
|
1711 "PEP8/BuiltinsChecker", self.__getBuiltinsIgnoreList()) |
|
1712 Preferences.getSettings().setValue( |
|
1713 "PEP8/AggressiveSearch", self.aggressiveCheckBox.isChecked()) |
|
1714 Preferences.getSettings().setValue( |
|
1715 "PEP8/CommentedCodeWhitelist", |
|
1716 self.__getCommentedCodeCheckerWhiteList()) |
|
1717 |
|
1718 # Type Annotations Checker |
|
1719 Preferences.getSettings().setValue( |
|
1720 "PEP8/MinimumAnnotationsCoverage", |
|
1721 self.minAnnotationsCoverageSpinBox.value()) |
|
1722 Preferences.getSettings().setValue( |
|
1723 "PEP8/MaximumAnnotationComplexity", |
|
1724 self.maxAnnotationsComplexitySpinBox.value()) |
|
1725 Preferences.getSettings().setValue( |
|
1726 "PEP8/MaximumAnnotationLength", |
|
1727 self.maxAnnotationsLengthSpinBox.value()) |
|
1728 Preferences.getSettings().setValue( |
|
1729 "PEP8/SuppressNoneReturning", |
|
1730 self.suppressNoneReturningCheckBox.isChecked()) |
|
1731 Preferences.getSettings().setValue( |
|
1732 "PEP8/SuppressDummyArgs", |
|
1733 self.suppressDummyArgsCheckBox.isChecked()) |
|
1734 Preferences.getSettings().setValue( |
|
1735 "PEP8/AllowUntypedDefs", |
|
1736 self.allowUntypedDefsCheckBox.isChecked()) |
|
1737 Preferences.getSettings().setValue( |
|
1738 "PEP8/AllowUntypedNested", |
|
1739 self.allowUntypedNestedCheckBox.isChecked()) |
|
1740 Preferences.getSettings().setValue( |
|
1741 "PEP8/MypyInitReturn", |
|
1742 self.mypyInitReturnCheckBox.isChecked()) |
|
1743 Preferences.getSettings().setValue( |
|
1744 "PEP8/DispatchDecorators", |
|
1745 [d.strip() |
|
1746 for d in self.dispatchDecoratorEdit.text().split(",")]) |
|
1747 Preferences.getSettings().setValue( |
|
1748 "PEP8/OverloadDecorators", |
|
1749 [d.strip() |
|
1750 for d in self.overloadDecoratorEdit.text().split(",")]) |
|
1751 |
|
1752 # Security Checker |
|
1753 Preferences.getSettings().setValue( |
|
1754 "PEP8/HardcodedTmpDirectories", |
|
1755 [t.strip() |
|
1756 for t in self.tmpDirectoriesEdit.toPlainText().splitlines() |
|
1757 ]), |
|
1758 Preferences.getSettings().setValue( |
|
1759 "PEP8/InsecureHashes", |
|
1760 [h.strip() |
|
1761 for h in self.hashesEdit.text().split(",") |
|
1762 ]), |
|
1763 Preferences.getSettings().setValue( |
|
1764 "PEP8/InsecureSslProtocolVersions", |
|
1765 [p.strip() |
|
1766 for p in self.insecureSslProtocolsEdit.toPlainText().splitlines() |
|
1767 ]), |
|
1768 Preferences.getSettings().setValue( |
|
1769 "PEP8/WeakKeySizeDsaHigh", |
|
1770 self.dsaHighRiskCombo.currentText()), |
|
1771 Preferences.getSettings().setValue( |
|
1772 "PEP8/WeakKeySizeDsaMedium", |
|
1773 self.dsaMediumRiskCombo.currentText()), |
|
1774 Preferences.getSettings().setValue( |
|
1775 "PEP8/WeakKeySizeRsaHigh", |
|
1776 self.rsaHighRiskCombo.currentText()), |
|
1777 Preferences.getSettings().setValue( |
|
1778 "PEP8/WeakKeySizeRsaMedium", |
|
1779 self.rsaMediumRiskCombo.currentText()), |
|
1780 Preferences.getSettings().setValue( |
|
1781 "PEP8/WeakKeySizeEcHigh", |
|
1782 self.ecHighRiskCombo.currentText()), |
|
1783 Preferences.getSettings().setValue( |
|
1784 "PEP8/WeakKeySizeEcMedium", |
|
1785 self.ecMediumRiskCombo.currentText()), |
|
1786 Preferences.getSettings().setValue( |
|
1787 "PEP8/CheckTypedException", |
|
1788 self.typedExceptionsCheckBox.isChecked()), |
|
1789 |
|
1790 # Imports Checker |
|
1791 Preferences.getSettings().setValue( |
|
1792 "PEP8/ApplicationPackageNames", |
|
1793 sorted(self.appPackagesEdit.toPlainText().split())) |
|
1794 Preferences.getSettings().setValue( |
|
1795 "PEP8/BannedModules", |
|
1796 sorted(self.bannedModulesEdit.toPlainText().split())) |
|
1797 Preferences.getSettings().setValue( |
|
1798 "PEP8/BanRelativeImports", |
|
1799 self.__getBanRelativeImportsValue()) |
|
1800 |
|
1801 @pyqtSlot() |
|
1802 def on_resetDefaultButton_clicked(self): |
|
1803 """ |
|
1804 Private slot to reset the configuration values to their default values. |
|
1805 """ |
|
1806 Preferences.getSettings().setValue( |
|
1807 "PEP8/EnabledCheckerCategories", |
|
1808 ",".join(CodeStyleCheckerDialog.checkCategories.keys())) |
|
1809 Preferences.getSettings().setValue("PEP8/ExcludeFilePatterns", "") |
|
1810 Preferences.getSettings().setValue( |
|
1811 "PEP8/ExcludeMessages", pycodestyle.DEFAULT_IGNORE) |
|
1812 Preferences.getSettings().setValue("PEP8/IncludeMessages", "") |
|
1813 Preferences.getSettings().setValue("PEP8/RepeatMessages", False) |
|
1814 Preferences.getSettings().setValue("PEP8/FixCodes", "") |
|
1815 Preferences.getSettings().setValue("PEP8/NoFixCodes", "E501") |
|
1816 Preferences.getSettings().setValue("PEP8/FixIssues", False) |
|
1817 Preferences.getSettings().setValue("PEP8/ShowIgnored", False) |
|
1818 Preferences.getSettings().setValue("PEP8/MaxLineLength", 88) |
|
1819 Preferences.getSettings().setValue("PEP8/MaxDocLineLength", 88) |
|
1820 Preferences.getSettings().setValue("PEP8/BlankLinesBeforeTopLevel", 2) |
|
1821 Preferences.getSettings().setValue("PEP8/BlankLinesBeforeMethod", 1) |
|
1822 Preferences.getSettings().setValue("PEP8/HangClosing", False) |
|
1823 Preferences.getSettings().setValue("PEP8/DocstringType", "pep257") |
|
1824 Preferences.getSettings().setValue("PEP8/MaxCodeComplexity", 10) |
|
1825 Preferences.getSettings().setValue("PEP8/LineComplexity", 15) |
|
1826 Preferences.getSettings().setValue("PEP8/LineComplexityScore", 10) |
|
1827 Preferences.getSettings().setValue( |
|
1828 "PEP8/ValidEncodings", |
|
1829 MiscellaneousCheckerDefaultArgs["CodingChecker"] |
|
1830 ) |
|
1831 Preferences.getSettings().setValue( |
|
1832 "PEP8/CopyrightMinFileSize", |
|
1833 MiscellaneousCheckerDefaultArgs["CopyrightChecker"]["MinFilesize"] |
|
1834 ) |
|
1835 Preferences.getSettings().setValue( |
|
1836 "PEP8/CopyrightAuthor", |
|
1837 MiscellaneousCheckerDefaultArgs["CopyrightChecker"]["Author"] |
|
1838 ) |
|
1839 Preferences.getSettings().setValue("PEP8/FutureChecker", "") |
|
1840 Preferences.getSettings().setValue( |
|
1841 "PEP8/BuiltinsChecker", |
|
1842 MiscellaneousCheckerDefaultArgs["BuiltinsChecker"] |
|
1843 ) |
|
1844 Preferences.getSettings().setValue( |
|
1845 "PEP8/AggressiveSearch", |
|
1846 MiscellaneousCheckerDefaultArgs[ |
|
1847 "CommentedCodeChecker"]["Aggressive"] |
|
1848 ) |
|
1849 Preferences.getSettings().setValue( |
|
1850 "PEP8/CommentedCodeWhitelist", |
|
1851 MiscellaneousCheckerDefaultArgs[ |
|
1852 "CommentedCodeChecker"]["WhiteList"] |
|
1853 ) |
|
1854 |
|
1855 # Type Annotations Checker |
|
1856 Preferences.getSettings().setValue( |
|
1857 "PEP8/MinimumAnnotationsCoverage", |
|
1858 AnnotationsCheckerDefaultArgs["MinimumCoverage"]) |
|
1859 Preferences.getSettings().setValue( |
|
1860 "PEP8/MaximumAnnotationComplexity", |
|
1861 AnnotationsCheckerDefaultArgs["MaximumComplexity"]) |
|
1862 Preferences.getSettings().setValue( |
|
1863 "PEP8/MaximumAnnotationLength", |
|
1864 AnnotationsCheckerDefaultArgs["MaximumLength"]) |
|
1865 Preferences.getSettings().setValue( |
|
1866 "PEP8/SuppressNoneReturning", |
|
1867 AnnotationsCheckerDefaultArgs["SuppressNoneReturning"]) |
|
1868 Preferences.getSettings().setValue( |
|
1869 "PEP8/SuppressDummyArgs", |
|
1870 AnnotationsCheckerDefaultArgs["SuppressDummyArgs"]) |
|
1871 Preferences.getSettings().setValue( |
|
1872 "PEP8/AllowUntypedDefs", |
|
1873 AnnotationsCheckerDefaultArgs["AllowUntypedDefs"]) |
|
1874 Preferences.getSettings().setValue( |
|
1875 "PEP8/AllowUntypedNested", |
|
1876 AnnotationsCheckerDefaultArgs["AllowUntypedNested"]) |
|
1877 Preferences.getSettings().setValue( |
|
1878 "PEP8/MypyInitReturn", |
|
1879 AnnotationsCheckerDefaultArgs["MypyInitReturn"]) |
|
1880 Preferences.getSettings().setValue( |
|
1881 "PEP8/DispatchDecorators", |
|
1882 AnnotationsCheckerDefaultArgs["DispatchDecorators"]) |
|
1883 Preferences.getSettings().setValue( |
|
1884 "PEP8/OverloadDecorators", |
|
1885 AnnotationsCheckerDefaultArgs["OverloadDecorators"]) |
|
1886 |
|
1887 # Security Checker |
|
1888 from .Security.SecurityDefaults import SecurityDefaults |
|
1889 Preferences.getSettings().setValue( |
|
1890 "PEP8/HardcodedTmpDirectories", |
|
1891 SecurityDefaults["hardcoded_tmp_directories"]) |
|
1892 Preferences.getSettings().setValue( |
|
1893 "PEP8/InsecureHashes", |
|
1894 SecurityDefaults["insecure_hashes"]) |
|
1895 Preferences.getSettings().setValue( |
|
1896 "PEP8/InsecureSslProtocolVersions", |
|
1897 SecurityDefaults["insecure_ssl_protocol_versions"]) |
|
1898 Preferences.getSettings().setValue( |
|
1899 "PEP8/WeakKeySizeDsaHigh", |
|
1900 str(SecurityDefaults["weak_key_size_dsa_high"])) |
|
1901 Preferences.getSettings().setValue( |
|
1902 "PEP8/WeakKeySizeDsaMedium", |
|
1903 str(SecurityDefaults["weak_key_size_dsa_medium"])) |
|
1904 Preferences.getSettings().setValue( |
|
1905 "PEP8/WeakKeySizeRsaHigh", |
|
1906 str(SecurityDefaults["weak_key_size_rsa_high"])) |
|
1907 Preferences.getSettings().setValue( |
|
1908 "PEP8/WeakKeySizeRsaMedium", |
|
1909 str(SecurityDefaults["weak_key_size_rsa_medium"])) |
|
1910 Preferences.getSettings().setValue( |
|
1911 "PEP8/WeakKeySizeEcHigh", |
|
1912 str(SecurityDefaults["weak_key_size_ec_high"])) |
|
1913 Preferences.getSettings().setValue( |
|
1914 "PEP8/WeakKeySizeEcMedium", |
|
1915 str(SecurityDefaults["weak_key_size_ec_medium"])) |
|
1916 Preferences.getSettings().setValue( |
|
1917 "PEP8/CheckTypedException", |
|
1918 SecurityDefaults["check_typed_exception"]) |
|
1919 |
|
1920 # Imports Checker |
|
1921 Preferences.getSettings().setValue( |
|
1922 "PEP8/ApplicationPackageNames", []) |
|
1923 Preferences.getSettings().setValue( |
|
1924 "PEP8/BannedModules", []) |
|
1925 Preferences.getSettings().setValue( |
|
1926 "PEP8/BanRelativeImports", "") |
|
1927 |
|
1928 # Update UI with default values |
|
1929 self.on_loadDefaultButton_clicked() |
|
1930 |
|
1931 @pyqtSlot() |
|
1932 def on_cancelButton_clicked(self): |
|
1933 """ |
|
1934 Private slot to handle the "Cancel" button press. |
|
1935 """ |
|
1936 if self.__batch: |
|
1937 self.styleCheckService.cancelStyleBatchCheck() |
|
1938 QTimer.singleShot(1000, self.__finish) |
|
1939 else: |
|
1940 self.__finish() |
|
1941 |
|
1942 @pyqtSlot(QAbstractButton) |
|
1943 def on_buttonBox_clicked(self, button): |
|
1944 """ |
|
1945 Private slot called by a button of the button box clicked. |
|
1946 |
|
1947 @param button button that was clicked |
|
1948 @type QAbstractButton |
|
1949 """ |
|
1950 if button == self.buttonBox.button( |
|
1951 QDialogButtonBox.StandardButton.Close |
|
1952 ): |
|
1953 self.close() |
|
1954 |
|
1955 def __clearErrors(self, files): |
|
1956 """ |
|
1957 Private method to clear all warning markers of open editors to be |
|
1958 checked. |
|
1959 |
|
1960 @param files list of files to be checked |
|
1961 @type list of str |
|
1962 """ |
|
1963 vm = ericApp().getObject("ViewManager") |
|
1964 openFiles = vm.getOpenFilenames() |
|
1965 for file in [f for f in openFiles if f in files]: |
|
1966 editor = vm.getOpenEditor(file) |
|
1967 editor.clearStyleWarnings() |
|
1968 |
|
1969 @pyqtSlot() |
|
1970 def on_fixButton_clicked(self): |
|
1971 """ |
|
1972 Private slot to fix selected issues. |
|
1973 |
|
1974 Build a dictionary of issues to fix. Update the initialized __options. |
|
1975 Then call check with the dict as keyparam to fix selected issues. |
|
1976 """ |
|
1977 fixableItems = self.__getSelectedFixableItems() |
|
1978 # dictionary of lists of tuples containing the issue and the item |
|
1979 fixesDict = {} |
|
1980 for itm in fixableItems: |
|
1981 filename = itm.data(0, self.filenameRole) |
|
1982 if filename not in fixesDict: |
|
1983 fixesDict[filename] = [] |
|
1984 fixesDict[filename].append(( |
|
1985 { |
|
1986 "file": filename, |
|
1987 "line": itm.data(0, self.lineRole), |
|
1988 "offset": itm.data(0, self.positionRole), |
|
1989 "code": itm.data(0, self.codeRole), |
|
1990 "display": itm.data(0, self.messageRole), |
|
1991 "args": itm.data(0, self.argsRole), |
|
1992 }, |
|
1993 itm |
|
1994 )) |
|
1995 |
|
1996 # update the configuration values (3: fixCodes, 4: noFixCodes, |
|
1997 # 5: fixIssues, 6: maxLineLength) |
|
1998 self.__options[3] = self.fixIssuesEdit.text() |
|
1999 self.__options[4] = self.noFixIssuesEdit.text() |
|
2000 self.__options[5] = True |
|
2001 self.__options[6] = self.lineLengthSpinBox.value() |
|
2002 |
|
2003 self.files = list(fixesDict.keys()) |
|
2004 # now go through all the files |
|
2005 self.progress = 0 |
|
2006 self.files.sort() |
|
2007 self.cancelled = False |
|
2008 self.__onlyFixes = fixesDict |
|
2009 self.check() |
|
2010 |
|
2011 def __getSelectedFixableItems(self): |
|
2012 """ |
|
2013 Private method to extract all selected items for fixable issues. |
|
2014 |
|
2015 @return selected items for fixable issues |
|
2016 @rtype list of QTreeWidgetItem |
|
2017 """ |
|
2018 fixableItems = [] |
|
2019 for itm in self.resultList.selectedItems(): |
|
2020 if itm.childCount() > 0: |
|
2021 for index in range(itm.childCount()): |
|
2022 citm = itm.child(index) |
|
2023 if self.__itemFixable(citm) and citm not in fixableItems: |
|
2024 fixableItems.append(citm) |
|
2025 elif self.__itemFixable(itm) and itm not in fixableItems: |
|
2026 fixableItems.append(itm) |
|
2027 |
|
2028 return fixableItems |
|
2029 |
|
2030 def __itemFixable(self, itm): |
|
2031 """ |
|
2032 Private method to check, if an item has a fixable issue. |
|
2033 |
|
2034 @param itm item to be checked |
|
2035 @type QTreeWidgetItem |
|
2036 @return flag indicating a fixable issue |
|
2037 @rtype bool |
|
2038 """ |
|
2039 return (itm.data(0, self.fixableRole) and |
|
2040 not itm.data(0, self.ignoredRole)) |
|
2041 |
|
2042 def __initFuturesList(self, selectedFutures): |
|
2043 """ |
|
2044 Private method to set the selected status of the future imports. |
|
2045 |
|
2046 @param selectedFutures comma separated list of expected future imports |
|
2047 @type str |
|
2048 """ |
|
2049 expectedImports = ( |
|
2050 [i.strip() for i in selectedFutures.split(",") if bool(i.strip())] |
|
2051 if selectedFutures else |
|
2052 [] |
|
2053 ) |
|
2054 for row in range(self.futuresList.count()): |
|
2055 itm = self.futuresList.item(row) |
|
2056 if itm.text() in expectedImports: |
|
2057 itm.setCheckState(Qt.CheckState.Checked) |
|
2058 else: |
|
2059 itm.setCheckState(Qt.CheckState.Unchecked) |
|
2060 |
|
2061 def __getSelectedFutureImports(self): |
|
2062 """ |
|
2063 Private method to get the expected future imports. |
|
2064 |
|
2065 @return expected future imports as a comma separated string |
|
2066 @rtype str |
|
2067 """ |
|
2068 selectedFutures = [] |
|
2069 for row in range(self.futuresList.count()): |
|
2070 itm = self.futuresList.item(row) |
|
2071 if itm.checkState() == Qt.CheckState.Checked: |
|
2072 selectedFutures.append(itm.text()) |
|
2073 return ", ".join(selectedFutures) |
|
2074 |
|
2075 def __initBuiltinsIgnoreList(self, builtinsIgnoreDict): |
|
2076 """ |
|
2077 Private method to populate the list of shadowed builtins to be ignored. |
|
2078 |
|
2079 @param builtinsIgnoreDict dictionary containing the builtins |
|
2080 assignments to be ignored |
|
2081 @type dict of list of str |
|
2082 """ |
|
2083 self.builtinsAssignmentList.clear() |
|
2084 for left, rightList in builtinsIgnoreDict.items(): |
|
2085 for right in rightList: |
|
2086 QTreeWidgetItem(self.builtinsAssignmentList, [left, right]) |
|
2087 |
|
2088 self.on_builtinsAssignmentList_itemSelectionChanged() |
|
2089 |
|
2090 def __getBuiltinsIgnoreList(self): |
|
2091 """ |
|
2092 Private method to get a dictionary containing the builtins assignments |
|
2093 to be ignored. |
|
2094 |
|
2095 @return dictionary containing the builtins assignments to be ignored |
|
2096 @rtype dict of list of str |
|
2097 """ |
|
2098 builtinsIgnoreDict = {} |
|
2099 for row in range(self.builtinsAssignmentList.topLevelItemCount()): |
|
2100 itm = self.builtinsAssignmentList.topLevelItem(row) |
|
2101 left, right = itm.text(0), itm.text(1) |
|
2102 if left not in builtinsIgnoreDict: |
|
2103 builtinsIgnoreDict[left] = [] |
|
2104 builtinsIgnoreDict[left].append(right) |
|
2105 |
|
2106 return builtinsIgnoreDict |
|
2107 |
|
2108 @pyqtSlot() |
|
2109 def on_builtinsAssignmentList_itemSelectionChanged(self): |
|
2110 """ |
|
2111 Private slot to react upon changes of the selected builtin assignments. |
|
2112 """ |
|
2113 self.deleteBuiltinButton.setEnabled( |
|
2114 len(self.builtinsAssignmentList.selectedItems()) > 0) |
|
2115 |
|
2116 @pyqtSlot() |
|
2117 def on_addBuiltinButton_clicked(self): |
|
2118 """ |
|
2119 Private slot to add a built-in assignment to be ignored. |
|
2120 """ |
|
2121 from .CodeStyleAddBuiltinIgnoreDialog import ( |
|
2122 CodeStyleAddBuiltinIgnoreDialog |
|
2123 ) |
|
2124 dlg = CodeStyleAddBuiltinIgnoreDialog(self) |
|
2125 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
2126 left, right = dlg.getData() |
|
2127 QTreeWidgetItem(self.builtinsAssignmentList, [left, right]) |
|
2128 |
|
2129 @pyqtSlot() |
|
2130 def on_deleteBuiltinButton_clicked(self): |
|
2131 """ |
|
2132 Private slot to delete the selected items from the list. |
|
2133 """ |
|
2134 for itm in self.builtinsAssignmentList.selectedItems(): |
|
2135 index = self.builtinsAssignmentList.indexOfTopLevelItem(itm) |
|
2136 self.builtinsAssignmentList.takeTopLevelItem(index) |
|
2137 del itm |
|
2138 |
|
2139 def __initCategoriesList(self, enabledCategories): |
|
2140 """ |
|
2141 Private method to set the enabled status of the checker categories. |
|
2142 |
|
2143 @param enabledCategories comma separated list of enabled checker |
|
2144 categories |
|
2145 @type str |
|
2146 """ |
|
2147 enabledCategoriesList = ( |
|
2148 [c.strip() for c in enabledCategories.split(",") |
|
2149 if bool(c.strip())] |
|
2150 if enabledCategories else |
|
2151 list(CodeStyleCheckerDialog.checkCategories.keys()) |
|
2152 ) |
|
2153 for row in range(self.categoriesList.count()): |
|
2154 itm = self.categoriesList.item(row) |
|
2155 if itm.data(Qt.ItemDataRole.UserRole) in enabledCategoriesList: |
|
2156 itm.setCheckState(Qt.CheckState.Checked) |
|
2157 else: |
|
2158 itm.setCheckState(Qt.CheckState.Unchecked) |
|
2159 |
|
2160 def __getCategories(self, enabled, asList=False): |
|
2161 """ |
|
2162 Private method to get the enabled or disabled checker categories. |
|
2163 |
|
2164 @param enabled flag indicating to return enabled categories |
|
2165 @type bool |
|
2166 @param asList flag indicating to return the checker categories as a |
|
2167 Python list |
|
2168 @type bool |
|
2169 @return checker categories as a list or comma separated string |
|
2170 @rtype str or list of str |
|
2171 """ |
|
2172 state = Qt.CheckState.Checked if enabled else Qt.CheckState.Unchecked |
|
2173 |
|
2174 checkerList = [] |
|
2175 for row in range(self.categoriesList.count()): |
|
2176 itm = self.categoriesList.item(row) |
|
2177 if itm.checkState() == state: |
|
2178 checkerList.append(itm.data(Qt.ItemDataRole.UserRole)) |
|
2179 if asList: |
|
2180 return checkerList |
|
2181 else: |
|
2182 return ", ".join(checkerList) |
|
2183 |
|
2184 def __assembleExcludeMessages(self): |
|
2185 """ |
|
2186 Private method to assemble the list of excluded checks. |
|
2187 |
|
2188 @return list of excluded checks as a comma separated string. |
|
2189 @rtype str |
|
2190 """ |
|
2191 excludeMessages = self.excludeMessagesEdit.text() |
|
2192 disabledCategories = self.__getCategories(False) |
|
2193 |
|
2194 if excludeMessages and disabledCategories: |
|
2195 return disabledCategories + "," + excludeMessages |
|
2196 elif disabledCategories: |
|
2197 return disabledCategories |
|
2198 elif excludeMessages: |
|
2199 return excludeMessages |
|
2200 else: |
|
2201 return "" |
|
2202 |
|
2203 def __cleanupData(self): |
|
2204 """ |
|
2205 Private method to clean the loaded/entered data of redundant entries. |
|
2206 """ |
|
2207 # Migrate single letter exclude messages to disabled checker categories |
|
2208 # and delete them from exlude messages |
|
2209 excludedMessages = [ |
|
2210 m.strip() |
|
2211 for m in self.excludeMessagesEdit.text().split(",") |
|
2212 if bool(m) |
|
2213 ] |
|
2214 excludedMessageCategories = [ |
|
2215 c for c in excludedMessages if len(c) == 1 |
|
2216 ] |
|
2217 enabledCheckers = self.__getCategories(True, asList=True) |
|
2218 for category in excludedMessageCategories: |
|
2219 if category in enabledCheckers: |
|
2220 enabledCheckers.remove(category) |
|
2221 excludedMessages.remove(category) |
|
2222 |
|
2223 # Remove excluded messages of an already excluded category |
|
2224 disabledCheckers = self.__getCategories(False, asList=True) |
|
2225 for message in excludedMessages[:]: |
|
2226 if message[0] in disabledCheckers: |
|
2227 excludedMessages.remove(message) |
|
2228 |
|
2229 self.excludeMessagesEdit.setText(",".join(excludedMessages)) |
|
2230 self.__initCategoriesList(",".join(enabledCheckers)) |
|
2231 |
|
2232 def __initCommentedCodeCheckerWhiteList(self, whitelist): |
|
2233 """ |
|
2234 Private method to populate the list of commented code whitelist |
|
2235 patterns. |
|
2236 |
|
2237 @param whitelist list of commented code whitelist patterns |
|
2238 @type list of str |
|
2239 """ |
|
2240 self.whitelistWidget.clear() |
|
2241 |
|
2242 for pattern in whitelist: |
|
2243 QListWidgetItem(pattern, self.whitelistWidget) |
|
2244 |
|
2245 self.on_whitelistWidget_itemSelectionChanged() |
|
2246 |
|
2247 def __getCommentedCodeCheckerWhiteList(self): |
|
2248 """ |
|
2249 Private method to get the list of commented code whitelist patterns. |
|
2250 |
|
2251 @return list of commented code whitelist patterns |
|
2252 @rtype list of str |
|
2253 """ |
|
2254 whitelist = [] |
|
2255 |
|
2256 for row in range(self.whitelistWidget.count()): |
|
2257 whitelist.append(self.whitelistWidget.item(row).text()) |
|
2258 |
|
2259 return whitelist |
|
2260 |
|
2261 @pyqtSlot() |
|
2262 def on_whitelistWidget_itemSelectionChanged(self): |
|
2263 """ |
|
2264 Private slot to react upon changes of the selected whitelist patterns. |
|
2265 """ |
|
2266 self.deleteWhitelistButton.setEnabled( |
|
2267 len(self.whitelistWidget.selectedItems()) > 0) |
|
2268 |
|
2269 @pyqtSlot() |
|
2270 def on_addWhitelistButton_clicked(self): |
|
2271 """ |
|
2272 Private slot to add a commented code whitelist pattern. |
|
2273 """ |
|
2274 pattern, ok = QInputDialog.getText( |
|
2275 self, |
|
2276 self.tr("Commented Code Whitelist Pattern"), |
|
2277 self.tr("Enter a Commented Code Whitelist Pattern"), |
|
2278 QLineEdit.EchoMode.Normal) |
|
2279 if ok and pattern: |
|
2280 QListWidgetItem(pattern, self.whitelistWidget) |
|
2281 |
|
2282 @pyqtSlot() |
|
2283 def on_deleteWhitelistButton_clicked(self): |
|
2284 """ |
|
2285 Private slot to delete the selected items from the list. |
|
2286 """ |
|
2287 for itm in self.whitelistWidget.selectedItems(): |
|
2288 row = self.whitelistWidget.row(itm) |
|
2289 self.whitelistWidget.takeItem(row) |
|
2290 del itm |
|
2291 |
|
2292 @pyqtSlot() |
|
2293 def on_filterButton_clicked(self): |
|
2294 """ |
|
2295 Private slot to filter the list of messages based on selected message |
|
2296 code. |
|
2297 """ |
|
2298 selectedMessageCode = self.filterComboBox.currentText() |
|
2299 |
|
2300 for topRow in range(self.resultList.topLevelItemCount()): |
|
2301 topItem = self.resultList.topLevelItem(topRow) |
|
2302 topItem.setExpanded(True) |
|
2303 visibleChildren = topItem.childCount() |
|
2304 for childIndex in range(topItem.childCount()): |
|
2305 childItem = topItem.child(childIndex) |
|
2306 hideChild = ( |
|
2307 childItem.data(0, self.codeRole) != selectedMessageCode |
|
2308 if selectedMessageCode else |
|
2309 False |
|
2310 ) |
|
2311 childItem.setHidden(hideChild) |
|
2312 if hideChild: |
|
2313 visibleChildren -= 1 |
|
2314 topItem.setHidden(visibleChildren == 0) |