def _visit_block_stmt(self, node): # output coment (prefix) self.accessor.emit_prefix_and_consuming(self.m, node) # main process children = node.children blocks = [] # (name, expr, body) st = 0 for i, snode in enumerate(children): typ = type_repr(snode.type) if typ == "suite": assert children[i - 1].value == ":" blocks.append((children[st], children[st + 1:i - 1], children[i])) st = i + 1 for (name, expr, body) in blocks: if expr: args = " ".join([str(x).strip() for x in expr]).lstrip() self.m.stmt("with m.{}_({!r}):", name.value.lstrip(), args) elif hasattr(name, "value"): # Leaf self.m.stmt("with m.{}_():", name.value.lstrip()) else: typ = type_repr(name.type) if typ == "except_clause": self.m.stmt( "with m.{}_({!r}):", name.children[0].value, " ".join([str(x).strip() for x in name.children[1:]]).lstrip(), ) else: raise ValueError("unexpected blocks: {!r}, {!r}".format(name, expr)) self.visit_suite(body)
def visit_import_name(self, node: Node) -> t.Optional[t.Any]: # import [ <Leaf> | <dotted_as_name> | <dotted_name> ] assert len(node.children) == 2, node.children if type_repr(node.children[1].type) == "dotted_as_name": sym = parse_dotted_as_name(node.children[1]) sym = dataclasses.replace(sym, id=id(node), from_=type_repr(node.type)) self.symbols[sym.name] = sym elif type_repr(node.children[1].type) == "dotted_name": # <Leaf> { . <Leaf> }+ module = parse_dotted_name(node.children[1]) sym = Symbol(name=module, fullname=module) sym = dataclasses.replace(sym, id=id(node), from_=type_repr(node.type)) self.symbols[module] = sym else: module = node.children[1].value.strip() sym = Symbol(name=module, fullname=module) sym = dataclasses.replace(sym, id=id(node), from_=type_repr(node.type)) self.symbols[module] = sym return True # stop
def visit_decorated(self, node): for snode in node.children[:-1]: assert type_repr(snode.type) == "decorator" self.m.stmt( "m.stmt({!r})", " ".join([str(x) for x in snode.children]).strip().replace("@ ", "@") ) return self.visit(node.children[-1])
def parse_dotted_as_name(node: Node) -> Symbol: # <Leaf> as <Leaf> assert len(node.children) == 3, str(node.children) if type_repr(node.children[0].type) == "dotted_name": module = parse_dotted_name(node.children[0]) assert node.children[1].value == "as" name = node.children[2].value.strip() return Symbol(fullname=module, name=name) else: module = node.children[0].value.strip() assert node.children[1].value == "as" name = node.children[2].value.strip() return Symbol(fullname=module, name=name)
def parse_import_as_names(node: Node, *, module: str) -> t.List[Symbol]: # <Leaf> { , <Leaf> }+ syms = [] for i in range(0, len(node.children), 2): x = node.children[i] if type_repr(x.type) == "import_as_name": sym = parse_import_as_name(x) sym = Symbol(name=sym.name, fullname=f"{module}.{sym.fullname}") else: name = x.value.strip() sym = Symbol(name=name, fullname=f"{module}.{name}") syms.append(sym) if i - 1 > 0: assert node.children[i - 1].value == "," return syms
def visit_import_from(self, node: Node) -> t.Optional[t.Any]: assert node.children[0].value == "from" module_path = [] for i, x in enumerate(node.children[1:], 1): if type_repr(x.type) == "dotted_name": name = parse_dotted_name(x) else: name = x.value.strip() if name == "import": break module_path.append(name) if name != ".": module_path.append(".") module_path.pop() module = "".join(module_path) sym_list = [] for x in node.children[i + 1:]: if type_repr(x.type) == "import_as_name": sym = parse_import_as_name(x) sym = dataclasses.replace( sym, fullname=f"{module}.{sym.name}", id=id(node), from_=type_repr(node.type), ) sym_list.append(sym) elif type_repr(x.type) == "import_as_names": syms = parse_import_as_names(x, module=module) syms = [ dataclasses.replace(sym, id=id(node), from_=type_repr(node.type)) for sym in syms ] sym_list.extend(syms) elif x.value.strip() == "(": continue elif x.value.strip() == ")": continue else: name = x.value.strip() sym = Symbol(name=name, fullname=f"{module}.{name}") sym = dataclasses.replace(sym, id=id(node), from_=type_repr(node.type)) sym_list.append(sym) # todo: relative for sym in sym_list: self.symbols[sym.name] = sym return True # stop
def visit_suite(self, node): prefixes = [] if node.prefix: prefixes.append(node) # main process itr = iter(node.children) found_indent = False for snode in itr: if snode.prefix: prefixes.append(snode) typ = type_repr(snode.type) if typ == token.INDENT: found_indent = True break if not found_indent: with self.m.scope(): self.m.stmt("m.stmt({!r})", str(node).strip()) return suffixes = [] with self.m.scope(): # output coment (prefix) comments = [] for has_prefix_node in prefixes: comments.append(has_prefix_node.prefix) has_prefix_node.prefix = "" self.accessor.emit_comment(self.m, "\n".join(comments)) for snode in itr: if snode.type == token.DEDENT: suffixes.append(snode.prefix) snode.prefix = "" self.visit(snode) # comment (suffix DEDENT) if suffixes: assert len(suffixes) == 1 self.accessor.emit_comment(self.m, suffixes[0])
def to_arguments(self, node): typ = type_repr(node.type) if typ == "parameters": # '(', <typedargslist>, ')' children = node.children if len(children) == 2: return LParams(kwargs=LKwargs()) assert len(children) == 3 assert children[0].value == "(" node = children[1] assert children[2].value == ")" typ = type_repr(node.type) if typ == "typedargslist": argslist = node.children elif hasattr(node, "value"): # leaf argslist = [node] else: raise ValueError("invalid type {}".format(typ)) params = LParams(kwargs=LKwargs()) itr = iter(argslist) while True: arg_name = None arg_type = None prefix = "" try: snode = next(itr) if snode.type == token.DOUBLESTAR: prefix = "**" snode = next(itr) elif snode.type == token.STAR: prefix = "*" snode = next(itr) if snode.type == token.COMMA: params.append("*") continue if type_repr(snode.type) == "tname": # with type len(snode.children) == "3" arg_name = snode.children[0].value assert snode.children[1].type == token.COLON arg_type = str(snode.children[2]).strip() else: arg_name = snode.value arg_default = None snode = next(itr) # or EOF if snode.type == token.COMMA: pass elif snode.type == token.EQUAL: snode = next(itr) arg_default = str(snode) snode = next(itr) # , or EOF assert snode.type == token.COMMA else: raise ValueError("invalid arglist {!r}".format(argslist)) except StopIteration: break finally: if arg_name is not None: if prefix: # *args or **kwargs params.append_tail(prefix + arg_name, type=arg_type) elif arg_default is None: params.append(arg_name, type=arg_type) else: params.set(arg_name, arg_default, type=arg_type) return params
def visit_simple_stmt(self, node): # output coment (prefix) self.accessor.emit_prefix_and_consuming(self.m, node) # main process children = node.children typ = type_repr(children[0].type) # docstring if hasattr(children[0], "value") and children[0].value.startswith(("'''", '"""')): docstring = "".join([snode.value for snode in children]).strip() if "\n" not in docstring: self.m.stmt("m.docstring({!r})", docstring.strip("\"'")) else: self.m.g.import_("textwrap") self.m.stmt('m.docstring(textwrap.dedent(') for line in docstring.split("\n"): self.m.stmt(line) self.m.stmt("))") return # from x import (y, z) ? elif typ == "import_name": # 'import' <dotted_as_names> nodes = children[0].children assert nodes[0].value == "import" self.accessor.import_contextually(self.m, node, str(nodes[1]).strip()) rest = nodes[2:] # import_name | import_from elif typ == "import_from": # 'from' <module> 'import' ('*' | '(' <import_asnames> ')' | <import_asnames>) nodes = children[0].children assert nodes[0].value == "from" module = str(nodes[1]).strip() assert nodes[2].value == "import" names = [] for snode in nodes[2:]: typ = type_repr(snode.type) if typ == "import_as_names": for ssnode in snode.children: if type_repr(ssnode.type) == token.COMMA: continue names.append(str(ssnode).strip()) elif typ == "import_as_name": assert len(snode.children) == 3 names.append(str(snode).strip()) elif typ == token.COMMA: continue elif typ == token.LPAR: continue elif typ == token.RPAR: continue elif snode.value == "import": continue else: names.append(snode.value) self.accessor.from_contextually(self.m, node, module, names) rest = children[1:] else: rest = node.children if rest: self.accessor.emit_stmt_multiline(self.m, "".join([str(x) for x in rest]))
def is_toplevel(self, node): return type_repr(node.parent.type) == "file_input"
def is_def(self, node, _candidates=("funcdef", "classdef")): typ = type_repr(node.type) return typ in _candidates
def main(): code = inspect.getsource(hello) code = inspect.getsource(sys.modules[__name__]) t = parse_string(code) print("# before\n", t, sep="") v = Visitor() v.visit(t) for kind, node in changes: if kind == "args": assert node.children[0].type == token.LPAR assert node.children[-1].type == token.RPAR node.children.insert(1, fixer_util.Name("m")) elif kind == "stmt": # TODO: support ";" # <stmt> # ↓ # m.stmt(<stmt>) node.prefix = f"{node.prefix}m.stmt(" assert node.children[-1].value == "\n" node.children[-1].prefix = f"){node.children[-1].prefix}" elif kind == "let": if type_repr(node.children[0].type) == "power": # TODO: foo.bar.boo = v => setattr(getattr(foo, "bar"), "boo", v) # <recv>.<attr> = <val> # ↓ # m.setattr(<recv>, "<attr>", <val>) # todo: recursive assert type_repr(node.children[0].type) == "power" power = node.children[0] name = power.children[0].value assert type_repr(power.children[1].type) == "trailer" trailer = power.children[1] assert trailer.children[0].type == token.DOT trailer.children[0] = fixer_util.Comma() attr = trailer.children[1] node.prefix = f"{node.prefix}m.setattr(" attr.prefix = f'{attr.prefix} "' assert node.children[1].type == token.EQUAL eq_node = node.children[1] node.children[1] = fixer_util.Comma() node.children[1].prefix = f'"{eq_node.prefix}' node.parent.children[-1].prefix = f"){node.parent.children[-1].prefix}" # attr_next.prefix = f'"){attr_next.prefix}' continue # <var> = <val> # ↓ # <var> = m.let("<var>", <val>) name = node.children[0].value assert node.children[1].value == "=" node.children[2].prefix = f"{node.children[2].prefix}m.let({name!r}, " next_sibling = node.next_sibling assert next_sibling is not None next_sibling.prefix = f"){next_sibling}" print("") print("# after\n", t, sep="")
def visit_simple_stmt(self, node: Node): if type_repr(node.children[0].type) == "expr_stmt": return False changes.append(("stmt", node)) return True # stop