def _is_decorator( self, node: ast.AST, ) -> bool: parent = walk.get_closest_parent(node, FunctionNodes) if isinstance(parent, FunctionNodes) and parent.decorator_list: return any( node == decorator or walk.is_contained_by(node, decorator) for decorator in parent.decorator_list) return False
def _check_nested_ifexpr(self, node: AnyIf) -> None: is_nested_in_if = bool( isinstance(node, ast.If) and list(walk.get_subnodes_by_type(node.test, ast.IfExp)), ) is_nested_poorly = walk.get_closest_parent( node, self._forbidden_expression_parents, ) if is_nested_in_if or is_nested_poorly: self.add_violation(NestedTernaryViolation(node))
def _add_expression(self, node: ast.AST) -> None: if any(ignore(node) for ignore in self._ignore_predicates): return source_code = source.node_to_string(node) self._module_expressions[source_code].append(node) maybe_function = walk.get_closest_parent(node, FunctionNodes) if maybe_function is not None: self._function_expressions[maybe_function][source_code].append( node, )
def is_decorator(node: ast.AST) -> bool: """ Detects if node is used as a decorator. We use this predicates because decorators can be used miltiple times. Like ``@auth_required(login_url=LOGIN_URL)`` and similar. """ parent = walk.get_closest_parent(node, FunctionNodes) if isinstance(parent, FunctionNodes) and parent.decorator_list: return any(node == decorator or walk.is_contained_by(node, decorator) for decorator in parent.decorator_list) return False
def _is_annotation(self, node: ast.AST) -> bool: typed_assign = walk.get_closest_parent( node, (ast.AnnAssign, ast.arg), ) if isinstance(typed_assign, _AnnNodes) and typed_assign.annotation: is_same_node = node == typed_assign.annotation is_child_annotation = walk.is_contained_by( node, typed_assign.annotation, ) return is_same_node or is_child_annotation return False
def _add_expression(self, node: ast.AST) -> None: ignore_predicates = [ self._is_decorator, self._is_self_method, self._is_annotation, # We use this predicate because classes have quite complex # DSL to be created: like django-orm, attrs, and dataclasses. # And these DSLs are built using attributes and calls. _is_class_context, ] if any(ignore(node) for ignore in ignore_predicates): return source_code = astor.to_source(node).strip() self._module_expressions[source_code].append(node) maybe_function = walk.get_closest_parent(node, FunctionNodes) if maybe_function is not None: self._function_expressions[maybe_function][source_code].append( node, )
def is_annotation(node: ast.AST) -> bool: """ Detects if node is an annotation. Or a part of it. We use this predicate to allow all types of repetetive function and instance annotations. """ if not isinstance(node, _AnnParts): return False annotated = walk.get_closest_parent(node, (*_AnnNodes, *FunctionNodes)) if isinstance(annotated, FunctionNodes): contains_node = bool( annotated.returns and walk.is_contained_by(node, annotated.returns), ) return node == annotated.returns or contains_node elif isinstance(annotated, _AnnNodes): contains_node = bool( annotated.annotation and walk.is_contained_by(node, annotated.annotation), ) return node == annotated.annotation or contains_node return False
def _check_bare_raise(self, node: ast.Raise) -> None: if node.exc is None: parent_except = walk.get_closest_parent(node, ast.ExceptHandler) if not parent_except: self.add_violation(BareRaiseViolation(node))
def _check_nested_lambdas(self, node: ast.Lambda) -> None: is_direct = isinstance(get_context(node), ast.Lambda) is_deep = get_closest_parent(node, ast.Lambda) if is_direct or is_deep: self.add_violation(NestedFunctionViolation(node, text='lambda'))