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