diff -r 9125da0c227e -r e1157bd8b4c2 eric6/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyNodeVisitor.py --- a/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyNodeVisitor.py Fri Apr 02 15:35:44 2021 +0200 +++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyNodeVisitor.py Fri Apr 02 18:13:12 2021 +0200 @@ -115,6 +115,7 @@ self.__check104(node) self.__check110_111(node) self.__check113(node) + self.__check118b(node) self.generic_visit(node) @@ -141,6 +142,39 @@ self.generic_visit(node) + def visit_With(self, node): + """ + Public method to process a With node. + + @param node reference to the With node + @type ast.With + """ + self.__check117(node) + + self.generic_visit(node) + + def visit_Compare(self, node): + """ + Public method to process a Compare node. + + @param node reference to the Compare node + @type ast.Compare + """ + self.__check118a(node) + + self.generic_visit(node) + + def visit_ClassDef(self, node): + """ + Public method to process a ClassDef node. + + @param node reference to the ClassDef node + @type ast.ClassDef + """ + self.__check119(node) + + self.generic_visit(node) + ############################################################# ## Helper methods for the various checkers below ############################################################# @@ -468,8 +502,13 @@ body = unparse(node.body[0].value) cond = unparse(node.test) orelse = unparse(node.orelse[0].value) - self.__error(node.lineno - 1, node.col_offset, "Y108", - assign, body, cond, orelse) + if len( + "{0} = {1} if {2} else {3}".format(assign, body, cond, orelse) + ) > 79: + self.__error(node.lineno - 1, node.col_offset, "Y108a") + else: + self.__error(node.lineno - 1, node.col_offset, "Y108b", + assign, body, cond, orelse) def __check109(self, node): """ @@ -730,20 +769,21 @@ variable = node.test.left child = node.orelse[0] elseValue = None + if node.body[0].value is not None: + bodyValueStr = unparse(node.body[0].value).strip("'") + else: + bodyValueStr = "None" if isinstance(node.test.comparators[0], ast.Str): keyValuePairs = { - node.test.comparators[0].s: - unparse(node.body[0].value).strip("'") + node.test.comparators[0].s: bodyValueStr } elif isinstance(node.test.comparators[0], ast.Num): keyValuePairs = { - node.test.comparators[0].n: - unparse(node.body[0].value).strip("'") + node.test.comparators[0].n: bodyValueStr, } else: keyValuePairs = { - node.test.comparators[0].value: - unparse(node.body[0].value).strip("'") + node.test.comparators[0].value: bodyValueStr } while child: if not ( @@ -787,6 +827,113 @@ ret = f"{keyValuePairs}.get({variable.id})" self.__error(node.lineno - 1, node.col_offset, "Y116", ret) + + def __check117(self, node): + """ + Private method to check for multiple with-statements with same scope. + + @param node reference to the AST node to be checked + @type ast.With + """ + # with A() as a: + # with B() as b: + # print("hello") + if ( + len(node.body) == 1 and + isinstance(node.body[0], ast.With) + ): + withItems = [] + for withitem in node.items + node.body[0].items: + withItems.append(f"{unparse(withitem)}") + mergedWith = f"with {', '.join(withItems)}:" + self.__error(node.lineno - 1, node.col_offset, "Y117", mergedWith) + + def __check118a(self, node): + """ + Private method to check for usages of "key in dict.keys()". + + @param node reference to the AST node to be checked + @type ast.Compare + """ + # key in dict.keys() + if ( + len(node.ops) == 1 and + isinstance(node.ops[0], ast.In) and + len(node.comparators) == 1 + ): + callNode = node.comparators[0] + if not isinstance(callNode, ast.Call): + return + + attrNode = callNode.func + if ( + isinstance(callNode.func, ast.Attribute) and + callNode.func.attr == "keys" and + isinstance(callNode.func.ctx, ast.Load) + ): + keyStr = unparse(node.left) + dictStr = unparse(attrNode.value) + self.__error(node.lineno - 1, node.col_offset, "Y118", + keyStr, dictStr) + + def __check118b(self, node): + """ + Private method to check for usages of "key in dict.keys()". + + @param node reference to the AST node to be checked + @type ast.For + """ + # for key in dict.keys(): + # # do something + callNode = node.iter + if not isinstance(callNode, ast.Call): + return + + attrNode = callNode.func + if ( + isinstance(callNode.func, ast.Attribute) and + callNode.func.attr == "keys" and + isinstance(callNode.func.ctx, ast.Load) + ): + keyStr = unparse(node.target) + dictStr = unparse(attrNode.value) + self.__error(node.lineno - 1, node.col_offset, "Y118", + keyStr, dictStr) + + def __check119(self, node): + """ + Public method to check for classes that should be "dataclasses". + + @param node reference to the AST node to be checked + @type ast.ClassDef + """ + if ( + len(node.decorator_list) == 0 and + len(node.bases) == 0 + ): + dataclassFunctions = [ + "__init__", + "__eq__", + "__hash__", + "__repr__", + "__str__", + ] + hasOnlyConstructorMethod = True + for bodyElement in node.body: + if ( + isinstance(bodyElement, ast.FunctionDef) and + bodyElement.name not in dataclassFunctions + ): + hasOnlyConstructorMethod = False + break + + if ( + hasOnlyConstructorMethod and + sum(1 for el in node.body + if isinstance(el, ast.FunctionDef)) > 0 + ): + self.__error(node.lineno - 1, node.col_offset, "Y119", + node.name) # # eflag: noqa = M891