lib3to2 / lib3to2 / fixes /

Full commit
Fixer for bytes -> str.

import re
from lib2to3 import fixer_base
from lib2to3.patcomp import compile_pattern
from ..fixer_util import Name, token, syms, parse_args, Call, Comma

_literal_re = re.compile(r"[bB][rR]?[\'\"]")

class FixBytes(fixer_base.BaseFix):

    PATTERN = "STRING | power< name='bytes' [trailer< '(' (args=arglist | any*) ')' >] > | name='bytes'"

    def match(self, node):
        results = super().match(node)
        if results and node.type == token.NAME and node.parent is not None:
            if super().match(node.parent):
                # Do not match a simple name if it is within a larger matching context
                return False
        return results

    def transform(self, node, results):
        name = results.get("name")
        arglist = results.get("args")
        if name is not None:
            assert name.value == "bytes"
            name.value = "str"
        elif node.type == token.STRING:
            if _literal_re.match(node.value):
                new = node.clone()
                new.value = new.value[1:]
                return new
        if arglist is not None:
            args = arglist.children
            parsed = parse_args(args, ("source", "encoding", "errors"))

            source, encoding, errors = (parsed[v] for v in ("source", "encoding", "errors"))
            encoding.prefix = ""
            str_call = Call(Name("str"), ([source.clone()]))
            if errors is None:
                node.replace(Call(Name(str(str_call) + ".encode"), (encoding.clone(),)))
                errors.prefix = " "
                node.replace(Call(Name(str(str_call) + ".encode"), (encoding.clone(), Comma(), errors.clone())))