17db96d56Sopenharmony_ci"""Utility functions, node construction macros, etc.""" 27db96d56Sopenharmony_ci# Author: Collin Winter 37db96d56Sopenharmony_ci 47db96d56Sopenharmony_ci# Local imports 57db96d56Sopenharmony_cifrom .pgen2 import token 67db96d56Sopenharmony_cifrom .pytree import Leaf, Node 77db96d56Sopenharmony_cifrom .pygram import python_symbols as syms 87db96d56Sopenharmony_cifrom . import patcomp 97db96d56Sopenharmony_ci 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_ci########################################################### 127db96d56Sopenharmony_ci### Common node-construction "macros" 137db96d56Sopenharmony_ci########################################################### 147db96d56Sopenharmony_ci 157db96d56Sopenharmony_cidef KeywordArg(keyword, value): 167db96d56Sopenharmony_ci return Node(syms.argument, 177db96d56Sopenharmony_ci [keyword, Leaf(token.EQUAL, "="), value]) 187db96d56Sopenharmony_ci 197db96d56Sopenharmony_cidef LParen(): 207db96d56Sopenharmony_ci return Leaf(token.LPAR, "(") 217db96d56Sopenharmony_ci 227db96d56Sopenharmony_cidef RParen(): 237db96d56Sopenharmony_ci return Leaf(token.RPAR, ")") 247db96d56Sopenharmony_ci 257db96d56Sopenharmony_cidef Assign(target, source): 267db96d56Sopenharmony_ci """Build an assignment statement""" 277db96d56Sopenharmony_ci if not isinstance(target, list): 287db96d56Sopenharmony_ci target = [target] 297db96d56Sopenharmony_ci if not isinstance(source, list): 307db96d56Sopenharmony_ci source.prefix = " " 317db96d56Sopenharmony_ci source = [source] 327db96d56Sopenharmony_ci 337db96d56Sopenharmony_ci return Node(syms.atom, 347db96d56Sopenharmony_ci target + [Leaf(token.EQUAL, "=", prefix=" ")] + source) 357db96d56Sopenharmony_ci 367db96d56Sopenharmony_cidef Name(name, prefix=None): 377db96d56Sopenharmony_ci """Return a NAME leaf""" 387db96d56Sopenharmony_ci return Leaf(token.NAME, name, prefix=prefix) 397db96d56Sopenharmony_ci 407db96d56Sopenharmony_cidef Attr(obj, attr): 417db96d56Sopenharmony_ci """A node tuple for obj.attr""" 427db96d56Sopenharmony_ci return [obj, Node(syms.trailer, [Dot(), attr])] 437db96d56Sopenharmony_ci 447db96d56Sopenharmony_cidef Comma(): 457db96d56Sopenharmony_ci """A comma leaf""" 467db96d56Sopenharmony_ci return Leaf(token.COMMA, ",") 477db96d56Sopenharmony_ci 487db96d56Sopenharmony_cidef Dot(): 497db96d56Sopenharmony_ci """A period (.) leaf""" 507db96d56Sopenharmony_ci return Leaf(token.DOT, ".") 517db96d56Sopenharmony_ci 527db96d56Sopenharmony_cidef ArgList(args, lparen=LParen(), rparen=RParen()): 537db96d56Sopenharmony_ci """A parenthesised argument list, used by Call()""" 547db96d56Sopenharmony_ci node = Node(syms.trailer, [lparen.clone(), rparen.clone()]) 557db96d56Sopenharmony_ci if args: 567db96d56Sopenharmony_ci node.insert_child(1, Node(syms.arglist, args)) 577db96d56Sopenharmony_ci return node 587db96d56Sopenharmony_ci 597db96d56Sopenharmony_cidef Call(func_name, args=None, prefix=None): 607db96d56Sopenharmony_ci """A function call""" 617db96d56Sopenharmony_ci node = Node(syms.power, [func_name, ArgList(args)]) 627db96d56Sopenharmony_ci if prefix is not None: 637db96d56Sopenharmony_ci node.prefix = prefix 647db96d56Sopenharmony_ci return node 657db96d56Sopenharmony_ci 667db96d56Sopenharmony_cidef Newline(): 677db96d56Sopenharmony_ci """A newline literal""" 687db96d56Sopenharmony_ci return Leaf(token.NEWLINE, "\n") 697db96d56Sopenharmony_ci 707db96d56Sopenharmony_cidef BlankLine(): 717db96d56Sopenharmony_ci """A blank line""" 727db96d56Sopenharmony_ci return Leaf(token.NEWLINE, "") 737db96d56Sopenharmony_ci 747db96d56Sopenharmony_cidef Number(n, prefix=None): 757db96d56Sopenharmony_ci return Leaf(token.NUMBER, n, prefix=prefix) 767db96d56Sopenharmony_ci 777db96d56Sopenharmony_cidef Subscript(index_node): 787db96d56Sopenharmony_ci """A numeric or string subscript""" 797db96d56Sopenharmony_ci return Node(syms.trailer, [Leaf(token.LBRACE, "["), 807db96d56Sopenharmony_ci index_node, 817db96d56Sopenharmony_ci Leaf(token.RBRACE, "]")]) 827db96d56Sopenharmony_ci 837db96d56Sopenharmony_cidef String(string, prefix=None): 847db96d56Sopenharmony_ci """A string leaf""" 857db96d56Sopenharmony_ci return Leaf(token.STRING, string, prefix=prefix) 867db96d56Sopenharmony_ci 877db96d56Sopenharmony_cidef ListComp(xp, fp, it, test=None): 887db96d56Sopenharmony_ci """A list comprehension of the form [xp for fp in it if test]. 897db96d56Sopenharmony_ci 907db96d56Sopenharmony_ci If test is None, the "if test" part is omitted. 917db96d56Sopenharmony_ci """ 927db96d56Sopenharmony_ci xp.prefix = "" 937db96d56Sopenharmony_ci fp.prefix = " " 947db96d56Sopenharmony_ci it.prefix = " " 957db96d56Sopenharmony_ci for_leaf = Leaf(token.NAME, "for") 967db96d56Sopenharmony_ci for_leaf.prefix = " " 977db96d56Sopenharmony_ci in_leaf = Leaf(token.NAME, "in") 987db96d56Sopenharmony_ci in_leaf.prefix = " " 997db96d56Sopenharmony_ci inner_args = [for_leaf, fp, in_leaf, it] 1007db96d56Sopenharmony_ci if test: 1017db96d56Sopenharmony_ci test.prefix = " " 1027db96d56Sopenharmony_ci if_leaf = Leaf(token.NAME, "if") 1037db96d56Sopenharmony_ci if_leaf.prefix = " " 1047db96d56Sopenharmony_ci inner_args.append(Node(syms.comp_if, [if_leaf, test])) 1057db96d56Sopenharmony_ci inner = Node(syms.listmaker, [xp, Node(syms.comp_for, inner_args)]) 1067db96d56Sopenharmony_ci return Node(syms.atom, 1077db96d56Sopenharmony_ci [Leaf(token.LBRACE, "["), 1087db96d56Sopenharmony_ci inner, 1097db96d56Sopenharmony_ci Leaf(token.RBRACE, "]")]) 1107db96d56Sopenharmony_ci 1117db96d56Sopenharmony_cidef FromImport(package_name, name_leafs): 1127db96d56Sopenharmony_ci """ Return an import statement in the form: 1137db96d56Sopenharmony_ci from package import name_leafs""" 1147db96d56Sopenharmony_ci # XXX: May not handle dotted imports properly (eg, package_name='foo.bar') 1157db96d56Sopenharmony_ci #assert package_name == '.' or '.' not in package_name, "FromImport has "\ 1167db96d56Sopenharmony_ci # "not been tested with dotted package names -- use at your own "\ 1177db96d56Sopenharmony_ci # "peril!" 1187db96d56Sopenharmony_ci 1197db96d56Sopenharmony_ci for leaf in name_leafs: 1207db96d56Sopenharmony_ci # Pull the leaves out of their old tree 1217db96d56Sopenharmony_ci leaf.remove() 1227db96d56Sopenharmony_ci 1237db96d56Sopenharmony_ci children = [Leaf(token.NAME, "from"), 1247db96d56Sopenharmony_ci Leaf(token.NAME, package_name, prefix=" "), 1257db96d56Sopenharmony_ci Leaf(token.NAME, "import", prefix=" "), 1267db96d56Sopenharmony_ci Node(syms.import_as_names, name_leafs)] 1277db96d56Sopenharmony_ci imp = Node(syms.import_from, children) 1287db96d56Sopenharmony_ci return imp 1297db96d56Sopenharmony_ci 1307db96d56Sopenharmony_cidef ImportAndCall(node, results, names): 1317db96d56Sopenharmony_ci """Returns an import statement and calls a method 1327db96d56Sopenharmony_ci of the module: 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_ci import module 1357db96d56Sopenharmony_ci module.name()""" 1367db96d56Sopenharmony_ci obj = results["obj"].clone() 1377db96d56Sopenharmony_ci if obj.type == syms.arglist: 1387db96d56Sopenharmony_ci newarglist = obj.clone() 1397db96d56Sopenharmony_ci else: 1407db96d56Sopenharmony_ci newarglist = Node(syms.arglist, [obj.clone()]) 1417db96d56Sopenharmony_ci after = results["after"] 1427db96d56Sopenharmony_ci if after: 1437db96d56Sopenharmony_ci after = [n.clone() for n in after] 1447db96d56Sopenharmony_ci new = Node(syms.power, 1457db96d56Sopenharmony_ci Attr(Name(names[0]), Name(names[1])) + 1467db96d56Sopenharmony_ci [Node(syms.trailer, 1477db96d56Sopenharmony_ci [results["lpar"].clone(), 1487db96d56Sopenharmony_ci newarglist, 1497db96d56Sopenharmony_ci results["rpar"].clone()])] + after) 1507db96d56Sopenharmony_ci new.prefix = node.prefix 1517db96d56Sopenharmony_ci return new 1527db96d56Sopenharmony_ci 1537db96d56Sopenharmony_ci 1547db96d56Sopenharmony_ci########################################################### 1557db96d56Sopenharmony_ci### Determine whether a node represents a given literal 1567db96d56Sopenharmony_ci########################################################### 1577db96d56Sopenharmony_ci 1587db96d56Sopenharmony_cidef is_tuple(node): 1597db96d56Sopenharmony_ci """Does the node represent a tuple literal?""" 1607db96d56Sopenharmony_ci if isinstance(node, Node) and node.children == [LParen(), RParen()]: 1617db96d56Sopenharmony_ci return True 1627db96d56Sopenharmony_ci return (isinstance(node, Node) 1637db96d56Sopenharmony_ci and len(node.children) == 3 1647db96d56Sopenharmony_ci and isinstance(node.children[0], Leaf) 1657db96d56Sopenharmony_ci and isinstance(node.children[1], Node) 1667db96d56Sopenharmony_ci and isinstance(node.children[2], Leaf) 1677db96d56Sopenharmony_ci and node.children[0].value == "(" 1687db96d56Sopenharmony_ci and node.children[2].value == ")") 1697db96d56Sopenharmony_ci 1707db96d56Sopenharmony_cidef is_list(node): 1717db96d56Sopenharmony_ci """Does the node represent a list literal?""" 1727db96d56Sopenharmony_ci return (isinstance(node, Node) 1737db96d56Sopenharmony_ci and len(node.children) > 1 1747db96d56Sopenharmony_ci and isinstance(node.children[0], Leaf) 1757db96d56Sopenharmony_ci and isinstance(node.children[-1], Leaf) 1767db96d56Sopenharmony_ci and node.children[0].value == "[" 1777db96d56Sopenharmony_ci and node.children[-1].value == "]") 1787db96d56Sopenharmony_ci 1797db96d56Sopenharmony_ci 1807db96d56Sopenharmony_ci########################################################### 1817db96d56Sopenharmony_ci### Misc 1827db96d56Sopenharmony_ci########################################################### 1837db96d56Sopenharmony_ci 1847db96d56Sopenharmony_cidef parenthesize(node): 1857db96d56Sopenharmony_ci return Node(syms.atom, [LParen(), node, RParen()]) 1867db96d56Sopenharmony_ci 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_ciconsuming_calls = {"sorted", "list", "set", "any", "all", "tuple", "sum", 1897db96d56Sopenharmony_ci "min", "max", "enumerate"} 1907db96d56Sopenharmony_ci 1917db96d56Sopenharmony_cidef attr_chain(obj, attr): 1927db96d56Sopenharmony_ci """Follow an attribute chain. 1937db96d56Sopenharmony_ci 1947db96d56Sopenharmony_ci If you have a chain of objects where a.foo -> b, b.foo-> c, etc, 1957db96d56Sopenharmony_ci use this to iterate over all objects in the chain. Iteration is 1967db96d56Sopenharmony_ci terminated by getattr(x, attr) is None. 1977db96d56Sopenharmony_ci 1987db96d56Sopenharmony_ci Args: 1997db96d56Sopenharmony_ci obj: the starting object 2007db96d56Sopenharmony_ci attr: the name of the chaining attribute 2017db96d56Sopenharmony_ci 2027db96d56Sopenharmony_ci Yields: 2037db96d56Sopenharmony_ci Each successive object in the chain. 2047db96d56Sopenharmony_ci """ 2057db96d56Sopenharmony_ci next = getattr(obj, attr) 2067db96d56Sopenharmony_ci while next: 2077db96d56Sopenharmony_ci yield next 2087db96d56Sopenharmony_ci next = getattr(next, attr) 2097db96d56Sopenharmony_ci 2107db96d56Sopenharmony_cip0 = """for_stmt< 'for' any 'in' node=any ':' any* > 2117db96d56Sopenharmony_ci | comp_for< 'for' any 'in' node=any any* > 2127db96d56Sopenharmony_ci """ 2137db96d56Sopenharmony_cip1 = """ 2147db96d56Sopenharmony_cipower< 2157db96d56Sopenharmony_ci ( 'iter' | 'list' | 'tuple' | 'sorted' | 'set' | 'sum' | 2167db96d56Sopenharmony_ci 'any' | 'all' | 'enumerate' | (any* trailer< '.' 'join' >) ) 2177db96d56Sopenharmony_ci trailer< '(' node=any ')' > 2187db96d56Sopenharmony_ci any* 2197db96d56Sopenharmony_ci> 2207db96d56Sopenharmony_ci""" 2217db96d56Sopenharmony_cip2 = """ 2227db96d56Sopenharmony_cipower< 2237db96d56Sopenharmony_ci ( 'sorted' | 'enumerate' ) 2247db96d56Sopenharmony_ci trailer< '(' arglist<node=any any*> ')' > 2257db96d56Sopenharmony_ci any* 2267db96d56Sopenharmony_ci> 2277db96d56Sopenharmony_ci""" 2287db96d56Sopenharmony_cipats_built = False 2297db96d56Sopenharmony_cidef in_special_context(node): 2307db96d56Sopenharmony_ci """ Returns true if node is in an environment where all that is required 2317db96d56Sopenharmony_ci of it is being iterable (ie, it doesn't matter if it returns a list 2327db96d56Sopenharmony_ci or an iterator). 2337db96d56Sopenharmony_ci See test_map_nochange in test_fixers.py for some examples and tests. 2347db96d56Sopenharmony_ci """ 2357db96d56Sopenharmony_ci global p0, p1, p2, pats_built 2367db96d56Sopenharmony_ci if not pats_built: 2377db96d56Sopenharmony_ci p0 = patcomp.compile_pattern(p0) 2387db96d56Sopenharmony_ci p1 = patcomp.compile_pattern(p1) 2397db96d56Sopenharmony_ci p2 = patcomp.compile_pattern(p2) 2407db96d56Sopenharmony_ci pats_built = True 2417db96d56Sopenharmony_ci patterns = [p0, p1, p2] 2427db96d56Sopenharmony_ci for pattern, parent in zip(patterns, attr_chain(node, "parent")): 2437db96d56Sopenharmony_ci results = {} 2447db96d56Sopenharmony_ci if pattern.match(parent, results) and results["node"] is node: 2457db96d56Sopenharmony_ci return True 2467db96d56Sopenharmony_ci return False 2477db96d56Sopenharmony_ci 2487db96d56Sopenharmony_cidef is_probably_builtin(node): 2497db96d56Sopenharmony_ci """ 2507db96d56Sopenharmony_ci Check that something isn't an attribute or function name etc. 2517db96d56Sopenharmony_ci """ 2527db96d56Sopenharmony_ci prev = node.prev_sibling 2537db96d56Sopenharmony_ci if prev is not None and prev.type == token.DOT: 2547db96d56Sopenharmony_ci # Attribute lookup. 2557db96d56Sopenharmony_ci return False 2567db96d56Sopenharmony_ci parent = node.parent 2577db96d56Sopenharmony_ci if parent.type in (syms.funcdef, syms.classdef): 2587db96d56Sopenharmony_ci return False 2597db96d56Sopenharmony_ci if parent.type == syms.expr_stmt and parent.children[0] is node: 2607db96d56Sopenharmony_ci # Assignment. 2617db96d56Sopenharmony_ci return False 2627db96d56Sopenharmony_ci if parent.type == syms.parameters or \ 2637db96d56Sopenharmony_ci (parent.type == syms.typedargslist and ( 2647db96d56Sopenharmony_ci (prev is not None and prev.type == token.COMMA) or 2657db96d56Sopenharmony_ci parent.children[0] is node 2667db96d56Sopenharmony_ci )): 2677db96d56Sopenharmony_ci # The name of an argument. 2687db96d56Sopenharmony_ci return False 2697db96d56Sopenharmony_ci return True 2707db96d56Sopenharmony_ci 2717db96d56Sopenharmony_cidef find_indentation(node): 2727db96d56Sopenharmony_ci """Find the indentation of *node*.""" 2737db96d56Sopenharmony_ci while node is not None: 2747db96d56Sopenharmony_ci if node.type == syms.suite and len(node.children) > 2: 2757db96d56Sopenharmony_ci indent = node.children[1] 2767db96d56Sopenharmony_ci if indent.type == token.INDENT: 2777db96d56Sopenharmony_ci return indent.value 2787db96d56Sopenharmony_ci node = node.parent 2797db96d56Sopenharmony_ci return "" 2807db96d56Sopenharmony_ci 2817db96d56Sopenharmony_ci########################################################### 2827db96d56Sopenharmony_ci### The following functions are to find bindings in a suite 2837db96d56Sopenharmony_ci########################################################### 2847db96d56Sopenharmony_ci 2857db96d56Sopenharmony_cidef make_suite(node): 2867db96d56Sopenharmony_ci if node.type == syms.suite: 2877db96d56Sopenharmony_ci return node 2887db96d56Sopenharmony_ci node = node.clone() 2897db96d56Sopenharmony_ci parent, node.parent = node.parent, None 2907db96d56Sopenharmony_ci suite = Node(syms.suite, [node]) 2917db96d56Sopenharmony_ci suite.parent = parent 2927db96d56Sopenharmony_ci return suite 2937db96d56Sopenharmony_ci 2947db96d56Sopenharmony_cidef find_root(node): 2957db96d56Sopenharmony_ci """Find the top level namespace.""" 2967db96d56Sopenharmony_ci # Scamper up to the top level namespace 2977db96d56Sopenharmony_ci while node.type != syms.file_input: 2987db96d56Sopenharmony_ci node = node.parent 2997db96d56Sopenharmony_ci if not node: 3007db96d56Sopenharmony_ci raise ValueError("root found before file_input node was found.") 3017db96d56Sopenharmony_ci return node 3027db96d56Sopenharmony_ci 3037db96d56Sopenharmony_cidef does_tree_import(package, name, node): 3047db96d56Sopenharmony_ci """ Returns true if name is imported from package at the 3057db96d56Sopenharmony_ci top level of the tree which node belongs to. 3067db96d56Sopenharmony_ci To cover the case of an import like 'import foo', use 3077db96d56Sopenharmony_ci None for the package and 'foo' for the name. """ 3087db96d56Sopenharmony_ci binding = find_binding(name, find_root(node), package) 3097db96d56Sopenharmony_ci return bool(binding) 3107db96d56Sopenharmony_ci 3117db96d56Sopenharmony_cidef is_import(node): 3127db96d56Sopenharmony_ci """Returns true if the node is an import statement.""" 3137db96d56Sopenharmony_ci return node.type in (syms.import_name, syms.import_from) 3147db96d56Sopenharmony_ci 3157db96d56Sopenharmony_cidef touch_import(package, name, node): 3167db96d56Sopenharmony_ci """ Works like `does_tree_import` but adds an import statement 3177db96d56Sopenharmony_ci if it was not imported. """ 3187db96d56Sopenharmony_ci def is_import_stmt(node): 3197db96d56Sopenharmony_ci return (node.type == syms.simple_stmt and node.children and 3207db96d56Sopenharmony_ci is_import(node.children[0])) 3217db96d56Sopenharmony_ci 3227db96d56Sopenharmony_ci root = find_root(node) 3237db96d56Sopenharmony_ci 3247db96d56Sopenharmony_ci if does_tree_import(package, name, root): 3257db96d56Sopenharmony_ci return 3267db96d56Sopenharmony_ci 3277db96d56Sopenharmony_ci # figure out where to insert the new import. First try to find 3287db96d56Sopenharmony_ci # the first import and then skip to the last one. 3297db96d56Sopenharmony_ci insert_pos = offset = 0 3307db96d56Sopenharmony_ci for idx, node in enumerate(root.children): 3317db96d56Sopenharmony_ci if not is_import_stmt(node): 3327db96d56Sopenharmony_ci continue 3337db96d56Sopenharmony_ci for offset, node2 in enumerate(root.children[idx:]): 3347db96d56Sopenharmony_ci if not is_import_stmt(node2): 3357db96d56Sopenharmony_ci break 3367db96d56Sopenharmony_ci insert_pos = idx + offset 3377db96d56Sopenharmony_ci break 3387db96d56Sopenharmony_ci 3397db96d56Sopenharmony_ci # if there are no imports where we can insert, find the docstring. 3407db96d56Sopenharmony_ci # if that also fails, we stick to the beginning of the file 3417db96d56Sopenharmony_ci if insert_pos == 0: 3427db96d56Sopenharmony_ci for idx, node in enumerate(root.children): 3437db96d56Sopenharmony_ci if (node.type == syms.simple_stmt and node.children and 3447db96d56Sopenharmony_ci node.children[0].type == token.STRING): 3457db96d56Sopenharmony_ci insert_pos = idx + 1 3467db96d56Sopenharmony_ci break 3477db96d56Sopenharmony_ci 3487db96d56Sopenharmony_ci if package is None: 3497db96d56Sopenharmony_ci import_ = Node(syms.import_name, [ 3507db96d56Sopenharmony_ci Leaf(token.NAME, "import"), 3517db96d56Sopenharmony_ci Leaf(token.NAME, name, prefix=" ") 3527db96d56Sopenharmony_ci ]) 3537db96d56Sopenharmony_ci else: 3547db96d56Sopenharmony_ci import_ = FromImport(package, [Leaf(token.NAME, name, prefix=" ")]) 3557db96d56Sopenharmony_ci 3567db96d56Sopenharmony_ci children = [import_, Newline()] 3577db96d56Sopenharmony_ci root.insert_child(insert_pos, Node(syms.simple_stmt, children)) 3587db96d56Sopenharmony_ci 3597db96d56Sopenharmony_ci 3607db96d56Sopenharmony_ci_def_syms = {syms.classdef, syms.funcdef} 3617db96d56Sopenharmony_cidef find_binding(name, node, package=None): 3627db96d56Sopenharmony_ci """ Returns the node which binds variable name, otherwise None. 3637db96d56Sopenharmony_ci If optional argument package is supplied, only imports will 3647db96d56Sopenharmony_ci be returned. 3657db96d56Sopenharmony_ci See test cases for examples.""" 3667db96d56Sopenharmony_ci for child in node.children: 3677db96d56Sopenharmony_ci ret = None 3687db96d56Sopenharmony_ci if child.type == syms.for_stmt: 3697db96d56Sopenharmony_ci if _find(name, child.children[1]): 3707db96d56Sopenharmony_ci return child 3717db96d56Sopenharmony_ci n = find_binding(name, make_suite(child.children[-1]), package) 3727db96d56Sopenharmony_ci if n: ret = n 3737db96d56Sopenharmony_ci elif child.type in (syms.if_stmt, syms.while_stmt): 3747db96d56Sopenharmony_ci n = find_binding(name, make_suite(child.children[-1]), package) 3757db96d56Sopenharmony_ci if n: ret = n 3767db96d56Sopenharmony_ci elif child.type == syms.try_stmt: 3777db96d56Sopenharmony_ci n = find_binding(name, make_suite(child.children[2]), package) 3787db96d56Sopenharmony_ci if n: 3797db96d56Sopenharmony_ci ret = n 3807db96d56Sopenharmony_ci else: 3817db96d56Sopenharmony_ci for i, kid in enumerate(child.children[3:]): 3827db96d56Sopenharmony_ci if kid.type == token.COLON and kid.value == ":": 3837db96d56Sopenharmony_ci # i+3 is the colon, i+4 is the suite 3847db96d56Sopenharmony_ci n = find_binding(name, make_suite(child.children[i+4]), package) 3857db96d56Sopenharmony_ci if n: ret = n 3867db96d56Sopenharmony_ci elif child.type in _def_syms and child.children[1].value == name: 3877db96d56Sopenharmony_ci ret = child 3887db96d56Sopenharmony_ci elif _is_import_binding(child, name, package): 3897db96d56Sopenharmony_ci ret = child 3907db96d56Sopenharmony_ci elif child.type == syms.simple_stmt: 3917db96d56Sopenharmony_ci ret = find_binding(name, child, package) 3927db96d56Sopenharmony_ci elif child.type == syms.expr_stmt: 3937db96d56Sopenharmony_ci if _find(name, child.children[0]): 3947db96d56Sopenharmony_ci ret = child 3957db96d56Sopenharmony_ci 3967db96d56Sopenharmony_ci if ret: 3977db96d56Sopenharmony_ci if not package: 3987db96d56Sopenharmony_ci return ret 3997db96d56Sopenharmony_ci if is_import(ret): 4007db96d56Sopenharmony_ci return ret 4017db96d56Sopenharmony_ci return None 4027db96d56Sopenharmony_ci 4037db96d56Sopenharmony_ci_block_syms = {syms.funcdef, syms.classdef, syms.trailer} 4047db96d56Sopenharmony_cidef _find(name, node): 4057db96d56Sopenharmony_ci nodes = [node] 4067db96d56Sopenharmony_ci while nodes: 4077db96d56Sopenharmony_ci node = nodes.pop() 4087db96d56Sopenharmony_ci if node.type > 256 and node.type not in _block_syms: 4097db96d56Sopenharmony_ci nodes.extend(node.children) 4107db96d56Sopenharmony_ci elif node.type == token.NAME and node.value == name: 4117db96d56Sopenharmony_ci return node 4127db96d56Sopenharmony_ci return None 4137db96d56Sopenharmony_ci 4147db96d56Sopenharmony_cidef _is_import_binding(node, name, package=None): 4157db96d56Sopenharmony_ci """ Will return node if node will import name, or node 4167db96d56Sopenharmony_ci will import * from package. None is returned otherwise. 4177db96d56Sopenharmony_ci See test cases for examples. """ 4187db96d56Sopenharmony_ci 4197db96d56Sopenharmony_ci if node.type == syms.import_name and not package: 4207db96d56Sopenharmony_ci imp = node.children[1] 4217db96d56Sopenharmony_ci if imp.type == syms.dotted_as_names: 4227db96d56Sopenharmony_ci for child in imp.children: 4237db96d56Sopenharmony_ci if child.type == syms.dotted_as_name: 4247db96d56Sopenharmony_ci if child.children[2].value == name: 4257db96d56Sopenharmony_ci return node 4267db96d56Sopenharmony_ci elif child.type == token.NAME and child.value == name: 4277db96d56Sopenharmony_ci return node 4287db96d56Sopenharmony_ci elif imp.type == syms.dotted_as_name: 4297db96d56Sopenharmony_ci last = imp.children[-1] 4307db96d56Sopenharmony_ci if last.type == token.NAME and last.value == name: 4317db96d56Sopenharmony_ci return node 4327db96d56Sopenharmony_ci elif imp.type == token.NAME and imp.value == name: 4337db96d56Sopenharmony_ci return node 4347db96d56Sopenharmony_ci elif node.type == syms.import_from: 4357db96d56Sopenharmony_ci # str(...) is used to make life easier here, because 4367db96d56Sopenharmony_ci # from a.b import parses to ['import', ['a', '.', 'b'], ...] 4377db96d56Sopenharmony_ci if package and str(node.children[1]).strip() != package: 4387db96d56Sopenharmony_ci return None 4397db96d56Sopenharmony_ci n = node.children[3] 4407db96d56Sopenharmony_ci if package and _find("as", n): 4417db96d56Sopenharmony_ci # See test_from_import_as for explanation 4427db96d56Sopenharmony_ci return None 4437db96d56Sopenharmony_ci elif n.type == syms.import_as_names and _find(name, n): 4447db96d56Sopenharmony_ci return node 4457db96d56Sopenharmony_ci elif n.type == syms.import_as_name: 4467db96d56Sopenharmony_ci child = n.children[2] 4477db96d56Sopenharmony_ci if child.type == token.NAME and child.value == name: 4487db96d56Sopenharmony_ci return node 4497db96d56Sopenharmony_ci elif n.type == token.NAME and n.value == name: 4507db96d56Sopenharmony_ci return node 4517db96d56Sopenharmony_ci elif package and n.type == token.STAR: 4527db96d56Sopenharmony_ci return node 4537db96d56Sopenharmony_ci return None 454