--- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/MiscellaneousChecker.py Wed Nov 29 14:31:31 2023 +0100 +++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/MiscellaneousChecker.py Wed Nov 29 14:31:50 2023 +0100 @@ -143,6 +143,7 @@ "M532", "M533", "M534", + "M535", ## Bugbear++ "M581", "M582", @@ -339,6 +340,7 @@ "M532", "M533", "M534", + "M535", "M581", "M582", ), @@ -1617,7 +1619,7 @@ ####################################################################### ## BugBearVisitor ## -## adapted from: flake8-bugbear v23.7.10 +## adapted from: flake8-bugbear v23.11.26 ## ## Original: Copyright (c) 2016 Ćukasz Langa ####################################################################### @@ -1897,6 +1899,36 @@ for node in nodes: yield from ast.walk(node) + def __getNamesFromTuple(self, node): + """ + Private method to get the names from an ast.Tuple node. + + @param node ast node to be processed + @type ast.Tuple + @yield names + @ytype str + """ + for dim in node.elts: + if isinstance(dim, ast.Name): + yield dim.id + elif isinstance(dim, ast.Tuple): + yield from self.__getNamesFromTuple(dim) + + def __getDictCompLoopVarNames(self, node): + """ + Private method to get the names of comprehension loop variables. + + @param node ast node to be processed + @type ast.DictComp + @yield loop variable names + @ytype str + """ + for gen in node.generators: + if isinstance(gen.target, ast.Name): + yield gen.target.id + elif isinstance(gen.target, ast.Tuple): + yield from self.__getNamesFromTuple(gen.target) + def visit(self, node): """ Public method to traverse a given AST node. @@ -2024,7 +2056,7 @@ ): self.violations.append((node, "M510")) - self.__checkForM526(node) + self.__checkForM526(node) self.__checkForM528(node) self.__checkForM534(node) @@ -2129,6 +2161,7 @@ @type ast.DictComp """ self.__checkForM523(node) + self.__checkForM535(node) self.generic_visit(node) @@ -2265,6 +2298,15 @@ self.generic_visit(node) + def visit_ImportFrom(self, node): + """ + Public method to check from imports. + + @param node reference to the node to be processed + @type ast.Import + """ + self.visit_Import(node) + def visit_Set(self, node): """ Public method to check a set. @@ -2286,6 +2328,9 @@ if isinstance(node, ast.Import): for name in node.names: self.__M505Imports.add(name.asname or name.name) + elif isinstance(node, ast.ImportFrom): + for name in node.names: + self.__M505Imports.add(f"{node.module}.{name.name or name.asname}") elif isinstance(node, ast.Call): if node.func.attr not in ("lstrip", "rstrip", "strip"): return # method name doesn't match @@ -2386,13 +2431,25 @@ itemContext = item.context_expr if ( hasattr(itemContext, "func") - and isinstance(itemContext.func, ast.Attribute) and ( - itemContext.func.attr == "assertRaises" + ( + isinstance(itemContext.func, ast.Attribute) + and ( + itemContext.func.attr == "assertRaises" + or ( + itemContext.func.attr == "raises" + and isinstance(itemContext.func.value, ast.Name) + and itemContext.func.value.id == "pytest" + and "match" + not in [kwd.arg for kwd in itemContext.keywords] + ) + ) + ) or ( - itemContext.func.attr == "raises" - and isinstance(itemContext.func.value, ast.Name) - and itemContext.func.value.id == "pytest" + isinstance(itemContext.func, ast.Name) + and itemContext.func.id == "raises" + and isinstance(itemContext.func.ctx, ast.Load) + and "pytest.raises" in self.__M505Imports and "match" not in [kwd.arg for kwd in itemContext.keywords] ) ) @@ -2846,6 +2903,27 @@ elif node.func.attr == "split": check(2, "maxsplit") + def __checkForM535(self, node: ast.DictComp): + """ + Private method to check that a static key isn't used in a dict comprehension. + + Record a warning if a likely unchanging key is used - either a constant, + or a variable that isn't coming from the generator expression. + + @param node reference to the node to be processed + @type ast.DictComp + """ + """Check that a static key isn't used in a dict comprehension. + + Emit a warning if a likely unchanging key is used - either a constant, + or a variable that isn't coming from the generator expression. + """ + if isinstance(node.key, ast.Constant): + self.violations.append((node, "M535", node.key.value)) + elif isinstance(node.key, ast.Name): + if node.key.id not in self.__getDictCompLoopVarNames(node): + self.violations.append((node, "M535", node.key.id)) + class NameFinder(ast.NodeVisitor): """