def flatten_ast(node: Any, prefix="", path="") -> str: if isinstance(node, ast.AST): acc = [f"{prefix}/_type={type(node).__name__}\n"] if isinstance(node, ast.expr): node_repr = remove_context("", ast.dump(node)) expr_hash = hex(hash(node_repr) & 0xFFFFFFFF) acc.append(f"{prefix}/_hash={expr_hash}\n") if "lineno" in node._attributes: acc.append(f"{prefix}/_pos={node.lineno}:{path[2:]}\n") fields = ast.iter_fields(node) if isinstance(node, ast.FunctionDef): # reject `body` behind `decorator_list` and `returns`, whose line number is those of `def` clause fields = iter(sorted(fields, key=lambda c: c[0] == "body")) for (i, (name, x)) in enumerate(fields): if name == "orelse" and isinstance(node, (ast.For, ast.While, ast.AsyncFor)): name = "loopelse" # this makes the "orelse" clause specific to conditionals elif name == "targets" and isinstance(node, ast.Assign): name = "assigntargets" elif name == "target" and isinstance(node, ast.AugAssign): name = "assigntarget" elif name == "value" and isinstance(node, (ast.Assign, ast.AugAssign)): name = "assignvalue" acc.append(flatten_ast(x, f"{prefix}/{name}", f"{path}{i}-")) return "".join(acc) elif isinstance(node, list): acc = [f"{prefix}/length={len(node)}\n"] for (i, x) in enumerate(node, 1): acc.append(flatten_ast(x, f"{prefix}/{i}", f"{path}{i}-")) return "".join(acc) elif prefix.endswith(("/s", "/module")): return f"""{prefix}={repr(node)}\n""" else: return f"""{prefix}={repr(node).strip("'")}\n"""
def generic_visit(self, node): if isinstance(node, list): nodestart = "[" nodeend = "]" children = [("", child) for child in node] else: nodestart = type(node).__name__ + "(" nodeend = ")" children = [(name + "=", value) for name, value in ast.iter_fields(node)] if len(children) > 1: self.indentation += 1 self.write(nodestart) for i, pair in enumerate(children): attr, child = pair if len(children) > 1: self.write("\n" + self.indent_with * self.indentation) if isinstance(child, (ast.AST, list)): self.write(attr) self.visit(child) else: self.write(attr + repr(child)) if i != len(children) - 1: self.write(",") self.write(nodeend) if len(children) > 1: self.indentation -= 1
def flatten_node( node: Any, prefix: str = "", path: str = "", remove_context: Callable = regex.compile(r", ctx=.+?\(\)").sub, ) -> str: r"""Traverse recursively (in pre-order) the given AST node and flatten its subtree. Args: node (Any): The node to traverse. Initially, the whole AST. prefix (str, optional): The prefix of the current line to dump. Defaults to `""`. path (str, optional): The path of the current node. Defaults to `""`. remove_context (Callable, optional): A function removing the node context encoded in the result of `ast3.dump()`. [Not to be explicitly provided.](developer_manual/index.html#default-argument-trick) Defaults to `regex.compile(r", ctx=.+?\(\)").sub`. Returns: str: A flat representation of the given node. """ if isinstance(node, ast.AST): acc = [f"{prefix}/_type={type(node).__name__}\n"] if isinstance(node, ast.expr): node_repr = remove_context("", ast.dump(node)) acc.append(f"{prefix}/_hash={pseudo_hash(node_repr)}\n") if "lineno" in node._attributes: acc.append(f"{prefix}/_pos={node.lineno}:{path[2:]}\n") fields = ast.iter_fields(node) if isinstance(node, ast.FunctionDef): # make `body` the last item of a `def` clause fields = iter(sorted(fields, key=lambda c: c[0] == "body")) for (i, (name, x)) in enumerate(fields): if name == "orelse" and isinstance( node, (ast.For, ast.While, ast.AsyncFor)): name = "loopelse" # make the `orelse` clause specific to conditionals elif name == "targets" and isinstance(node, ast.Assign): name = "assigntargets" # `targets` is also used for `Delete`, etc. elif name == "target" and isinstance(node, ast.AugAssign): name = "assigntarget" # `target` is also used for comprehension, etc. elif name == "value" and isinstance(node, (ast.Assign, ast.AugAssign)): name = "assignvalue" # `value` is also used for comprehension, etc. acc.append(flatten_node(x, f"{prefix}/{name}", f"{path}{i}-")) return "".join(acc) elif isinstance(node, list): acc = [f"{prefix}/_length={len(node)}\n"] for (i, x) in enumerate(node, 1): # number the children from 1 acc.append(flatten_node(x, f"{prefix}/{i}", f"{path}{i}-")) return "".join(acc) else: # If the node is a terminal value, dump it unquoted return f"""{prefix}={repr(node).strip("'")}\n"""
def generic_visit(self, node): """Copied implementation from parent class - get rid of it at some point.""" if not self._fields_first: _LOG.debug('visiting node %s', node) node = self.visit_node(node) if not hasattr(node, '_fields'): return node _LOG.debug('visiting all fields of node %s', node) for name, value in typed_ast3.iter_fields(node): setattr(node, name, self.generic_visit_field(node, name, value)) if self._fields_first: _LOG.debug('visiting node %s', node) node = self.visit_node(node) return node
def ast_to_string(ast_node, line_indent=''): next_line_indent = line_indent + ' ' if isinstance(ast_node, ast.AST): return (ast_node.__class__.__name__ + '(' + ','.join( '\n' + next_line_indent + field_name + ' = ' + ast_to_string(child_node, next_line_indent) for field_name, child_node in ast.iter_fields(ast_node)) + ')') elif isinstance(ast_node, list): return ('[' + ','.join('\n' + next_line_indent + ast_to_string(child_node, next_line_indent) for child_node in ast_node) + ']') else: return repr(ast_node)
def helper(v: Any) -> Any: "Takes a `typed_ast.AST` and converts it into a python ast.AST" if isinstance(v, ast3.AST): cls = getattr(ast, type(v).__name__) return cls(**{ f: typed_ast3_to_ast(node) for f, node in ast3.iter_fields(v) }) elif isinstance(v, list): return [typed_ast3_to_ast(e) for e in v] elif isinstance(v, (str, int, float)) or v is None: return v raise AstTransformerError( "typed_ast3_to_ast: The type '{}' is unknown to me".format( type(v)))
def helper(v: Any) -> Any: "Takes a `typed_ast.AST` and converts it into a python ast.AST" if isinstance(v, ast3.AST): cls = type(v) new_v = cls( **{f: copy_ast3(node) for f, node in ast3.iter_fields(v)}) if hasattr(v, 'ctx'): new_v.ctx = v.ctx # type: ignore return ast3.copy_location(new_v, v) elif isinstance(v, list): return [copy_ast3(e) for e in v] elif isinstance(v, (str, int, float)) or v is None: return v raise AstTransformerError( "typed_ast3_to_ast: The type '{}' is unknown to me".format( type(v)))
def generic_visit(self, node): for field, old_value in ast3.iter_fields(node): if isinstance(old_value, list): new_values = [] for value in old_value: if isinstance(value, ast3.AST): value = self.visit(value) if value is None: continue elif not isinstance(value, ast.AST): new_values.extend(value) continue new_values.append(value) old_value[:] = new_values elif isinstance(old_value, ast3.AST): new_node = self.visit(old_value) if new_node is None: delattr(node, field) else: setattr(node, field, new_node) return node
def _format(node, level=0): if isinstance(node, AST): fields = [(a, _format(b, level)) for a, b in iter_fields(node)] rv = '%s(%s' % (node.__class__.__name__, ', '.join( ('%s=%s' % field for field in fields) if annotate_fields else (b for a, b in fields))) if include_attributes and node._attributes: rv += fields and ', ' or ' ' rv += ', '.join('%s=%s' % (a, _format(getattr(node, a))) for a in node._attributes) return rv + ')' elif isinstance(node, list): lines = ['['] lines.extend((indent * (level + 2) + _format(x, level + 2) + ',' for x in node)) if len(lines) > 1: lines.append(indent * (level + 1) + ']') else: lines[-1] += ']' return '\n'.join(lines) return repr(node)
def print_node_value(node, str): for f in ast3.iter_fields(node): print(str) print(f)