diff -r fb0ef164f536 -r 698ae46f40a4 eric6/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/MiscellaneousChecker.py --- a/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/MiscellaneousChecker.py Fri Apr 02 11:59:41 2021 +0200 +++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/MiscellaneousChecker.py Sat May 01 14:27:20 2021 +0200 @@ -14,6 +14,8 @@ from string import Formatter from collections import defaultdict import tokenize +import copy +import contextlib import AstUtilities @@ -32,14 +34,13 @@ @ytype str """ if isinstance(node, ast.Attribute): - for v in composeCallPath(node.value): - yield v + yield from composeCallPath(node.value) yield node.attr elif isinstance(node, ast.Name): yield node.id -class MiscellaneousChecker(object): +class MiscellaneousChecker: """ Class implementing a checker for miscellaneous checks. """ @@ -110,9 +111,6 @@ ## commented code "M891", - - ## syntax error - "M901", ] Formatter = Formatter() @@ -124,8 +122,8 @@ "credits", ] - def __init__(self, source, filename, select, ignore, expected, repeat, - args): + def __init__(self, source, filename, tree, select, ignore, expected, + repeat, args): """ Constructor @@ -133,6 +131,8 @@ @type list of str @param filename name of the source file @type str + @param tree AST tree of the source code + @type ast.Module @param select list of selected codes @type list of str @param ignore list of codes to be ignored @@ -150,6 +150,7 @@ self.__repeat = repeat self.__filename = filename self.__source = source[:] + self.__tree = copy.deepcopy(tree) self.__args = args self.__pep3101FormatRegex = re.compile( @@ -267,29 +268,6 @@ } ) - def __reportInvalidSyntax(self): - """ - Private method to report a syntax error. - """ - exc_type, exc = sys.exc_info()[:2] - if len(exc.args) > 1: - offset = exc.args[1] - if len(offset) > 2: - offset = offset[1:3] - else: - offset = (1, 0) - self.__error(offset[0] - 1, offset[1] or 0, - 'M901', exc_type.__name__, exc.args[0]) - - def __generateTree(self): - """ - Private method to generate an AST for our source. - - @return generated AST - @rtype ast.AST - """ - return ast.parse("".join(self.__source), self.__filename) - def run(self): """ Public method to check the given source against miscellaneous @@ -303,12 +281,6 @@ # don't do anything, if no codes were selected return - try: - self.__tree = self.__generateTree() - except (SyntaxError, TypeError): - self.__reportInvalidSyntax() - return - for check in self.__checkers: check() @@ -480,7 +452,7 @@ if isinstance(node, ast.Module) or not hasCode: return - if not (imports >= expectedImports): + if imports < expectedImports: if imports: self.__error(node.lineno - 1, node.col_offset, "M701", ", ".join(expectedImports), ", ".join(imports)) @@ -572,14 +544,12 @@ # if starargs or kwargs is not None, it can't count the # parameters but at least check if the args are used - if hasKwArgs: - if not names: - # No names but kwargs - self.__error(call.lineno - 1, call.col_offset, "M623") - if hasStarArgs: - if not numbers: - # No numbers but args - self.__error(call.lineno - 1, call.col_offset, "M624") + if hasKwArgs and not names: + # No names but kwargs + self.__error(call.lineno - 1, call.col_offset, "M623") + if hasStarArgs and not numbers: + # No numbers but args + self.__error(call.lineno - 1, call.col_offset, "M624") if not hasKwArgs and not hasStarArgs: # can actually verify numbers and names @@ -642,10 +612,8 @@ Private method to check, if built-ins are shadowed. """ functionDefs = [ast.FunctionDef] - try: + with contextlib.suppress(AttributeError): functionDefs.append(ast.AsyncFunctionDef) - except AttributeError: - pass ignoreBuiltinAssignments = self.__args.get( "BuiltinsChecker", @@ -830,19 +798,15 @@ "frozenset", ) functionDefs = [ast.FunctionDef] - try: + with contextlib.suppress(AttributeError): functionDefs.append(ast.AsyncFunctionDef) - except AttributeError: - pass for node in ast.walk(self.__tree): if any(isinstance(node, functionDef) for functionDef in functionDefs): defaults = node.args.defaults[:] - try: + with contextlib.suppress(AttributeError): defaults += node.args.kw_defaults[:] - except AttributeError: - pass for default in defaults: if any(isinstance(default, mutableType) for mutableType in mutableTypes): @@ -943,7 +907,7 @@ """ # step 1: generate an augmented node tree containing parent info # for each child node - tree = self.__generateTree() + tree = copy.deepcopy(self.__tree) for node in ast.walk(tree): for childNode in ast.iter_child_nodes(node): childNode._dtCheckerParent = node @@ -981,7 +945,7 @@ """ Constructor """ - super(TextVisitor, self).__init__() + super().__init__() self.nodes = [] self.calls = {} @@ -1025,9 +989,9 @@ if AstUtilities.isBaseString(node): self.__addNode(node) else: - super(TextVisitor, self).generic_visit(node) + super().generic_visit(node) else: - super(TextVisitor, self).generic_visit(node) + super().generic_visit(node) def __visitDefinition(self, node): """ @@ -1123,7 +1087,7 @@ AstUtilities.isBaseString(node.args[0]) ): self.calls[node.args[0]] = (node, True) - super(TextVisitor, self).generic_visit(node) + super().generic_visit(node) class LoggingVisitor(ast.NodeVisitor): @@ -1143,7 +1107,7 @@ """ Constructor """ - super(LoggingVisitor, self).__init__() + super().__init__() self.__currentLoggingCall = None self.__currentLoggingArgument = None @@ -1192,14 +1156,12 @@ @return logging level @rtype str or None """ - try: + with contextlib.suppress(AttributeError): if node.func.value.id == "warnings": return None if node.func.attr in LoggingVisitor.LoggingLevels: return node.func.attr - except AttributeError: - pass return None @@ -1228,11 +1190,14 @@ @type ast.Call """ # we are in a logging statement - if self.__withinLoggingStatement(): - if self.__withinLoggingArgument() and self.__isFormatCall(node): - self.violations.append((node, "M651")) - super(LoggingVisitor, self).generic_visit(node) - return + if ( + self.__withinLoggingStatement() and + self.__withinLoggingArgument() and + self.__isFormatCall(node) + ): + self.violations.append((node, "M651")) + super().generic_visit(node) + return loggingLevel = self.__detectLoggingLevel(node) @@ -1241,7 +1206,7 @@ # we are in some other statement if loggingLevel is None: - super(LoggingVisitor, self).generic_visit(node) + super().generic_visit(node) return # we are entering a new logging statement @@ -1260,7 +1225,7 @@ ): self.__currentExtraKeyword = child - super(LoggingVisitor, self).visit(child) + super().visit(child) self.__currentLoggingArgument = None self.__currentExtraKeyword = None @@ -1285,7 +1250,7 @@ if isinstance(node.op, ast.Add): self.violations.append((node, "M653")) - super(LoggingVisitor, self).generic_visit(node) + super().generic_visit(node) def visit_JoinedStr(self, node): """ @@ -1294,12 +1259,14 @@ @param node reference to the node to be processed @type ast.JoinedStr """ - if self.__withinLoggingStatement(): - if any(isinstance(i, ast.FormattedValue) for i in node.values): - if self.__withinLoggingArgument(): - self.violations.append((node, "M654")) - - super(LoggingVisitor, self).generic_visit(node) + if ( + self.__withinLoggingStatement() and + any(isinstance(i, ast.FormattedValue) for i in node.values) and + self.__withinLoggingArgument() + ): + self.violations.append((node, "M654")) + + super().generic_visit(node) class BugBearVisitor(ast.NodeVisitor): @@ -1317,7 +1284,7 @@ """ Constructor """ - super(BugBearVisitor, self).__init__() + super().__init__() self.__nodeStack = [] self.__nodeWindow = [] @@ -1334,7 +1301,7 @@ self.__nodeWindow.append(node) self.__nodeWindow = self.__nodeWindow[-BugBearVisitor.NodeWindowSize:] - super(BugBearVisitor, self).visit(node) + super().visit(node) self.__nodeStack.pop() @@ -1376,7 +1343,7 @@ else: self.__checkForM502(node) else: - try: + with contextlib.suppress(AttributeError, IndexError): # bad super() call if isinstance(node.func, ast.Name) and node.func.id == "super": args = node.args @@ -1407,8 +1374,6 @@ AstUtilities.isString(node.args[1]) ): self.violations.append((node, "M513")) - except (AttributeError, IndexError): - pass self.generic_visit(node) @@ -1452,10 +1417,10 @@ target = node.targets[0] if ( isinstance(target, ast.Attribute) and - isinstance(target.value, ast.Name) + isinstance(target.value, ast.Name) and + (target.value.id, target.attr) == ('os', 'environ') ): - if (target.value.id, target.attr) == ('os', 'environ'): - self.violations.append((node, "M506")) + self.violations.append((node, "M506")) self.generic_visit(node) @@ -1559,7 +1524,7 @@ """ Constructor """ - super(NameFinder, self).__init__() + super().__init__() self.__names = {} @@ -1581,9 +1546,9 @@ """ if isinstance(node, list): for elem in node: - super(NameFinder, self).visit(elem) + super().visit(elem) else: - super(NameFinder, self).visit(node) + super().visit(node) def getNames(self): """ @@ -1607,7 +1572,7 @@ """ Constructor """ - super(ReturnVisitor, self).__init__() + super().__init__() self.__stack = [] self.violations = [] @@ -1968,7 +1933,7 @@ """ Constructor """ - super(DateTimeVisitor, self).__init__() + super().__init__() self.violations = [] @@ -2126,10 +2091,11 @@ # datetime.strptime(...).replace(tzinfo=UTC) parent = getattr(node, '_dtCheckerParent', None) pparent = getattr(parent, '_dtCheckerParent', None) - if not (isinstance(parent, ast.Attribute) and - parent.attr == 'replace'): - isCase1 = False - elif not isinstance(pparent, ast.Call): + if ( + not (isinstance(parent, ast.Attribute) and + parent.attr == 'replace') or + not isinstance(pparent, ast.Call) + ): isCase1 = False else: tzinfoKeyword = self.__getFromKeywords(pparent.keywords, @@ -2188,7 +2154,7 @@ """ Constructor """ - super(SysVersionVisitor, self).__init__() + super().__init__() self.violations = [] self.__fromImports = {} @@ -2219,16 +2185,13 @@ """ match = False if ( - isinstance(node, ast.Attribute) and - isinstance(node.value, ast.Name) and - node.value.id == "sys" and - node.attr == attr - ): - match = True - elif ( - isinstance(node, ast.Name) and - node.id == attr and - self.__fromImports.get(node.id) == "sys" + (isinstance(node, ast.Attribute) and + isinstance(node.value, ast.Name) and + node.value.id == "sys" and + node.attr == attr) or + (isinstance(node, ast.Name) and + node.id == attr and + self.__fromImports.get(node.id) == "sys") ): match = True