def visit_Call(
    state: State,
    node: ast.Call,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (state.settings.min_version >= (3, )
            and isinstance(node.func, ast.Name) and node.func.id == 'open'
            and not has_starargs(node)):
        if len(node.args) >= 2 and isinstance(node.args[1], ast.Str):
            if (node.args[1].s in U_MODE_REPLACE or
                (len(node.args) == 2 and node.args[1].s in U_MODE_REMOVE)):
                func = functools.partial(
                    _fix_open_mode,
                    arg_idx=1,
                )
                yield ast_to_offset(node), func
        elif node.keywords and (len(node.keywords) + len(node.args) > 1):
            mode = next(
                (FunctionArg(n, keyword.value)
                 for n, keyword in enumerate(node.keywords)
                 if keyword.arg == 'mode'),
                None,
            )
            if (mode is not None and isinstance(mode.value, ast.Str)
                    and (mode.value.s in U_MODE_REMOVE
                         or mode.value.s in U_MODE_REPLACE)):
                func = functools.partial(
                    _fix_open_mode,
                    arg_idx=len(node.args) + mode.arg_idx,
                )
                yield ast_to_offset(node), func
Exemple #2
0
def visit_Call(
    state: State,
    node: ast.Call,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (state.settings.min_version >= (3, 8) and not node.args
            and not node.keywords and is_name_attr(
                node.func,
                state.from_imports,
                'functools',
                ('lru_cache', ),
            )):
        yield ast_to_offset(node), _remove_call
    elif (state.settings.min_version >= (3, 9)
          and isinstance(node.func, ast.Attribute)
          and node.func.attr == 'lru_cache'
          and isinstance(node.func.value, ast.Name)
          and node.func.value.id == 'functools' and not node.args
          and len(node.keywords) == 1 and node.keywords[0].arg == 'maxsize'
          and isinstance(node.keywords[0].value, ast.NameConstant)
          and node.keywords[0].value.value is None):
        func = functools.partial(
            find_and_replace_call,
            template='functools.cache',
        )
        yield ast_to_offset(node), func
Exemple #3
0
def visit_Subscript(
    state: State,
    node: ast.Subscript,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if not _supported_version(state):
        return

    if is_name_attr(node.value, state.from_imports, 'typing', ('Optional', )):
        yield ast_to_offset(node), _fix_optional
    elif is_name_attr(node.value, state.from_imports, 'typing', ('Union', )):
        if sys.version_info >= (3, 9):  # pragma: no cover (py39+)
            node_slice: ast.expr = node.slice
        elif isinstance(node.slice, ast.Index):  # pragma: no cover (<py39)
            node_slice = node.slice.value
        else:  # pragma: no cover (<py39)
            return  # unexpected slice type

        if isinstance(node_slice, ast.Tuple):
            if node_slice.elts:
                arg_count = len(node_slice.elts)
            else:
                return  # empty Union
        else:
            arg_count = 1

        func = functools.partial(_fix_union, arg_count=arg_count)
        yield ast_to_offset(node), func
Exemple #4
0
def visit_ClassDef(
        state: State,
        node: ast.ClassDef,
        parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if state.settings.min_version < (3,):
        return

    for decorator in node.decorator_list:
        if (
                isinstance(decorator, ast.Call) and
                is_name_attr(
                    decorator.func,
                    state.from_imports,
                    'six',
                    ('add_metaclass',),
                ) and
                not has_starargs(decorator)
        ):
            yield ast_to_offset(decorator), _fix_add_metaclass

    if (
            len(node.bases) == 1 and
            isinstance(node.bases[0], ast.Call) and
            is_name_attr(
                node.bases[0].func,
                state.from_imports,
                'six',
                ('with_metaclass',),
            ) and
            not has_starargs(node.bases[0])
    ):
        yield ast_to_offset(node.bases[0]), _fix_with_metaclass
Exemple #5
0
    def visit_Assign(self, node: ast.Assign) -> None:
        if (
                # NT = ...("NT", ...)
                len(node.targets) == 1 and
                isinstance(node.targets[0], ast.Name) and
                isinstance(node.value, ast.Call) and
                len(node.value.args) >= 1 and
                isinstance(node.value.args[0], ast.Str) and
                node.targets[0].id == node.value.args[0].s and
                not has_starargs(node.value)
        ):
            if (
                    self._is_attr(
                        node.value.func, {'typing'}, 'NamedTuple',
                    ) and
                    len(node.value.args) == 2 and
                    not node.value.keywords and
                    isinstance(node.value.args[1], (ast.List, ast.Tuple)) and
                    len(node.value.args[1].elts) > 0 and
                    all(
                        isinstance(tup, ast.Tuple) and
                        len(tup.elts) == 2 and
                        isinstance(tup.elts[0], ast.Str) and
                        tup.elts[0].s.isidentifier() and
                        tup.elts[0].s not in KEYWORDS
                        for tup in node.value.args[1].elts
                    )
            ):
                self.named_tuples[ast_to_offset(node)] = node.value
            elif (
                    self._is_attr(
                        node.value.func,
                        {'typing', 'typing_extensions'},
                        'TypedDict',
                    ) and
                    len(node.value.args) == 1 and
                    len(node.value.keywords) > 0
            ):
                self.kw_typed_dicts[ast_to_offset(node)] = node.value
            elif (
                    self._is_attr(
                        node.value.func,
                        {'typing', 'typing_extensions'},
                        'TypedDict',
                    ) and
                    len(node.value.args) == 2 and
                    not node.value.keywords and
                    isinstance(node.value.args[1], ast.Dict) and
                    node.value.args[1].keys and
                    all(
                        isinstance(k, ast.Str) and
                        k.s.isidentifier() and
                        k.s not in KEYWORDS
                        for k in node.value.args[1].keys
                    )
            ):
                self.dict_typed_dicts[ast_to_offset(node)] = node.value

        self.generic_visit(node)
Exemple #6
0
def visit_BinOp(
    state: State,
    node: ast.BinOp,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (not state.settings.keep_percent_format
            and isinstance(node.op, ast.Mod)
            and isinstance(node.left, ast.Str)):
        try:
            parsed = _parse_percent_format(node.left.s)
        except ValueError:
            pass
        else:
            for _, fmt in parsed:
                if not fmt:
                    continue
                key, conversion_flag, width, precision, conversion = fmt
                # timid: these require out-of-order parameter consumption
                if width == '*' or precision == '.*':
                    break
                # these conversions require modification of parameters
                if conversion in {'d', 'i', 'u', 'c'}:
                    break
                # timid: py2: %#o formats different from {:#o} (--py3?)
                if '#' in (conversion_flag or '') and conversion == 'o':
                    break
                # no equivalent in format
                if key == '':
                    break
                # timid: py2: conversion is subject to modifiers (--py3?)
                nontrivial_fmt = any((conversion_flag, width, precision))
                if conversion == '%' and nontrivial_fmt:
                    break
                # no equivalent in format
                if conversion in {'a', 'r'} and nontrivial_fmt:
                    break
                # all dict substitutions must be named
                if isinstance(node.right, ast.Dict) and not key:
                    break
            else:
                if isinstance(node.right, ast.Tuple):
                    func = functools.partial(
                        _fix_percent_format_tuple,
                        node_right=node.right,
                    )
                    yield ast_to_offset(node), func
                elif isinstance(node.right, ast.Dict):
                    func = functools.partial(
                        _fix_percent_format_dict,
                        node_right=node.right,
                    )
                    yield ast_to_offset(node), func
Exemple #7
0
def _is_oserror_alias(
    node: ast.AST,
    from_imports: Dict[str, Set[str]],
) -> Optional[Tuple[Offset, str]]:
    if isinstance(node, ast.Name) and node.id in ERROR_NAMES:
        return ast_to_offset(node), node.id
    elif (isinstance(node, ast.Name) and node.id == 'error'
          and any(node.id in from_imports[mod] for mod in ERROR_MODULES)):
        return ast_to_offset(node), node.id
    elif (isinstance(node, ast.Attribute) and isinstance(node.value, ast.Name)
          and node.value.id in ERROR_MODULES and node.attr == 'error'):
        return ast_to_offset(node), node.attr
    else:
        return None
Exemple #8
0
def visit_Call(
    state: State,
    node: ast.Call,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (isinstance(node.func, ast.Name) and node.func.id == 'set'
            and len(node.args) == 1 and not node.keywords
            and isinstance(node.args[0], SET_TRANSFORM)):
        arg, = node.args
        if isinstance(arg, (ast.List, ast.Tuple)) and not arg.elts:
            yield ast_to_offset(node.func), _fix_set_empty_literal
        else:
            func = functools.partial(_fix_set_literal, arg=arg)
            yield ast_to_offset(node.func), func
Exemple #9
0
def visit_If(
    state: State,
    node: ast.If,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (state.settings.min_version >= (3, ) and (
            # if six.PY2:
            is_name_attr(node.test, state.from_imports, 'six', ('PY2', )) or
            # if not six.PY3:
        (isinstance(node.test, ast.UnaryOp)
         and isinstance(node.test.op, ast.Not) and is_name_attr(
             node.test.operand,
             state.from_imports,
             'six',
             ('PY3', ),
         )) or
            # sys.version_info == 2 or < (3,)
        (isinstance(node.test, ast.Compare) and is_name_attr(
            node.test.left,
            state.from_imports,
            'sys',
            ('version_info', ),
        ) and len(node.test.ops) == 1 and
         (_eq(node.test, 2) or _compare_to_3(node.test, ast.Lt))))):
        if node.orelse and not isinstance(node.orelse[0], ast.If):
            yield ast_to_offset(node), _fix_py2_block
    elif (state.settings.min_version >= (3, ) and (
            # if six.PY3:
            is_name_attr(node.test, state.from_imports, 'six', ('PY3', )) or
            # if not six.PY2:
        (isinstance(node.test, ast.UnaryOp)
         and isinstance(node.test.op, ast.Not) and is_name_attr(
             node.test.operand,
             state.from_imports,
             'six',
             ('PY2', ),
         )) or
            # sys.version_info == 3 or >= (3,) or > (3,)
        (isinstance(node.test, ast.Compare) and is_name_attr(
            node.test.left,
            state.from_imports,
            'sys',
            ('version_info', ),
        ) and len(node.test.ops) == 1 and
         (_eq(node.test, 3) or _compare_to_3(node.test, (ast.Gt, ast.GtE)))))):
        if node.orelse and not isinstance(node.orelse[0], ast.If):
            yield ast_to_offset(node), _fix_py3_block_else
        elif not node.orelse:
            yield ast_to_offset(node), _fix_py3_block
Exemple #10
0
def visit_Dict(
    state: State,
    node: ast.Dict,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if state.settings.min_version < (3, 9):
        return

    if all(key is None for key in node.keys) and len(node.values) > 1:
        yield ast_to_offset(node), _replace_dict_brackets
        arg_count = len(node.values)
        for idx, arg in enumerate(node.values):
            yield ast_to_offset(arg), _remove_double_star
            if idx < arg_count - 1:
                yield ast_to_offset(arg), _replace_comma_with_pipe
def visit_Call(
        state: State,
        node: ast.Call,
        parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (
            isinstance(node.func, ast.Name) and
            node.func.id in ALLOWED_FUNCS and
            node.args and
            isinstance(node.args[0], ast.ListComp)
    ):
        if len(node.args) == 1 and not node.keywords:
            yield ast_to_offset(node.args[0]), _delete_list_comp_brackets
        else:
            yield ast_to_offset(node.args[0]), replace_list_comp_brackets
Exemple #12
0
    def visit_Call(self, node: ast.Call) -> None:
        parsed = self._parse(node)
        if parsed is not None:
            params = _format_params(node)
            seen: Set[str] = set()
            i = 0
            for _, name, spec, _ in parsed:
                # timid: difficult to rewrite correctly
                if spec is not None and '{' in spec:
                    break
                if name is not None:
                    candidate, _, _ = name.partition('.')
                    # timid: could make the f-string longer
                    if candidate and candidate in seen:
                        break
                    # timid: bracketed
                    elif '[' in name:
                        break
                    seen.add(candidate)

                    key = candidate or str(i)
                    # their .format() call is broken currently
                    if key not in params:
                        break
                    if not candidate:
                        i += 1
            else:
                self.fstrings[ast_to_offset(node)] = node

        self.generic_visit(node)
Exemple #13
0
def visit_Call(
    state: State,
    node: ast.Call,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (state.settings.min_version >= (3, )
            and is_a_native_literal_call(node, state.from_imports)):
        yield ast_to_offset(node), _fix_native_str
Exemple #14
0
def visit_Starred(
    state: State,
    node: ast.Starred,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (state.settings.min_version >= (3, )
            and isinstance(node.value, ast.ListComp)):
        yield ast_to_offset(node.value), replace_list_comp_brackets
Exemple #15
0
def _fix_percent_format_dict(
    i: int,
    tokens: List[Token],
    *,
    node_right: ast.Dict,
) -> None:
    # TODO: handle \N escape sequences
    if r'\N' in tokens[i].src:
        return

    seen_keys: Set[str] = set()
    keys = {}

    for k in node_right.keys:
        # not a string key
        if not isinstance(k, ast.Str):
            return
        # duplicate key
        elif k.s in seen_keys:
            return
        # not an identifier
        elif not k.s.isidentifier():
            return
        # a keyword
        elif k.s in KEYWORDS:
            return
        seen_keys.add(k.s)
        keys[ast_to_offset(k)] = k

    # TODO: this is overly timid
    brace = i + 4
    if tokens_to_src(tokens[i + 1:brace + 1]) != ' % {':
        return

    fmt_victims = victims(tokens, brace, node_right, gen=False)
    brace_end = fmt_victims.ends[-1]

    key_indices = []
    for j, token in enumerate(tokens[brace:brace_end], brace):
        key = keys.pop(token.offset, None)
        if key is None:
            continue
        # we found the key, but the string didn't match (implicit join?)
        elif ast.literal_eval(token.src) != key.s:
            return
        # the map uses some strange syntax that's not `'key': value`
        elif tokens[j + 1].src != ':' or tokens[j + 2].src != ' ':
            return
        else:
            key_indices.append((j, key.s))
    assert not keys, keys

    tokens[brace_end] = tokens[brace_end]._replace(src=')')
    for key_index, s in reversed(key_indices):
        tokens[key_index:key_index + 3] = [Token('CODE', f'{s}=')]
    newsrc = _percent_to_format(tokens[i].src)
    tokens[i] = tokens[i]._replace(src=newsrc)
    tokens[i + 1:brace + 1] = [Token('CODE', '.format'), Token('OP', '(')]
Exemple #16
0
def visit_ClassDef(
    state: State,
    node: ast.ClassDef,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if state.settings.min_version >= (3, ):
        for base in node.bases:
            if is_name_attr(base, state.from_imports, 'six', ('Iterator', )):
                yield ast_to_offset(base), remove_base_class
def visit_ClassDef(
    state: State,
    node: ast.ClassDef,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if state.settings.min_version >= (3, ):
        for base in node.bases:
            if isinstance(base, ast.Name) and base.id == 'object':
                yield ast_to_offset(base), remove_base_class
Exemple #18
0
def visit_Call(
    state: State,
    node: ast.Call,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (state.settings.min_version >= (3, 7) and is_name_attr(
            node.func,
            state.from_imports,
            'subprocess',
        ('run', ),
    )):
        stdout_idx = None
        stderr_idx = None
        universal_newlines_idx = None
        for n, keyword in enumerate(node.keywords):
            if keyword.arg == 'stdout' and is_name_attr(
                    keyword.value,
                    state.from_imports,
                    'subprocess',
                ('PIPE', ),
            ):
                stdout_idx = n
            elif keyword.arg == 'stderr' and is_name_attr(
                    keyword.value,
                    state.from_imports,
                    'subprocess',
                ('PIPE', ),
            ):
                stderr_idx = n
            elif keyword.arg == 'universal_newlines':
                universal_newlines_idx = n
        if universal_newlines_idx is not None:
            func = functools.partial(
                _replace_universal_newlines_with_text,
                arg_idx=len(node.args) + universal_newlines_idx,
            )
            yield ast_to_offset(node), func
        if stdout_idx is not None and stderr_idx is not None:
            func = functools.partial(
                _use_capture_output,
                stdout_arg_idx=len(node.args) + stdout_idx,
                stderr_arg_idx=len(node.args) + stderr_idx,
            )
            yield ast_to_offset(node), func
Exemple #19
0
def visit_Call(
    state: State,
    node: ast.Call,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (state.settings.min_version >= (3, )
            and isinstance(node.func, ast.Attribute)
            and isinstance(node.func.value, ast.Name)
            and node.func.value.id == 'io' and node.func.attr == 'open'):
        yield ast_to_offset(node.func), _replace_io_open
Exemple #20
0
def visit_Call(
    state: State,
    node: ast.Call,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (state.settings.min_version >= (3, )
            and isinstance(node.func, ast.Name) and node.func.id == 'open'
            and not has_starargs(node) and len(node.args) >= 2
            and isinstance(node.args[1], ast.Str)
            and (node.args[1].s in U_MODE_REPLACE or
                 (len(node.args) == 2 and node.args[1].s in U_MODE_REMOVE))):
        yield ast_to_offset(node), _fix_open_mode
Exemple #21
0
def visit_ImportFrom(
        state: State,
        node: ast.ImportFrom,
        parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (
            state.settings.min_version >= (3,) and
            not state.settings.keep_mock and
            not node.level and
            node.module in MOCK_MODULES
    ):
        yield ast_to_offset(node), _fix_import_from_mock
def visit_Assign(
        state: State,
        node: ast.Assign,
        parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (
            state.settings.min_version >= (3,) and
            len(node.targets) == 1 and
            isinstance(node.targets[0], ast.Tuple) and
            isinstance(node.value, ast.ListComp)
    ):
        yield ast_to_offset(node.value), _replace_list_comprehension
Exemple #23
0
def visit_Import(
        state: State,
        node: ast.Import,
        parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (
            state.settings.min_version >= (3,) and
            not state.settings.keep_mock and
            len(node.names) == 1 and
            node.names[0].name in MOCK_MODULES
    ):
        yield ast_to_offset(node), _fix_import_mock
Exemple #24
0
def visit_Call(
    state: State,
    node: ast.Call,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (isinstance(node.func, ast.Name) and node.func.id == 'dict'
            and len(node.args) == 1 and not node.keywords
            and isinstance(node.args[0], (ast.ListComp, ast.GeneratorExp))
            and isinstance(node.args[0].elt, (ast.Tuple, ast.List))
            and len(node.args[0].elt.elts) == 2):
        func = functools.partial(_fix_dict_comp, arg=node.args[0])
        yield ast_to_offset(node.func), func
def visit_Call(
    state: State,
    node: ast.Call,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (state.settings.min_version >= (3, )
            and isinstance(node.func, ast.Attribute)
            and isinstance(node.func.value, ast.Str)
            and node.func.attr == 'encode' and not has_starargs(node)
            and len(node.args) == 1 and isinstance(node.args[0], ast.Str)
            and is_codec(node.args[0].s, 'utf-8')):
        yield ast_to_offset(node), _fix_default_encoding
Exemple #26
0
def visit_Name(
    state: State,
    node: ast.Name,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (_should_rewrite(state) and node.id in state.from_imports['typing']
            and node.id in PEP585_BUILTINS):
        func = functools.partial(
            replace_name,
            name=node.id,
            new=node.id.lower(),
        )
        yield ast_to_offset(node), func
Exemple #27
0
def visit_Attribute(
    state: State,
    node: ast.Attribute,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (_should_rewrite(state) and isinstance(node.value, ast.Name)
            and node.value.id == 'typing' and node.attr in PEP585_BUILTINS):
        func = functools.partial(
            replace_name,
            name=node.attr,
            new=node.attr.lower(),
        )
        yield ast_to_offset(node), func
Exemple #28
0
def visit_Attribute(
        state: State,
        node: ast.Attribute,
        parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (
            state.settings.min_version >= (3,) and
            not state.settings.keep_mock and
            isinstance(node.value, ast.Name) and
            node.value.id == 'mock' and
            node.attr == 'mock'
    ):
        yield ast_to_offset(node), _fix_mock_mock
Exemple #29
0
def visit_Name(
    state: State,
    node: ast.Name,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    if (state.settings.min_version >= (3, )
            and node.id in state.from_imports['typing'] and node.id == 'Text'):
        func = functools.partial(
            replace_name,
            name=node.id,
            new='str',
        )
        yield ast_to_offset(node), func
Exemple #30
0
def visit_Compare(
    state: State,
    node: ast.Compare,
    parent: ast.AST,
) -> Iterable[Tuple[Offset, TokenFunc]]:
    left = node.left
    for op, right in zip(node.ops, node.comparators):
        if (isinstance(op, (ast.Is, ast.IsNot))
                and (isinstance(left, LITERAL_TYPES)
                     or isinstance(right, LITERAL_TYPES))):
            func = functools.partial(_fix_is_literal, op=op)
            yield ast_to_offset(right), func
        left = right