diff -r fc45672fae42 -r 73d80859079c src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py --- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py Thu Feb 27 09:22:15 2025 +0100 +++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py Thu Feb 27 14:42:39 2025 +0100 @@ -11,8 +11,10 @@ import copy import re +from CodeStyleTopicChecker import CodeStyleTopicChecker -class ImportsChecker: + +class ImportsChecker(CodeStyleTopicChecker): """ Class implementing a checker for import statements. """ @@ -28,6 +30,7 @@ "I-903", "I-904", ] + Category = "I" def __init__(self, source, filename, tree, select, ignore, expected, repeat, args): """ @@ -50,96 +53,23 @@ @param args dictionary of arguments for the various checks @type dict """ - self.__select = tuple(select) - self.__ignore = tuple(ignore) - self.__expected = expected[:] - self.__repeat = repeat - self.__filename = filename - self.__source = source[:] - self.__tree = copy.deepcopy(tree) - self.__args = args - - # statistics counters - self.counters = {} - - # collection of detected errors - self.errors = [] + super().__init__( + ImportsChecker.Category, + source, + filename, + tree, + select, + ignore, + expected, + repeat, + args, + ) checkersWithCodes = [ (self.__checkLocalImports, ("I-101", "I-102", "I-103")), (self.__tidyImports, ("I-901", "I-902", "I-903", "I-904")), ] - - self.__checkers = [] - for checker, codes in checkersWithCodes: - if any(not (code and self.__ignoreCode(code)) for code in codes): - self.__checkers.append(checker) - - def __ignoreCode(self, code): - """ - Private method to check if the message code should be ignored. - - @param code message code to check for - @type str - @return flag indicating to ignore the given code - @rtype bool - """ - return code in self.__ignore or ( - code.startswith(self.__ignore) and not code.startswith(self.__select) - ) - - def __error(self, lineNumber, offset, code, *args): - """ - Private method to record an issue. - - @param lineNumber line number of the issue - @type int - @param offset position within line of the issue - @type int - @param code message code - @type str - @param args arguments for the message - @type list - """ - if self.__ignoreCode(code): - return - - if code in self.counters: - self.counters[code] += 1 - else: - self.counters[code] = 1 - - # Don't care about expected codes - if code in self.__expected: - return - - if code and (self.counters[code] == 1 or self.__repeat): - # record the issue with one based line number - self.errors.append( - { - "file": self.__filename, - "line": lineNumber + 1, - "offset": offset, - "code": code, - "args": args, - } - ) - - def run(self): - """ - Public method to check the given source against miscellaneous - conditions. - """ - if not self.__filename: - # don't do anything, if essential data is missing - return - - if not self.__checkers: - # don't do anything, if no codes were selected - return - - for check in self.__checkers: - check() + self._initializeCheckers(checkersWithCodes) ####################################################################### ## Local imports @@ -153,13 +83,10 @@ """ from .LocalImportVisitor import LocalImportVisitor - visitor = LocalImportVisitor(self.__args, self) - visitor.visit(copy.deepcopy(self.__tree)) + visitor = LocalImportVisitor(self.args, self) + visitor.visit(copy.deepcopy(self.tree)) for violation in visitor.violations: - if not self.__ignoreCode(violation[1]): - node = violation[0] - reason = violation[1] - self.__error(node.lineno - 1, node.col_offset, reason) + self.addErrorFromNode(violation[0], violation[1]) ####################################################################### ## Tidy imports @@ -171,11 +98,11 @@ """ Private method to check various other import related topics. """ - self.__banRelativeImports = self.__args.get("BanRelativeImports", "") + self.__banRelativeImports = self.args.get("BanRelativeImports", "") self.__bannedModules = [] self.__bannedStructuredPatterns = [] self.__bannedUnstructuredPatterns = [] - for module in self.__args.get("BannedModules", []): + for module in self.args.get("BannedModules", []): module = module.strip() if "*" in module[:-1] or module == "*": # unstructured @@ -197,16 +124,16 @@ self.__bannedStructuredPatterns.sort(key=lambda x: len(x[0]), reverse=True) ruleMethods = [] - if not self.__ignoreCode("I-901"): + if not self._ignoreCode("I-901"): ruleMethods.append(self.__checkUnnecessaryAlias) - if not self.__ignoreCode("I-902") and bool(self.__bannedModules): + if not self._ignoreCode("I-902") and bool(self.__bannedModules): ruleMethods.append(self.__checkBannedImport) if ( - not self.__ignoreCode("I-903") and self.__banRelativeImports == "parents" - ) or (not self.__ignoreCode("I-904") and self.__banRelativeImports == "true"): + not self._ignoreCode("I-903") and self.__banRelativeImports == "parents" + ) or (not self._ignoreCode("I-904") and self.__banRelativeImports == "true"): ruleMethods.append(self.__checkBannedRelativeImports) - for node in ast.walk(self.__tree): + for node in ast.walk(self.tree): for method in ruleMethods: method(node) @@ -251,14 +178,14 @@ else: rewritten = f"import {importedName}" - self.__error(node.lineno - 1, node.col_offset, "I-901", rewritten) + self.addErrorFromNode(node, "I-901", rewritten) elif isinstance(node, ast.ImportFrom): for alias in node.names: if alias.name == alias.asname: rewritten = f"from {node.module} import {alias.name}" - self.__error(node.lineno - 1, node.col_offset, "I-901", rewritten) + self.addErrorFromNode(node, "I-901", rewritten) def __isModuleBanned(self, moduleName): """ @@ -326,7 +253,7 @@ continue else: warned.add(moduleName) - self.__error(node.lineno - 1, node.col_offset, "I-902", moduleName) + self.addErrorFromNode(node, "I-902", moduleName) def __checkBannedRelativeImports(self, node): """ @@ -347,4 +274,4 @@ msgCode = "I-904" if isinstance(node, ast.ImportFrom) and node.level > minNodeLevel: - self.__error(node.lineno - 1, node.col_offset, msgCode) + self.addErrorFromNode(node, msgCode)