def _check_range_len(self, node: ast.Call) -> None: if not isinstance(nodes.get_parent(node), ForNodes): return if not functions.given_function_called(node, {'range'}): return args_len = len(node.args) is_one_arg_range = ( args_len == 1 and isinstance(node.args[0], ast.Call) and functions.given_function_called(node.args[0], {'len'}) ) is_two_args_range = ( self._is_multiple_args_range_with_len(node) and args_len == 2 ) # for three args add violation # only if `step` arg do not equals 1 or -1 step_arg = args_len == 3 and operators.unwrap_unary_node(node.args[2]) is_three_args_range = ( self._is_multiple_args_range_with_len(node) and args_len == 3 and isinstance(step_arg, ast.Num) and abs(step_arg.n) == 1 ) if any([is_one_arg_range, is_two_args_range, is_three_args_range]): self.add_violation(ImplicitEnumerateViolation(node))
def _check_range_len(self, node: ast.Call) -> None: function_name = functions.given_function_called(node, {'range'}) if not function_name: return is_one_argument_range = (len(node.args) == 1 and isinstance(node.args[0], ast.Call) and functions.given_function_called( node.args[0], {'len'})) is_two_arguments_range = (len(node.args) in {2, 3} and isinstance(node.args[1], ast.Call) and functions.given_function_called( node.args[1], {'len'})) if is_one_argument_range or is_two_arguments_range: self.add_violation(ImplicitEnumerateViolation(node))
def _check_type_compare(self, node: ast.Call) -> None: function_name = functions.given_function_called(node, {'type'}) if not function_name: return if isinstance(nodes.get_parent(node), ast.Compare): self.add_violation(TypeCompareViolation(node))
def _check_unnecessary_literals(self, node: ast.Call) -> None: function_name = functions.given_function_called( node, LITERALS_BLACKLIST, ) if function_name and not node.args: self.add_violation(consistency.UnnecessaryLiteralsViolation(node))
def _check_isinstance_call(self, node: ast.Call) -> None: function_name = functions.given_function_called(node, {'isinstance'}) if not function_name or len(node.args) != 2: return if isinstance(node.args[1], ast.Tuple): if len(node.args[1].elts) == 1: self.add_violation(WrongIsinstanceWithTupleViolation(node))
def _check_wrong_function_called(self, node: ast.Call) -> None: function_name = functions.given_function_called( node, FUNCTIONS_BLACKLIST, ) if function_name: self.add_violation( WrongFunctionCallViolation(node, text=function_name), )
def test_given_function_called_no_split( parse_ast_tree, function_call: str, function_name: str, ) -> None: """Test given_function_called without splitting the modules.""" tree = parse_ast_tree(function_call) node = tree.body[0].value called_function = functions.given_function_called(node, [function_name]) assert called_function == function_name
def _check_open_call_context(self, node: ast.Call) -> None: function_name = functions.given_function_called(node, {'open'}) if not function_name: return if isinstance(nodes.get_parent(node), ast.withitem): # We do not care about `with` or `async with` - both are fine. return self.add_violation(OpenWithoutContextManagerViolation(node))
def _check_unpythonic_compare(self, node: ast.Compare) -> None: all_nodes = [node.left, *node.comparators] for index, compare in enumerate(all_nodes): if not isinstance(compare, ast.Call): continue if functions.given_function_called(compare, {'len'}): ps = index - len(all_nodes) + 1 if not _is_correct_len(node.ops[ps], node.comparators[ps]): self.add_violation(UselessLenCompareViolation(node))
def visit_Call(self, node: ast.Call) -> None: """Used to find ``FUNCTIONS_BLACKLIST`` calls.""" self._check_wrong_function_called(node) self._check_boolean_arguments(node) self._check_isinstance_call(node) if functions.given_function_called(node, {'super'}): self._check_super_context(node) self._check_super_arguments(node) self.generic_visit(node)
def _check_floating_nan(self, node: ast.Call) -> None: if len(node.args) != 1: return if not isinstance(node.args[0], (ast.Str, ast.Bytes)): return if not functions.given_function_called(node, 'float'): return if node.args[0].s.lower() in self._nan_variants: self.add_violation(FloatingNanViolation(node))
def _is_call_ignored(self, node: ast.Call) -> bool: call = source.node_to_string(node.func) func_called = functions.given_function_called( node, self._functions.keys(), ) return bool( func_called and len(node.args) == self._functions[func_called], ) or any( call.endswith(post) for post in self._postfixes if len(node.args) == self._postfixes[post])
def _check_slice_assignment(self, node: ast.Subscript) -> None: if not isinstance(node.ctx, ast.Store): return subscript_slice_assignment = isinstance(node.slice, ast.Slice) slice_expr = get_slice_expr(node) slice_function_assignment = (isinstance(slice_expr, ast.Call) and functions.given_function_called( slice_expr, {'slice'})) if subscript_slice_assignment or slice_function_assignment: self.add_violation(consistency.AssignToSliceViolation(node), )
def _duplicated_isinstance_call(node: ast.BoolOp) -> List[str]: counter: DefaultDict[str, int] = defaultdict(int) for call in node.values: if not isinstance(call, ast.Call) or len(call.args) != 2: continue if not given_function_called(call, {'isinstance'}): continue isinstance_object = source.node_to_string(call.args[0]) counter[isinstance_object] += 1 return [node_name for node_name, count in counter.items() if count > 1]
def _is_modulo_pattern_exception(self, parent: Optional[ast.AST]) -> bool: """ Check if string with modulo pattern is in an exceptional situation. Basically we have some function names in which we allow strings with modulo patterns because they must have them for the functions to work properly. """ if parent and isinstance(parent, ast.Call): return bool( functions.given_function_called( parent, self._modulo_pattern_exceptions, split_modules=True, )) return False
def visit_Call(self, node: ast.Call) -> None: """ Used to find ``FUNCTIONS_BLACKLIST`` calls. Raises: BooleanPositionalArgumentViolation WrongFunctionCallViolation WrongIsinstanceWithTupleViolation WrongSuperCallAccessViolation WrongSuperCallViolation """ self._check_wrong_function_called(node) self._check_boolean_arguments(node) self._check_isinstance_call(node) if functions.given_function_called(node, {'super'}): self._check_super_context(node) self._check_super_arguments(node) self.generic_visit(node)
def _is_self_call(func: AnyFunctionDef, node: AST) -> bool: return ( isinstance(node, Call) and isinstance(node.func, Attribute) and bool(given_function_called(node, {'self.{0}'.format(func.name)})) )
def _check_useless_len(self, node: AnyIf) -> None: if isinstance(node.test, ast.Call): if given_function_called(node.test, {'len'}): self.add_violation(UselessLenCompareViolation(node))
def _check_super_call(self, node: ast.Call) -> None: function_name = functions.given_function_called(node, {'super'}) if function_name: self._ensure_super_context(node) self._ensure_super_arguments(node)
def _is_wrong_len(self, node: ast.BinOp, element: str) -> bool: return (isinstance(node.left, ast.Call) and bool(functions.given_function_called(node.left, {'len'})) and source.node_to_string(node.left.args[0]) == element)
def _is_multiple_args_range_with_len(self, node: ast.Call) -> bool: return bool( len(node.args) in {2, 3} and isinstance(node.args[1], ast.Call) and functions.given_function_called(node.args[1], {'len'}), )
def _check_function_recursion(func: AnyFunctionDef) -> bool: return bool([ node for node in walk(func) if isinstance(node, Call) and given_function_called(node, {func.name}) ])