diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyNodeVisitor.py --- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyNodeVisitor.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyNodeVisitor.py Wed Jul 13 14:55:47 2022 +0200 @@ -56,45 +56,46 @@ Class to traverse the AST node tree and check for code that can be simplified. """ + def __init__(self, errorCallback): """ Constructor - + @param errorCallback callback function to register an error @type func """ super().__init__() - + self.__error = errorCallback - + self.__classDefinitionStack = [] - + def visit_Expr(self, node): """ Public method to process an Expr node. - + @param node reference to the Expr node @type ast.Expr """ self.__check112(node) - + self.generic_visit(node) - + def visit_Assign(self, node): """ Public method to process an Assign node. - + @param node reference to the Assign node @type ast.Assign """ self.__check181(node) - + self.generic_visit(node) - + def visit_BoolOp(self, node): """ Public method to process a BoolOp node. - + @param node reference to the BoolOp node @type ast.BoolOp """ @@ -104,13 +105,13 @@ self.__check222(node) self.__check223(node) self.__check224(node) - + self.generic_visit(node) - + def visit_If(self, node): """ Public method to process an If node. - + @param node reference to the If node @type ast.If """ @@ -121,26 +122,26 @@ self.__check114(node) self.__check116(node) self.__check122(node) - + self.generic_visit(node) - + def visit_IfExp(self, node): """ Public method to process an IfExp node. - + @param node reference to the IfExp node @type ast.IfExp """ self.__check211(node) self.__check212(node) self.__check213(node) - + self.generic_visit(node) - + def visit_For(self, node): """ Public method to process a For node. - + @param node reference to the For node @type ast.For """ @@ -148,25 +149,25 @@ self.__check110_111(node) self.__check113(node) self.__check118(node) - + self.generic_visit(node) - + def visit_Try(self, node): """ Public method to process a Try node. - + @param node reference to the Try node @type ast.Try """ self.__check105(node) self.__check107(node) - + self.generic_visit(node) - + def visit_Call(self, node): """ Public method to process a Call node. - + @param node reference to the Call node @type ast.Call """ @@ -174,53 +175,53 @@ self.__check182(node) self.__check401(node) self.__check402(node) - + 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.__check118(node) self.__check301(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 """ # register the name of the class being defined self.__classDefinitionStack.append(node.name) - + self.__check119(node) self.__check120_121(node) - + self.generic_visit(node) - + self.__classDefinitionStack.pop() - + def visit_UnaryOp(self, node): """ Public method to process a UnaryOp node. - + @param node reference to the UnaryOp node @type ast.UnaryOp """ @@ -232,25 +233,25 @@ self.__check206(node) self.__check207(node) self.__check208(node) - + self.generic_visit(node) - + ############################################################# ## Helper methods for the various checkers below ############################################################# - + def __getDuplicatedIsinstanceCall(self, node): """ Private method to get a list of isinstance arguments which could be combined. - + @param node reference to the AST node to be inspected @type ast.BoolOp @return list of variable names of duplicated isinstance calls @rtype list of str """ counter = collections.defaultdict(int) - + for call in node.values: # Ensure this is a call of the built-in isinstance() function. if not isinstance(call, ast.Call) or len(call.args) != 2: @@ -258,35 +259,35 @@ functionName = unparse(call.func) if functionName != "isinstance": continue - + arg0Name = unparse(call.args[0]) counter[arg0Name] += 1 - + return [name for name, count in counter.items() if count > 1] - + def __isConstantIncrease(self, expression): """ Private method check the given expression for being a constant increase. - + @param expression reference to the expression node @type ast.AugAssign @return flag indicating a constant increase @rtype bool """ - return ( - isinstance(expression.op, ast.Add) and ( - (isinstance(expression.value, ast.Constant) and - isinstance(expression.value.value, int)) or - isinstance(expression.value, ast.Num) + return isinstance(expression.op, ast.Add) and ( + ( + isinstance(expression.value, ast.Constant) + and isinstance(expression.value.value, int) ) + or isinstance(expression.value, ast.Num) ) - + def __getIfBodyPairs(self, node): """ Private method to extract a list of pairs of test and body for an If node. - + @param node reference to the If node to be processed @type ast.If @return list of pairs of test and body @@ -295,18 +296,18 @@ pairs = [(node.test, node.body)] orelse = node.orelse while ( - isinstance(orelse, list) and - len(orelse) == 1 and - isinstance(orelse[0], ast.If) + isinstance(orelse, list) + and len(orelse) == 1 + and isinstance(orelse[0], ast.If) ): pairs.append((orelse[0].test, orelse[0].body)) orelse = orelse[0].orelse return pairs - + def __isSameBody(self, body1, body2): """ Private method check, if the given bodies are equivalent. - + @param body1 list of statements of the first body @type list of ast.stmt @param body2 list of statements of the second body @@ -323,13 +324,13 @@ statementEqual = False if not statementEqual: return False - + return True - + def __isSameExpression(self, a, b): """ Private method to check, if two expressions are equal. - + @param a first expression to be checked @type ast.expr @param b second expression to be checked @@ -341,11 +342,11 @@ return a.id == b.id else: return False - + def __isStatementEqual(self, a, b): """ Private method to check, if two statements are equal. - + @param a reference to the first statement @type ast.stmt @param b reference to the second statement @@ -355,11 +356,10 @@ """ if type(a) is not type(b): return False - + if isinstance(a, ast.AST): for k, v in vars(a).items(): - if k in ("lineno", "col_offset", "ctx", "end_lineno", - "parent"): + if k in ("lineno", "col_offset", "ctx", "end_lineno", "parent"): continue if not self.__isStatementEqual(v, getattr(b, k)): return False @@ -368,24 +368,22 @@ return all(itertools.starmap(self.__isStatementEqual, zip(a, b))) else: return a == b - + def __isExceptionCheck(self, node): """ Private method to check, if the node is checking an exception. - + @param node reference to the node to be checked @type ast.If @return flag indicating an exception check @rtype bool """ - return ( - len(node.body) == 1 and isinstance(node.body[0], ast.Raise) - ) - + return len(node.body) == 1 and isinstance(node.body[0], ast.Raise) + def __negateTest(self, node): """ Private method negate the given Compare node. - + @param node reference to the node to be negated @type ast.Compare @return node with negated logic @@ -415,50 +413,49 @@ op = ast.In() newNode.ops = [op] return newNode - + ############################################################# ## Methods to check for possible code simplifications below ############################################################# - + def __check101(self, node): """ Private method to check for duplicate isinstance() calls. - + @param node reference to the AST node to be checked @type ast.BoolOp """ if isinstance(node.op, ast.Or): for variable in self.__getDuplicatedIsinstanceCall(node): - self.__error(node.lineno - 1, node.col_offset, "Y101", - variable) - + self.__error(node.lineno - 1, node.col_offset, "Y101", variable) + def __check102(self, node): """ Private method to check for nested if statements without else blocks. - + @param node reference to the AST node to be checked @type ast.If """ # Don't treat 'if __name__ == "__main__":' as an issue. if ( - isinstance(node.test, ast.Compare) and - isinstance(node.test.left, ast.Name) and - node.test.left.id == "__name__" and - isinstance(node.test.ops[0], ast.Eq) and - isinstance(node.test.comparators[0], ast.Constant) and - node.test.comparators[0].value == "__main__" + isinstance(node.test, ast.Compare) + and isinstance(node.test.left, ast.Name) + and node.test.left.id == "__name__" + and isinstance(node.test.ops[0], ast.Eq) + and isinstance(node.test.comparators[0], ast.Constant) + and node.test.comparators[0].value == "__main__" ): return - + # ## Pattern 1 # if a: <--- # if b: <--- # c isPattern1 = ( - node.orelse == [] and - len(node.body) == 1 and - isinstance(node.body[0], ast.If) and - node.body[0].orelse == [] + node.orelse == [] + and len(node.body) == 1 + and isinstance(node.body[0], ast.If) + and node.body[0].orelse == [] ) # ## Pattern 2 # if a: < irrelevant for here @@ -468,12 +465,12 @@ # d if isPattern1: self.__error(node.lineno - 1, node.col_offset, "Y102") - + def __check103(self, node): """ Private method to check for calls that wrap a condition to return a bool. - + @param node reference to the AST node to be checked @type ast.If """ @@ -482,49 +479,48 @@ # else: # return False if not ( - len(node.body) != 1 or - not isinstance(node.body[0], ast.Return) or - not isinstance(node.body[0].value, BOOL_CONST_TYPES) or - not ( - node.body[0].value.value is True or - node.body[0].value.value is False - ) or - len(node.orelse) != 1 or - not isinstance(node.orelse[0], ast.Return) or - not isinstance(node.orelse[0].value, BOOL_CONST_TYPES) or - not ( - node.orelse[0].value.value is True or - node.orelse[0].value.value is False + len(node.body) != 1 + or not isinstance(node.body[0], ast.Return) + or not isinstance(node.body[0].value, BOOL_CONST_TYPES) + or not ( + node.body[0].value.value is True or node.body[0].value.value is False + ) + or len(node.orelse) != 1 + or not isinstance(node.orelse[0], ast.Return) + or not isinstance(node.orelse[0].value, BOOL_CONST_TYPES) + or not ( + node.orelse[0].value.value is True + or node.orelse[0].value.value is False ) ): condition = unparse(node.test) self.__error(node.lineno - 1, node.col_offset, "Y103", condition) - + def __check104(self, node): """ Private method to check for "iterate and yield" patterns. - + @param node reference to the AST node to be checked @type ast.For """ # for item in iterable: # yield item if not ( - len(node.body) != 1 or - not isinstance(node.body[0], ast.Expr) or - not isinstance(node.body[0].value, ast.Yield) or - not isinstance(node.target, ast.Name) or - not isinstance(node.body[0].value.value, ast.Name) or - node.target.id != node.body[0].value.value.id or - node.orelse != [] + len(node.body) != 1 + or not isinstance(node.body[0], ast.Expr) + or not isinstance(node.body[0].value, ast.Yield) + or not isinstance(node.target, ast.Name) + or not isinstance(node.body[0].value.value, ast.Name) + or node.target.id != node.body[0].value.value.id + or node.orelse != [] ): iterable = unparse(node.iter) self.__error(node.lineno - 1, node.col_offset, "Y104", iterable) - + def __check105(self, node): """ Private method to check for "try-except-pass" patterns. - + @param node reference to the AST node to be checked @type ast.Try """ @@ -533,25 +529,24 @@ # except ValueError: # pass if not ( - len(node.handlers) != 1 or - not isinstance(node.handlers[0], ast.ExceptHandler) or - len(node.handlers[0].body) != 1 or - not isinstance(node.handlers[0].body[0], ast.Pass) or - node.orelse != [] + len(node.handlers) != 1 + or not isinstance(node.handlers[0], ast.ExceptHandler) + or len(node.handlers[0].body) != 1 + or not isinstance(node.handlers[0].body[0], ast.Pass) + or node.orelse != [] ): if node.handlers[0].type is None: exception = "Exception" elif isinstance(node.handlers[0].type, ast.Tuple): - exception = ", ".join( - [unparse(n) for n in node.handlers[0].type.elts]) + exception = ", ".join([unparse(n) for n in node.handlers[0].type.elts]) else: exception = unparse(node.handlers[0].type) self.__error(node.lineno - 1, node.col_offset, "Y105", exception) - + def __check106(self, node): """ Private method to check for calls where an exception is raised in else. - + @param node reference to the AST node to be checked @type ast.If """ @@ -560,25 +555,25 @@ # else: # raise Exception just_one = ( - len(node.body) == 1 and - len(node.orelse) >= 1 and - isinstance(node.orelse[-1], ast.Raise) and - not isinstance(node.body[-1], ast.Raise) + len(node.body) == 1 + and len(node.orelse) >= 1 + and isinstance(node.orelse[-1], ast.Raise) + and not isinstance(node.body[-1], ast.Raise) ) many = ( - len(node.body) > 2 * len(node.orelse) and - len(node.orelse) >= 1 and - isinstance(node.orelse[-1], ast.Raise) and - not isinstance(node.body[-1], ast.Raise) + len(node.body) > 2 * len(node.orelse) + and len(node.orelse) >= 1 + and isinstance(node.orelse[-1], ast.Raise) + and not isinstance(node.body[-1], ast.Raise) ) if just_one or many: self.__error(node.lineno - 1, node.col_offset, "Y106") - + def __check107(self, node): """ Private method to check for calls where try/except and finally have 'return'. - + @param node reference to the AST node to be checked @type ast.Try """ @@ -611,18 +606,17 @@ break if ( - (tryHasReturn or exceptHasReturn) and - finallyHasReturn and - finallyReturn is not None + (tryHasReturn or exceptHasReturn) + and finallyHasReturn + and finallyReturn is not None ): - self.__error(finallyReturn.lineno - 1, - finallyReturn.col_offset, "Y107") - + self.__error(finallyReturn.lineno - 1, finallyReturn.col_offset, "Y107") + def __check108(self, node): """ Private method to check for if-elses which could be a ternary operator assignment. - + @param node reference to the AST node to be checked @type ast.If """ @@ -639,30 +633,31 @@ # else: # b = d if ( - len(node.body) == 1 and - len(node.orelse) == 1 and - isinstance(node.body[0], ast.Assign) and - isinstance(node.orelse[0], ast.Assign) and - len(node.body[0].targets) == 1 and - len(node.orelse[0].targets) == 1 and - isinstance(node.body[0].targets[0], ast.Name) and - isinstance(node.orelse[0].targets[0], ast.Name) and - node.body[0].targets[0].id == node.orelse[0].targets[0].id and - not isinstance(node.parent, ast.If) + len(node.body) == 1 + and len(node.orelse) == 1 + and isinstance(node.body[0], ast.Assign) + and isinstance(node.orelse[0], ast.Assign) + and len(node.body[0].targets) == 1 + and len(node.orelse[0].targets) == 1 + and isinstance(node.body[0].targets[0], ast.Name) + and isinstance(node.orelse[0].targets[0], ast.Name) + and node.body[0].targets[0].id == node.orelse[0].targets[0].id + and not isinstance(node.parent, ast.If) ): assign = unparse(node.body[0].targets[0]) 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) - + + self.__error( + node.lineno - 1, node.col_offset, "Y108", assign, body, cond, orelse + ) + def __check109(self, node): """ Private method to check for multiple equalities with the same value are combined via "or". - + @param node reference to the AST node to be checked @type ast.BoolOp """ @@ -672,18 +667,15 @@ equalities = [ value for value in node.values - if isinstance(value, ast.Compare) and - len(value.ops) == 1 and - isinstance(value.ops[0], ast.Eq) + if isinstance(value, ast.Compare) + and len(value.ops) == 1 + and isinstance(value.ops[0], ast.Eq) ] ids = [] # (name, compared_to) for eq in equalities: if isinstance(eq.left, ast.Name): ids.append((eq.left, eq.comparators[0])) - if ( - len(eq.comparators) == 1 and - isinstance(eq.comparators[0], ast.Name) - ): + if len(eq.comparators) == 1 and isinstance(eq.comparators[0], ast.Name): ids.append((eq.comparators[0], eq.left)) id2count = {} @@ -694,15 +686,20 @@ for value, values in id2count.items(): if len(values) == 1: continue - - self.__error(node.lineno - 1, node.col_offset, "Y109", - value, unparse(ast.Tuple(elts=values)), - unparse(node)) - + + self.__error( + node.lineno - 1, + node.col_offset, + "Y109", + value, + unparse(ast.Tuple(elts=values)), + unparse(node), + ) + def __check110_111(self, node): """ Private method to check if any / all could be used. - + @param node reference to the AST node to be checked @type ast.For """ @@ -716,48 +713,50 @@ # return False # return True if ( - len(node.body) == 1 and - isinstance(node.body[0], ast.If) and - len(node.body[0].body) == 1 and - isinstance(node.body[0].body[0], ast.Return) and - isinstance(node.body[0].body[0].value, BOOL_CONST_TYPES) and - hasattr(node.body[0].body[0].value, "value") + len(node.body) == 1 + and isinstance(node.body[0], ast.If) + and len(node.body[0].body) == 1 + and isinstance(node.body[0].body[0], ast.Return) + and isinstance(node.body[0].body[0].value, BOOL_CONST_TYPES) + and hasattr(node.body[0].body[0].value, "value") ): check = unparse(node.body[0].test) target = unparse(node.target) iterable = unparse(node.iter) if node.body[0].body[0].value.value is True: - self.__error(node.lineno - 1, node.col_offset, "Y110", - check, target, iterable) + self.__error( + node.lineno - 1, node.col_offset, "Y110", check, target, iterable + ) elif node.body[0].body[0].value.value is False: check = "not " + check if check.startswith("not not "): - check = check[len("not not "):] - self.__error(node.lineno - 1, node.col_offset, "Y111", - check, target, iterable) - + check = check[len("not not ") :] + self.__error( + node.lineno - 1, node.col_offset, "Y111", check, target, iterable + ) + def __check112(self, node): """ Private method to check for non-capitalized calls to environment variables. - + @param node reference to the AST node to be checked @type ast.Expr """ # os.environ["foo"] # os.environ.get("bar") isIndexCall = ( - isinstance(node.value, ast.Subscript) and - isinstance(node.value.value, ast.Attribute) and - isinstance(node.value.value.value, ast.Name) and - node.value.value.value.id == "os" and - node.value.value.attr == "environ" and - ( + isinstance(node.value, ast.Subscript) + and isinstance(node.value.value, ast.Attribute) + and isinstance(node.value.value.value, ast.Name) + and node.value.value.value.id == "os" + and node.value.value.attr == "environ" + and ( ( - isinstance(node.value.slice, ast.Index) and - isinstance(node.value.slice.value, STR_TYPES) - ) or - isinstance(node.value.slice, ast.Constant) + isinstance(node.value.slice, ast.Index) + and isinstance(node.value.slice.value, STR_TYPES) + ) + or isinstance(node.value.slice, ast.Constant) ) ) if isIndexCall: @@ -778,15 +777,15 @@ hasChange = envName != envName.upper() isGetCall = ( - isinstance(node.value, ast.Call) and - isinstance(node.value.func, ast.Attribute) and - isinstance(node.value.func.value, ast.Attribute) and - isinstance(node.value.func.value.value, ast.Name) and - node.value.func.value.value.id == "os" and - node.value.func.value.attr == "environ" and - node.value.func.attr == "get" and - len(node.value.args) in [1, 2] and - isinstance(node.value.args[0], STR_TYPES) + isinstance(node.value, ast.Call) + and isinstance(node.value.func, ast.Attribute) + and isinstance(node.value.func.value, ast.Attribute) + and isinstance(node.value.func.value.value, ast.Name) + and node.value.func.value.value.id == "os" + and node.value.func.value.attr == "environ" + and node.value.func.attr == "get" + and len(node.value.args) in [1, 2] + and isinstance(node.value.args[0], STR_TYPES) ) if isGetCall: call = node.value @@ -808,20 +807,17 @@ expected = f"os.environ.get('{envName.upper()}')" else: defaultValue = unparse(node.value.args[1]) - expected = ( - f"os.environ.get('{envName.upper()}', '{defaultValue}')" - ) + expected = f"os.environ.get('{envName.upper()}', '{defaultValue}')" else: return - - self.__error(node.lineno - 1, node.col_offset, "Y112", expected, - original) - + + self.__error(node.lineno - 1, node.col_offset, "Y112", expected, original) + def __check113(self, node): """ Private method to check for loops in which "enumerate" should be used. - + @param node reference to the AST node to be checked @type ast.For """ @@ -832,21 +828,22 @@ variableCandidates = [] for expression in node.body: if ( - isinstance(expression, ast.AugAssign) and - self.__isConstantIncrease(expression) and - isinstance(expression.target, ast.Name) + isinstance(expression, ast.AugAssign) + and self.__isConstantIncrease(expression) + and isinstance(expression.target, ast.Name) ): variableCandidates.append(expression.target) for candidate in variableCandidates: - self.__error(candidate.lineno - 1, candidate.col_offset, "Y113", - unparse(candidate)) - + self.__error( + candidate.lineno - 1, candidate.col_offset, "Y113", unparse(candidate) + ) + def __check114(self, node): """ Private method to check for alternative if clauses with identical bodies. - + @param node reference to the AST node to be checked @type ast.If """ @@ -860,37 +857,42 @@ if self.__isSameBody(ifbody1[1], ifbody2[1]): errorPairs.append((ifbody1, ifbody2)) for ifbody1, ifbody2 in errorPairs: - self.__error(ifbody1[0].lineno - 1, ifbody1[0].col_offset, "Y114", - unparse(ifbody1[0]), unparse(ifbody2[0])) - + self.__error( + ifbody1[0].lineno - 1, + ifbody1[0].col_offset, + "Y114", + unparse(ifbody1[0]), + unparse(ifbody2[0]), + ) + def __check115(self, node): """ Private method to to check for places where open() is called without a context handler. - + @param node reference to the AST node to be checked @type ast.Call """ # f = open(...) - #. .. # (do something with f) + # . .. # (do something with f) # f.close() if ( - isinstance(node.func, ast.Name) and - node.func.id == "open" and - not isinstance(node.parent, ast.withitem) + isinstance(node.func, ast.Name) + and node.func.id == "open" + and not isinstance(node.parent, ast.withitem) ): self.__error(node.lineno - 1, node.col_offset, "Y115") - + def __check116(self, node): """ Private method to check for places with 3 or more consecutive if-statements with direct returns. - + * Each if-statement must be a check for equality with the same variable * Each if-statement must just have a "return" * Else must also just have a return - + @param node reference to the AST node to be checked @type ast.If """ @@ -903,16 +905,16 @@ # else: # return 42 if ( - isinstance(node.test, ast.Compare) and - isinstance(node.test.left, ast.Name) and - len(node.test.ops) == 1 and - isinstance(node.test.ops[0], ast.Eq) and - len(node.test.comparators) == 1 and - isinstance(node.test.comparators[0], AST_CONST_TYPES) and - len(node.body) == 1 and - isinstance(node.body[0], ast.Return) and - len(node.orelse) == 1 and - isinstance(node.orelse[0], ast.If) + isinstance(node.test, ast.Compare) + and isinstance(node.test.left, ast.Name) + and len(node.test.ops) == 1 + and isinstance(node.test.ops[0], ast.Eq) + and len(node.test.comparators) == 1 + and isinstance(node.test.comparators[0], AST_CONST_TYPES) + and len(node.body) == 1 + and isinstance(node.body[0], ast.Return) + and len(node.orelse) == 1 + and isinstance(node.orelse[0], ast.If) ): variable = node.test.left child = node.orelse[0] @@ -922,32 +924,28 @@ else: bodyValueStr = "None" if isinstance(node.test.comparators[0], ast.Str): - keyValuePairs = { - node.test.comparators[0].s: bodyValueStr - } + keyValuePairs = {node.test.comparators[0].s: bodyValueStr} elif isinstance(node.test.comparators[0], ast.Num): keyValuePairs = { node.test.comparators[0].n: bodyValueStr, } else: - keyValuePairs = { - node.test.comparators[0].value: bodyValueStr - } + keyValuePairs = {node.test.comparators[0].value: bodyValueStr} while child: if not ( - isinstance(child.test, ast.Compare) and - isinstance(child.test.left, ast.Name) and - child.test.left.id == variable.id and - len(child.test.ops) == 1 and - isinstance(child.test.ops[0], ast.Eq) and - len(child.test.comparators) == 1 and - isinstance(child.test.comparators[0], AST_CONST_TYPES) and - len(child.body) == 1 and - isinstance(child.body[0], ast.Return) and - len(child.orelse) <= 1 + isinstance(child.test, ast.Compare) + and isinstance(child.test.left, ast.Name) + and child.test.left.id == variable.id + and len(child.test.ops) == 1 + and isinstance(child.test.ops[0], ast.Eq) + and len(child.test.comparators) == 1 + and isinstance(child.test.comparators[0], AST_CONST_TYPES) + and len(child.body) == 1 + and isinstance(child.body[0], ast.Return) + and len(child.orelse) <= 1 ): return - + if isinstance(child.test.comparators[0], ast.Str): key = child.test.comparators[0].s elif isinstance(child.test.comparators[0], ast.Num): @@ -965,41 +963,38 @@ return else: child = None - + if len(keyValuePairs) < 3: return - + if elseValue: ret = f"{keyValuePairs}.get({variable.id}, {elseValue})" else: 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) - ): + 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 __check118(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 or ast.For """ @@ -1013,47 +1008,41 @@ # for key in dict.keys(): # # do something if ( - isinstance(node, ast.Compare) and - len(node.ops) == 1 and - isinstance(node.ops[0], ast.In) and - len(node.comparators) == 1 + isinstance(node, ast.Compare) + and len(node.ops) == 1 + and isinstance(node.ops[0], ast.In) + and len(node.comparators) == 1 ): callNode = node.comparators[0] - elif ( - isinstance(node, ast.For) - ): + elif isinstance(node, ast.For): callNode = node.iter else: callNode = None - + 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) + isinstance(callNode.func, ast.Attribute) + and callNode.func.attr == "keys" + and isinstance(callNode.func.ctx, ast.Load) ): if isinstance(node, ast.Compare): keyStr = unparse(node.left) else: keyStr = unparse(node.target) dictStr = unparse(attrNode.value) - self.__error(node.lineno - 1, node.col_offset, "Y118", - keyStr, dictStr) - + self.__error(node.lineno - 1, node.col_offset, "Y118", keyStr, dictStr) + def __check119(self, node): """ Private 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 - ): + if len(node.decorator_list) == 0 and len(node.bases) == 0: dataclassFunctions = [ "__init__", "__eq__", @@ -1064,341 +1053,337 @@ hasOnlyConstructorMethod = True for bodyElement in node.body: if ( - isinstance(bodyElement, ast.FunctionDef) and - bodyElement.name not in dataclassFunctions + 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 + 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) - + self.__error(node.lineno - 1, node.col_offset, "Y119", node.name) + def __check120_121(self, node): """ Private method to check for classes that inherit from object. - + @param node reference to the AST node to be checked @type ast.ClassDef """ # class FooBar(object): # ... if ( - len(node.bases) == 1 and - isinstance(node.bases[0], ast.Name) and - node.bases[0].id == "object" + len(node.bases) == 1 + and isinstance(node.bases[0], ast.Name) + and node.bases[0].id == "object" ): - self.__error(node.lineno - 1, node.col_offset, "Y120", - node.name) - + self.__error(node.lineno - 1, node.col_offset, "Y120", node.name) + elif ( - len(node.bases) > 1 and - isinstance(node.bases[-1], ast.Name) and - node.bases[-1].id == "object" + len(node.bases) > 1 + and isinstance(node.bases[-1], ast.Name) + and node.bases[-1].id == "object" ): - self.__error(node.lineno - 1, node.col_offset, "Y121", - node.name, ", ".join(b.id for b in node.bases[:-1])) - + self.__error( + node.lineno - 1, + node.col_offset, + "Y121", + node.name, + ", ".join(b.id for b in node.bases[:-1]), + ) + def __check122(self, node): """ Private method to check for all if-blocks which only check if a key is in a dictionary. - + @param node reference to the AST node to be checked @type ast.If """ if ( - isinstance(node.test, ast.Compare) and - len(node.test.ops) == 1 and - isinstance(node.test.ops[0], ast.In) and - len(node.body) == 1 and - len(node.orelse) == 0 + isinstance(node.test, ast.Compare) + and len(node.test.ops) == 1 + and isinstance(node.test.ops[0], ast.In) + and len(node.body) == 1 + and len(node.orelse) == 0 ) and ( # We might still be left with a check if a value is in a list or # in the body the developer might remove the element from the list. # We need to have a look at the body. - isinstance(node.body[0], ast.Assign) and - isinstance(node.body[0].value, ast.Subscript) and - len(node.body[0].targets) == 1 and - isinstance(node.body[0].targets[0], ast.Name) and - isinstance(node.body[0].value.value, ast.Name) and - isinstance(node.test.comparators[0], ast.Name) and - node.body[0].value.value.id == node.test.comparators[0].id + isinstance(node.body[0], ast.Assign) + and isinstance(node.body[0].value, ast.Subscript) + and len(node.body[0].targets) == 1 + and isinstance(node.body[0].targets[0], ast.Name) + and isinstance(node.body[0].value.value, ast.Name) + and isinstance(node.test.comparators[0], ast.Name) + and node.body[0].value.value.id == node.test.comparators[0].id ): key = unparse(node.test.left) dictname = unparse(node.test.comparators[0]) - self.__error(node.lineno - 1, node.col_offset, "Y122", - dictname, key) - + self.__error(node.lineno - 1, node.col_offset, "Y122", dictname, key) + def __check181(self, node): """ Private method to check for assignments that could be converted into an augmented assignment. - + @param node reference to the AST node to be checked @type ast.Assign """ # a = a - b if ( - len(node.targets) == 1 and - isinstance(node.targets[0], ast.Name) and - isinstance(node.value, ast.BinOp) and - isinstance(node.value.left, ast.Name) and - node.value.left.id == node.targets[0].id and - not isinstance(node.value.right, ast.Tuple) + len(node.targets) == 1 + and isinstance(node.targets[0], ast.Name) + and isinstance(node.value, ast.BinOp) + and isinstance(node.value.left, ast.Name) + and node.value.left.id == node.targets[0].id + and not isinstance(node.value.right, ast.Tuple) ): - newNode = ast.AugAssign(node.targets[0], node.value.op, - node.value.right) - self.__error(node.lineno - 1, node.col_offset, "Y181", - unparse(newNode), unparse(node)) - + newNode = ast.AugAssign(node.targets[0], node.value.op, node.value.right) + self.__error( + node.lineno - 1, + node.col_offset, + "Y181", + unparse(newNode), + unparse(node), + ) + def __check182(self, node): """ Private method to check for calls of type 'super()' that could be shortened to 'super()'. - + @param node reference to the AST node to be checked @type ast.Call """ # super() if ( - self.__classDefinitionStack and - isinstance(node.func, ast.Name) and - node.func.id == "super" and - len(node.args) == 2 and - all(isinstance(arg, ast.Name) for arg in node.args) and - node.args[0].id == self.__classDefinitionStack[-1] and - node.args[1].id == "self" + self.__classDefinitionStack + and isinstance(node.func, ast.Name) + and node.func.id == "super" + and len(node.args) == 2 + and all(isinstance(arg, ast.Name) for arg in node.args) + and node.args[0].id == self.__classDefinitionStack[-1] + and node.args[1].id == "self" ): - self.__error(node.lineno - 1, node.col_offset, "Y182", - unparse(node)) - + self.__error(node.lineno - 1, node.col_offset, "Y182", unparse(node)) + def __check201(self, node): """ Private method to check for calls where an unary 'not' is used for an unequality. - + @param node reference to the UnaryOp node @type ast.UnaryOp """ # not a == b if not ( ( - not isinstance(node.op, ast.Not) or - not isinstance(node.operand, ast.Compare) or - len(node.operand.ops) != 1 or - not isinstance(node.operand.ops[0], ast.Eq) - ) or - isinstance(node.parent, ast.If) and - self.__isExceptionCheck(node.parent) + not isinstance(node.op, ast.Not) + or not isinstance(node.operand, ast.Compare) + or len(node.operand.ops) != 1 + or not isinstance(node.operand.ops[0], ast.Eq) + ) + or isinstance(node.parent, ast.If) + and self.__isExceptionCheck(node.parent) ): comparison = node.operand left = unparse(comparison.left) right = unparse(comparison.comparators[0]) - self.__error(node.lineno - 1, node.col_offset, "Y201", - left, right) - + self.__error(node.lineno - 1, node.col_offset, "Y201", left, right) + def __check202(self, node): """ Private method to check for calls where an unary 'not' is used for an equality. - + @param node reference to the UnaryOp node @type ast.UnaryOp """ # not a != b if not ( ( - not isinstance(node.op, ast.Not) or - not isinstance(node.operand, ast.Compare) or - len(node.operand.ops) != 1 or - not isinstance(node.operand.ops[0], ast.NotEq) - ) or - isinstance(node.parent, ast.If) and - self.__isExceptionCheck(node.parent) + not isinstance(node.op, ast.Not) + or not isinstance(node.operand, ast.Compare) + or len(node.operand.ops) != 1 + or not isinstance(node.operand.ops[0], ast.NotEq) + ) + or isinstance(node.parent, ast.If) + and self.__isExceptionCheck(node.parent) ): comparison = node.operand left = unparse(comparison.left) right = unparse(comparison.comparators[0]) - self.__error(node.lineno - 1, node.col_offset, "Y202", - left, right) - + self.__error(node.lineno - 1, node.col_offset, "Y202", left, right) + def __check203(self, node): """ Private method to check for calls where an unary 'not' is used for an in-check. - + @param node reference to the UnaryOp node @type ast.UnaryOp """ # not a in b if not ( ( - not isinstance(node.op, ast.Not) or - not isinstance(node.operand, ast.Compare) or - len(node.operand.ops) != 1 or - not isinstance(node.operand.ops[0], ast.In) - ) or - isinstance(node.parent, ast.If) and - self.__isExceptionCheck(node.parent) + not isinstance(node.op, ast.Not) + or not isinstance(node.operand, ast.Compare) + or len(node.operand.ops) != 1 + or not isinstance(node.operand.ops[0], ast.In) + ) + or isinstance(node.parent, ast.If) + and self.__isExceptionCheck(node.parent) ): comparison = node.operand left = unparse(comparison.left) right = unparse(comparison.comparators[0]) - self.__error(node.lineno - 1, node.col_offset, "Y203", - left, right) - + self.__error(node.lineno - 1, node.col_offset, "Y203", left, right) + def __check204(self, node): """ Private method to check for calls of the type "not (a < b)". - + @param node reference to the UnaryOp node @type ast.UnaryOp """ # not a < b if not ( ( - not isinstance(node.op, ast.Not) or - not isinstance(node.operand, ast.Compare) or - len(node.operand.ops) != 1 or - not isinstance(node.operand.ops[0], ast.Lt) - ) or - isinstance(node.parent, ast.If) and - self.__isExceptionCheck(node.parent) + not isinstance(node.op, ast.Not) + or not isinstance(node.operand, ast.Compare) + or len(node.operand.ops) != 1 + or not isinstance(node.operand.ops[0], ast.Lt) + ) + or isinstance(node.parent, ast.If) + and self.__isExceptionCheck(node.parent) ): comparison = node.operand left = unparse(comparison.left) right = unparse(comparison.comparators[0]) - self.__error(node.lineno - 1, node.col_offset, "Y204", - left, right) - + self.__error(node.lineno - 1, node.col_offset, "Y204", left, right) + def __check205(self, node): """ Private method to check for calls of the type "not (a <= b)". - + @param node reference to the UnaryOp node @type ast.UnaryOp """ # not a <= b if not ( ( - not isinstance(node.op, ast.Not) or - not isinstance(node.operand, ast.Compare) or - len(node.operand.ops) != 1 or - not isinstance(node.operand.ops[0], ast.LtE) - ) or - isinstance(node.parent, ast.If) and - self.__isExceptionCheck(node.parent) + not isinstance(node.op, ast.Not) + or not isinstance(node.operand, ast.Compare) + or len(node.operand.ops) != 1 + or not isinstance(node.operand.ops[0], ast.LtE) + ) + or isinstance(node.parent, ast.If) + and self.__isExceptionCheck(node.parent) ): comparison = node.operand left = unparse(comparison.left) right = unparse(comparison.comparators[0]) - self.__error(node.lineno - 1, node.col_offset, "Y205", - left, right) - + self.__error(node.lineno - 1, node.col_offset, "Y205", left, right) + def __check206(self, node): """ Private method to check for calls of the type "not (a > b)". - + @param node reference to the UnaryOp node @type ast.UnaryOp """ # not a > b if not ( ( - not isinstance(node.op, ast.Not) or - not isinstance(node.operand, ast.Compare) or - len(node.operand.ops) != 1 or - not isinstance(node.operand.ops[0], ast.Gt) - ) or - isinstance(node.parent, ast.If) and - self.__isExceptionCheck(node.parent) + not isinstance(node.op, ast.Not) + or not isinstance(node.operand, ast.Compare) + or len(node.operand.ops) != 1 + or not isinstance(node.operand.ops[0], ast.Gt) + ) + or isinstance(node.parent, ast.If) + and self.__isExceptionCheck(node.parent) ): comparison = node.operand left = unparse(comparison.left) right = unparse(comparison.comparators[0]) - self.__error(node.lineno - 1, node.col_offset, "Y206", - left, right) - + self.__error(node.lineno - 1, node.col_offset, "Y206", left, right) + def __check207(self, node): """ Private method to check for calls of the type "not (a >= b)". - + @param node reference to the UnaryOp node @type ast.UnaryOp """ # not a >= b if not ( ( - not isinstance(node.op, ast.Not) or - not isinstance(node.operand, ast.Compare) or - len(node.operand.ops) != 1 or - not isinstance(node.operand.ops[0], ast.GtE) - ) or - isinstance(node.parent, ast.If) and - self.__isExceptionCheck(node.parent) + not isinstance(node.op, ast.Not) + or not isinstance(node.operand, ast.Compare) + or len(node.operand.ops) != 1 + or not isinstance(node.operand.ops[0], ast.GtE) + ) + or isinstance(node.parent, ast.If) + and self.__isExceptionCheck(node.parent) ): comparison = node.operand left = unparse(comparison.left) right = unparse(comparison.comparators[0]) - self.__error(node.lineno - 1, node.col_offset, "Y207", - left, right) - + self.__error(node.lineno - 1, node.col_offset, "Y207", left, right) + def __check208(self, node): """ Private method to check for calls of the type "not (not a)". - + @param node reference to the UnaryOp node @type ast.UnaryOp """ # not (not a) if ( - isinstance(node.op, ast.Not) and - isinstance(node.operand, ast.UnaryOp) and - isinstance(node.operand.op, ast.Not) + isinstance(node.op, ast.Not) + and isinstance(node.operand, ast.UnaryOp) + and isinstance(node.operand.op, ast.Not) ): var = unparse(node.operand.operand) self.__error(node.lineno - 1, node.col_offset, "Y208", var) - + def __check211(self, node): """ Private method to check for calls of the type "True if a else False". - + @param node reference to the AST node to be checked @type ast.IfExp """ # True if a else False if ( - isinstance(node.body, BOOL_CONST_TYPES) and - node.body.value is True and - isinstance(node.orelse, BOOL_CONST_TYPES) and - node.orelse.value is False + isinstance(node.body, BOOL_CONST_TYPES) + and node.body.value is True + and isinstance(node.orelse, BOOL_CONST_TYPES) + and node.orelse.value is False ): cond = unparse(node.test) if isinstance(node.test, ast.Name): newCond = "bool({0})".format(cond) else: newCond = cond - self.__error(node.lineno - 1, node.col_offset, "Y211", - cond, newCond) - + self.__error(node.lineno - 1, node.col_offset, "Y211", cond, newCond) + def __check212(self, node): """ Private method to check for calls of the type "False if a else True". - + @param node reference to the AST node to be checked @type ast.IfExp """ # False if a else True if ( - isinstance(node.body, BOOL_CONST_TYPES) and - node.body.value is False and - isinstance(node.orelse, BOOL_CONST_TYPES) and - node.orelse.value is True + isinstance(node.body, BOOL_CONST_TYPES) + and node.body.value is False + and isinstance(node.orelse, BOOL_CONST_TYPES) + and node.orelse.value is True ): cond = unparse(node.test) if isinstance(node.test, ast.Name): @@ -1408,96 +1393,77 @@ newCond = unparse(self.__negateTest(node.test)) else: newCond = "not ({0})".format(cond) - self.__error(node.lineno - 1, node.col_offset, "Y212", - cond, newCond) - + self.__error(node.lineno - 1, node.col_offset, "Y212", cond, newCond) + def __check213(self, node): """ Private method to check for calls of the type "b if not a else a". - + @param node reference to the AST node to be checked @type ast.IfExp """ # b if not a else a if ( - isinstance(node.test, ast.UnaryOp) and - isinstance(node.test.op, ast.Not) and - self.__isSameExpression(node.test.operand, node.orelse) + isinstance(node.test, ast.UnaryOp) + and isinstance(node.test.op, ast.Not) + and self.__isSameExpression(node.test.operand, node.orelse) ): a = unparse(node.test.operand) b = unparse(node.body) self.__error(node.lineno - 1, node.col_offset, "Y213", a, b) - + def __check221(self, node): """ Private method to check for calls of the type "a and not a". - + @param node reference to the AST node to be checked @type ast.BoolOp """ # a and not a - if ( - isinstance(node.op, ast.And) and - len(node.values) >= 2 - ): + if isinstance(node.op, ast.And) and len(node.values) >= 2: # We have a boolean And. Let's make sure there is two times the # same expression, but once with a "not" negatedExpressions = [] nonNegatedExpressions = [] for exp in node.values: - if ( - isinstance(exp, ast.UnaryOp) and - isinstance(exp.op, ast.Not) - ): + if isinstance(exp, ast.UnaryOp) and isinstance(exp.op, ast.Not): negatedExpressions.append(exp.operand) else: nonNegatedExpressions.append(exp) for negatedExpression in negatedExpressions: for nonNegatedExpression in nonNegatedExpressions: - if self.__isSameExpression( - negatedExpression, nonNegatedExpression - ): + if self.__isSameExpression(negatedExpression, nonNegatedExpression): negExp = unparse(negatedExpression) - self.__error(node.lineno - 1, node.col_offset, "Y221", - negExp) - + self.__error(node.lineno - 1, node.col_offset, "Y221", negExp) + def __check222(self, node): """ Private method to check for calls of the type "a or not a". - + @param node reference to the AST node to be checked @type ast.BoolOp """ # a or not a - if ( - isinstance(node.op, ast.Or) and - len(node.values) >= 2 - ): + if isinstance(node.op, ast.Or) and len(node.values) >= 2: # We have a boolean And. Let's make sure there is two times the # same expression, but once with a "not" negatedExpressions = [] nonNegatedExpressions = [] for exp in node.values: - if ( - isinstance(exp, ast.UnaryOp) and - isinstance(exp.op, ast.Not) - ): + if isinstance(exp, ast.UnaryOp) and isinstance(exp.op, ast.Not): negatedExpressions.append(exp.operand) else: nonNegatedExpressions.append(exp) for negatedExpression in negatedExpressions: for nonNegatedExpression in nonNegatedExpressions: - if self.__isSameExpression( - negatedExpression, nonNegatedExpression - ): + if self.__isSameExpression(negatedExpression, nonNegatedExpression): negExp = unparse(negatedExpression) - self.__error(node.lineno - 1, node.col_offset, "Y222", - negExp) - + self.__error(node.lineno - 1, node.col_offset, "Y222", negExp) + def __check223(self, node): """ Private method to check for calls of the type "... or True". - + @param node reference to the AST node to be checked @type ast.BoolOp """ @@ -1506,11 +1472,11 @@ for exp in node.values: if isinstance(exp, BOOL_CONST_TYPES) and exp.value is True: self.__error(node.lineno - 1, node.col_offset, "Y223") - + def __check224(self, node): """ Private method to check for calls of the type "... and False". - + @param node reference to the AST node to be checked @type ast.BoolOp """ @@ -1519,79 +1485,70 @@ for exp in node.values: if isinstance(exp, BOOL_CONST_TYPES) and exp.value is False: self.__error(node.lineno - 1, node.col_offset, "Y224") - + def __check301(self, node): """ Private method to check for Yoda conditions. - + @param node reference to the AST node to be checked @type ast.Compare """ # 42 == age if ( - isinstance(node.left, AST_CONST_TYPES) and - len(node.ops) == 1 and - isinstance(node.ops[0], ast.Eq) + isinstance(node.left, AST_CONST_TYPES) + and len(node.ops) == 1 + and isinstance(node.ops[0], ast.Eq) ): left = unparse(node.left) isPy37Str = isinstance(node.left, ast.Str) - isPy38Str = ( - isinstance(node.left, ast.Constant) and - isinstance(node.left.value, str) + isPy38Str = isinstance(node.left, ast.Constant) and isinstance( + node.left.value, str ) if isPy37Str or isPy38Str: left = f"'{left}'" right = unparse(node.comparators[0]) - self.__error(node.lineno - 1, node.col_offset, "Y301", - left, right) - + self.__error(node.lineno - 1, node.col_offset, "Y301", left, right) + def __check401(self, node): """ Private method to check for bare boolean function arguments. - + @param node reference to the AST node to be checked @type ast.Call """ # foo(a, b, True) hasBareBool = any( - isinstance(callArg, ast.Constant) and - (callArg.value is True or callArg.value is False) + isinstance(callArg, ast.Constant) + and (callArg.value is True or callArg.value is False) for callArg in node.args ) - isException = ( - isinstance(node.func, ast.Attribute) and - node.func.attr in ["get"] - ) - + isException = isinstance(node.func, ast.Attribute) and node.func.attr in ["get"] + if hasBareBool and not isException: self.__error(node.lineno - 1, node.col_offset, "Y401") - + def __check402(self, node): """ Private method to check for bare numeric function arguments. - + @param node reference to the AST node to be checked @type ast.Call """ # foo(a, b, 123123) hasBareNumeric = any( - isinstance(callArg, ast.Constant) and - type(callArg.value) in (float, int) + isinstance(callArg, ast.Constant) and type(callArg.value) in (float, int) for callArg in node.args ) - isException = ( - isinstance(node.func, ast.Name) and - node.func.id == "range" - ) + isException = isinstance(node.func, ast.Name) and node.func.id == "range" isException = isException or ( - isinstance(node.func, ast.Attribute) and - node.func.attr in ("get", "insert") + isinstance(node.func, ast.Attribute) and node.func.attr in ("get", "insert") ) - + if hasBareNumeric and not isException: self.__error(node.lineno - 1, node.col_offset, "Y402") + # # eflag: noqa = M891