def fix_line_number(tree: ast.AST) -> ast.AST: """ Adjusts line number for some nodes. They are set incorrectly for some collections. It might be either a bug or a feature. We do several checks here, to be sure that we won't get an incorrect line number. But, we basically check if there's a parent, so we can compare and adjust. Example:: print(( # should start from here 1, 2, 3, # actually starts from here )) """ affected = (ast.Tuple, ) for node in ast.walk(tree): if isinstance(node, affected): parent_lineno = getattr(get_parent(node), 'lineno', None) if parent_lineno and parent_lineno < node.lineno: node.lineno = node.lineno - 1 return tree
def _check_last_return_in_function(self, node: ast.Return) -> None: parent = get_parent(node) if not isinstance(parent, (ast.FunctionDef, ast.AsyncFunctionDef)): return if node is parent.body[-1] and node.value is None: self.add_violation(InconsistentReturnViolation(node))
def _check_variable_definitions(self, node: ast.withitem) -> None: if node.optional_vars is None: return if not is_valid_block_variable_definition(node.optional_vars): self.add_violation( ContextManagerVariableDefinitionViolation(get_parent(node)), )
def _check_nested_classes(self, node: ast.ClassDef) -> None: parent = get_parent(node) is_inside_class = isinstance(parent, ast.ClassDef) is_inside_function = isinstance(parent, self._function_nodes) if is_inside_class and node.name not in NESTED_CLASSES_WHITELIST: self.add_violation(NestedClassViolation(node, text=node.name)) elif is_inside_function: self.add_violation(NestedClassViolation(node, text=node.name))
def _check_metadata(self, node: ast.Assign) -> None: if not isinstance(nodes.get_parent(node), ast.Module): return for target_node in node.targets: target_node_id = getattr(target_node, 'id', None) if target_node_id in MODULE_METADATA_VARIABLES_BLACKLIST: self.add_violation( WrongModuleMetadataViolation(node, text=target_node_id), )
def count_unary_operator( node: ast.AST, operator: AnyUnaryOp, amount: int = 0, ) -> int: """Returns amount of unary operators matching input.""" parent = get_parent(node) if parent is None or not isinstance(parent, ast.UnaryOp): return amount if isinstance(parent.op, (operator, )): return count_unary_operator(parent, operator, amount + 1) return count_unary_operator(parent, operator, amount)
def _check_expression( self, node: ast.Expr, is_first: bool = False, ) -> None: if isinstance(node.value, self._have_effect): return if is_first and is_doc_string(node): if isinstance(get_parent(node), self._have_doc_strings): return self.add_violation(StatementHasNoEffectViolation(node))
def get_parent_ignoring_unary(node: ast.AST) -> Optional[ast.AST]: """ Returns real parent ignoring proxy unary parent level. What can go wrong? 1. Number can be negative: ``x = -1``, so ``1`` has ``UnaryOp`` as parent, but should return ``Assign`` 2. Some values can be negated: ``x = --some``, so ``some`` has ``UnaryOp`` as parent, but should return ``Assign`` """ parent = get_parent(node) if parent is None or not isinstance(parent, ast.UnaryOp): return parent return get_parent_ignoring_unary(parent)
def _update_variables( self, function: AnyFunctionDef, variable_def: ast.Name, ) -> None: """ Increases the counter of local variables. What is treated as a local variable? Check ``TooManyLocalsViolation`` documentation. """ function_variables = self.variables[function] if variable_def.id not in function_variables: if variable_def.id == UNUSED_VARIABLE: return if isinstance(get_parent(variable_def), self._not_contain_locals): return function_variables.append(variable_def.id)
def _check_members_count(self, node: ModuleMembers) -> None: """This method increases the number of module members.""" is_real_method = is_method(getattr(node, 'function_type', None)) if isinstance(get_parent(node), ast.Module) and not is_real_method: self._public_items_count += 1
def _check_method(self, node: AnyFunctionDef) -> None: parent = get_parent(node) if isinstance(parent, ast.ClassDef): self._methods[parent] += 1
def _check_fors(self, node: ast.comprehension) -> None: parent = get_parent(node) self._fors[parent] = len(parent.generators) # type: ignore
def _check_ifs(self, node: ast.comprehension) -> None: if len(node.ifs) > self._max_ifs: # We are trying to fix line number in the report, # since `comprehension` does not have this property. parent = get_parent(node) or node self.add_violation(MultipleIfsInComprehensionViolation(parent))
def check_nested_import(self, node: AnyImport) -> None: parent = nodes.get_parent(node) if parent is not None and not isinstance(parent, ast.Module): self._error_callback(NestedImportViolation(node))
def _check_nested_lambdas(self, node: ast.Lambda) -> None: if isinstance(get_parent(node), ast.Lambda): self.add_violation(NestedFunctionViolation(node, text='lambda'))
def _check_nested_function(self, node: AnyFunctionDef) -> None: is_inside_function = isinstance(get_parent(node), self._function_nodes) if is_inside_function and node.name not in NESTED_FUNCTIONS_WHITELIST: self.add_violation(NestedFunctionViolation(node, text=node.name))