src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.py

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

eric ide

mercurial