eric7/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.py

branch
eric7
changeset 8312
800c432b34c8
parent 8259
2bbec88047dd
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
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)

eric ide

mercurial