17db96d56Sopenharmony_ci""" 27db96d56Sopenharmony_ci ast 37db96d56Sopenharmony_ci ~~~ 47db96d56Sopenharmony_ci 57db96d56Sopenharmony_ci The `ast` module helps Python applications to process trees of the Python 67db96d56Sopenharmony_ci abstract syntax grammar. The abstract syntax itself might change with 77db96d56Sopenharmony_ci each Python release; this module helps to find out programmatically what 87db96d56Sopenharmony_ci the current grammar looks like and allows modifications of it. 97db96d56Sopenharmony_ci 107db96d56Sopenharmony_ci An abstract syntax tree can be generated by passing `ast.PyCF_ONLY_AST` as 117db96d56Sopenharmony_ci a flag to the `compile()` builtin function or by using the `parse()` 127db96d56Sopenharmony_ci function from this module. The result will be a tree of objects whose 137db96d56Sopenharmony_ci classes all inherit from `ast.AST`. 147db96d56Sopenharmony_ci 157db96d56Sopenharmony_ci A modified abstract syntax tree can be compiled into a Python code object 167db96d56Sopenharmony_ci using the built-in `compile()` function. 177db96d56Sopenharmony_ci 187db96d56Sopenharmony_ci Additionally various helper functions are provided that make working with 197db96d56Sopenharmony_ci the trees simpler. The main intention of the helper functions and this 207db96d56Sopenharmony_ci module in general is to provide an easy to use interface for libraries 217db96d56Sopenharmony_ci that work tightly with the python syntax (template engines for example). 227db96d56Sopenharmony_ci 237db96d56Sopenharmony_ci 247db96d56Sopenharmony_ci :copyright: Copyright 2008 by Armin Ronacher. 257db96d56Sopenharmony_ci :license: Python License. 267db96d56Sopenharmony_ci""" 277db96d56Sopenharmony_ciimport sys 287db96d56Sopenharmony_cifrom _ast import * 297db96d56Sopenharmony_cifrom contextlib import contextmanager, nullcontext 307db96d56Sopenharmony_cifrom enum import IntEnum, auto, _simple_enum 317db96d56Sopenharmony_ci 327db96d56Sopenharmony_ci 337db96d56Sopenharmony_cidef parse(source, filename='<unknown>', mode='exec', *, 347db96d56Sopenharmony_ci type_comments=False, feature_version=None): 357db96d56Sopenharmony_ci """ 367db96d56Sopenharmony_ci Parse the source into an AST node. 377db96d56Sopenharmony_ci Equivalent to compile(source, filename, mode, PyCF_ONLY_AST). 387db96d56Sopenharmony_ci Pass type_comments=True to get back type comments where the syntax allows. 397db96d56Sopenharmony_ci """ 407db96d56Sopenharmony_ci flags = PyCF_ONLY_AST 417db96d56Sopenharmony_ci if type_comments: 427db96d56Sopenharmony_ci flags |= PyCF_TYPE_COMMENTS 437db96d56Sopenharmony_ci if isinstance(feature_version, tuple): 447db96d56Sopenharmony_ci major, minor = feature_version # Should be a 2-tuple. 457db96d56Sopenharmony_ci assert major == 3 467db96d56Sopenharmony_ci feature_version = minor 477db96d56Sopenharmony_ci elif feature_version is None: 487db96d56Sopenharmony_ci feature_version = -1 497db96d56Sopenharmony_ci # Else it should be an int giving the minor version for 3.x. 507db96d56Sopenharmony_ci return compile(source, filename, mode, flags, 517db96d56Sopenharmony_ci _feature_version=feature_version) 527db96d56Sopenharmony_ci 537db96d56Sopenharmony_ci 547db96d56Sopenharmony_cidef literal_eval(node_or_string): 557db96d56Sopenharmony_ci """ 567db96d56Sopenharmony_ci Evaluate an expression node or a string containing only a Python 577db96d56Sopenharmony_ci expression. The string or node provided may only consist of the following 587db96d56Sopenharmony_ci Python literal structures: strings, bytes, numbers, tuples, lists, dicts, 597db96d56Sopenharmony_ci sets, booleans, and None. 607db96d56Sopenharmony_ci 617db96d56Sopenharmony_ci Caution: A complex expression can overflow the C stack and cause a crash. 627db96d56Sopenharmony_ci """ 637db96d56Sopenharmony_ci if isinstance(node_or_string, str): 647db96d56Sopenharmony_ci node_or_string = parse(node_or_string.lstrip(" \t"), mode='eval') 657db96d56Sopenharmony_ci if isinstance(node_or_string, Expression): 667db96d56Sopenharmony_ci node_or_string = node_or_string.body 677db96d56Sopenharmony_ci def _raise_malformed_node(node): 687db96d56Sopenharmony_ci msg = "malformed node or string" 697db96d56Sopenharmony_ci if lno := getattr(node, 'lineno', None): 707db96d56Sopenharmony_ci msg += f' on line {lno}' 717db96d56Sopenharmony_ci raise ValueError(msg + f': {node!r}') 727db96d56Sopenharmony_ci def _convert_num(node): 737db96d56Sopenharmony_ci if not isinstance(node, Constant) or type(node.value) not in (int, float, complex): 747db96d56Sopenharmony_ci _raise_malformed_node(node) 757db96d56Sopenharmony_ci return node.value 767db96d56Sopenharmony_ci def _convert_signed_num(node): 777db96d56Sopenharmony_ci if isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)): 787db96d56Sopenharmony_ci operand = _convert_num(node.operand) 797db96d56Sopenharmony_ci if isinstance(node.op, UAdd): 807db96d56Sopenharmony_ci return + operand 817db96d56Sopenharmony_ci else: 827db96d56Sopenharmony_ci return - operand 837db96d56Sopenharmony_ci return _convert_num(node) 847db96d56Sopenharmony_ci def _convert(node): 857db96d56Sopenharmony_ci if isinstance(node, Constant): 867db96d56Sopenharmony_ci return node.value 877db96d56Sopenharmony_ci elif isinstance(node, Tuple): 887db96d56Sopenharmony_ci return tuple(map(_convert, node.elts)) 897db96d56Sopenharmony_ci elif isinstance(node, List): 907db96d56Sopenharmony_ci return list(map(_convert, node.elts)) 917db96d56Sopenharmony_ci elif isinstance(node, Set): 927db96d56Sopenharmony_ci return set(map(_convert, node.elts)) 937db96d56Sopenharmony_ci elif (isinstance(node, Call) and isinstance(node.func, Name) and 947db96d56Sopenharmony_ci node.func.id == 'set' and node.args == node.keywords == []): 957db96d56Sopenharmony_ci return set() 967db96d56Sopenharmony_ci elif isinstance(node, Dict): 977db96d56Sopenharmony_ci if len(node.keys) != len(node.values): 987db96d56Sopenharmony_ci _raise_malformed_node(node) 997db96d56Sopenharmony_ci return dict(zip(map(_convert, node.keys), 1007db96d56Sopenharmony_ci map(_convert, node.values))) 1017db96d56Sopenharmony_ci elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)): 1027db96d56Sopenharmony_ci left = _convert_signed_num(node.left) 1037db96d56Sopenharmony_ci right = _convert_num(node.right) 1047db96d56Sopenharmony_ci if isinstance(left, (int, float)) and isinstance(right, complex): 1057db96d56Sopenharmony_ci if isinstance(node.op, Add): 1067db96d56Sopenharmony_ci return left + right 1077db96d56Sopenharmony_ci else: 1087db96d56Sopenharmony_ci return left - right 1097db96d56Sopenharmony_ci return _convert_signed_num(node) 1107db96d56Sopenharmony_ci return _convert(node_or_string) 1117db96d56Sopenharmony_ci 1127db96d56Sopenharmony_ci 1137db96d56Sopenharmony_cidef dump(node, annotate_fields=True, include_attributes=False, *, indent=None): 1147db96d56Sopenharmony_ci """ 1157db96d56Sopenharmony_ci Return a formatted dump of the tree in node. This is mainly useful for 1167db96d56Sopenharmony_ci debugging purposes. If annotate_fields is true (by default), 1177db96d56Sopenharmony_ci the returned string will show the names and the values for fields. 1187db96d56Sopenharmony_ci If annotate_fields is false, the result string will be more compact by 1197db96d56Sopenharmony_ci omitting unambiguous field names. Attributes such as line 1207db96d56Sopenharmony_ci numbers and column offsets are not dumped by default. If this is wanted, 1217db96d56Sopenharmony_ci include_attributes can be set to true. If indent is a non-negative 1227db96d56Sopenharmony_ci integer or string, then the tree will be pretty-printed with that indent 1237db96d56Sopenharmony_ci level. None (the default) selects the single line representation. 1247db96d56Sopenharmony_ci """ 1257db96d56Sopenharmony_ci def _format(node, level=0): 1267db96d56Sopenharmony_ci if indent is not None: 1277db96d56Sopenharmony_ci level += 1 1287db96d56Sopenharmony_ci prefix = '\n' + indent * level 1297db96d56Sopenharmony_ci sep = ',\n' + indent * level 1307db96d56Sopenharmony_ci else: 1317db96d56Sopenharmony_ci prefix = '' 1327db96d56Sopenharmony_ci sep = ', ' 1337db96d56Sopenharmony_ci if isinstance(node, AST): 1347db96d56Sopenharmony_ci cls = type(node) 1357db96d56Sopenharmony_ci args = [] 1367db96d56Sopenharmony_ci allsimple = True 1377db96d56Sopenharmony_ci keywords = annotate_fields 1387db96d56Sopenharmony_ci for name in node._fields: 1397db96d56Sopenharmony_ci try: 1407db96d56Sopenharmony_ci value = getattr(node, name) 1417db96d56Sopenharmony_ci except AttributeError: 1427db96d56Sopenharmony_ci keywords = True 1437db96d56Sopenharmony_ci continue 1447db96d56Sopenharmony_ci if value is None and getattr(cls, name, ...) is None: 1457db96d56Sopenharmony_ci keywords = True 1467db96d56Sopenharmony_ci continue 1477db96d56Sopenharmony_ci value, simple = _format(value, level) 1487db96d56Sopenharmony_ci allsimple = allsimple and simple 1497db96d56Sopenharmony_ci if keywords: 1507db96d56Sopenharmony_ci args.append('%s=%s' % (name, value)) 1517db96d56Sopenharmony_ci else: 1527db96d56Sopenharmony_ci args.append(value) 1537db96d56Sopenharmony_ci if include_attributes and node._attributes: 1547db96d56Sopenharmony_ci for name in node._attributes: 1557db96d56Sopenharmony_ci try: 1567db96d56Sopenharmony_ci value = getattr(node, name) 1577db96d56Sopenharmony_ci except AttributeError: 1587db96d56Sopenharmony_ci continue 1597db96d56Sopenharmony_ci if value is None and getattr(cls, name, ...) is None: 1607db96d56Sopenharmony_ci continue 1617db96d56Sopenharmony_ci value, simple = _format(value, level) 1627db96d56Sopenharmony_ci allsimple = allsimple and simple 1637db96d56Sopenharmony_ci args.append('%s=%s' % (name, value)) 1647db96d56Sopenharmony_ci if allsimple and len(args) <= 3: 1657db96d56Sopenharmony_ci return '%s(%s)' % (node.__class__.__name__, ', '.join(args)), not args 1667db96d56Sopenharmony_ci return '%s(%s%s)' % (node.__class__.__name__, prefix, sep.join(args)), False 1677db96d56Sopenharmony_ci elif isinstance(node, list): 1687db96d56Sopenharmony_ci if not node: 1697db96d56Sopenharmony_ci return '[]', True 1707db96d56Sopenharmony_ci return '[%s%s]' % (prefix, sep.join(_format(x, level)[0] for x in node)), False 1717db96d56Sopenharmony_ci return repr(node), True 1727db96d56Sopenharmony_ci 1737db96d56Sopenharmony_ci if not isinstance(node, AST): 1747db96d56Sopenharmony_ci raise TypeError('expected AST, got %r' % node.__class__.__name__) 1757db96d56Sopenharmony_ci if indent is not None and not isinstance(indent, str): 1767db96d56Sopenharmony_ci indent = ' ' * indent 1777db96d56Sopenharmony_ci return _format(node)[0] 1787db96d56Sopenharmony_ci 1797db96d56Sopenharmony_ci 1807db96d56Sopenharmony_cidef copy_location(new_node, old_node): 1817db96d56Sopenharmony_ci """ 1827db96d56Sopenharmony_ci Copy source location (`lineno`, `col_offset`, `end_lineno`, and `end_col_offset` 1837db96d56Sopenharmony_ci attributes) from *old_node* to *new_node* if possible, and return *new_node*. 1847db96d56Sopenharmony_ci """ 1857db96d56Sopenharmony_ci for attr in 'lineno', 'col_offset', 'end_lineno', 'end_col_offset': 1867db96d56Sopenharmony_ci if attr in old_node._attributes and attr in new_node._attributes: 1877db96d56Sopenharmony_ci value = getattr(old_node, attr, None) 1887db96d56Sopenharmony_ci # end_lineno and end_col_offset are optional attributes, and they 1897db96d56Sopenharmony_ci # should be copied whether the value is None or not. 1907db96d56Sopenharmony_ci if value is not None or ( 1917db96d56Sopenharmony_ci hasattr(old_node, attr) and attr.startswith("end_") 1927db96d56Sopenharmony_ci ): 1937db96d56Sopenharmony_ci setattr(new_node, attr, value) 1947db96d56Sopenharmony_ci return new_node 1957db96d56Sopenharmony_ci 1967db96d56Sopenharmony_ci 1977db96d56Sopenharmony_cidef fix_missing_locations(node): 1987db96d56Sopenharmony_ci """ 1997db96d56Sopenharmony_ci When you compile a node tree with compile(), the compiler expects lineno and 2007db96d56Sopenharmony_ci col_offset attributes for every node that supports them. This is rather 2017db96d56Sopenharmony_ci tedious to fill in for generated nodes, so this helper adds these attributes 2027db96d56Sopenharmony_ci recursively where not already set, by setting them to the values of the 2037db96d56Sopenharmony_ci parent node. It works recursively starting at *node*. 2047db96d56Sopenharmony_ci """ 2057db96d56Sopenharmony_ci def _fix(node, lineno, col_offset, end_lineno, end_col_offset): 2067db96d56Sopenharmony_ci if 'lineno' in node._attributes: 2077db96d56Sopenharmony_ci if not hasattr(node, 'lineno'): 2087db96d56Sopenharmony_ci node.lineno = lineno 2097db96d56Sopenharmony_ci else: 2107db96d56Sopenharmony_ci lineno = node.lineno 2117db96d56Sopenharmony_ci if 'end_lineno' in node._attributes: 2127db96d56Sopenharmony_ci if getattr(node, 'end_lineno', None) is None: 2137db96d56Sopenharmony_ci node.end_lineno = end_lineno 2147db96d56Sopenharmony_ci else: 2157db96d56Sopenharmony_ci end_lineno = node.end_lineno 2167db96d56Sopenharmony_ci if 'col_offset' in node._attributes: 2177db96d56Sopenharmony_ci if not hasattr(node, 'col_offset'): 2187db96d56Sopenharmony_ci node.col_offset = col_offset 2197db96d56Sopenharmony_ci else: 2207db96d56Sopenharmony_ci col_offset = node.col_offset 2217db96d56Sopenharmony_ci if 'end_col_offset' in node._attributes: 2227db96d56Sopenharmony_ci if getattr(node, 'end_col_offset', None) is None: 2237db96d56Sopenharmony_ci node.end_col_offset = end_col_offset 2247db96d56Sopenharmony_ci else: 2257db96d56Sopenharmony_ci end_col_offset = node.end_col_offset 2267db96d56Sopenharmony_ci for child in iter_child_nodes(node): 2277db96d56Sopenharmony_ci _fix(child, lineno, col_offset, end_lineno, end_col_offset) 2287db96d56Sopenharmony_ci _fix(node, 1, 0, 1, 0) 2297db96d56Sopenharmony_ci return node 2307db96d56Sopenharmony_ci 2317db96d56Sopenharmony_ci 2327db96d56Sopenharmony_cidef increment_lineno(node, n=1): 2337db96d56Sopenharmony_ci """ 2347db96d56Sopenharmony_ci Increment the line number and end line number of each node in the tree 2357db96d56Sopenharmony_ci starting at *node* by *n*. This is useful to "move code" to a different 2367db96d56Sopenharmony_ci location in a file. 2377db96d56Sopenharmony_ci """ 2387db96d56Sopenharmony_ci for child in walk(node): 2397db96d56Sopenharmony_ci # TypeIgnore is a special case where lineno is not an attribute 2407db96d56Sopenharmony_ci # but rather a field of the node itself. 2417db96d56Sopenharmony_ci if isinstance(child, TypeIgnore): 2427db96d56Sopenharmony_ci child.lineno = getattr(child, 'lineno', 0) + n 2437db96d56Sopenharmony_ci continue 2447db96d56Sopenharmony_ci 2457db96d56Sopenharmony_ci if 'lineno' in child._attributes: 2467db96d56Sopenharmony_ci child.lineno = getattr(child, 'lineno', 0) + n 2477db96d56Sopenharmony_ci if ( 2487db96d56Sopenharmony_ci "end_lineno" in child._attributes 2497db96d56Sopenharmony_ci and (end_lineno := getattr(child, "end_lineno", 0)) is not None 2507db96d56Sopenharmony_ci ): 2517db96d56Sopenharmony_ci child.end_lineno = end_lineno + n 2527db96d56Sopenharmony_ci return node 2537db96d56Sopenharmony_ci 2547db96d56Sopenharmony_ci 2557db96d56Sopenharmony_cidef iter_fields(node): 2567db96d56Sopenharmony_ci """ 2577db96d56Sopenharmony_ci Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields`` 2587db96d56Sopenharmony_ci that is present on *node*. 2597db96d56Sopenharmony_ci """ 2607db96d56Sopenharmony_ci for field in node._fields: 2617db96d56Sopenharmony_ci try: 2627db96d56Sopenharmony_ci yield field, getattr(node, field) 2637db96d56Sopenharmony_ci except AttributeError: 2647db96d56Sopenharmony_ci pass 2657db96d56Sopenharmony_ci 2667db96d56Sopenharmony_ci 2677db96d56Sopenharmony_cidef iter_child_nodes(node): 2687db96d56Sopenharmony_ci """ 2697db96d56Sopenharmony_ci Yield all direct child nodes of *node*, that is, all fields that are nodes 2707db96d56Sopenharmony_ci and all items of fields that are lists of nodes. 2717db96d56Sopenharmony_ci """ 2727db96d56Sopenharmony_ci for name, field in iter_fields(node): 2737db96d56Sopenharmony_ci if isinstance(field, AST): 2747db96d56Sopenharmony_ci yield field 2757db96d56Sopenharmony_ci elif isinstance(field, list): 2767db96d56Sopenharmony_ci for item in field: 2777db96d56Sopenharmony_ci if isinstance(item, AST): 2787db96d56Sopenharmony_ci yield item 2797db96d56Sopenharmony_ci 2807db96d56Sopenharmony_ci 2817db96d56Sopenharmony_cidef get_docstring(node, clean=True): 2827db96d56Sopenharmony_ci """ 2837db96d56Sopenharmony_ci Return the docstring for the given node or None if no docstring can 2847db96d56Sopenharmony_ci be found. If the node provided does not have docstrings a TypeError 2857db96d56Sopenharmony_ci will be raised. 2867db96d56Sopenharmony_ci 2877db96d56Sopenharmony_ci If *clean* is `True`, all tabs are expanded to spaces and any whitespace 2887db96d56Sopenharmony_ci that can be uniformly removed from the second line onwards is removed. 2897db96d56Sopenharmony_ci """ 2907db96d56Sopenharmony_ci if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)): 2917db96d56Sopenharmony_ci raise TypeError("%r can't have docstrings" % node.__class__.__name__) 2927db96d56Sopenharmony_ci if not(node.body and isinstance(node.body[0], Expr)): 2937db96d56Sopenharmony_ci return None 2947db96d56Sopenharmony_ci node = node.body[0].value 2957db96d56Sopenharmony_ci if isinstance(node, Str): 2967db96d56Sopenharmony_ci text = node.s 2977db96d56Sopenharmony_ci elif isinstance(node, Constant) and isinstance(node.value, str): 2987db96d56Sopenharmony_ci text = node.value 2997db96d56Sopenharmony_ci else: 3007db96d56Sopenharmony_ci return None 3017db96d56Sopenharmony_ci if clean: 3027db96d56Sopenharmony_ci import inspect 3037db96d56Sopenharmony_ci text = inspect.cleandoc(text) 3047db96d56Sopenharmony_ci return text 3057db96d56Sopenharmony_ci 3067db96d56Sopenharmony_ci 3077db96d56Sopenharmony_cidef _splitlines_no_ff(source): 3087db96d56Sopenharmony_ci """Split a string into lines ignoring form feed and other chars. 3097db96d56Sopenharmony_ci 3107db96d56Sopenharmony_ci This mimics how the Python parser splits source code. 3117db96d56Sopenharmony_ci """ 3127db96d56Sopenharmony_ci idx = 0 3137db96d56Sopenharmony_ci lines = [] 3147db96d56Sopenharmony_ci next_line = '' 3157db96d56Sopenharmony_ci while idx < len(source): 3167db96d56Sopenharmony_ci c = source[idx] 3177db96d56Sopenharmony_ci next_line += c 3187db96d56Sopenharmony_ci idx += 1 3197db96d56Sopenharmony_ci # Keep \r\n together 3207db96d56Sopenharmony_ci if c == '\r' and idx < len(source) and source[idx] == '\n': 3217db96d56Sopenharmony_ci next_line += '\n' 3227db96d56Sopenharmony_ci idx += 1 3237db96d56Sopenharmony_ci if c in '\r\n': 3247db96d56Sopenharmony_ci lines.append(next_line) 3257db96d56Sopenharmony_ci next_line = '' 3267db96d56Sopenharmony_ci 3277db96d56Sopenharmony_ci if next_line: 3287db96d56Sopenharmony_ci lines.append(next_line) 3297db96d56Sopenharmony_ci return lines 3307db96d56Sopenharmony_ci 3317db96d56Sopenharmony_ci 3327db96d56Sopenharmony_cidef _pad_whitespace(source): 3337db96d56Sopenharmony_ci r"""Replace all chars except '\f\t' in a line with spaces.""" 3347db96d56Sopenharmony_ci result = '' 3357db96d56Sopenharmony_ci for c in source: 3367db96d56Sopenharmony_ci if c in '\f\t': 3377db96d56Sopenharmony_ci result += c 3387db96d56Sopenharmony_ci else: 3397db96d56Sopenharmony_ci result += ' ' 3407db96d56Sopenharmony_ci return result 3417db96d56Sopenharmony_ci 3427db96d56Sopenharmony_ci 3437db96d56Sopenharmony_cidef get_source_segment(source, node, *, padded=False): 3447db96d56Sopenharmony_ci """Get source code segment of the *source* that generated *node*. 3457db96d56Sopenharmony_ci 3467db96d56Sopenharmony_ci If some location information (`lineno`, `end_lineno`, `col_offset`, 3477db96d56Sopenharmony_ci or `end_col_offset`) is missing, return None. 3487db96d56Sopenharmony_ci 3497db96d56Sopenharmony_ci If *padded* is `True`, the first line of a multi-line statement will 3507db96d56Sopenharmony_ci be padded with spaces to match its original position. 3517db96d56Sopenharmony_ci """ 3527db96d56Sopenharmony_ci try: 3537db96d56Sopenharmony_ci if node.end_lineno is None or node.end_col_offset is None: 3547db96d56Sopenharmony_ci return None 3557db96d56Sopenharmony_ci lineno = node.lineno - 1 3567db96d56Sopenharmony_ci end_lineno = node.end_lineno - 1 3577db96d56Sopenharmony_ci col_offset = node.col_offset 3587db96d56Sopenharmony_ci end_col_offset = node.end_col_offset 3597db96d56Sopenharmony_ci except AttributeError: 3607db96d56Sopenharmony_ci return None 3617db96d56Sopenharmony_ci 3627db96d56Sopenharmony_ci lines = _splitlines_no_ff(source) 3637db96d56Sopenharmony_ci if end_lineno == lineno: 3647db96d56Sopenharmony_ci return lines[lineno].encode()[col_offset:end_col_offset].decode() 3657db96d56Sopenharmony_ci 3667db96d56Sopenharmony_ci if padded: 3677db96d56Sopenharmony_ci padding = _pad_whitespace(lines[lineno].encode()[:col_offset].decode()) 3687db96d56Sopenharmony_ci else: 3697db96d56Sopenharmony_ci padding = '' 3707db96d56Sopenharmony_ci 3717db96d56Sopenharmony_ci first = padding + lines[lineno].encode()[col_offset:].decode() 3727db96d56Sopenharmony_ci last = lines[end_lineno].encode()[:end_col_offset].decode() 3737db96d56Sopenharmony_ci lines = lines[lineno+1:end_lineno] 3747db96d56Sopenharmony_ci 3757db96d56Sopenharmony_ci lines.insert(0, first) 3767db96d56Sopenharmony_ci lines.append(last) 3777db96d56Sopenharmony_ci return ''.join(lines) 3787db96d56Sopenharmony_ci 3797db96d56Sopenharmony_ci 3807db96d56Sopenharmony_cidef walk(node): 3817db96d56Sopenharmony_ci """ 3827db96d56Sopenharmony_ci Recursively yield all descendant nodes in the tree starting at *node* 3837db96d56Sopenharmony_ci (including *node* itself), in no specified order. This is useful if you 3847db96d56Sopenharmony_ci only want to modify nodes in place and don't care about the context. 3857db96d56Sopenharmony_ci """ 3867db96d56Sopenharmony_ci from collections import deque 3877db96d56Sopenharmony_ci todo = deque([node]) 3887db96d56Sopenharmony_ci while todo: 3897db96d56Sopenharmony_ci node = todo.popleft() 3907db96d56Sopenharmony_ci todo.extend(iter_child_nodes(node)) 3917db96d56Sopenharmony_ci yield node 3927db96d56Sopenharmony_ci 3937db96d56Sopenharmony_ci 3947db96d56Sopenharmony_ciclass NodeVisitor(object): 3957db96d56Sopenharmony_ci """ 3967db96d56Sopenharmony_ci A node visitor base class that walks the abstract syntax tree and calls a 3977db96d56Sopenharmony_ci visitor function for every node found. This function may return a value 3987db96d56Sopenharmony_ci which is forwarded by the `visit` method. 3997db96d56Sopenharmony_ci 4007db96d56Sopenharmony_ci This class is meant to be subclassed, with the subclass adding visitor 4017db96d56Sopenharmony_ci methods. 4027db96d56Sopenharmony_ci 4037db96d56Sopenharmony_ci Per default the visitor functions for the nodes are ``'visit_'`` + 4047db96d56Sopenharmony_ci class name of the node. So a `TryFinally` node visit function would 4057db96d56Sopenharmony_ci be `visit_TryFinally`. This behavior can be changed by overriding 4067db96d56Sopenharmony_ci the `visit` method. If no visitor function exists for a node 4077db96d56Sopenharmony_ci (return value `None`) the `generic_visit` visitor is used instead. 4087db96d56Sopenharmony_ci 4097db96d56Sopenharmony_ci Don't use the `NodeVisitor` if you want to apply changes to nodes during 4107db96d56Sopenharmony_ci traversing. For this a special visitor exists (`NodeTransformer`) that 4117db96d56Sopenharmony_ci allows modifications. 4127db96d56Sopenharmony_ci """ 4137db96d56Sopenharmony_ci 4147db96d56Sopenharmony_ci def visit(self, node): 4157db96d56Sopenharmony_ci """Visit a node.""" 4167db96d56Sopenharmony_ci method = 'visit_' + node.__class__.__name__ 4177db96d56Sopenharmony_ci visitor = getattr(self, method, self.generic_visit) 4187db96d56Sopenharmony_ci return visitor(node) 4197db96d56Sopenharmony_ci 4207db96d56Sopenharmony_ci def generic_visit(self, node): 4217db96d56Sopenharmony_ci """Called if no explicit visitor function exists for a node.""" 4227db96d56Sopenharmony_ci for field, value in iter_fields(node): 4237db96d56Sopenharmony_ci if isinstance(value, list): 4247db96d56Sopenharmony_ci for item in value: 4257db96d56Sopenharmony_ci if isinstance(item, AST): 4267db96d56Sopenharmony_ci self.visit(item) 4277db96d56Sopenharmony_ci elif isinstance(value, AST): 4287db96d56Sopenharmony_ci self.visit(value) 4297db96d56Sopenharmony_ci 4307db96d56Sopenharmony_ci def visit_Constant(self, node): 4317db96d56Sopenharmony_ci value = node.value 4327db96d56Sopenharmony_ci type_name = _const_node_type_names.get(type(value)) 4337db96d56Sopenharmony_ci if type_name is None: 4347db96d56Sopenharmony_ci for cls, name in _const_node_type_names.items(): 4357db96d56Sopenharmony_ci if isinstance(value, cls): 4367db96d56Sopenharmony_ci type_name = name 4377db96d56Sopenharmony_ci break 4387db96d56Sopenharmony_ci if type_name is not None: 4397db96d56Sopenharmony_ci method = 'visit_' + type_name 4407db96d56Sopenharmony_ci try: 4417db96d56Sopenharmony_ci visitor = getattr(self, method) 4427db96d56Sopenharmony_ci except AttributeError: 4437db96d56Sopenharmony_ci pass 4447db96d56Sopenharmony_ci else: 4457db96d56Sopenharmony_ci import warnings 4467db96d56Sopenharmony_ci warnings.warn(f"{method} is deprecated; add visit_Constant", 4477db96d56Sopenharmony_ci DeprecationWarning, 2) 4487db96d56Sopenharmony_ci return visitor(node) 4497db96d56Sopenharmony_ci return self.generic_visit(node) 4507db96d56Sopenharmony_ci 4517db96d56Sopenharmony_ci 4527db96d56Sopenharmony_ciclass NodeTransformer(NodeVisitor): 4537db96d56Sopenharmony_ci """ 4547db96d56Sopenharmony_ci A :class:`NodeVisitor` subclass that walks the abstract syntax tree and 4557db96d56Sopenharmony_ci allows modification of nodes. 4567db96d56Sopenharmony_ci 4577db96d56Sopenharmony_ci The `NodeTransformer` will walk the AST and use the return value of the 4587db96d56Sopenharmony_ci visitor methods to replace or remove the old node. If the return value of 4597db96d56Sopenharmony_ci the visitor method is ``None``, the node will be removed from its location, 4607db96d56Sopenharmony_ci otherwise it is replaced with the return value. The return value may be the 4617db96d56Sopenharmony_ci original node in which case no replacement takes place. 4627db96d56Sopenharmony_ci 4637db96d56Sopenharmony_ci Here is an example transformer that rewrites all occurrences of name lookups 4647db96d56Sopenharmony_ci (``foo``) to ``data['foo']``:: 4657db96d56Sopenharmony_ci 4667db96d56Sopenharmony_ci class RewriteName(NodeTransformer): 4677db96d56Sopenharmony_ci 4687db96d56Sopenharmony_ci def visit_Name(self, node): 4697db96d56Sopenharmony_ci return Subscript( 4707db96d56Sopenharmony_ci value=Name(id='data', ctx=Load()), 4717db96d56Sopenharmony_ci slice=Constant(value=node.id), 4727db96d56Sopenharmony_ci ctx=node.ctx 4737db96d56Sopenharmony_ci ) 4747db96d56Sopenharmony_ci 4757db96d56Sopenharmony_ci Keep in mind that if the node you're operating on has child nodes you must 4767db96d56Sopenharmony_ci either transform the child nodes yourself or call the :meth:`generic_visit` 4777db96d56Sopenharmony_ci method for the node first. 4787db96d56Sopenharmony_ci 4797db96d56Sopenharmony_ci For nodes that were part of a collection of statements (that applies to all 4807db96d56Sopenharmony_ci statement nodes), the visitor may also return a list of nodes rather than 4817db96d56Sopenharmony_ci just a single node. 4827db96d56Sopenharmony_ci 4837db96d56Sopenharmony_ci Usually you use the transformer like this:: 4847db96d56Sopenharmony_ci 4857db96d56Sopenharmony_ci node = YourTransformer().visit(node) 4867db96d56Sopenharmony_ci """ 4877db96d56Sopenharmony_ci 4887db96d56Sopenharmony_ci def generic_visit(self, node): 4897db96d56Sopenharmony_ci for field, old_value in iter_fields(node): 4907db96d56Sopenharmony_ci if isinstance(old_value, list): 4917db96d56Sopenharmony_ci new_values = [] 4927db96d56Sopenharmony_ci for value in old_value: 4937db96d56Sopenharmony_ci if isinstance(value, AST): 4947db96d56Sopenharmony_ci value = self.visit(value) 4957db96d56Sopenharmony_ci if value is None: 4967db96d56Sopenharmony_ci continue 4977db96d56Sopenharmony_ci elif not isinstance(value, AST): 4987db96d56Sopenharmony_ci new_values.extend(value) 4997db96d56Sopenharmony_ci continue 5007db96d56Sopenharmony_ci new_values.append(value) 5017db96d56Sopenharmony_ci old_value[:] = new_values 5027db96d56Sopenharmony_ci elif isinstance(old_value, AST): 5037db96d56Sopenharmony_ci new_node = self.visit(old_value) 5047db96d56Sopenharmony_ci if new_node is None: 5057db96d56Sopenharmony_ci delattr(node, field) 5067db96d56Sopenharmony_ci else: 5077db96d56Sopenharmony_ci setattr(node, field, new_node) 5087db96d56Sopenharmony_ci return node 5097db96d56Sopenharmony_ci 5107db96d56Sopenharmony_ci 5117db96d56Sopenharmony_ci# If the ast module is loaded more than once, only add deprecated methods once 5127db96d56Sopenharmony_ciif not hasattr(Constant, 'n'): 5137db96d56Sopenharmony_ci # The following code is for backward compatibility. 5147db96d56Sopenharmony_ci # It will be removed in future. 5157db96d56Sopenharmony_ci 5167db96d56Sopenharmony_ci def _getter(self): 5177db96d56Sopenharmony_ci """Deprecated. Use value instead.""" 5187db96d56Sopenharmony_ci return self.value 5197db96d56Sopenharmony_ci 5207db96d56Sopenharmony_ci def _setter(self, value): 5217db96d56Sopenharmony_ci self.value = value 5227db96d56Sopenharmony_ci 5237db96d56Sopenharmony_ci Constant.n = property(_getter, _setter) 5247db96d56Sopenharmony_ci Constant.s = property(_getter, _setter) 5257db96d56Sopenharmony_ci 5267db96d56Sopenharmony_ciclass _ABC(type): 5277db96d56Sopenharmony_ci 5287db96d56Sopenharmony_ci def __init__(cls, *args): 5297db96d56Sopenharmony_ci cls.__doc__ = """Deprecated AST node class. Use ast.Constant instead""" 5307db96d56Sopenharmony_ci 5317db96d56Sopenharmony_ci def __instancecheck__(cls, inst): 5327db96d56Sopenharmony_ci if not isinstance(inst, Constant): 5337db96d56Sopenharmony_ci return False 5347db96d56Sopenharmony_ci if cls in _const_types: 5357db96d56Sopenharmony_ci try: 5367db96d56Sopenharmony_ci value = inst.value 5377db96d56Sopenharmony_ci except AttributeError: 5387db96d56Sopenharmony_ci return False 5397db96d56Sopenharmony_ci else: 5407db96d56Sopenharmony_ci return ( 5417db96d56Sopenharmony_ci isinstance(value, _const_types[cls]) and 5427db96d56Sopenharmony_ci not isinstance(value, _const_types_not.get(cls, ())) 5437db96d56Sopenharmony_ci ) 5447db96d56Sopenharmony_ci return type.__instancecheck__(cls, inst) 5457db96d56Sopenharmony_ci 5467db96d56Sopenharmony_cidef _new(cls, *args, **kwargs): 5477db96d56Sopenharmony_ci for key in kwargs: 5487db96d56Sopenharmony_ci if key not in cls._fields: 5497db96d56Sopenharmony_ci # arbitrary keyword arguments are accepted 5507db96d56Sopenharmony_ci continue 5517db96d56Sopenharmony_ci pos = cls._fields.index(key) 5527db96d56Sopenharmony_ci if pos < len(args): 5537db96d56Sopenharmony_ci raise TypeError(f"{cls.__name__} got multiple values for argument {key!r}") 5547db96d56Sopenharmony_ci if cls in _const_types: 5557db96d56Sopenharmony_ci return Constant(*args, **kwargs) 5567db96d56Sopenharmony_ci return Constant.__new__(cls, *args, **kwargs) 5577db96d56Sopenharmony_ci 5587db96d56Sopenharmony_ciclass Num(Constant, metaclass=_ABC): 5597db96d56Sopenharmony_ci _fields = ('n',) 5607db96d56Sopenharmony_ci __new__ = _new 5617db96d56Sopenharmony_ci 5627db96d56Sopenharmony_ciclass Str(Constant, metaclass=_ABC): 5637db96d56Sopenharmony_ci _fields = ('s',) 5647db96d56Sopenharmony_ci __new__ = _new 5657db96d56Sopenharmony_ci 5667db96d56Sopenharmony_ciclass Bytes(Constant, metaclass=_ABC): 5677db96d56Sopenharmony_ci _fields = ('s',) 5687db96d56Sopenharmony_ci __new__ = _new 5697db96d56Sopenharmony_ci 5707db96d56Sopenharmony_ciclass NameConstant(Constant, metaclass=_ABC): 5717db96d56Sopenharmony_ci __new__ = _new 5727db96d56Sopenharmony_ci 5737db96d56Sopenharmony_ciclass Ellipsis(Constant, metaclass=_ABC): 5747db96d56Sopenharmony_ci _fields = () 5757db96d56Sopenharmony_ci 5767db96d56Sopenharmony_ci def __new__(cls, *args, **kwargs): 5777db96d56Sopenharmony_ci if cls is Ellipsis: 5787db96d56Sopenharmony_ci return Constant(..., *args, **kwargs) 5797db96d56Sopenharmony_ci return Constant.__new__(cls, *args, **kwargs) 5807db96d56Sopenharmony_ci 5817db96d56Sopenharmony_ci_const_types = { 5827db96d56Sopenharmony_ci Num: (int, float, complex), 5837db96d56Sopenharmony_ci Str: (str,), 5847db96d56Sopenharmony_ci Bytes: (bytes,), 5857db96d56Sopenharmony_ci NameConstant: (type(None), bool), 5867db96d56Sopenharmony_ci Ellipsis: (type(...),), 5877db96d56Sopenharmony_ci} 5887db96d56Sopenharmony_ci_const_types_not = { 5897db96d56Sopenharmony_ci Num: (bool,), 5907db96d56Sopenharmony_ci} 5917db96d56Sopenharmony_ci 5927db96d56Sopenharmony_ci_const_node_type_names = { 5937db96d56Sopenharmony_ci bool: 'NameConstant', # should be before int 5947db96d56Sopenharmony_ci type(None): 'NameConstant', 5957db96d56Sopenharmony_ci int: 'Num', 5967db96d56Sopenharmony_ci float: 'Num', 5977db96d56Sopenharmony_ci complex: 'Num', 5987db96d56Sopenharmony_ci str: 'Str', 5997db96d56Sopenharmony_ci bytes: 'Bytes', 6007db96d56Sopenharmony_ci type(...): 'Ellipsis', 6017db96d56Sopenharmony_ci} 6027db96d56Sopenharmony_ci 6037db96d56Sopenharmony_ciclass slice(AST): 6047db96d56Sopenharmony_ci """Deprecated AST node class.""" 6057db96d56Sopenharmony_ci 6067db96d56Sopenharmony_ciclass Index(slice): 6077db96d56Sopenharmony_ci """Deprecated AST node class. Use the index value directly instead.""" 6087db96d56Sopenharmony_ci def __new__(cls, value, **kwargs): 6097db96d56Sopenharmony_ci return value 6107db96d56Sopenharmony_ci 6117db96d56Sopenharmony_ciclass ExtSlice(slice): 6127db96d56Sopenharmony_ci """Deprecated AST node class. Use ast.Tuple instead.""" 6137db96d56Sopenharmony_ci def __new__(cls, dims=(), **kwargs): 6147db96d56Sopenharmony_ci return Tuple(list(dims), Load(), **kwargs) 6157db96d56Sopenharmony_ci 6167db96d56Sopenharmony_ci# If the ast module is loaded more than once, only add deprecated methods once 6177db96d56Sopenharmony_ciif not hasattr(Tuple, 'dims'): 6187db96d56Sopenharmony_ci # The following code is for backward compatibility. 6197db96d56Sopenharmony_ci # It will be removed in future. 6207db96d56Sopenharmony_ci 6217db96d56Sopenharmony_ci def _dims_getter(self): 6227db96d56Sopenharmony_ci """Deprecated. Use elts instead.""" 6237db96d56Sopenharmony_ci return self.elts 6247db96d56Sopenharmony_ci 6257db96d56Sopenharmony_ci def _dims_setter(self, value): 6267db96d56Sopenharmony_ci self.elts = value 6277db96d56Sopenharmony_ci 6287db96d56Sopenharmony_ci Tuple.dims = property(_dims_getter, _dims_setter) 6297db96d56Sopenharmony_ci 6307db96d56Sopenharmony_ciclass Suite(mod): 6317db96d56Sopenharmony_ci """Deprecated AST node class. Unused in Python 3.""" 6327db96d56Sopenharmony_ci 6337db96d56Sopenharmony_ciclass AugLoad(expr_context): 6347db96d56Sopenharmony_ci """Deprecated AST node class. Unused in Python 3.""" 6357db96d56Sopenharmony_ci 6367db96d56Sopenharmony_ciclass AugStore(expr_context): 6377db96d56Sopenharmony_ci """Deprecated AST node class. Unused in Python 3.""" 6387db96d56Sopenharmony_ci 6397db96d56Sopenharmony_ciclass Param(expr_context): 6407db96d56Sopenharmony_ci """Deprecated AST node class. Unused in Python 3.""" 6417db96d56Sopenharmony_ci 6427db96d56Sopenharmony_ci 6437db96d56Sopenharmony_ci# Large float and imaginary literals get turned into infinities in the AST. 6447db96d56Sopenharmony_ci# We unparse those infinities to INFSTR. 6457db96d56Sopenharmony_ci_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1) 6467db96d56Sopenharmony_ci 6477db96d56Sopenharmony_ci@_simple_enum(IntEnum) 6487db96d56Sopenharmony_ciclass _Precedence: 6497db96d56Sopenharmony_ci """Precedence table that originated from python grammar.""" 6507db96d56Sopenharmony_ci 6517db96d56Sopenharmony_ci NAMED_EXPR = auto() # <target> := <expr1> 6527db96d56Sopenharmony_ci TUPLE = auto() # <expr1>, <expr2> 6537db96d56Sopenharmony_ci YIELD = auto() # 'yield', 'yield from' 6547db96d56Sopenharmony_ci TEST = auto() # 'if'-'else', 'lambda' 6557db96d56Sopenharmony_ci OR = auto() # 'or' 6567db96d56Sopenharmony_ci AND = auto() # 'and' 6577db96d56Sopenharmony_ci NOT = auto() # 'not' 6587db96d56Sopenharmony_ci CMP = auto() # '<', '>', '==', '>=', '<=', '!=', 6597db96d56Sopenharmony_ci # 'in', 'not in', 'is', 'is not' 6607db96d56Sopenharmony_ci EXPR = auto() 6617db96d56Sopenharmony_ci BOR = EXPR # '|' 6627db96d56Sopenharmony_ci BXOR = auto() # '^' 6637db96d56Sopenharmony_ci BAND = auto() # '&' 6647db96d56Sopenharmony_ci SHIFT = auto() # '<<', '>>' 6657db96d56Sopenharmony_ci ARITH = auto() # '+', '-' 6667db96d56Sopenharmony_ci TERM = auto() # '*', '@', '/', '%', '//' 6677db96d56Sopenharmony_ci FACTOR = auto() # unary '+', '-', '~' 6687db96d56Sopenharmony_ci POWER = auto() # '**' 6697db96d56Sopenharmony_ci AWAIT = auto() # 'await' 6707db96d56Sopenharmony_ci ATOM = auto() 6717db96d56Sopenharmony_ci 6727db96d56Sopenharmony_ci def next(self): 6737db96d56Sopenharmony_ci try: 6747db96d56Sopenharmony_ci return self.__class__(self + 1) 6757db96d56Sopenharmony_ci except ValueError: 6767db96d56Sopenharmony_ci return self 6777db96d56Sopenharmony_ci 6787db96d56Sopenharmony_ci 6797db96d56Sopenharmony_ci_SINGLE_QUOTES = ("'", '"') 6807db96d56Sopenharmony_ci_MULTI_QUOTES = ('"""', "'''") 6817db96d56Sopenharmony_ci_ALL_QUOTES = (*_SINGLE_QUOTES, *_MULTI_QUOTES) 6827db96d56Sopenharmony_ci 6837db96d56Sopenharmony_ciclass _Unparser(NodeVisitor): 6847db96d56Sopenharmony_ci """Methods in this class recursively traverse an AST and 6857db96d56Sopenharmony_ci output source code for the abstract syntax; original formatting 6867db96d56Sopenharmony_ci is disregarded.""" 6877db96d56Sopenharmony_ci 6887db96d56Sopenharmony_ci def __init__(self, *, _avoid_backslashes=False): 6897db96d56Sopenharmony_ci self._source = [] 6907db96d56Sopenharmony_ci self._precedences = {} 6917db96d56Sopenharmony_ci self._type_ignores = {} 6927db96d56Sopenharmony_ci self._indent = 0 6937db96d56Sopenharmony_ci self._avoid_backslashes = _avoid_backslashes 6947db96d56Sopenharmony_ci self._in_try_star = False 6957db96d56Sopenharmony_ci 6967db96d56Sopenharmony_ci def interleave(self, inter, f, seq): 6977db96d56Sopenharmony_ci """Call f on each item in seq, calling inter() in between.""" 6987db96d56Sopenharmony_ci seq = iter(seq) 6997db96d56Sopenharmony_ci try: 7007db96d56Sopenharmony_ci f(next(seq)) 7017db96d56Sopenharmony_ci except StopIteration: 7027db96d56Sopenharmony_ci pass 7037db96d56Sopenharmony_ci else: 7047db96d56Sopenharmony_ci for x in seq: 7057db96d56Sopenharmony_ci inter() 7067db96d56Sopenharmony_ci f(x) 7077db96d56Sopenharmony_ci 7087db96d56Sopenharmony_ci def items_view(self, traverser, items): 7097db96d56Sopenharmony_ci """Traverse and separate the given *items* with a comma and append it to 7107db96d56Sopenharmony_ci the buffer. If *items* is a single item sequence, a trailing comma 7117db96d56Sopenharmony_ci will be added.""" 7127db96d56Sopenharmony_ci if len(items) == 1: 7137db96d56Sopenharmony_ci traverser(items[0]) 7147db96d56Sopenharmony_ci self.write(",") 7157db96d56Sopenharmony_ci else: 7167db96d56Sopenharmony_ci self.interleave(lambda: self.write(", "), traverser, items) 7177db96d56Sopenharmony_ci 7187db96d56Sopenharmony_ci def maybe_newline(self): 7197db96d56Sopenharmony_ci """Adds a newline if it isn't the start of generated source""" 7207db96d56Sopenharmony_ci if self._source: 7217db96d56Sopenharmony_ci self.write("\n") 7227db96d56Sopenharmony_ci 7237db96d56Sopenharmony_ci def fill(self, text=""): 7247db96d56Sopenharmony_ci """Indent a piece of text and append it, according to the current 7257db96d56Sopenharmony_ci indentation level""" 7267db96d56Sopenharmony_ci self.maybe_newline() 7277db96d56Sopenharmony_ci self.write(" " * self._indent + text) 7287db96d56Sopenharmony_ci 7297db96d56Sopenharmony_ci def write(self, *text): 7307db96d56Sopenharmony_ci """Add new source parts""" 7317db96d56Sopenharmony_ci self._source.extend(text) 7327db96d56Sopenharmony_ci 7337db96d56Sopenharmony_ci @contextmanager 7347db96d56Sopenharmony_ci def buffered(self, buffer = None): 7357db96d56Sopenharmony_ci if buffer is None: 7367db96d56Sopenharmony_ci buffer = [] 7377db96d56Sopenharmony_ci 7387db96d56Sopenharmony_ci original_source = self._source 7397db96d56Sopenharmony_ci self._source = buffer 7407db96d56Sopenharmony_ci yield buffer 7417db96d56Sopenharmony_ci self._source = original_source 7427db96d56Sopenharmony_ci 7437db96d56Sopenharmony_ci @contextmanager 7447db96d56Sopenharmony_ci def block(self, *, extra = None): 7457db96d56Sopenharmony_ci """A context manager for preparing the source for blocks. It adds 7467db96d56Sopenharmony_ci the character':', increases the indentation on enter and decreases 7477db96d56Sopenharmony_ci the indentation on exit. If *extra* is given, it will be directly 7487db96d56Sopenharmony_ci appended after the colon character. 7497db96d56Sopenharmony_ci """ 7507db96d56Sopenharmony_ci self.write(":") 7517db96d56Sopenharmony_ci if extra: 7527db96d56Sopenharmony_ci self.write(extra) 7537db96d56Sopenharmony_ci self._indent += 1 7547db96d56Sopenharmony_ci yield 7557db96d56Sopenharmony_ci self._indent -= 1 7567db96d56Sopenharmony_ci 7577db96d56Sopenharmony_ci @contextmanager 7587db96d56Sopenharmony_ci def delimit(self, start, end): 7597db96d56Sopenharmony_ci """A context manager for preparing the source for expressions. It adds 7607db96d56Sopenharmony_ci *start* to the buffer and enters, after exit it adds *end*.""" 7617db96d56Sopenharmony_ci 7627db96d56Sopenharmony_ci self.write(start) 7637db96d56Sopenharmony_ci yield 7647db96d56Sopenharmony_ci self.write(end) 7657db96d56Sopenharmony_ci 7667db96d56Sopenharmony_ci def delimit_if(self, start, end, condition): 7677db96d56Sopenharmony_ci if condition: 7687db96d56Sopenharmony_ci return self.delimit(start, end) 7697db96d56Sopenharmony_ci else: 7707db96d56Sopenharmony_ci return nullcontext() 7717db96d56Sopenharmony_ci 7727db96d56Sopenharmony_ci def require_parens(self, precedence, node): 7737db96d56Sopenharmony_ci """Shortcut to adding precedence related parens""" 7747db96d56Sopenharmony_ci return self.delimit_if("(", ")", self.get_precedence(node) > precedence) 7757db96d56Sopenharmony_ci 7767db96d56Sopenharmony_ci def get_precedence(self, node): 7777db96d56Sopenharmony_ci return self._precedences.get(node, _Precedence.TEST) 7787db96d56Sopenharmony_ci 7797db96d56Sopenharmony_ci def set_precedence(self, precedence, *nodes): 7807db96d56Sopenharmony_ci for node in nodes: 7817db96d56Sopenharmony_ci self._precedences[node] = precedence 7827db96d56Sopenharmony_ci 7837db96d56Sopenharmony_ci def get_raw_docstring(self, node): 7847db96d56Sopenharmony_ci """If a docstring node is found in the body of the *node* parameter, 7857db96d56Sopenharmony_ci return that docstring node, None otherwise. 7867db96d56Sopenharmony_ci 7877db96d56Sopenharmony_ci Logic mirrored from ``_PyAST_GetDocString``.""" 7887db96d56Sopenharmony_ci if not isinstance( 7897db96d56Sopenharmony_ci node, (AsyncFunctionDef, FunctionDef, ClassDef, Module) 7907db96d56Sopenharmony_ci ) or len(node.body) < 1: 7917db96d56Sopenharmony_ci return None 7927db96d56Sopenharmony_ci node = node.body[0] 7937db96d56Sopenharmony_ci if not isinstance(node, Expr): 7947db96d56Sopenharmony_ci return None 7957db96d56Sopenharmony_ci node = node.value 7967db96d56Sopenharmony_ci if isinstance(node, Constant) and isinstance(node.value, str): 7977db96d56Sopenharmony_ci return node 7987db96d56Sopenharmony_ci 7997db96d56Sopenharmony_ci def get_type_comment(self, node): 8007db96d56Sopenharmony_ci comment = self._type_ignores.get(node.lineno) or node.type_comment 8017db96d56Sopenharmony_ci if comment is not None: 8027db96d56Sopenharmony_ci return f" # type: {comment}" 8037db96d56Sopenharmony_ci 8047db96d56Sopenharmony_ci def traverse(self, node): 8057db96d56Sopenharmony_ci if isinstance(node, list): 8067db96d56Sopenharmony_ci for item in node: 8077db96d56Sopenharmony_ci self.traverse(item) 8087db96d56Sopenharmony_ci else: 8097db96d56Sopenharmony_ci super().visit(node) 8107db96d56Sopenharmony_ci 8117db96d56Sopenharmony_ci # Note: as visit() resets the output text, do NOT rely on 8127db96d56Sopenharmony_ci # NodeVisitor.generic_visit to handle any nodes (as it calls back in to 8137db96d56Sopenharmony_ci # the subclass visit() method, which resets self._source to an empty list) 8147db96d56Sopenharmony_ci def visit(self, node): 8157db96d56Sopenharmony_ci """Outputs a source code string that, if converted back to an ast 8167db96d56Sopenharmony_ci (using ast.parse) will generate an AST equivalent to *node*""" 8177db96d56Sopenharmony_ci self._source = [] 8187db96d56Sopenharmony_ci self.traverse(node) 8197db96d56Sopenharmony_ci return "".join(self._source) 8207db96d56Sopenharmony_ci 8217db96d56Sopenharmony_ci def _write_docstring_and_traverse_body(self, node): 8227db96d56Sopenharmony_ci if (docstring := self.get_raw_docstring(node)): 8237db96d56Sopenharmony_ci self._write_docstring(docstring) 8247db96d56Sopenharmony_ci self.traverse(node.body[1:]) 8257db96d56Sopenharmony_ci else: 8267db96d56Sopenharmony_ci self.traverse(node.body) 8277db96d56Sopenharmony_ci 8287db96d56Sopenharmony_ci def visit_Module(self, node): 8297db96d56Sopenharmony_ci self._type_ignores = { 8307db96d56Sopenharmony_ci ignore.lineno: f"ignore{ignore.tag}" 8317db96d56Sopenharmony_ci for ignore in node.type_ignores 8327db96d56Sopenharmony_ci } 8337db96d56Sopenharmony_ci self._write_docstring_and_traverse_body(node) 8347db96d56Sopenharmony_ci self._type_ignores.clear() 8357db96d56Sopenharmony_ci 8367db96d56Sopenharmony_ci def visit_FunctionType(self, node): 8377db96d56Sopenharmony_ci with self.delimit("(", ")"): 8387db96d56Sopenharmony_ci self.interleave( 8397db96d56Sopenharmony_ci lambda: self.write(", "), self.traverse, node.argtypes 8407db96d56Sopenharmony_ci ) 8417db96d56Sopenharmony_ci 8427db96d56Sopenharmony_ci self.write(" -> ") 8437db96d56Sopenharmony_ci self.traverse(node.returns) 8447db96d56Sopenharmony_ci 8457db96d56Sopenharmony_ci def visit_Expr(self, node): 8467db96d56Sopenharmony_ci self.fill() 8477db96d56Sopenharmony_ci self.set_precedence(_Precedence.YIELD, node.value) 8487db96d56Sopenharmony_ci self.traverse(node.value) 8497db96d56Sopenharmony_ci 8507db96d56Sopenharmony_ci def visit_NamedExpr(self, node): 8517db96d56Sopenharmony_ci with self.require_parens(_Precedence.NAMED_EXPR, node): 8527db96d56Sopenharmony_ci self.set_precedence(_Precedence.ATOM, node.target, node.value) 8537db96d56Sopenharmony_ci self.traverse(node.target) 8547db96d56Sopenharmony_ci self.write(" := ") 8557db96d56Sopenharmony_ci self.traverse(node.value) 8567db96d56Sopenharmony_ci 8577db96d56Sopenharmony_ci def visit_Import(self, node): 8587db96d56Sopenharmony_ci self.fill("import ") 8597db96d56Sopenharmony_ci self.interleave(lambda: self.write(", "), self.traverse, node.names) 8607db96d56Sopenharmony_ci 8617db96d56Sopenharmony_ci def visit_ImportFrom(self, node): 8627db96d56Sopenharmony_ci self.fill("from ") 8637db96d56Sopenharmony_ci self.write("." * (node.level or 0)) 8647db96d56Sopenharmony_ci if node.module: 8657db96d56Sopenharmony_ci self.write(node.module) 8667db96d56Sopenharmony_ci self.write(" import ") 8677db96d56Sopenharmony_ci self.interleave(lambda: self.write(", "), self.traverse, node.names) 8687db96d56Sopenharmony_ci 8697db96d56Sopenharmony_ci def visit_Assign(self, node): 8707db96d56Sopenharmony_ci self.fill() 8717db96d56Sopenharmony_ci for target in node.targets: 8727db96d56Sopenharmony_ci self.set_precedence(_Precedence.TUPLE, target) 8737db96d56Sopenharmony_ci self.traverse(target) 8747db96d56Sopenharmony_ci self.write(" = ") 8757db96d56Sopenharmony_ci self.traverse(node.value) 8767db96d56Sopenharmony_ci if type_comment := self.get_type_comment(node): 8777db96d56Sopenharmony_ci self.write(type_comment) 8787db96d56Sopenharmony_ci 8797db96d56Sopenharmony_ci def visit_AugAssign(self, node): 8807db96d56Sopenharmony_ci self.fill() 8817db96d56Sopenharmony_ci self.traverse(node.target) 8827db96d56Sopenharmony_ci self.write(" " + self.binop[node.op.__class__.__name__] + "= ") 8837db96d56Sopenharmony_ci self.traverse(node.value) 8847db96d56Sopenharmony_ci 8857db96d56Sopenharmony_ci def visit_AnnAssign(self, node): 8867db96d56Sopenharmony_ci self.fill() 8877db96d56Sopenharmony_ci with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)): 8887db96d56Sopenharmony_ci self.traverse(node.target) 8897db96d56Sopenharmony_ci self.write(": ") 8907db96d56Sopenharmony_ci self.traverse(node.annotation) 8917db96d56Sopenharmony_ci if node.value: 8927db96d56Sopenharmony_ci self.write(" = ") 8937db96d56Sopenharmony_ci self.traverse(node.value) 8947db96d56Sopenharmony_ci 8957db96d56Sopenharmony_ci def visit_Return(self, node): 8967db96d56Sopenharmony_ci self.fill("return") 8977db96d56Sopenharmony_ci if node.value: 8987db96d56Sopenharmony_ci self.write(" ") 8997db96d56Sopenharmony_ci self.traverse(node.value) 9007db96d56Sopenharmony_ci 9017db96d56Sopenharmony_ci def visit_Pass(self, node): 9027db96d56Sopenharmony_ci self.fill("pass") 9037db96d56Sopenharmony_ci 9047db96d56Sopenharmony_ci def visit_Break(self, node): 9057db96d56Sopenharmony_ci self.fill("break") 9067db96d56Sopenharmony_ci 9077db96d56Sopenharmony_ci def visit_Continue(self, node): 9087db96d56Sopenharmony_ci self.fill("continue") 9097db96d56Sopenharmony_ci 9107db96d56Sopenharmony_ci def visit_Delete(self, node): 9117db96d56Sopenharmony_ci self.fill("del ") 9127db96d56Sopenharmony_ci self.interleave(lambda: self.write(", "), self.traverse, node.targets) 9137db96d56Sopenharmony_ci 9147db96d56Sopenharmony_ci def visit_Assert(self, node): 9157db96d56Sopenharmony_ci self.fill("assert ") 9167db96d56Sopenharmony_ci self.traverse(node.test) 9177db96d56Sopenharmony_ci if node.msg: 9187db96d56Sopenharmony_ci self.write(", ") 9197db96d56Sopenharmony_ci self.traverse(node.msg) 9207db96d56Sopenharmony_ci 9217db96d56Sopenharmony_ci def visit_Global(self, node): 9227db96d56Sopenharmony_ci self.fill("global ") 9237db96d56Sopenharmony_ci self.interleave(lambda: self.write(", "), self.write, node.names) 9247db96d56Sopenharmony_ci 9257db96d56Sopenharmony_ci def visit_Nonlocal(self, node): 9267db96d56Sopenharmony_ci self.fill("nonlocal ") 9277db96d56Sopenharmony_ci self.interleave(lambda: self.write(", "), self.write, node.names) 9287db96d56Sopenharmony_ci 9297db96d56Sopenharmony_ci def visit_Await(self, node): 9307db96d56Sopenharmony_ci with self.require_parens(_Precedence.AWAIT, node): 9317db96d56Sopenharmony_ci self.write("await") 9327db96d56Sopenharmony_ci if node.value: 9337db96d56Sopenharmony_ci self.write(" ") 9347db96d56Sopenharmony_ci self.set_precedence(_Precedence.ATOM, node.value) 9357db96d56Sopenharmony_ci self.traverse(node.value) 9367db96d56Sopenharmony_ci 9377db96d56Sopenharmony_ci def visit_Yield(self, node): 9387db96d56Sopenharmony_ci with self.require_parens(_Precedence.YIELD, node): 9397db96d56Sopenharmony_ci self.write("yield") 9407db96d56Sopenharmony_ci if node.value: 9417db96d56Sopenharmony_ci self.write(" ") 9427db96d56Sopenharmony_ci self.set_precedence(_Precedence.ATOM, node.value) 9437db96d56Sopenharmony_ci self.traverse(node.value) 9447db96d56Sopenharmony_ci 9457db96d56Sopenharmony_ci def visit_YieldFrom(self, node): 9467db96d56Sopenharmony_ci with self.require_parens(_Precedence.YIELD, node): 9477db96d56Sopenharmony_ci self.write("yield from ") 9487db96d56Sopenharmony_ci if not node.value: 9497db96d56Sopenharmony_ci raise ValueError("Node can't be used without a value attribute.") 9507db96d56Sopenharmony_ci self.set_precedence(_Precedence.ATOM, node.value) 9517db96d56Sopenharmony_ci self.traverse(node.value) 9527db96d56Sopenharmony_ci 9537db96d56Sopenharmony_ci def visit_Raise(self, node): 9547db96d56Sopenharmony_ci self.fill("raise") 9557db96d56Sopenharmony_ci if not node.exc: 9567db96d56Sopenharmony_ci if node.cause: 9577db96d56Sopenharmony_ci raise ValueError(f"Node can't use cause without an exception.") 9587db96d56Sopenharmony_ci return 9597db96d56Sopenharmony_ci self.write(" ") 9607db96d56Sopenharmony_ci self.traverse(node.exc) 9617db96d56Sopenharmony_ci if node.cause: 9627db96d56Sopenharmony_ci self.write(" from ") 9637db96d56Sopenharmony_ci self.traverse(node.cause) 9647db96d56Sopenharmony_ci 9657db96d56Sopenharmony_ci def do_visit_try(self, node): 9667db96d56Sopenharmony_ci self.fill("try") 9677db96d56Sopenharmony_ci with self.block(): 9687db96d56Sopenharmony_ci self.traverse(node.body) 9697db96d56Sopenharmony_ci for ex in node.handlers: 9707db96d56Sopenharmony_ci self.traverse(ex) 9717db96d56Sopenharmony_ci if node.orelse: 9727db96d56Sopenharmony_ci self.fill("else") 9737db96d56Sopenharmony_ci with self.block(): 9747db96d56Sopenharmony_ci self.traverse(node.orelse) 9757db96d56Sopenharmony_ci if node.finalbody: 9767db96d56Sopenharmony_ci self.fill("finally") 9777db96d56Sopenharmony_ci with self.block(): 9787db96d56Sopenharmony_ci self.traverse(node.finalbody) 9797db96d56Sopenharmony_ci 9807db96d56Sopenharmony_ci def visit_Try(self, node): 9817db96d56Sopenharmony_ci prev_in_try_star = self._in_try_star 9827db96d56Sopenharmony_ci try: 9837db96d56Sopenharmony_ci self._in_try_star = False 9847db96d56Sopenharmony_ci self.do_visit_try(node) 9857db96d56Sopenharmony_ci finally: 9867db96d56Sopenharmony_ci self._in_try_star = prev_in_try_star 9877db96d56Sopenharmony_ci 9887db96d56Sopenharmony_ci def visit_TryStar(self, node): 9897db96d56Sopenharmony_ci prev_in_try_star = self._in_try_star 9907db96d56Sopenharmony_ci try: 9917db96d56Sopenharmony_ci self._in_try_star = True 9927db96d56Sopenharmony_ci self.do_visit_try(node) 9937db96d56Sopenharmony_ci finally: 9947db96d56Sopenharmony_ci self._in_try_star = prev_in_try_star 9957db96d56Sopenharmony_ci 9967db96d56Sopenharmony_ci def visit_ExceptHandler(self, node): 9977db96d56Sopenharmony_ci self.fill("except*" if self._in_try_star else "except") 9987db96d56Sopenharmony_ci if node.type: 9997db96d56Sopenharmony_ci self.write(" ") 10007db96d56Sopenharmony_ci self.traverse(node.type) 10017db96d56Sopenharmony_ci if node.name: 10027db96d56Sopenharmony_ci self.write(" as ") 10037db96d56Sopenharmony_ci self.write(node.name) 10047db96d56Sopenharmony_ci with self.block(): 10057db96d56Sopenharmony_ci self.traverse(node.body) 10067db96d56Sopenharmony_ci 10077db96d56Sopenharmony_ci def visit_ClassDef(self, node): 10087db96d56Sopenharmony_ci self.maybe_newline() 10097db96d56Sopenharmony_ci for deco in node.decorator_list: 10107db96d56Sopenharmony_ci self.fill("@") 10117db96d56Sopenharmony_ci self.traverse(deco) 10127db96d56Sopenharmony_ci self.fill("class " + node.name) 10137db96d56Sopenharmony_ci with self.delimit_if("(", ")", condition = node.bases or node.keywords): 10147db96d56Sopenharmony_ci comma = False 10157db96d56Sopenharmony_ci for e in node.bases: 10167db96d56Sopenharmony_ci if comma: 10177db96d56Sopenharmony_ci self.write(", ") 10187db96d56Sopenharmony_ci else: 10197db96d56Sopenharmony_ci comma = True 10207db96d56Sopenharmony_ci self.traverse(e) 10217db96d56Sopenharmony_ci for e in node.keywords: 10227db96d56Sopenharmony_ci if comma: 10237db96d56Sopenharmony_ci self.write(", ") 10247db96d56Sopenharmony_ci else: 10257db96d56Sopenharmony_ci comma = True 10267db96d56Sopenharmony_ci self.traverse(e) 10277db96d56Sopenharmony_ci 10287db96d56Sopenharmony_ci with self.block(): 10297db96d56Sopenharmony_ci self._write_docstring_and_traverse_body(node) 10307db96d56Sopenharmony_ci 10317db96d56Sopenharmony_ci def visit_FunctionDef(self, node): 10327db96d56Sopenharmony_ci self._function_helper(node, "def") 10337db96d56Sopenharmony_ci 10347db96d56Sopenharmony_ci def visit_AsyncFunctionDef(self, node): 10357db96d56Sopenharmony_ci self._function_helper(node, "async def") 10367db96d56Sopenharmony_ci 10377db96d56Sopenharmony_ci def _function_helper(self, node, fill_suffix): 10387db96d56Sopenharmony_ci self.maybe_newline() 10397db96d56Sopenharmony_ci for deco in node.decorator_list: 10407db96d56Sopenharmony_ci self.fill("@") 10417db96d56Sopenharmony_ci self.traverse(deco) 10427db96d56Sopenharmony_ci def_str = fill_suffix + " " + node.name 10437db96d56Sopenharmony_ci self.fill(def_str) 10447db96d56Sopenharmony_ci with self.delimit("(", ")"): 10457db96d56Sopenharmony_ci self.traverse(node.args) 10467db96d56Sopenharmony_ci if node.returns: 10477db96d56Sopenharmony_ci self.write(" -> ") 10487db96d56Sopenharmony_ci self.traverse(node.returns) 10497db96d56Sopenharmony_ci with self.block(extra=self.get_type_comment(node)): 10507db96d56Sopenharmony_ci self._write_docstring_and_traverse_body(node) 10517db96d56Sopenharmony_ci 10527db96d56Sopenharmony_ci def visit_For(self, node): 10537db96d56Sopenharmony_ci self._for_helper("for ", node) 10547db96d56Sopenharmony_ci 10557db96d56Sopenharmony_ci def visit_AsyncFor(self, node): 10567db96d56Sopenharmony_ci self._for_helper("async for ", node) 10577db96d56Sopenharmony_ci 10587db96d56Sopenharmony_ci def _for_helper(self, fill, node): 10597db96d56Sopenharmony_ci self.fill(fill) 10607db96d56Sopenharmony_ci self.set_precedence(_Precedence.TUPLE, node.target) 10617db96d56Sopenharmony_ci self.traverse(node.target) 10627db96d56Sopenharmony_ci self.write(" in ") 10637db96d56Sopenharmony_ci self.traverse(node.iter) 10647db96d56Sopenharmony_ci with self.block(extra=self.get_type_comment(node)): 10657db96d56Sopenharmony_ci self.traverse(node.body) 10667db96d56Sopenharmony_ci if node.orelse: 10677db96d56Sopenharmony_ci self.fill("else") 10687db96d56Sopenharmony_ci with self.block(): 10697db96d56Sopenharmony_ci self.traverse(node.orelse) 10707db96d56Sopenharmony_ci 10717db96d56Sopenharmony_ci def visit_If(self, node): 10727db96d56Sopenharmony_ci self.fill("if ") 10737db96d56Sopenharmony_ci self.traverse(node.test) 10747db96d56Sopenharmony_ci with self.block(): 10757db96d56Sopenharmony_ci self.traverse(node.body) 10767db96d56Sopenharmony_ci # collapse nested ifs into equivalent elifs. 10777db96d56Sopenharmony_ci while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If): 10787db96d56Sopenharmony_ci node = node.orelse[0] 10797db96d56Sopenharmony_ci self.fill("elif ") 10807db96d56Sopenharmony_ci self.traverse(node.test) 10817db96d56Sopenharmony_ci with self.block(): 10827db96d56Sopenharmony_ci self.traverse(node.body) 10837db96d56Sopenharmony_ci # final else 10847db96d56Sopenharmony_ci if node.orelse: 10857db96d56Sopenharmony_ci self.fill("else") 10867db96d56Sopenharmony_ci with self.block(): 10877db96d56Sopenharmony_ci self.traverse(node.orelse) 10887db96d56Sopenharmony_ci 10897db96d56Sopenharmony_ci def visit_While(self, node): 10907db96d56Sopenharmony_ci self.fill("while ") 10917db96d56Sopenharmony_ci self.traverse(node.test) 10927db96d56Sopenharmony_ci with self.block(): 10937db96d56Sopenharmony_ci self.traverse(node.body) 10947db96d56Sopenharmony_ci if node.orelse: 10957db96d56Sopenharmony_ci self.fill("else") 10967db96d56Sopenharmony_ci with self.block(): 10977db96d56Sopenharmony_ci self.traverse(node.orelse) 10987db96d56Sopenharmony_ci 10997db96d56Sopenharmony_ci def visit_With(self, node): 11007db96d56Sopenharmony_ci self.fill("with ") 11017db96d56Sopenharmony_ci self.interleave(lambda: self.write(", "), self.traverse, node.items) 11027db96d56Sopenharmony_ci with self.block(extra=self.get_type_comment(node)): 11037db96d56Sopenharmony_ci self.traverse(node.body) 11047db96d56Sopenharmony_ci 11057db96d56Sopenharmony_ci def visit_AsyncWith(self, node): 11067db96d56Sopenharmony_ci self.fill("async with ") 11077db96d56Sopenharmony_ci self.interleave(lambda: self.write(", "), self.traverse, node.items) 11087db96d56Sopenharmony_ci with self.block(extra=self.get_type_comment(node)): 11097db96d56Sopenharmony_ci self.traverse(node.body) 11107db96d56Sopenharmony_ci 11117db96d56Sopenharmony_ci def _str_literal_helper( 11127db96d56Sopenharmony_ci self, string, *, quote_types=_ALL_QUOTES, escape_special_whitespace=False 11137db96d56Sopenharmony_ci ): 11147db96d56Sopenharmony_ci """Helper for writing string literals, minimizing escapes. 11157db96d56Sopenharmony_ci Returns the tuple (string literal to write, possible quote types). 11167db96d56Sopenharmony_ci """ 11177db96d56Sopenharmony_ci def escape_char(c): 11187db96d56Sopenharmony_ci # \n and \t are non-printable, but we only escape them if 11197db96d56Sopenharmony_ci # escape_special_whitespace is True 11207db96d56Sopenharmony_ci if not escape_special_whitespace and c in "\n\t": 11217db96d56Sopenharmony_ci return c 11227db96d56Sopenharmony_ci # Always escape backslashes and other non-printable characters 11237db96d56Sopenharmony_ci if c == "\\" or not c.isprintable(): 11247db96d56Sopenharmony_ci return c.encode("unicode_escape").decode("ascii") 11257db96d56Sopenharmony_ci return c 11267db96d56Sopenharmony_ci 11277db96d56Sopenharmony_ci escaped_string = "".join(map(escape_char, string)) 11287db96d56Sopenharmony_ci possible_quotes = quote_types 11297db96d56Sopenharmony_ci if "\n" in escaped_string: 11307db96d56Sopenharmony_ci possible_quotes = [q for q in possible_quotes if q in _MULTI_QUOTES] 11317db96d56Sopenharmony_ci possible_quotes = [q for q in possible_quotes if q not in escaped_string] 11327db96d56Sopenharmony_ci if not possible_quotes: 11337db96d56Sopenharmony_ci # If there aren't any possible_quotes, fallback to using repr 11347db96d56Sopenharmony_ci # on the original string. Try to use a quote from quote_types, 11357db96d56Sopenharmony_ci # e.g., so that we use triple quotes for docstrings. 11367db96d56Sopenharmony_ci string = repr(string) 11377db96d56Sopenharmony_ci quote = next((q for q in quote_types if string[0] in q), string[0]) 11387db96d56Sopenharmony_ci return string[1:-1], [quote] 11397db96d56Sopenharmony_ci if escaped_string: 11407db96d56Sopenharmony_ci # Sort so that we prefer '''"''' over """\"""" 11417db96d56Sopenharmony_ci possible_quotes.sort(key=lambda q: q[0] == escaped_string[-1]) 11427db96d56Sopenharmony_ci # If we're using triple quotes and we'd need to escape a final 11437db96d56Sopenharmony_ci # quote, escape it 11447db96d56Sopenharmony_ci if possible_quotes[0][0] == escaped_string[-1]: 11457db96d56Sopenharmony_ci assert len(possible_quotes[0]) == 3 11467db96d56Sopenharmony_ci escaped_string = escaped_string[:-1] + "\\" + escaped_string[-1] 11477db96d56Sopenharmony_ci return escaped_string, possible_quotes 11487db96d56Sopenharmony_ci 11497db96d56Sopenharmony_ci def _write_str_avoiding_backslashes(self, string, *, quote_types=_ALL_QUOTES): 11507db96d56Sopenharmony_ci """Write string literal value with a best effort attempt to avoid backslashes.""" 11517db96d56Sopenharmony_ci string, quote_types = self._str_literal_helper(string, quote_types=quote_types) 11527db96d56Sopenharmony_ci quote_type = quote_types[0] 11537db96d56Sopenharmony_ci self.write(f"{quote_type}{string}{quote_type}") 11547db96d56Sopenharmony_ci 11557db96d56Sopenharmony_ci def visit_JoinedStr(self, node): 11567db96d56Sopenharmony_ci self.write("f") 11577db96d56Sopenharmony_ci if self._avoid_backslashes: 11587db96d56Sopenharmony_ci with self.buffered() as buffer: 11597db96d56Sopenharmony_ci self._write_fstring_inner(node) 11607db96d56Sopenharmony_ci return self._write_str_avoiding_backslashes("".join(buffer)) 11617db96d56Sopenharmony_ci 11627db96d56Sopenharmony_ci # If we don't need to avoid backslashes globally (i.e., we only need 11637db96d56Sopenharmony_ci # to avoid them inside FormattedValues), it's cosmetically preferred 11647db96d56Sopenharmony_ci # to use escaped whitespace. That is, it's preferred to use backslashes 11657db96d56Sopenharmony_ci # for cases like: f"{x}\n". To accomplish this, we keep track of what 11667db96d56Sopenharmony_ci # in our buffer corresponds to FormattedValues and what corresponds to 11677db96d56Sopenharmony_ci # Constant parts of the f-string, and allow escapes accordingly. 11687db96d56Sopenharmony_ci fstring_parts = [] 11697db96d56Sopenharmony_ci for value in node.values: 11707db96d56Sopenharmony_ci with self.buffered() as buffer: 11717db96d56Sopenharmony_ci self._write_fstring_inner(value) 11727db96d56Sopenharmony_ci fstring_parts.append( 11737db96d56Sopenharmony_ci ("".join(buffer), isinstance(value, Constant)) 11747db96d56Sopenharmony_ci ) 11757db96d56Sopenharmony_ci 11767db96d56Sopenharmony_ci new_fstring_parts = [] 11777db96d56Sopenharmony_ci quote_types = list(_ALL_QUOTES) 11787db96d56Sopenharmony_ci for value, is_constant in fstring_parts: 11797db96d56Sopenharmony_ci value, quote_types = self._str_literal_helper( 11807db96d56Sopenharmony_ci value, 11817db96d56Sopenharmony_ci quote_types=quote_types, 11827db96d56Sopenharmony_ci escape_special_whitespace=is_constant, 11837db96d56Sopenharmony_ci ) 11847db96d56Sopenharmony_ci new_fstring_parts.append(value) 11857db96d56Sopenharmony_ci 11867db96d56Sopenharmony_ci value = "".join(new_fstring_parts) 11877db96d56Sopenharmony_ci quote_type = quote_types[0] 11887db96d56Sopenharmony_ci self.write(f"{quote_type}{value}{quote_type}") 11897db96d56Sopenharmony_ci 11907db96d56Sopenharmony_ci def _write_fstring_inner(self, node): 11917db96d56Sopenharmony_ci if isinstance(node, JoinedStr): 11927db96d56Sopenharmony_ci # for both the f-string itself, and format_spec 11937db96d56Sopenharmony_ci for value in node.values: 11947db96d56Sopenharmony_ci self._write_fstring_inner(value) 11957db96d56Sopenharmony_ci elif isinstance(node, Constant) and isinstance(node.value, str): 11967db96d56Sopenharmony_ci value = node.value.replace("{", "{{").replace("}", "}}") 11977db96d56Sopenharmony_ci self.write(value) 11987db96d56Sopenharmony_ci elif isinstance(node, FormattedValue): 11997db96d56Sopenharmony_ci self.visit_FormattedValue(node) 12007db96d56Sopenharmony_ci else: 12017db96d56Sopenharmony_ci raise ValueError(f"Unexpected node inside JoinedStr, {node!r}") 12027db96d56Sopenharmony_ci 12037db96d56Sopenharmony_ci def visit_FormattedValue(self, node): 12047db96d56Sopenharmony_ci def unparse_inner(inner): 12057db96d56Sopenharmony_ci unparser = type(self)(_avoid_backslashes=True) 12067db96d56Sopenharmony_ci unparser.set_precedence(_Precedence.TEST.next(), inner) 12077db96d56Sopenharmony_ci return unparser.visit(inner) 12087db96d56Sopenharmony_ci 12097db96d56Sopenharmony_ci with self.delimit("{", "}"): 12107db96d56Sopenharmony_ci expr = unparse_inner(node.value) 12117db96d56Sopenharmony_ci if "\\" in expr: 12127db96d56Sopenharmony_ci raise ValueError( 12137db96d56Sopenharmony_ci "Unable to avoid backslash in f-string expression part" 12147db96d56Sopenharmony_ci ) 12157db96d56Sopenharmony_ci if expr.startswith("{"): 12167db96d56Sopenharmony_ci # Separate pair of opening brackets as "{ {" 12177db96d56Sopenharmony_ci self.write(" ") 12187db96d56Sopenharmony_ci self.write(expr) 12197db96d56Sopenharmony_ci if node.conversion != -1: 12207db96d56Sopenharmony_ci self.write(f"!{chr(node.conversion)}") 12217db96d56Sopenharmony_ci if node.format_spec: 12227db96d56Sopenharmony_ci self.write(":") 12237db96d56Sopenharmony_ci self._write_fstring_inner(node.format_spec) 12247db96d56Sopenharmony_ci 12257db96d56Sopenharmony_ci def visit_Name(self, node): 12267db96d56Sopenharmony_ci self.write(node.id) 12277db96d56Sopenharmony_ci 12287db96d56Sopenharmony_ci def _write_docstring(self, node): 12297db96d56Sopenharmony_ci self.fill() 12307db96d56Sopenharmony_ci if node.kind == "u": 12317db96d56Sopenharmony_ci self.write("u") 12327db96d56Sopenharmony_ci self._write_str_avoiding_backslashes(node.value, quote_types=_MULTI_QUOTES) 12337db96d56Sopenharmony_ci 12347db96d56Sopenharmony_ci def _write_constant(self, value): 12357db96d56Sopenharmony_ci if isinstance(value, (float, complex)): 12367db96d56Sopenharmony_ci # Substitute overflowing decimal literal for AST infinities, 12377db96d56Sopenharmony_ci # and inf - inf for NaNs. 12387db96d56Sopenharmony_ci self.write( 12397db96d56Sopenharmony_ci repr(value) 12407db96d56Sopenharmony_ci .replace("inf", _INFSTR) 12417db96d56Sopenharmony_ci .replace("nan", f"({_INFSTR}-{_INFSTR})") 12427db96d56Sopenharmony_ci ) 12437db96d56Sopenharmony_ci elif self._avoid_backslashes and isinstance(value, str): 12447db96d56Sopenharmony_ci self._write_str_avoiding_backslashes(value) 12457db96d56Sopenharmony_ci else: 12467db96d56Sopenharmony_ci self.write(repr(value)) 12477db96d56Sopenharmony_ci 12487db96d56Sopenharmony_ci def visit_Constant(self, node): 12497db96d56Sopenharmony_ci value = node.value 12507db96d56Sopenharmony_ci if isinstance(value, tuple): 12517db96d56Sopenharmony_ci with self.delimit("(", ")"): 12527db96d56Sopenharmony_ci self.items_view(self._write_constant, value) 12537db96d56Sopenharmony_ci elif value is ...: 12547db96d56Sopenharmony_ci self.write("...") 12557db96d56Sopenharmony_ci else: 12567db96d56Sopenharmony_ci if node.kind == "u": 12577db96d56Sopenharmony_ci self.write("u") 12587db96d56Sopenharmony_ci self._write_constant(node.value) 12597db96d56Sopenharmony_ci 12607db96d56Sopenharmony_ci def visit_List(self, node): 12617db96d56Sopenharmony_ci with self.delimit("[", "]"): 12627db96d56Sopenharmony_ci self.interleave(lambda: self.write(", "), self.traverse, node.elts) 12637db96d56Sopenharmony_ci 12647db96d56Sopenharmony_ci def visit_ListComp(self, node): 12657db96d56Sopenharmony_ci with self.delimit("[", "]"): 12667db96d56Sopenharmony_ci self.traverse(node.elt) 12677db96d56Sopenharmony_ci for gen in node.generators: 12687db96d56Sopenharmony_ci self.traverse(gen) 12697db96d56Sopenharmony_ci 12707db96d56Sopenharmony_ci def visit_GeneratorExp(self, node): 12717db96d56Sopenharmony_ci with self.delimit("(", ")"): 12727db96d56Sopenharmony_ci self.traverse(node.elt) 12737db96d56Sopenharmony_ci for gen in node.generators: 12747db96d56Sopenharmony_ci self.traverse(gen) 12757db96d56Sopenharmony_ci 12767db96d56Sopenharmony_ci def visit_SetComp(self, node): 12777db96d56Sopenharmony_ci with self.delimit("{", "}"): 12787db96d56Sopenharmony_ci self.traverse(node.elt) 12797db96d56Sopenharmony_ci for gen in node.generators: 12807db96d56Sopenharmony_ci self.traverse(gen) 12817db96d56Sopenharmony_ci 12827db96d56Sopenharmony_ci def visit_DictComp(self, node): 12837db96d56Sopenharmony_ci with self.delimit("{", "}"): 12847db96d56Sopenharmony_ci self.traverse(node.key) 12857db96d56Sopenharmony_ci self.write(": ") 12867db96d56Sopenharmony_ci self.traverse(node.value) 12877db96d56Sopenharmony_ci for gen in node.generators: 12887db96d56Sopenharmony_ci self.traverse(gen) 12897db96d56Sopenharmony_ci 12907db96d56Sopenharmony_ci def visit_comprehension(self, node): 12917db96d56Sopenharmony_ci if node.is_async: 12927db96d56Sopenharmony_ci self.write(" async for ") 12937db96d56Sopenharmony_ci else: 12947db96d56Sopenharmony_ci self.write(" for ") 12957db96d56Sopenharmony_ci self.set_precedence(_Precedence.TUPLE, node.target) 12967db96d56Sopenharmony_ci self.traverse(node.target) 12977db96d56Sopenharmony_ci self.write(" in ") 12987db96d56Sopenharmony_ci self.set_precedence(_Precedence.TEST.next(), node.iter, *node.ifs) 12997db96d56Sopenharmony_ci self.traverse(node.iter) 13007db96d56Sopenharmony_ci for if_clause in node.ifs: 13017db96d56Sopenharmony_ci self.write(" if ") 13027db96d56Sopenharmony_ci self.traverse(if_clause) 13037db96d56Sopenharmony_ci 13047db96d56Sopenharmony_ci def visit_IfExp(self, node): 13057db96d56Sopenharmony_ci with self.require_parens(_Precedence.TEST, node): 13067db96d56Sopenharmony_ci self.set_precedence(_Precedence.TEST.next(), node.body, node.test) 13077db96d56Sopenharmony_ci self.traverse(node.body) 13087db96d56Sopenharmony_ci self.write(" if ") 13097db96d56Sopenharmony_ci self.traverse(node.test) 13107db96d56Sopenharmony_ci self.write(" else ") 13117db96d56Sopenharmony_ci self.set_precedence(_Precedence.TEST, node.orelse) 13127db96d56Sopenharmony_ci self.traverse(node.orelse) 13137db96d56Sopenharmony_ci 13147db96d56Sopenharmony_ci def visit_Set(self, node): 13157db96d56Sopenharmony_ci if node.elts: 13167db96d56Sopenharmony_ci with self.delimit("{", "}"): 13177db96d56Sopenharmony_ci self.interleave(lambda: self.write(", "), self.traverse, node.elts) 13187db96d56Sopenharmony_ci else: 13197db96d56Sopenharmony_ci # `{}` would be interpreted as a dictionary literal, and 13207db96d56Sopenharmony_ci # `set` might be shadowed. Thus: 13217db96d56Sopenharmony_ci self.write('{*()}') 13227db96d56Sopenharmony_ci 13237db96d56Sopenharmony_ci def visit_Dict(self, node): 13247db96d56Sopenharmony_ci def write_key_value_pair(k, v): 13257db96d56Sopenharmony_ci self.traverse(k) 13267db96d56Sopenharmony_ci self.write(": ") 13277db96d56Sopenharmony_ci self.traverse(v) 13287db96d56Sopenharmony_ci 13297db96d56Sopenharmony_ci def write_item(item): 13307db96d56Sopenharmony_ci k, v = item 13317db96d56Sopenharmony_ci if k is None: 13327db96d56Sopenharmony_ci # for dictionary unpacking operator in dicts {**{'y': 2}} 13337db96d56Sopenharmony_ci # see PEP 448 for details 13347db96d56Sopenharmony_ci self.write("**") 13357db96d56Sopenharmony_ci self.set_precedence(_Precedence.EXPR, v) 13367db96d56Sopenharmony_ci self.traverse(v) 13377db96d56Sopenharmony_ci else: 13387db96d56Sopenharmony_ci write_key_value_pair(k, v) 13397db96d56Sopenharmony_ci 13407db96d56Sopenharmony_ci with self.delimit("{", "}"): 13417db96d56Sopenharmony_ci self.interleave( 13427db96d56Sopenharmony_ci lambda: self.write(", "), write_item, zip(node.keys, node.values) 13437db96d56Sopenharmony_ci ) 13447db96d56Sopenharmony_ci 13457db96d56Sopenharmony_ci def visit_Tuple(self, node): 13467db96d56Sopenharmony_ci with self.delimit_if( 13477db96d56Sopenharmony_ci "(", 13487db96d56Sopenharmony_ci ")", 13497db96d56Sopenharmony_ci len(node.elts) == 0 or self.get_precedence(node) > _Precedence.TUPLE 13507db96d56Sopenharmony_ci ): 13517db96d56Sopenharmony_ci self.items_view(self.traverse, node.elts) 13527db96d56Sopenharmony_ci 13537db96d56Sopenharmony_ci unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"} 13547db96d56Sopenharmony_ci unop_precedence = { 13557db96d56Sopenharmony_ci "not": _Precedence.NOT, 13567db96d56Sopenharmony_ci "~": _Precedence.FACTOR, 13577db96d56Sopenharmony_ci "+": _Precedence.FACTOR, 13587db96d56Sopenharmony_ci "-": _Precedence.FACTOR, 13597db96d56Sopenharmony_ci } 13607db96d56Sopenharmony_ci 13617db96d56Sopenharmony_ci def visit_UnaryOp(self, node): 13627db96d56Sopenharmony_ci operator = self.unop[node.op.__class__.__name__] 13637db96d56Sopenharmony_ci operator_precedence = self.unop_precedence[operator] 13647db96d56Sopenharmony_ci with self.require_parens(operator_precedence, node): 13657db96d56Sopenharmony_ci self.write(operator) 13667db96d56Sopenharmony_ci # factor prefixes (+, -, ~) shouldn't be separated 13677db96d56Sopenharmony_ci # from the value they belong, (e.g: +1 instead of + 1) 13687db96d56Sopenharmony_ci if operator_precedence is not _Precedence.FACTOR: 13697db96d56Sopenharmony_ci self.write(" ") 13707db96d56Sopenharmony_ci self.set_precedence(operator_precedence, node.operand) 13717db96d56Sopenharmony_ci self.traverse(node.operand) 13727db96d56Sopenharmony_ci 13737db96d56Sopenharmony_ci binop = { 13747db96d56Sopenharmony_ci "Add": "+", 13757db96d56Sopenharmony_ci "Sub": "-", 13767db96d56Sopenharmony_ci "Mult": "*", 13777db96d56Sopenharmony_ci "MatMult": "@", 13787db96d56Sopenharmony_ci "Div": "/", 13797db96d56Sopenharmony_ci "Mod": "%", 13807db96d56Sopenharmony_ci "LShift": "<<", 13817db96d56Sopenharmony_ci "RShift": ">>", 13827db96d56Sopenharmony_ci "BitOr": "|", 13837db96d56Sopenharmony_ci "BitXor": "^", 13847db96d56Sopenharmony_ci "BitAnd": "&", 13857db96d56Sopenharmony_ci "FloorDiv": "//", 13867db96d56Sopenharmony_ci "Pow": "**", 13877db96d56Sopenharmony_ci } 13887db96d56Sopenharmony_ci 13897db96d56Sopenharmony_ci binop_precedence = { 13907db96d56Sopenharmony_ci "+": _Precedence.ARITH, 13917db96d56Sopenharmony_ci "-": _Precedence.ARITH, 13927db96d56Sopenharmony_ci "*": _Precedence.TERM, 13937db96d56Sopenharmony_ci "@": _Precedence.TERM, 13947db96d56Sopenharmony_ci "/": _Precedence.TERM, 13957db96d56Sopenharmony_ci "%": _Precedence.TERM, 13967db96d56Sopenharmony_ci "<<": _Precedence.SHIFT, 13977db96d56Sopenharmony_ci ">>": _Precedence.SHIFT, 13987db96d56Sopenharmony_ci "|": _Precedence.BOR, 13997db96d56Sopenharmony_ci "^": _Precedence.BXOR, 14007db96d56Sopenharmony_ci "&": _Precedence.BAND, 14017db96d56Sopenharmony_ci "//": _Precedence.TERM, 14027db96d56Sopenharmony_ci "**": _Precedence.POWER, 14037db96d56Sopenharmony_ci } 14047db96d56Sopenharmony_ci 14057db96d56Sopenharmony_ci binop_rassoc = frozenset(("**",)) 14067db96d56Sopenharmony_ci def visit_BinOp(self, node): 14077db96d56Sopenharmony_ci operator = self.binop[node.op.__class__.__name__] 14087db96d56Sopenharmony_ci operator_precedence = self.binop_precedence[operator] 14097db96d56Sopenharmony_ci with self.require_parens(operator_precedence, node): 14107db96d56Sopenharmony_ci if operator in self.binop_rassoc: 14117db96d56Sopenharmony_ci left_precedence = operator_precedence.next() 14127db96d56Sopenharmony_ci right_precedence = operator_precedence 14137db96d56Sopenharmony_ci else: 14147db96d56Sopenharmony_ci left_precedence = operator_precedence 14157db96d56Sopenharmony_ci right_precedence = operator_precedence.next() 14167db96d56Sopenharmony_ci 14177db96d56Sopenharmony_ci self.set_precedence(left_precedence, node.left) 14187db96d56Sopenharmony_ci self.traverse(node.left) 14197db96d56Sopenharmony_ci self.write(f" {operator} ") 14207db96d56Sopenharmony_ci self.set_precedence(right_precedence, node.right) 14217db96d56Sopenharmony_ci self.traverse(node.right) 14227db96d56Sopenharmony_ci 14237db96d56Sopenharmony_ci cmpops = { 14247db96d56Sopenharmony_ci "Eq": "==", 14257db96d56Sopenharmony_ci "NotEq": "!=", 14267db96d56Sopenharmony_ci "Lt": "<", 14277db96d56Sopenharmony_ci "LtE": "<=", 14287db96d56Sopenharmony_ci "Gt": ">", 14297db96d56Sopenharmony_ci "GtE": ">=", 14307db96d56Sopenharmony_ci "Is": "is", 14317db96d56Sopenharmony_ci "IsNot": "is not", 14327db96d56Sopenharmony_ci "In": "in", 14337db96d56Sopenharmony_ci "NotIn": "not in", 14347db96d56Sopenharmony_ci } 14357db96d56Sopenharmony_ci 14367db96d56Sopenharmony_ci def visit_Compare(self, node): 14377db96d56Sopenharmony_ci with self.require_parens(_Precedence.CMP, node): 14387db96d56Sopenharmony_ci self.set_precedence(_Precedence.CMP.next(), node.left, *node.comparators) 14397db96d56Sopenharmony_ci self.traverse(node.left) 14407db96d56Sopenharmony_ci for o, e in zip(node.ops, node.comparators): 14417db96d56Sopenharmony_ci self.write(" " + self.cmpops[o.__class__.__name__] + " ") 14427db96d56Sopenharmony_ci self.traverse(e) 14437db96d56Sopenharmony_ci 14447db96d56Sopenharmony_ci boolops = {"And": "and", "Or": "or"} 14457db96d56Sopenharmony_ci boolop_precedence = {"and": _Precedence.AND, "or": _Precedence.OR} 14467db96d56Sopenharmony_ci 14477db96d56Sopenharmony_ci def visit_BoolOp(self, node): 14487db96d56Sopenharmony_ci operator = self.boolops[node.op.__class__.__name__] 14497db96d56Sopenharmony_ci operator_precedence = self.boolop_precedence[operator] 14507db96d56Sopenharmony_ci 14517db96d56Sopenharmony_ci def increasing_level_traverse(node): 14527db96d56Sopenharmony_ci nonlocal operator_precedence 14537db96d56Sopenharmony_ci operator_precedence = operator_precedence.next() 14547db96d56Sopenharmony_ci self.set_precedence(operator_precedence, node) 14557db96d56Sopenharmony_ci self.traverse(node) 14567db96d56Sopenharmony_ci 14577db96d56Sopenharmony_ci with self.require_parens(operator_precedence, node): 14587db96d56Sopenharmony_ci s = f" {operator} " 14597db96d56Sopenharmony_ci self.interleave(lambda: self.write(s), increasing_level_traverse, node.values) 14607db96d56Sopenharmony_ci 14617db96d56Sopenharmony_ci def visit_Attribute(self, node): 14627db96d56Sopenharmony_ci self.set_precedence(_Precedence.ATOM, node.value) 14637db96d56Sopenharmony_ci self.traverse(node.value) 14647db96d56Sopenharmony_ci # Special case: 3.__abs__() is a syntax error, so if node.value 14657db96d56Sopenharmony_ci # is an integer literal then we need to either parenthesize 14667db96d56Sopenharmony_ci # it or add an extra space to get 3 .__abs__(). 14677db96d56Sopenharmony_ci if isinstance(node.value, Constant) and isinstance(node.value.value, int): 14687db96d56Sopenharmony_ci self.write(" ") 14697db96d56Sopenharmony_ci self.write(".") 14707db96d56Sopenharmony_ci self.write(node.attr) 14717db96d56Sopenharmony_ci 14727db96d56Sopenharmony_ci def visit_Call(self, node): 14737db96d56Sopenharmony_ci self.set_precedence(_Precedence.ATOM, node.func) 14747db96d56Sopenharmony_ci self.traverse(node.func) 14757db96d56Sopenharmony_ci with self.delimit("(", ")"): 14767db96d56Sopenharmony_ci comma = False 14777db96d56Sopenharmony_ci for e in node.args: 14787db96d56Sopenharmony_ci if comma: 14797db96d56Sopenharmony_ci self.write(", ") 14807db96d56Sopenharmony_ci else: 14817db96d56Sopenharmony_ci comma = True 14827db96d56Sopenharmony_ci self.traverse(e) 14837db96d56Sopenharmony_ci for e in node.keywords: 14847db96d56Sopenharmony_ci if comma: 14857db96d56Sopenharmony_ci self.write(", ") 14867db96d56Sopenharmony_ci else: 14877db96d56Sopenharmony_ci comma = True 14887db96d56Sopenharmony_ci self.traverse(e) 14897db96d56Sopenharmony_ci 14907db96d56Sopenharmony_ci def visit_Subscript(self, node): 14917db96d56Sopenharmony_ci def is_non_empty_tuple(slice_value): 14927db96d56Sopenharmony_ci return ( 14937db96d56Sopenharmony_ci isinstance(slice_value, Tuple) 14947db96d56Sopenharmony_ci and slice_value.elts 14957db96d56Sopenharmony_ci ) 14967db96d56Sopenharmony_ci 14977db96d56Sopenharmony_ci self.set_precedence(_Precedence.ATOM, node.value) 14987db96d56Sopenharmony_ci self.traverse(node.value) 14997db96d56Sopenharmony_ci with self.delimit("[", "]"): 15007db96d56Sopenharmony_ci if is_non_empty_tuple(node.slice): 15017db96d56Sopenharmony_ci # parentheses can be omitted if the tuple isn't empty 15027db96d56Sopenharmony_ci self.items_view(self.traverse, node.slice.elts) 15037db96d56Sopenharmony_ci else: 15047db96d56Sopenharmony_ci self.traverse(node.slice) 15057db96d56Sopenharmony_ci 15067db96d56Sopenharmony_ci def visit_Starred(self, node): 15077db96d56Sopenharmony_ci self.write("*") 15087db96d56Sopenharmony_ci self.set_precedence(_Precedence.EXPR, node.value) 15097db96d56Sopenharmony_ci self.traverse(node.value) 15107db96d56Sopenharmony_ci 15117db96d56Sopenharmony_ci def visit_Ellipsis(self, node): 15127db96d56Sopenharmony_ci self.write("...") 15137db96d56Sopenharmony_ci 15147db96d56Sopenharmony_ci def visit_Slice(self, node): 15157db96d56Sopenharmony_ci if node.lower: 15167db96d56Sopenharmony_ci self.traverse(node.lower) 15177db96d56Sopenharmony_ci self.write(":") 15187db96d56Sopenharmony_ci if node.upper: 15197db96d56Sopenharmony_ci self.traverse(node.upper) 15207db96d56Sopenharmony_ci if node.step: 15217db96d56Sopenharmony_ci self.write(":") 15227db96d56Sopenharmony_ci self.traverse(node.step) 15237db96d56Sopenharmony_ci 15247db96d56Sopenharmony_ci def visit_Match(self, node): 15257db96d56Sopenharmony_ci self.fill("match ") 15267db96d56Sopenharmony_ci self.traverse(node.subject) 15277db96d56Sopenharmony_ci with self.block(): 15287db96d56Sopenharmony_ci for case in node.cases: 15297db96d56Sopenharmony_ci self.traverse(case) 15307db96d56Sopenharmony_ci 15317db96d56Sopenharmony_ci def visit_arg(self, node): 15327db96d56Sopenharmony_ci self.write(node.arg) 15337db96d56Sopenharmony_ci if node.annotation: 15347db96d56Sopenharmony_ci self.write(": ") 15357db96d56Sopenharmony_ci self.traverse(node.annotation) 15367db96d56Sopenharmony_ci 15377db96d56Sopenharmony_ci def visit_arguments(self, node): 15387db96d56Sopenharmony_ci first = True 15397db96d56Sopenharmony_ci # normal arguments 15407db96d56Sopenharmony_ci all_args = node.posonlyargs + node.args 15417db96d56Sopenharmony_ci defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults 15427db96d56Sopenharmony_ci for index, elements in enumerate(zip(all_args, defaults), 1): 15437db96d56Sopenharmony_ci a, d = elements 15447db96d56Sopenharmony_ci if first: 15457db96d56Sopenharmony_ci first = False 15467db96d56Sopenharmony_ci else: 15477db96d56Sopenharmony_ci self.write(", ") 15487db96d56Sopenharmony_ci self.traverse(a) 15497db96d56Sopenharmony_ci if d: 15507db96d56Sopenharmony_ci self.write("=") 15517db96d56Sopenharmony_ci self.traverse(d) 15527db96d56Sopenharmony_ci if index == len(node.posonlyargs): 15537db96d56Sopenharmony_ci self.write(", /") 15547db96d56Sopenharmony_ci 15557db96d56Sopenharmony_ci # varargs, or bare '*' if no varargs but keyword-only arguments present 15567db96d56Sopenharmony_ci if node.vararg or node.kwonlyargs: 15577db96d56Sopenharmony_ci if first: 15587db96d56Sopenharmony_ci first = False 15597db96d56Sopenharmony_ci else: 15607db96d56Sopenharmony_ci self.write(", ") 15617db96d56Sopenharmony_ci self.write("*") 15627db96d56Sopenharmony_ci if node.vararg: 15637db96d56Sopenharmony_ci self.write(node.vararg.arg) 15647db96d56Sopenharmony_ci if node.vararg.annotation: 15657db96d56Sopenharmony_ci self.write(": ") 15667db96d56Sopenharmony_ci self.traverse(node.vararg.annotation) 15677db96d56Sopenharmony_ci 15687db96d56Sopenharmony_ci # keyword-only arguments 15697db96d56Sopenharmony_ci if node.kwonlyargs: 15707db96d56Sopenharmony_ci for a, d in zip(node.kwonlyargs, node.kw_defaults): 15717db96d56Sopenharmony_ci self.write(", ") 15727db96d56Sopenharmony_ci self.traverse(a) 15737db96d56Sopenharmony_ci if d: 15747db96d56Sopenharmony_ci self.write("=") 15757db96d56Sopenharmony_ci self.traverse(d) 15767db96d56Sopenharmony_ci 15777db96d56Sopenharmony_ci # kwargs 15787db96d56Sopenharmony_ci if node.kwarg: 15797db96d56Sopenharmony_ci if first: 15807db96d56Sopenharmony_ci first = False 15817db96d56Sopenharmony_ci else: 15827db96d56Sopenharmony_ci self.write(", ") 15837db96d56Sopenharmony_ci self.write("**" + node.kwarg.arg) 15847db96d56Sopenharmony_ci if node.kwarg.annotation: 15857db96d56Sopenharmony_ci self.write(": ") 15867db96d56Sopenharmony_ci self.traverse(node.kwarg.annotation) 15877db96d56Sopenharmony_ci 15887db96d56Sopenharmony_ci def visit_keyword(self, node): 15897db96d56Sopenharmony_ci if node.arg is None: 15907db96d56Sopenharmony_ci self.write("**") 15917db96d56Sopenharmony_ci else: 15927db96d56Sopenharmony_ci self.write(node.arg) 15937db96d56Sopenharmony_ci self.write("=") 15947db96d56Sopenharmony_ci self.traverse(node.value) 15957db96d56Sopenharmony_ci 15967db96d56Sopenharmony_ci def visit_Lambda(self, node): 15977db96d56Sopenharmony_ci with self.require_parens(_Precedence.TEST, node): 15987db96d56Sopenharmony_ci self.write("lambda") 15997db96d56Sopenharmony_ci with self.buffered() as buffer: 16007db96d56Sopenharmony_ci self.traverse(node.args) 16017db96d56Sopenharmony_ci if buffer: 16027db96d56Sopenharmony_ci self.write(" ", *buffer) 16037db96d56Sopenharmony_ci self.write(": ") 16047db96d56Sopenharmony_ci self.set_precedence(_Precedence.TEST, node.body) 16057db96d56Sopenharmony_ci self.traverse(node.body) 16067db96d56Sopenharmony_ci 16077db96d56Sopenharmony_ci def visit_alias(self, node): 16087db96d56Sopenharmony_ci self.write(node.name) 16097db96d56Sopenharmony_ci if node.asname: 16107db96d56Sopenharmony_ci self.write(" as " + node.asname) 16117db96d56Sopenharmony_ci 16127db96d56Sopenharmony_ci def visit_withitem(self, node): 16137db96d56Sopenharmony_ci self.traverse(node.context_expr) 16147db96d56Sopenharmony_ci if node.optional_vars: 16157db96d56Sopenharmony_ci self.write(" as ") 16167db96d56Sopenharmony_ci self.traverse(node.optional_vars) 16177db96d56Sopenharmony_ci 16187db96d56Sopenharmony_ci def visit_match_case(self, node): 16197db96d56Sopenharmony_ci self.fill("case ") 16207db96d56Sopenharmony_ci self.traverse(node.pattern) 16217db96d56Sopenharmony_ci if node.guard: 16227db96d56Sopenharmony_ci self.write(" if ") 16237db96d56Sopenharmony_ci self.traverse(node.guard) 16247db96d56Sopenharmony_ci with self.block(): 16257db96d56Sopenharmony_ci self.traverse(node.body) 16267db96d56Sopenharmony_ci 16277db96d56Sopenharmony_ci def visit_MatchValue(self, node): 16287db96d56Sopenharmony_ci self.traverse(node.value) 16297db96d56Sopenharmony_ci 16307db96d56Sopenharmony_ci def visit_MatchSingleton(self, node): 16317db96d56Sopenharmony_ci self._write_constant(node.value) 16327db96d56Sopenharmony_ci 16337db96d56Sopenharmony_ci def visit_MatchSequence(self, node): 16347db96d56Sopenharmony_ci with self.delimit("[", "]"): 16357db96d56Sopenharmony_ci self.interleave( 16367db96d56Sopenharmony_ci lambda: self.write(", "), self.traverse, node.patterns 16377db96d56Sopenharmony_ci ) 16387db96d56Sopenharmony_ci 16397db96d56Sopenharmony_ci def visit_MatchStar(self, node): 16407db96d56Sopenharmony_ci name = node.name 16417db96d56Sopenharmony_ci if name is None: 16427db96d56Sopenharmony_ci name = "_" 16437db96d56Sopenharmony_ci self.write(f"*{name}") 16447db96d56Sopenharmony_ci 16457db96d56Sopenharmony_ci def visit_MatchMapping(self, node): 16467db96d56Sopenharmony_ci def write_key_pattern_pair(pair): 16477db96d56Sopenharmony_ci k, p = pair 16487db96d56Sopenharmony_ci self.traverse(k) 16497db96d56Sopenharmony_ci self.write(": ") 16507db96d56Sopenharmony_ci self.traverse(p) 16517db96d56Sopenharmony_ci 16527db96d56Sopenharmony_ci with self.delimit("{", "}"): 16537db96d56Sopenharmony_ci keys = node.keys 16547db96d56Sopenharmony_ci self.interleave( 16557db96d56Sopenharmony_ci lambda: self.write(", "), 16567db96d56Sopenharmony_ci write_key_pattern_pair, 16577db96d56Sopenharmony_ci zip(keys, node.patterns, strict=True), 16587db96d56Sopenharmony_ci ) 16597db96d56Sopenharmony_ci rest = node.rest 16607db96d56Sopenharmony_ci if rest is not None: 16617db96d56Sopenharmony_ci if keys: 16627db96d56Sopenharmony_ci self.write(", ") 16637db96d56Sopenharmony_ci self.write(f"**{rest}") 16647db96d56Sopenharmony_ci 16657db96d56Sopenharmony_ci def visit_MatchClass(self, node): 16667db96d56Sopenharmony_ci self.set_precedence(_Precedence.ATOM, node.cls) 16677db96d56Sopenharmony_ci self.traverse(node.cls) 16687db96d56Sopenharmony_ci with self.delimit("(", ")"): 16697db96d56Sopenharmony_ci patterns = node.patterns 16707db96d56Sopenharmony_ci self.interleave( 16717db96d56Sopenharmony_ci lambda: self.write(", "), self.traverse, patterns 16727db96d56Sopenharmony_ci ) 16737db96d56Sopenharmony_ci attrs = node.kwd_attrs 16747db96d56Sopenharmony_ci if attrs: 16757db96d56Sopenharmony_ci def write_attr_pattern(pair): 16767db96d56Sopenharmony_ci attr, pattern = pair 16777db96d56Sopenharmony_ci self.write(f"{attr}=") 16787db96d56Sopenharmony_ci self.traverse(pattern) 16797db96d56Sopenharmony_ci 16807db96d56Sopenharmony_ci if patterns: 16817db96d56Sopenharmony_ci self.write(", ") 16827db96d56Sopenharmony_ci self.interleave( 16837db96d56Sopenharmony_ci lambda: self.write(", "), 16847db96d56Sopenharmony_ci write_attr_pattern, 16857db96d56Sopenharmony_ci zip(attrs, node.kwd_patterns, strict=True), 16867db96d56Sopenharmony_ci ) 16877db96d56Sopenharmony_ci 16887db96d56Sopenharmony_ci def visit_MatchAs(self, node): 16897db96d56Sopenharmony_ci name = node.name 16907db96d56Sopenharmony_ci pattern = node.pattern 16917db96d56Sopenharmony_ci if name is None: 16927db96d56Sopenharmony_ci self.write("_") 16937db96d56Sopenharmony_ci elif pattern is None: 16947db96d56Sopenharmony_ci self.write(node.name) 16957db96d56Sopenharmony_ci else: 16967db96d56Sopenharmony_ci with self.require_parens(_Precedence.TEST, node): 16977db96d56Sopenharmony_ci self.set_precedence(_Precedence.BOR, node.pattern) 16987db96d56Sopenharmony_ci self.traverse(node.pattern) 16997db96d56Sopenharmony_ci self.write(f" as {node.name}") 17007db96d56Sopenharmony_ci 17017db96d56Sopenharmony_ci def visit_MatchOr(self, node): 17027db96d56Sopenharmony_ci with self.require_parens(_Precedence.BOR, node): 17037db96d56Sopenharmony_ci self.set_precedence(_Precedence.BOR.next(), *node.patterns) 17047db96d56Sopenharmony_ci self.interleave(lambda: self.write(" | "), self.traverse, node.patterns) 17057db96d56Sopenharmony_ci 17067db96d56Sopenharmony_cidef unparse(ast_obj): 17077db96d56Sopenharmony_ci unparser = _Unparser() 17087db96d56Sopenharmony_ci return unparser.visit(ast_obj) 17097db96d56Sopenharmony_ci 17107db96d56Sopenharmony_ci 17117db96d56Sopenharmony_cidef main(): 17127db96d56Sopenharmony_ci import argparse 17137db96d56Sopenharmony_ci 17147db96d56Sopenharmony_ci parser = argparse.ArgumentParser(prog='python -m ast') 17157db96d56Sopenharmony_ci parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?', 17167db96d56Sopenharmony_ci default='-', 17177db96d56Sopenharmony_ci help='the file to parse; defaults to stdin') 17187db96d56Sopenharmony_ci parser.add_argument('-m', '--mode', default='exec', 17197db96d56Sopenharmony_ci choices=('exec', 'single', 'eval', 'func_type'), 17207db96d56Sopenharmony_ci help='specify what kind of code must be parsed') 17217db96d56Sopenharmony_ci parser.add_argument('--no-type-comments', default=True, action='store_false', 17227db96d56Sopenharmony_ci help="don't add information about type comments") 17237db96d56Sopenharmony_ci parser.add_argument('-a', '--include-attributes', action='store_true', 17247db96d56Sopenharmony_ci help='include attributes such as line numbers and ' 17257db96d56Sopenharmony_ci 'column offsets') 17267db96d56Sopenharmony_ci parser.add_argument('-i', '--indent', type=int, default=3, 17277db96d56Sopenharmony_ci help='indentation of nodes (number of spaces)') 17287db96d56Sopenharmony_ci args = parser.parse_args() 17297db96d56Sopenharmony_ci 17307db96d56Sopenharmony_ci with args.infile as infile: 17317db96d56Sopenharmony_ci source = infile.read() 17327db96d56Sopenharmony_ci tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments) 17337db96d56Sopenharmony_ci print(dump(tree, include_attributes=args.include_attributes, indent=args.indent)) 17347db96d56Sopenharmony_ci 17357db96d56Sopenharmony_ciif __name__ == '__main__': 17367db96d56Sopenharmony_ci main() 1737