def is_same_slice(
    iterable: str,
    target: str,
    node: ast.Subscript,
) -> bool:
    """Used to tell when slice is identical to some pair of name/index."""
    return (source.node_to_string(node.value) == iterable
            and source.node_to_string(get_slice_expr(node)) == target)
Example #2
0
def is_same_slice(
    iterable: str,
    target: str,
    node: ast.Subscript,
) -> bool:
    """Used to tell when slice is identical to some pair of name/index."""
    return (source.node_to_string(node.value) == iterable
            and isinstance(node.slice, ast.Index) and  # mypy is unhappy
            source.node_to_string(node.slice.value) == target)
    def _check_implicit_items(self, node: ast.For) -> None:
        iterable = source.node_to_string(node.iter)
        target = source.node_to_string(node.target)

        for sub in ast.walk(node):
            has_violation = (isinstance(sub, ast.Subscript)
                             and not self._is_assigned_target(sub)
                             and slices.is_same_slice(iterable, target, sub))
            if has_violation:
                self.add_violation(ImplicitItemsIteratorViolation(node))
                break
Example #4
0
def get_all_exception_names(node: ast.Try) -> List[str]:
    """Returns a list of all exceptions names in ``ast.Try``."""
    exceptions: List[str] = []
    for exc_handler in node.handlers:
        # There might be complex things hidden inside an exception type,
        # so we want to get the string representation of it:
        if isinstance(exc_handler.type, ast.Name):
            exceptions.append(source.node_to_string(exc_handler.type))
        elif isinstance(exc_handler.type, ast.Tuple):
            exceptions.extend([
                source.node_to_string(node) for node in exc_handler.type.elts
            ])
    return exceptions
Example #5
0
    def _check_implicit_get(self, node: ast.If) -> None:
        if not isinstance(node.test, ast.Compare):
            return
        if not isinstance(node.test.ops[0], ast.In):
            return

        checked_key = source.node_to_string(node.test.left)
        checked_collection = source.node_to_string(node.test.comparators[0])

        for sub in ast.walk(node):
            if not isinstance(sub, ast.Subscript):
                continue

            if slices.is_same_slice(checked_collection, checked_key, sub):
                self.add_violation(refactoring.ImplicitDictGetViolation(sub))
def is_property_setter(node: ast.AST, _=None):
    """Check that function decorated with `property.setter`."""
    if isinstance(node, FunctionNodes):
        for decorator in node.decorator_list:
            if node_to_string(decorator) in _property_exceptions:
                return True
    return False
    def _check_set_elements(
        self,
        node: Union[ast.Set, ast.Dict],
        keys_or_elts: _HashItems,
    ) -> None:
        elements: List[str] = []
        element_values = []

        for set_item in keys_or_elts:
            if set_item is None:
                continue  # happens for `{**a}`

            real_item = operators.unwrap_unary_node(set_item)
            if isinstance(real_item, self._elements_in_sets):
                # Similar look:
                node_repr = source.node_to_string(set_item)
                elements.append(node_repr.strip().strip('(').strip(')'))

            real_item = operators.unwrap_starred_node(real_item)

            # Non-constant nodes raise ValueError,
            # unhashables raise TypeError:
            with suppress(ValueError, TypeError):
                # Similar value:
                element_values.append(
                    safe_eval.literal_eval_with_names(real_item, )
                    if isinstance(
                        real_item,
                        self._elements_to_eval,
                    ) else set_item, )
        self._report_set_elements(node, elements, element_values)
def is_function_overload(node: ast.AST) -> bool:
    """Check that function decorated with `typing.overload`."""
    if isinstance(node, FunctionNodes):
        for decorator in node.decorator_list:
            if node_to_string(decorator) in _overload_exceptions:
                return True
    return False
def is_property_setter(node: ast.AST) -> bool:
    """Check that function decorated with ``@property.setter``."""
    if isinstance(node, FunctionNodes):
        for decorator in node.decorator_list:
            if node_to_string(decorator) in _PROPERTY_EXCEPTIONS:
                return True
    return False
    def _check_duplicate_exceptions(self, node: ast.Try) -> None:
        exceptions: List[str] = []
        for exc_handler in node.handlers:
            # There might be complex things hidden inside an exception type,
            # so we want to get the string representation of it:
            if isinstance(exc_handler.type, ast.Name):
                exceptions.append(source.node_to_string(exc_handler.type))
            elif isinstance(exc_handler.type, ast.Tuple):
                exceptions.extend([
                    source.node_to_string(node)
                    for node in exc_handler.type.elts
                ])

        for exc_name, count in Counter(exceptions).items():
            if count > 1:
                self.add_violation(
                    DuplicateExceptionViolation(node, text=exc_name), )
    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, )
Example #12
0
    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])
Example #13
0
    def _check_len_call(self, node: ast.Subscript) -> None:
        node_slice = get_slice_expr(node)
        is_len_call = (isinstance(node_slice, ast.BinOp)
                       and isinstance(node_slice.op, ast.Sub)
                       and self._is_wrong_len(
                           node_slice,
                           source.node_to_string(node.value),
                       ))

        if is_len_call:
            self.add_violation(
                refactoring.ImplicitNegativeIndexViolation(node), )
    def _check_implicit_in(self, node: ast.BoolOp) -> None:
        variables: List[Set[str]] = []

        for cmp in node.values:
            if not isinstance(cmp, ast.Compare) or len(cmp.ops) != 1:
                return
            if not isinstance(cmp.ops[0], self._allowed[node.op.__class__]):
                return

            variables.append({source.node_to_string(cmp.left)})

        for duplicate in _get_duplicate_names(variables):
            self.add_violation(
                ImplicitInConditionViolation(node, text=duplicate), )
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]
Example #16
0
    def _get_all_names(
        self,
        node: ast.BoolOp,
    ) -> List[str]:
        # We need to make sure that we do not visit
        # one chained `BoolOp` elements twice:
        self._same_nodes.append(node)

        names = []
        for operand in node.values:
            if isinstance(operand, ast.BoolOp):
                names.extend(self._get_all_names(operand))
            else:
                names.append(source.node_to_string(operand))
        return names
Example #17
0
    def _find_lower_upper_bounds(
        self,
        comparison_node: ast.Compare,
    ) -> None:
        left_operand = comparison_node.left
        comparators = zip(comparison_node.ops, comparison_node.comparators)

        for operator, right_operand in comparators:
            for operand in (left_operand, right_operand):
                self._mutate(
                    comparison_node,
                    operator,
                    source.node_to_string(operand),
                    operand is left_operand,
                )
            left_operand = right_operand
Example #18
0
    def _is_simplifiable_assign(
        self,
        node_body: List[ast.stmt],
    ) -> Optional[str]:
        wrong_length = len(node_body) != 1
        if wrong_length or not isinstance(node_body[0], AssignNodes):
            return None
        if not isinstance(node_body[0].value, ast.NameConstant):
            return None
        if node_body[0].value.value is None:
            return None

        targets = get_assign_targets(node_body[0])
        if len(targets) != 1:
            return None

        return source.node_to_string(targets[0])
def given_function_called(node: Call, to_check: Container[str]) -> str:
    """
    Returns function name if it is called and contained in the container.

    >>> import ast
    >>> module = ast.parse('print(123, 456)')
    >>> given_function_called(module.body[0].value, ['print'])
    'print'

    >>> given_function_called(module.body[0].value, ['adjust'])
    ''

    """
    function_name = source.node_to_string(node.func)
    if function_name in to_check:
        return function_name
    return ''
def given_function_called(
    node: Call,
    to_check: Container[str],
    *,
    split_modules: bool = False,
) -> str:
    """
    Returns function name if it is called and contained in the container.

    If `split_modules`, takes the modules or objects into account. Otherwise,
    it only cares about the function's name.
    """
    function_name = source.node_to_string(node.func)
    if split_modules:
        function_name = function_name.split('.')[-1]
    if function_name in to_check:
        return function_name
    return ''
    def _check_implicit_in(self, node: ast.BoolOp) -> None:
        allowed_ops: Dict[Type[ast.boolop], Type[ast.cmpop]] = {
            ast.And: ast.NotEq,
            ast.Or: ast.Eq,
        }

        variables: List[Set[str]] = []

        for compare in node.values:
            if not isinstance(compare, ast.Compare) or len(compare.ops) != 1:
                return

            if not isinstance(compare.ops[0], allowed_ops[node.op.__class__]):
                return

            variables.append({source.node_to_string(compare.left)})

        for duplicate in _get_duplicate_names(variables):
            self.add_violation(
                ImplicitInConditionViolation(node, text=duplicate), )
Example #22
0
    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 = 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, )
Example #23
0
 def _slot_item_name(self, node: ast.AST) -> Optional[str]:
     if isinstance(node, ast.Str):
         return node.s
     if isinstance(node, ast.Starred):
         return source.node_to_string(node)
     return None
 def _get_annotation(self, node: ast.AST) -> str:
     """Smartly turns annotation node to string."""
     full_annotation = source.node_to_string(node)
     for prefix in self._possible_prefixes:
         full_annotation = full_annotation.replace(prefix, '')
     return full_annotation
Example #25
0
 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)