1*cda5da8dSAndroid Build Coastguard Worker""" 2*cda5da8dSAndroid Build Coastguard Worker ast 3*cda5da8dSAndroid Build Coastguard Worker ~~~ 4*cda5da8dSAndroid Build Coastguard Worker 5*cda5da8dSAndroid Build Coastguard Worker The `ast` module helps Python applications to process trees of the Python 6*cda5da8dSAndroid Build Coastguard Worker abstract syntax grammar. The abstract syntax itself might change with 7*cda5da8dSAndroid Build Coastguard Worker each Python release; this module helps to find out programmatically what 8*cda5da8dSAndroid Build Coastguard Worker the current grammar looks like and allows modifications of it. 9*cda5da8dSAndroid Build Coastguard Worker 10*cda5da8dSAndroid Build Coastguard Worker An abstract syntax tree can be generated by passing `ast.PyCF_ONLY_AST` as 11*cda5da8dSAndroid Build Coastguard Worker a flag to the `compile()` builtin function or by using the `parse()` 12*cda5da8dSAndroid Build Coastguard Worker function from this module. The result will be a tree of objects whose 13*cda5da8dSAndroid Build Coastguard Worker classes all inherit from `ast.AST`. 14*cda5da8dSAndroid Build Coastguard Worker 15*cda5da8dSAndroid Build Coastguard Worker A modified abstract syntax tree can be compiled into a Python code object 16*cda5da8dSAndroid Build Coastguard Worker using the built-in `compile()` function. 17*cda5da8dSAndroid Build Coastguard Worker 18*cda5da8dSAndroid Build Coastguard Worker Additionally various helper functions are provided that make working with 19*cda5da8dSAndroid Build Coastguard Worker the trees simpler. The main intention of the helper functions and this 20*cda5da8dSAndroid Build Coastguard Worker module in general is to provide an easy to use interface for libraries 21*cda5da8dSAndroid Build Coastguard Worker that work tightly with the python syntax (template engines for example). 22*cda5da8dSAndroid Build Coastguard Worker 23*cda5da8dSAndroid Build Coastguard Worker 24*cda5da8dSAndroid Build Coastguard Worker :copyright: Copyright 2008 by Armin Ronacher. 25*cda5da8dSAndroid Build Coastguard Worker :license: Python License. 26*cda5da8dSAndroid Build Coastguard Worker""" 27*cda5da8dSAndroid Build Coastguard Workerimport sys 28*cda5da8dSAndroid Build Coastguard Workerfrom _ast import * 29*cda5da8dSAndroid Build Coastguard Workerfrom contextlib import contextmanager, nullcontext 30*cda5da8dSAndroid Build Coastguard Workerfrom enum import IntEnum, auto, _simple_enum 31*cda5da8dSAndroid Build Coastguard Worker 32*cda5da8dSAndroid Build Coastguard Worker 33*cda5da8dSAndroid Build Coastguard Workerdef parse(source, filename='<unknown>', mode='exec', *, 34*cda5da8dSAndroid Build Coastguard Worker type_comments=False, feature_version=None): 35*cda5da8dSAndroid Build Coastguard Worker """ 36*cda5da8dSAndroid Build Coastguard Worker Parse the source into an AST node. 37*cda5da8dSAndroid Build Coastguard Worker Equivalent to compile(source, filename, mode, PyCF_ONLY_AST). 38*cda5da8dSAndroid Build Coastguard Worker Pass type_comments=True to get back type comments where the syntax allows. 39*cda5da8dSAndroid Build Coastguard Worker """ 40*cda5da8dSAndroid Build Coastguard Worker flags = PyCF_ONLY_AST 41*cda5da8dSAndroid Build Coastguard Worker if type_comments: 42*cda5da8dSAndroid Build Coastguard Worker flags |= PyCF_TYPE_COMMENTS 43*cda5da8dSAndroid Build Coastguard Worker if isinstance(feature_version, tuple): 44*cda5da8dSAndroid Build Coastguard Worker major, minor = feature_version # Should be a 2-tuple. 45*cda5da8dSAndroid Build Coastguard Worker assert major == 3 46*cda5da8dSAndroid Build Coastguard Worker feature_version = minor 47*cda5da8dSAndroid Build Coastguard Worker elif feature_version is None: 48*cda5da8dSAndroid Build Coastguard Worker feature_version = -1 49*cda5da8dSAndroid Build Coastguard Worker # Else it should be an int giving the minor version for 3.x. 50*cda5da8dSAndroid Build Coastguard Worker return compile(source, filename, mode, flags, 51*cda5da8dSAndroid Build Coastguard Worker _feature_version=feature_version) 52*cda5da8dSAndroid Build Coastguard Worker 53*cda5da8dSAndroid Build Coastguard Worker 54*cda5da8dSAndroid Build Coastguard Workerdef literal_eval(node_or_string): 55*cda5da8dSAndroid Build Coastguard Worker """ 56*cda5da8dSAndroid Build Coastguard Worker Evaluate an expression node or a string containing only a Python 57*cda5da8dSAndroid Build Coastguard Worker expression. The string or node provided may only consist of the following 58*cda5da8dSAndroid Build Coastguard Worker Python literal structures: strings, bytes, numbers, tuples, lists, dicts, 59*cda5da8dSAndroid Build Coastguard Worker sets, booleans, and None. 60*cda5da8dSAndroid Build Coastguard Worker 61*cda5da8dSAndroid Build Coastguard Worker Caution: A complex expression can overflow the C stack and cause a crash. 62*cda5da8dSAndroid Build Coastguard Worker """ 63*cda5da8dSAndroid Build Coastguard Worker if isinstance(node_or_string, str): 64*cda5da8dSAndroid Build Coastguard Worker node_or_string = parse(node_or_string.lstrip(" \t"), mode='eval') 65*cda5da8dSAndroid Build Coastguard Worker if isinstance(node_or_string, Expression): 66*cda5da8dSAndroid Build Coastguard Worker node_or_string = node_or_string.body 67*cda5da8dSAndroid Build Coastguard Worker def _raise_malformed_node(node): 68*cda5da8dSAndroid Build Coastguard Worker msg = "malformed node or string" 69*cda5da8dSAndroid Build Coastguard Worker if lno := getattr(node, 'lineno', None): 70*cda5da8dSAndroid Build Coastguard Worker msg += f' on line {lno}' 71*cda5da8dSAndroid Build Coastguard Worker raise ValueError(msg + f': {node!r}') 72*cda5da8dSAndroid Build Coastguard Worker def _convert_num(node): 73*cda5da8dSAndroid Build Coastguard Worker if not isinstance(node, Constant) or type(node.value) not in (int, float, complex): 74*cda5da8dSAndroid Build Coastguard Worker _raise_malformed_node(node) 75*cda5da8dSAndroid Build Coastguard Worker return node.value 76*cda5da8dSAndroid Build Coastguard Worker def _convert_signed_num(node): 77*cda5da8dSAndroid Build Coastguard Worker if isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)): 78*cda5da8dSAndroid Build Coastguard Worker operand = _convert_num(node.operand) 79*cda5da8dSAndroid Build Coastguard Worker if isinstance(node.op, UAdd): 80*cda5da8dSAndroid Build Coastguard Worker return + operand 81*cda5da8dSAndroid Build Coastguard Worker else: 82*cda5da8dSAndroid Build Coastguard Worker return - operand 83*cda5da8dSAndroid Build Coastguard Worker return _convert_num(node) 84*cda5da8dSAndroid Build Coastguard Worker def _convert(node): 85*cda5da8dSAndroid Build Coastguard Worker if isinstance(node, Constant): 86*cda5da8dSAndroid Build Coastguard Worker return node.value 87*cda5da8dSAndroid Build Coastguard Worker elif isinstance(node, Tuple): 88*cda5da8dSAndroid Build Coastguard Worker return tuple(map(_convert, node.elts)) 89*cda5da8dSAndroid Build Coastguard Worker elif isinstance(node, List): 90*cda5da8dSAndroid Build Coastguard Worker return list(map(_convert, node.elts)) 91*cda5da8dSAndroid Build Coastguard Worker elif isinstance(node, Set): 92*cda5da8dSAndroid Build Coastguard Worker return set(map(_convert, node.elts)) 93*cda5da8dSAndroid Build Coastguard Worker elif (isinstance(node, Call) and isinstance(node.func, Name) and 94*cda5da8dSAndroid Build Coastguard Worker node.func.id == 'set' and node.args == node.keywords == []): 95*cda5da8dSAndroid Build Coastguard Worker return set() 96*cda5da8dSAndroid Build Coastguard Worker elif isinstance(node, Dict): 97*cda5da8dSAndroid Build Coastguard Worker if len(node.keys) != len(node.values): 98*cda5da8dSAndroid Build Coastguard Worker _raise_malformed_node(node) 99*cda5da8dSAndroid Build Coastguard Worker return dict(zip(map(_convert, node.keys), 100*cda5da8dSAndroid Build Coastguard Worker map(_convert, node.values))) 101*cda5da8dSAndroid Build Coastguard Worker elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)): 102*cda5da8dSAndroid Build Coastguard Worker left = _convert_signed_num(node.left) 103*cda5da8dSAndroid Build Coastguard Worker right = _convert_num(node.right) 104*cda5da8dSAndroid Build Coastguard Worker if isinstance(left, (int, float)) and isinstance(right, complex): 105*cda5da8dSAndroid Build Coastguard Worker if isinstance(node.op, Add): 106*cda5da8dSAndroid Build Coastguard Worker return left + right 107*cda5da8dSAndroid Build Coastguard Worker else: 108*cda5da8dSAndroid Build Coastguard Worker return left - right 109*cda5da8dSAndroid Build Coastguard Worker return _convert_signed_num(node) 110*cda5da8dSAndroid Build Coastguard Worker return _convert(node_or_string) 111*cda5da8dSAndroid Build Coastguard Worker 112*cda5da8dSAndroid Build Coastguard Worker 113*cda5da8dSAndroid Build Coastguard Workerdef dump(node, annotate_fields=True, include_attributes=False, *, indent=None): 114*cda5da8dSAndroid Build Coastguard Worker """ 115*cda5da8dSAndroid Build Coastguard Worker Return a formatted dump of the tree in node. This is mainly useful for 116*cda5da8dSAndroid Build Coastguard Worker debugging purposes. If annotate_fields is true (by default), 117*cda5da8dSAndroid Build Coastguard Worker the returned string will show the names and the values for fields. 118*cda5da8dSAndroid Build Coastguard Worker If annotate_fields is false, the result string will be more compact by 119*cda5da8dSAndroid Build Coastguard Worker omitting unambiguous field names. Attributes such as line 120*cda5da8dSAndroid Build Coastguard Worker numbers and column offsets are not dumped by default. If this is wanted, 121*cda5da8dSAndroid Build Coastguard Worker include_attributes can be set to true. If indent is a non-negative 122*cda5da8dSAndroid Build Coastguard Worker integer or string, then the tree will be pretty-printed with that indent 123*cda5da8dSAndroid Build Coastguard Worker level. None (the default) selects the single line representation. 124*cda5da8dSAndroid Build Coastguard Worker """ 125*cda5da8dSAndroid Build Coastguard Worker def _format(node, level=0): 126*cda5da8dSAndroid Build Coastguard Worker if indent is not None: 127*cda5da8dSAndroid Build Coastguard Worker level += 1 128*cda5da8dSAndroid Build Coastguard Worker prefix = '\n' + indent * level 129*cda5da8dSAndroid Build Coastguard Worker sep = ',\n' + indent * level 130*cda5da8dSAndroid Build Coastguard Worker else: 131*cda5da8dSAndroid Build Coastguard Worker prefix = '' 132*cda5da8dSAndroid Build Coastguard Worker sep = ', ' 133*cda5da8dSAndroid Build Coastguard Worker if isinstance(node, AST): 134*cda5da8dSAndroid Build Coastguard Worker cls = type(node) 135*cda5da8dSAndroid Build Coastguard Worker args = [] 136*cda5da8dSAndroid Build Coastguard Worker allsimple = True 137*cda5da8dSAndroid Build Coastguard Worker keywords = annotate_fields 138*cda5da8dSAndroid Build Coastguard Worker for name in node._fields: 139*cda5da8dSAndroid Build Coastguard Worker try: 140*cda5da8dSAndroid Build Coastguard Worker value = getattr(node, name) 141*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 142*cda5da8dSAndroid Build Coastguard Worker keywords = True 143*cda5da8dSAndroid Build Coastguard Worker continue 144*cda5da8dSAndroid Build Coastguard Worker if value is None and getattr(cls, name, ...) is None: 145*cda5da8dSAndroid Build Coastguard Worker keywords = True 146*cda5da8dSAndroid Build Coastguard Worker continue 147*cda5da8dSAndroid Build Coastguard Worker value, simple = _format(value, level) 148*cda5da8dSAndroid Build Coastguard Worker allsimple = allsimple and simple 149*cda5da8dSAndroid Build Coastguard Worker if keywords: 150*cda5da8dSAndroid Build Coastguard Worker args.append('%s=%s' % (name, value)) 151*cda5da8dSAndroid Build Coastguard Worker else: 152*cda5da8dSAndroid Build Coastguard Worker args.append(value) 153*cda5da8dSAndroid Build Coastguard Worker if include_attributes and node._attributes: 154*cda5da8dSAndroid Build Coastguard Worker for name in node._attributes: 155*cda5da8dSAndroid Build Coastguard Worker try: 156*cda5da8dSAndroid Build Coastguard Worker value = getattr(node, name) 157*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 158*cda5da8dSAndroid Build Coastguard Worker continue 159*cda5da8dSAndroid Build Coastguard Worker if value is None and getattr(cls, name, ...) is None: 160*cda5da8dSAndroid Build Coastguard Worker continue 161*cda5da8dSAndroid Build Coastguard Worker value, simple = _format(value, level) 162*cda5da8dSAndroid Build Coastguard Worker allsimple = allsimple and simple 163*cda5da8dSAndroid Build Coastguard Worker args.append('%s=%s' % (name, value)) 164*cda5da8dSAndroid Build Coastguard Worker if allsimple and len(args) <= 3: 165*cda5da8dSAndroid Build Coastguard Worker return '%s(%s)' % (node.__class__.__name__, ', '.join(args)), not args 166*cda5da8dSAndroid Build Coastguard Worker return '%s(%s%s)' % (node.__class__.__name__, prefix, sep.join(args)), False 167*cda5da8dSAndroid Build Coastguard Worker elif isinstance(node, list): 168*cda5da8dSAndroid Build Coastguard Worker if not node: 169*cda5da8dSAndroid Build Coastguard Worker return '[]', True 170*cda5da8dSAndroid Build Coastguard Worker return '[%s%s]' % (prefix, sep.join(_format(x, level)[0] for x in node)), False 171*cda5da8dSAndroid Build Coastguard Worker return repr(node), True 172*cda5da8dSAndroid Build Coastguard Worker 173*cda5da8dSAndroid Build Coastguard Worker if not isinstance(node, AST): 174*cda5da8dSAndroid Build Coastguard Worker raise TypeError('expected AST, got %r' % node.__class__.__name__) 175*cda5da8dSAndroid Build Coastguard Worker if indent is not None and not isinstance(indent, str): 176*cda5da8dSAndroid Build Coastguard Worker indent = ' ' * indent 177*cda5da8dSAndroid Build Coastguard Worker return _format(node)[0] 178*cda5da8dSAndroid Build Coastguard Worker 179*cda5da8dSAndroid Build Coastguard Worker 180*cda5da8dSAndroid Build Coastguard Workerdef copy_location(new_node, old_node): 181*cda5da8dSAndroid Build Coastguard Worker """ 182*cda5da8dSAndroid Build Coastguard Worker Copy source location (`lineno`, `col_offset`, `end_lineno`, and `end_col_offset` 183*cda5da8dSAndroid Build Coastguard Worker attributes) from *old_node* to *new_node* if possible, and return *new_node*. 184*cda5da8dSAndroid Build Coastguard Worker """ 185*cda5da8dSAndroid Build Coastguard Worker for attr in 'lineno', 'col_offset', 'end_lineno', 'end_col_offset': 186*cda5da8dSAndroid Build Coastguard Worker if attr in old_node._attributes and attr in new_node._attributes: 187*cda5da8dSAndroid Build Coastguard Worker value = getattr(old_node, attr, None) 188*cda5da8dSAndroid Build Coastguard Worker # end_lineno and end_col_offset are optional attributes, and they 189*cda5da8dSAndroid Build Coastguard Worker # should be copied whether the value is None or not. 190*cda5da8dSAndroid Build Coastguard Worker if value is not None or ( 191*cda5da8dSAndroid Build Coastguard Worker hasattr(old_node, attr) and attr.startswith("end_") 192*cda5da8dSAndroid Build Coastguard Worker ): 193*cda5da8dSAndroid Build Coastguard Worker setattr(new_node, attr, value) 194*cda5da8dSAndroid Build Coastguard Worker return new_node 195*cda5da8dSAndroid Build Coastguard Worker 196*cda5da8dSAndroid Build Coastguard Worker 197*cda5da8dSAndroid Build Coastguard Workerdef fix_missing_locations(node): 198*cda5da8dSAndroid Build Coastguard Worker """ 199*cda5da8dSAndroid Build Coastguard Worker When you compile a node tree with compile(), the compiler expects lineno and 200*cda5da8dSAndroid Build Coastguard Worker col_offset attributes for every node that supports them. This is rather 201*cda5da8dSAndroid Build Coastguard Worker tedious to fill in for generated nodes, so this helper adds these attributes 202*cda5da8dSAndroid Build Coastguard Worker recursively where not already set, by setting them to the values of the 203*cda5da8dSAndroid Build Coastguard Worker parent node. It works recursively starting at *node*. 204*cda5da8dSAndroid Build Coastguard Worker """ 205*cda5da8dSAndroid Build Coastguard Worker def _fix(node, lineno, col_offset, end_lineno, end_col_offset): 206*cda5da8dSAndroid Build Coastguard Worker if 'lineno' in node._attributes: 207*cda5da8dSAndroid Build Coastguard Worker if not hasattr(node, 'lineno'): 208*cda5da8dSAndroid Build Coastguard Worker node.lineno = lineno 209*cda5da8dSAndroid Build Coastguard Worker else: 210*cda5da8dSAndroid Build Coastguard Worker lineno = node.lineno 211*cda5da8dSAndroid Build Coastguard Worker if 'end_lineno' in node._attributes: 212*cda5da8dSAndroid Build Coastguard Worker if getattr(node, 'end_lineno', None) is None: 213*cda5da8dSAndroid Build Coastguard Worker node.end_lineno = end_lineno 214*cda5da8dSAndroid Build Coastguard Worker else: 215*cda5da8dSAndroid Build Coastguard Worker end_lineno = node.end_lineno 216*cda5da8dSAndroid Build Coastguard Worker if 'col_offset' in node._attributes: 217*cda5da8dSAndroid Build Coastguard Worker if not hasattr(node, 'col_offset'): 218*cda5da8dSAndroid Build Coastguard Worker node.col_offset = col_offset 219*cda5da8dSAndroid Build Coastguard Worker else: 220*cda5da8dSAndroid Build Coastguard Worker col_offset = node.col_offset 221*cda5da8dSAndroid Build Coastguard Worker if 'end_col_offset' in node._attributes: 222*cda5da8dSAndroid Build Coastguard Worker if getattr(node, 'end_col_offset', None) is None: 223*cda5da8dSAndroid Build Coastguard Worker node.end_col_offset = end_col_offset 224*cda5da8dSAndroid Build Coastguard Worker else: 225*cda5da8dSAndroid Build Coastguard Worker end_col_offset = node.end_col_offset 226*cda5da8dSAndroid Build Coastguard Worker for child in iter_child_nodes(node): 227*cda5da8dSAndroid Build Coastguard Worker _fix(child, lineno, col_offset, end_lineno, end_col_offset) 228*cda5da8dSAndroid Build Coastguard Worker _fix(node, 1, 0, 1, 0) 229*cda5da8dSAndroid Build Coastguard Worker return node 230*cda5da8dSAndroid Build Coastguard Worker 231*cda5da8dSAndroid Build Coastguard Worker 232*cda5da8dSAndroid Build Coastguard Workerdef increment_lineno(node, n=1): 233*cda5da8dSAndroid Build Coastguard Worker """ 234*cda5da8dSAndroid Build Coastguard Worker Increment the line number and end line number of each node in the tree 235*cda5da8dSAndroid Build Coastguard Worker starting at *node* by *n*. This is useful to "move code" to a different 236*cda5da8dSAndroid Build Coastguard Worker location in a file. 237*cda5da8dSAndroid Build Coastguard Worker """ 238*cda5da8dSAndroid Build Coastguard Worker for child in walk(node): 239*cda5da8dSAndroid Build Coastguard Worker # TypeIgnore is a special case where lineno is not an attribute 240*cda5da8dSAndroid Build Coastguard Worker # but rather a field of the node itself. 241*cda5da8dSAndroid Build Coastguard Worker if isinstance(child, TypeIgnore): 242*cda5da8dSAndroid Build Coastguard Worker child.lineno = getattr(child, 'lineno', 0) + n 243*cda5da8dSAndroid Build Coastguard Worker continue 244*cda5da8dSAndroid Build Coastguard Worker 245*cda5da8dSAndroid Build Coastguard Worker if 'lineno' in child._attributes: 246*cda5da8dSAndroid Build Coastguard Worker child.lineno = getattr(child, 'lineno', 0) + n 247*cda5da8dSAndroid Build Coastguard Worker if ( 248*cda5da8dSAndroid Build Coastguard Worker "end_lineno" in child._attributes 249*cda5da8dSAndroid Build Coastguard Worker and (end_lineno := getattr(child, "end_lineno", 0)) is not None 250*cda5da8dSAndroid Build Coastguard Worker ): 251*cda5da8dSAndroid Build Coastguard Worker child.end_lineno = end_lineno + n 252*cda5da8dSAndroid Build Coastguard Worker return node 253*cda5da8dSAndroid Build Coastguard Worker 254*cda5da8dSAndroid Build Coastguard Worker 255*cda5da8dSAndroid Build Coastguard Workerdef iter_fields(node): 256*cda5da8dSAndroid Build Coastguard Worker """ 257*cda5da8dSAndroid Build Coastguard Worker Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields`` 258*cda5da8dSAndroid Build Coastguard Worker that is present on *node*. 259*cda5da8dSAndroid Build Coastguard Worker """ 260*cda5da8dSAndroid Build Coastguard Worker for field in node._fields: 261*cda5da8dSAndroid Build Coastguard Worker try: 262*cda5da8dSAndroid Build Coastguard Worker yield field, getattr(node, field) 263*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 264*cda5da8dSAndroid Build Coastguard Worker pass 265*cda5da8dSAndroid Build Coastguard Worker 266*cda5da8dSAndroid Build Coastguard Worker 267*cda5da8dSAndroid Build Coastguard Workerdef iter_child_nodes(node): 268*cda5da8dSAndroid Build Coastguard Worker """ 269*cda5da8dSAndroid Build Coastguard Worker Yield all direct child nodes of *node*, that is, all fields that are nodes 270*cda5da8dSAndroid Build Coastguard Worker and all items of fields that are lists of nodes. 271*cda5da8dSAndroid Build Coastguard Worker """ 272*cda5da8dSAndroid Build Coastguard Worker for name, field in iter_fields(node): 273*cda5da8dSAndroid Build Coastguard Worker if isinstance(field, AST): 274*cda5da8dSAndroid Build Coastguard Worker yield field 275*cda5da8dSAndroid Build Coastguard Worker elif isinstance(field, list): 276*cda5da8dSAndroid Build Coastguard Worker for item in field: 277*cda5da8dSAndroid Build Coastguard Worker if isinstance(item, AST): 278*cda5da8dSAndroid Build Coastguard Worker yield item 279*cda5da8dSAndroid Build Coastguard Worker 280*cda5da8dSAndroid Build Coastguard Worker 281*cda5da8dSAndroid Build Coastguard Workerdef get_docstring(node, clean=True): 282*cda5da8dSAndroid Build Coastguard Worker """ 283*cda5da8dSAndroid Build Coastguard Worker Return the docstring for the given node or None if no docstring can 284*cda5da8dSAndroid Build Coastguard Worker be found. If the node provided does not have docstrings a TypeError 285*cda5da8dSAndroid Build Coastguard Worker will be raised. 286*cda5da8dSAndroid Build Coastguard Worker 287*cda5da8dSAndroid Build Coastguard Worker If *clean* is `True`, all tabs are expanded to spaces and any whitespace 288*cda5da8dSAndroid Build Coastguard Worker that can be uniformly removed from the second line onwards is removed. 289*cda5da8dSAndroid Build Coastguard Worker """ 290*cda5da8dSAndroid Build Coastguard Worker if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)): 291*cda5da8dSAndroid Build Coastguard Worker raise TypeError("%r can't have docstrings" % node.__class__.__name__) 292*cda5da8dSAndroid Build Coastguard Worker if not(node.body and isinstance(node.body[0], Expr)): 293*cda5da8dSAndroid Build Coastguard Worker return None 294*cda5da8dSAndroid Build Coastguard Worker node = node.body[0].value 295*cda5da8dSAndroid Build Coastguard Worker if isinstance(node, Str): 296*cda5da8dSAndroid Build Coastguard Worker text = node.s 297*cda5da8dSAndroid Build Coastguard Worker elif isinstance(node, Constant) and isinstance(node.value, str): 298*cda5da8dSAndroid Build Coastguard Worker text = node.value 299*cda5da8dSAndroid Build Coastguard Worker else: 300*cda5da8dSAndroid Build Coastguard Worker return None 301*cda5da8dSAndroid Build Coastguard Worker if clean: 302*cda5da8dSAndroid Build Coastguard Worker import inspect 303*cda5da8dSAndroid Build Coastguard Worker text = inspect.cleandoc(text) 304*cda5da8dSAndroid Build Coastguard Worker return text 305*cda5da8dSAndroid Build Coastguard Worker 306*cda5da8dSAndroid Build Coastguard Worker 307*cda5da8dSAndroid Build Coastguard Workerdef _splitlines_no_ff(source): 308*cda5da8dSAndroid Build Coastguard Worker """Split a string into lines ignoring form feed and other chars. 309*cda5da8dSAndroid Build Coastguard Worker 310*cda5da8dSAndroid Build Coastguard Worker This mimics how the Python parser splits source code. 311*cda5da8dSAndroid Build Coastguard Worker """ 312*cda5da8dSAndroid Build Coastguard Worker idx = 0 313*cda5da8dSAndroid Build Coastguard Worker lines = [] 314*cda5da8dSAndroid Build Coastguard Worker next_line = '' 315*cda5da8dSAndroid Build Coastguard Worker while idx < len(source): 316*cda5da8dSAndroid Build Coastguard Worker c = source[idx] 317*cda5da8dSAndroid Build Coastguard Worker next_line += c 318*cda5da8dSAndroid Build Coastguard Worker idx += 1 319*cda5da8dSAndroid Build Coastguard Worker # Keep \r\n together 320*cda5da8dSAndroid Build Coastguard Worker if c == '\r' and idx < len(source) and source[idx] == '\n': 321*cda5da8dSAndroid Build Coastguard Worker next_line += '\n' 322*cda5da8dSAndroid Build Coastguard Worker idx += 1 323*cda5da8dSAndroid Build Coastguard Worker if c in '\r\n': 324*cda5da8dSAndroid Build Coastguard Worker lines.append(next_line) 325*cda5da8dSAndroid Build Coastguard Worker next_line = '' 326*cda5da8dSAndroid Build Coastguard Worker 327*cda5da8dSAndroid Build Coastguard Worker if next_line: 328*cda5da8dSAndroid Build Coastguard Worker lines.append(next_line) 329*cda5da8dSAndroid Build Coastguard Worker return lines 330*cda5da8dSAndroid Build Coastguard Worker 331*cda5da8dSAndroid Build Coastguard Worker 332*cda5da8dSAndroid Build Coastguard Workerdef _pad_whitespace(source): 333*cda5da8dSAndroid Build Coastguard Worker r"""Replace all chars except '\f\t' in a line with spaces.""" 334*cda5da8dSAndroid Build Coastguard Worker result = '' 335*cda5da8dSAndroid Build Coastguard Worker for c in source: 336*cda5da8dSAndroid Build Coastguard Worker if c in '\f\t': 337*cda5da8dSAndroid Build Coastguard Worker result += c 338*cda5da8dSAndroid Build Coastguard Worker else: 339*cda5da8dSAndroid Build Coastguard Worker result += ' ' 340*cda5da8dSAndroid Build Coastguard Worker return result 341*cda5da8dSAndroid Build Coastguard Worker 342*cda5da8dSAndroid Build Coastguard Worker 343*cda5da8dSAndroid Build Coastguard Workerdef get_source_segment(source, node, *, padded=False): 344*cda5da8dSAndroid Build Coastguard Worker """Get source code segment of the *source* that generated *node*. 345*cda5da8dSAndroid Build Coastguard Worker 346*cda5da8dSAndroid Build Coastguard Worker If some location information (`lineno`, `end_lineno`, `col_offset`, 347*cda5da8dSAndroid Build Coastguard Worker or `end_col_offset`) is missing, return None. 348*cda5da8dSAndroid Build Coastguard Worker 349*cda5da8dSAndroid Build Coastguard Worker If *padded* is `True`, the first line of a multi-line statement will 350*cda5da8dSAndroid Build Coastguard Worker be padded with spaces to match its original position. 351*cda5da8dSAndroid Build Coastguard Worker """ 352*cda5da8dSAndroid Build Coastguard Worker try: 353*cda5da8dSAndroid Build Coastguard Worker if node.end_lineno is None or node.end_col_offset is None: 354*cda5da8dSAndroid Build Coastguard Worker return None 355*cda5da8dSAndroid Build Coastguard Worker lineno = node.lineno - 1 356*cda5da8dSAndroid Build Coastguard Worker end_lineno = node.end_lineno - 1 357*cda5da8dSAndroid Build Coastguard Worker col_offset = node.col_offset 358*cda5da8dSAndroid Build Coastguard Worker end_col_offset = node.end_col_offset 359*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 360*cda5da8dSAndroid Build Coastguard Worker return None 361*cda5da8dSAndroid Build Coastguard Worker 362*cda5da8dSAndroid Build Coastguard Worker lines = _splitlines_no_ff(source) 363*cda5da8dSAndroid Build Coastguard Worker if end_lineno == lineno: 364*cda5da8dSAndroid Build Coastguard Worker return lines[lineno].encode()[col_offset:end_col_offset].decode() 365*cda5da8dSAndroid Build Coastguard Worker 366*cda5da8dSAndroid Build Coastguard Worker if padded: 367*cda5da8dSAndroid Build Coastguard Worker padding = _pad_whitespace(lines[lineno].encode()[:col_offset].decode()) 368*cda5da8dSAndroid Build Coastguard Worker else: 369*cda5da8dSAndroid Build Coastguard Worker padding = '' 370*cda5da8dSAndroid Build Coastguard Worker 371*cda5da8dSAndroid Build Coastguard Worker first = padding + lines[lineno].encode()[col_offset:].decode() 372*cda5da8dSAndroid Build Coastguard Worker last = lines[end_lineno].encode()[:end_col_offset].decode() 373*cda5da8dSAndroid Build Coastguard Worker lines = lines[lineno+1:end_lineno] 374*cda5da8dSAndroid Build Coastguard Worker 375*cda5da8dSAndroid Build Coastguard Worker lines.insert(0, first) 376*cda5da8dSAndroid Build Coastguard Worker lines.append(last) 377*cda5da8dSAndroid Build Coastguard Worker return ''.join(lines) 378*cda5da8dSAndroid Build Coastguard Worker 379*cda5da8dSAndroid Build Coastguard Worker 380*cda5da8dSAndroid Build Coastguard Workerdef walk(node): 381*cda5da8dSAndroid Build Coastguard Worker """ 382*cda5da8dSAndroid Build Coastguard Worker Recursively yield all descendant nodes in the tree starting at *node* 383*cda5da8dSAndroid Build Coastguard Worker (including *node* itself), in no specified order. This is useful if you 384*cda5da8dSAndroid Build Coastguard Worker only want to modify nodes in place and don't care about the context. 385*cda5da8dSAndroid Build Coastguard Worker """ 386*cda5da8dSAndroid Build Coastguard Worker from collections import deque 387*cda5da8dSAndroid Build Coastguard Worker todo = deque([node]) 388*cda5da8dSAndroid Build Coastguard Worker while todo: 389*cda5da8dSAndroid Build Coastguard Worker node = todo.popleft() 390*cda5da8dSAndroid Build Coastguard Worker todo.extend(iter_child_nodes(node)) 391*cda5da8dSAndroid Build Coastguard Worker yield node 392*cda5da8dSAndroid Build Coastguard Worker 393*cda5da8dSAndroid Build Coastguard Worker 394*cda5da8dSAndroid Build Coastguard Workerclass NodeVisitor(object): 395*cda5da8dSAndroid Build Coastguard Worker """ 396*cda5da8dSAndroid Build Coastguard Worker A node visitor base class that walks the abstract syntax tree and calls a 397*cda5da8dSAndroid Build Coastguard Worker visitor function for every node found. This function may return a value 398*cda5da8dSAndroid Build Coastguard Worker which is forwarded by the `visit` method. 399*cda5da8dSAndroid Build Coastguard Worker 400*cda5da8dSAndroid Build Coastguard Worker This class is meant to be subclassed, with the subclass adding visitor 401*cda5da8dSAndroid Build Coastguard Worker methods. 402*cda5da8dSAndroid Build Coastguard Worker 403*cda5da8dSAndroid Build Coastguard Worker Per default the visitor functions for the nodes are ``'visit_'`` + 404*cda5da8dSAndroid Build Coastguard Worker class name of the node. So a `TryFinally` node visit function would 405*cda5da8dSAndroid Build Coastguard Worker be `visit_TryFinally`. This behavior can be changed by overriding 406*cda5da8dSAndroid Build Coastguard Worker the `visit` method. If no visitor function exists for a node 407*cda5da8dSAndroid Build Coastguard Worker (return value `None`) the `generic_visit` visitor is used instead. 408*cda5da8dSAndroid Build Coastguard Worker 409*cda5da8dSAndroid Build Coastguard Worker Don't use the `NodeVisitor` if you want to apply changes to nodes during 410*cda5da8dSAndroid Build Coastguard Worker traversing. For this a special visitor exists (`NodeTransformer`) that 411*cda5da8dSAndroid Build Coastguard Worker allows modifications. 412*cda5da8dSAndroid Build Coastguard Worker """ 413*cda5da8dSAndroid Build Coastguard Worker 414*cda5da8dSAndroid Build Coastguard Worker def visit(self, node): 415*cda5da8dSAndroid Build Coastguard Worker """Visit a node.""" 416*cda5da8dSAndroid Build Coastguard Worker method = 'visit_' + node.__class__.__name__ 417*cda5da8dSAndroid Build Coastguard Worker visitor = getattr(self, method, self.generic_visit) 418*cda5da8dSAndroid Build Coastguard Worker return visitor(node) 419*cda5da8dSAndroid Build Coastguard Worker 420*cda5da8dSAndroid Build Coastguard Worker def generic_visit(self, node): 421*cda5da8dSAndroid Build Coastguard Worker """Called if no explicit visitor function exists for a node.""" 422*cda5da8dSAndroid Build Coastguard Worker for field, value in iter_fields(node): 423*cda5da8dSAndroid Build Coastguard Worker if isinstance(value, list): 424*cda5da8dSAndroid Build Coastguard Worker for item in value: 425*cda5da8dSAndroid Build Coastguard Worker if isinstance(item, AST): 426*cda5da8dSAndroid Build Coastguard Worker self.visit(item) 427*cda5da8dSAndroid Build Coastguard Worker elif isinstance(value, AST): 428*cda5da8dSAndroid Build Coastguard Worker self.visit(value) 429*cda5da8dSAndroid Build Coastguard Worker 430*cda5da8dSAndroid Build Coastguard Worker def visit_Constant(self, node): 431*cda5da8dSAndroid Build Coastguard Worker value = node.value 432*cda5da8dSAndroid Build Coastguard Worker type_name = _const_node_type_names.get(type(value)) 433*cda5da8dSAndroid Build Coastguard Worker if type_name is None: 434*cda5da8dSAndroid Build Coastguard Worker for cls, name in _const_node_type_names.items(): 435*cda5da8dSAndroid Build Coastguard Worker if isinstance(value, cls): 436*cda5da8dSAndroid Build Coastguard Worker type_name = name 437*cda5da8dSAndroid Build Coastguard Worker break 438*cda5da8dSAndroid Build Coastguard Worker if type_name is not None: 439*cda5da8dSAndroid Build Coastguard Worker method = 'visit_' + type_name 440*cda5da8dSAndroid Build Coastguard Worker try: 441*cda5da8dSAndroid Build Coastguard Worker visitor = getattr(self, method) 442*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 443*cda5da8dSAndroid Build Coastguard Worker pass 444*cda5da8dSAndroid Build Coastguard Worker else: 445*cda5da8dSAndroid Build Coastguard Worker import warnings 446*cda5da8dSAndroid Build Coastguard Worker warnings.warn(f"{method} is deprecated; add visit_Constant", 447*cda5da8dSAndroid Build Coastguard Worker DeprecationWarning, 2) 448*cda5da8dSAndroid Build Coastguard Worker return visitor(node) 449*cda5da8dSAndroid Build Coastguard Worker return self.generic_visit(node) 450*cda5da8dSAndroid Build Coastguard Worker 451*cda5da8dSAndroid Build Coastguard Worker 452*cda5da8dSAndroid Build Coastguard Workerclass NodeTransformer(NodeVisitor): 453*cda5da8dSAndroid Build Coastguard Worker """ 454*cda5da8dSAndroid Build Coastguard Worker A :class:`NodeVisitor` subclass that walks the abstract syntax tree and 455*cda5da8dSAndroid Build Coastguard Worker allows modification of nodes. 456*cda5da8dSAndroid Build Coastguard Worker 457*cda5da8dSAndroid Build Coastguard Worker The `NodeTransformer` will walk the AST and use the return value of the 458*cda5da8dSAndroid Build Coastguard Worker visitor methods to replace or remove the old node. If the return value of 459*cda5da8dSAndroid Build Coastguard Worker the visitor method is ``None``, the node will be removed from its location, 460*cda5da8dSAndroid Build Coastguard Worker otherwise it is replaced with the return value. The return value may be the 461*cda5da8dSAndroid Build Coastguard Worker original node in which case no replacement takes place. 462*cda5da8dSAndroid Build Coastguard Worker 463*cda5da8dSAndroid Build Coastguard Worker Here is an example transformer that rewrites all occurrences of name lookups 464*cda5da8dSAndroid Build Coastguard Worker (``foo``) to ``data['foo']``:: 465*cda5da8dSAndroid Build Coastguard Worker 466*cda5da8dSAndroid Build Coastguard Worker class RewriteName(NodeTransformer): 467*cda5da8dSAndroid Build Coastguard Worker 468*cda5da8dSAndroid Build Coastguard Worker def visit_Name(self, node): 469*cda5da8dSAndroid Build Coastguard Worker return Subscript( 470*cda5da8dSAndroid Build Coastguard Worker value=Name(id='data', ctx=Load()), 471*cda5da8dSAndroid Build Coastguard Worker slice=Constant(value=node.id), 472*cda5da8dSAndroid Build Coastguard Worker ctx=node.ctx 473*cda5da8dSAndroid Build Coastguard Worker ) 474*cda5da8dSAndroid Build Coastguard Worker 475*cda5da8dSAndroid Build Coastguard Worker Keep in mind that if the node you're operating on has child nodes you must 476*cda5da8dSAndroid Build Coastguard Worker either transform the child nodes yourself or call the :meth:`generic_visit` 477*cda5da8dSAndroid Build Coastguard Worker method for the node first. 478*cda5da8dSAndroid Build Coastguard Worker 479*cda5da8dSAndroid Build Coastguard Worker For nodes that were part of a collection of statements (that applies to all 480*cda5da8dSAndroid Build Coastguard Worker statement nodes), the visitor may also return a list of nodes rather than 481*cda5da8dSAndroid Build Coastguard Worker just a single node. 482*cda5da8dSAndroid Build Coastguard Worker 483*cda5da8dSAndroid Build Coastguard Worker Usually you use the transformer like this:: 484*cda5da8dSAndroid Build Coastguard Worker 485*cda5da8dSAndroid Build Coastguard Worker node = YourTransformer().visit(node) 486*cda5da8dSAndroid Build Coastguard Worker """ 487*cda5da8dSAndroid Build Coastguard Worker 488*cda5da8dSAndroid Build Coastguard Worker def generic_visit(self, node): 489*cda5da8dSAndroid Build Coastguard Worker for field, old_value in iter_fields(node): 490*cda5da8dSAndroid Build Coastguard Worker if isinstance(old_value, list): 491*cda5da8dSAndroid Build Coastguard Worker new_values = [] 492*cda5da8dSAndroid Build Coastguard Worker for value in old_value: 493*cda5da8dSAndroid Build Coastguard Worker if isinstance(value, AST): 494*cda5da8dSAndroid Build Coastguard Worker value = self.visit(value) 495*cda5da8dSAndroid Build Coastguard Worker if value is None: 496*cda5da8dSAndroid Build Coastguard Worker continue 497*cda5da8dSAndroid Build Coastguard Worker elif not isinstance(value, AST): 498*cda5da8dSAndroid Build Coastguard Worker new_values.extend(value) 499*cda5da8dSAndroid Build Coastguard Worker continue 500*cda5da8dSAndroid Build Coastguard Worker new_values.append(value) 501*cda5da8dSAndroid Build Coastguard Worker old_value[:] = new_values 502*cda5da8dSAndroid Build Coastguard Worker elif isinstance(old_value, AST): 503*cda5da8dSAndroid Build Coastguard Worker new_node = self.visit(old_value) 504*cda5da8dSAndroid Build Coastguard Worker if new_node is None: 505*cda5da8dSAndroid Build Coastguard Worker delattr(node, field) 506*cda5da8dSAndroid Build Coastguard Worker else: 507*cda5da8dSAndroid Build Coastguard Worker setattr(node, field, new_node) 508*cda5da8dSAndroid Build Coastguard Worker return node 509*cda5da8dSAndroid Build Coastguard Worker 510*cda5da8dSAndroid Build Coastguard Worker 511*cda5da8dSAndroid Build Coastguard Worker# If the ast module is loaded more than once, only add deprecated methods once 512*cda5da8dSAndroid Build Coastguard Workerif not hasattr(Constant, 'n'): 513*cda5da8dSAndroid Build Coastguard Worker # The following code is for backward compatibility. 514*cda5da8dSAndroid Build Coastguard Worker # It will be removed in future. 515*cda5da8dSAndroid Build Coastguard Worker 516*cda5da8dSAndroid Build Coastguard Worker def _getter(self): 517*cda5da8dSAndroid Build Coastguard Worker """Deprecated. Use value instead.""" 518*cda5da8dSAndroid Build Coastguard Worker return self.value 519*cda5da8dSAndroid Build Coastguard Worker 520*cda5da8dSAndroid Build Coastguard Worker def _setter(self, value): 521*cda5da8dSAndroid Build Coastguard Worker self.value = value 522*cda5da8dSAndroid Build Coastguard Worker 523*cda5da8dSAndroid Build Coastguard Worker Constant.n = property(_getter, _setter) 524*cda5da8dSAndroid Build Coastguard Worker Constant.s = property(_getter, _setter) 525*cda5da8dSAndroid Build Coastguard Worker 526*cda5da8dSAndroid Build Coastguard Workerclass _ABC(type): 527*cda5da8dSAndroid Build Coastguard Worker 528*cda5da8dSAndroid Build Coastguard Worker def __init__(cls, *args): 529*cda5da8dSAndroid Build Coastguard Worker cls.__doc__ = """Deprecated AST node class. Use ast.Constant instead""" 530*cda5da8dSAndroid Build Coastguard Worker 531*cda5da8dSAndroid Build Coastguard Worker def __instancecheck__(cls, inst): 532*cda5da8dSAndroid Build Coastguard Worker if not isinstance(inst, Constant): 533*cda5da8dSAndroid Build Coastguard Worker return False 534*cda5da8dSAndroid Build Coastguard Worker if cls in _const_types: 535*cda5da8dSAndroid Build Coastguard Worker try: 536*cda5da8dSAndroid Build Coastguard Worker value = inst.value 537*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 538*cda5da8dSAndroid Build Coastguard Worker return False 539*cda5da8dSAndroid Build Coastguard Worker else: 540*cda5da8dSAndroid Build Coastguard Worker return ( 541*cda5da8dSAndroid Build Coastguard Worker isinstance(value, _const_types[cls]) and 542*cda5da8dSAndroid Build Coastguard Worker not isinstance(value, _const_types_not.get(cls, ())) 543*cda5da8dSAndroid Build Coastguard Worker ) 544*cda5da8dSAndroid Build Coastguard Worker return type.__instancecheck__(cls, inst) 545*cda5da8dSAndroid Build Coastguard Worker 546*cda5da8dSAndroid Build Coastguard Workerdef _new(cls, *args, **kwargs): 547*cda5da8dSAndroid Build Coastguard Worker for key in kwargs: 548*cda5da8dSAndroid Build Coastguard Worker if key not in cls._fields: 549*cda5da8dSAndroid Build Coastguard Worker # arbitrary keyword arguments are accepted 550*cda5da8dSAndroid Build Coastguard Worker continue 551*cda5da8dSAndroid Build Coastguard Worker pos = cls._fields.index(key) 552*cda5da8dSAndroid Build Coastguard Worker if pos < len(args): 553*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f"{cls.__name__} got multiple values for argument {key!r}") 554*cda5da8dSAndroid Build Coastguard Worker if cls in _const_types: 555*cda5da8dSAndroid Build Coastguard Worker return Constant(*args, **kwargs) 556*cda5da8dSAndroid Build Coastguard Worker return Constant.__new__(cls, *args, **kwargs) 557*cda5da8dSAndroid Build Coastguard Worker 558*cda5da8dSAndroid Build Coastguard Workerclass Num(Constant, metaclass=_ABC): 559*cda5da8dSAndroid Build Coastguard Worker _fields = ('n',) 560*cda5da8dSAndroid Build Coastguard Worker __new__ = _new 561*cda5da8dSAndroid Build Coastguard Worker 562*cda5da8dSAndroid Build Coastguard Workerclass Str(Constant, metaclass=_ABC): 563*cda5da8dSAndroid Build Coastguard Worker _fields = ('s',) 564*cda5da8dSAndroid Build Coastguard Worker __new__ = _new 565*cda5da8dSAndroid Build Coastguard Worker 566*cda5da8dSAndroid Build Coastguard Workerclass Bytes(Constant, metaclass=_ABC): 567*cda5da8dSAndroid Build Coastguard Worker _fields = ('s',) 568*cda5da8dSAndroid Build Coastguard Worker __new__ = _new 569*cda5da8dSAndroid Build Coastguard Worker 570*cda5da8dSAndroid Build Coastguard Workerclass NameConstant(Constant, metaclass=_ABC): 571*cda5da8dSAndroid Build Coastguard Worker __new__ = _new 572*cda5da8dSAndroid Build Coastguard Worker 573*cda5da8dSAndroid Build Coastguard Workerclass Ellipsis(Constant, metaclass=_ABC): 574*cda5da8dSAndroid Build Coastguard Worker _fields = () 575*cda5da8dSAndroid Build Coastguard Worker 576*cda5da8dSAndroid Build Coastguard Worker def __new__(cls, *args, **kwargs): 577*cda5da8dSAndroid Build Coastguard Worker if cls is Ellipsis: 578*cda5da8dSAndroid Build Coastguard Worker return Constant(..., *args, **kwargs) 579*cda5da8dSAndroid Build Coastguard Worker return Constant.__new__(cls, *args, **kwargs) 580*cda5da8dSAndroid Build Coastguard Worker 581*cda5da8dSAndroid Build Coastguard Worker_const_types = { 582*cda5da8dSAndroid Build Coastguard Worker Num: (int, float, complex), 583*cda5da8dSAndroid Build Coastguard Worker Str: (str,), 584*cda5da8dSAndroid Build Coastguard Worker Bytes: (bytes,), 585*cda5da8dSAndroid Build Coastguard Worker NameConstant: (type(None), bool), 586*cda5da8dSAndroid Build Coastguard Worker Ellipsis: (type(...),), 587*cda5da8dSAndroid Build Coastguard Worker} 588*cda5da8dSAndroid Build Coastguard Worker_const_types_not = { 589*cda5da8dSAndroid Build Coastguard Worker Num: (bool,), 590*cda5da8dSAndroid Build Coastguard Worker} 591*cda5da8dSAndroid Build Coastguard Worker 592*cda5da8dSAndroid Build Coastguard Worker_const_node_type_names = { 593*cda5da8dSAndroid Build Coastguard Worker bool: 'NameConstant', # should be before int 594*cda5da8dSAndroid Build Coastguard Worker type(None): 'NameConstant', 595*cda5da8dSAndroid Build Coastguard Worker int: 'Num', 596*cda5da8dSAndroid Build Coastguard Worker float: 'Num', 597*cda5da8dSAndroid Build Coastguard Worker complex: 'Num', 598*cda5da8dSAndroid Build Coastguard Worker str: 'Str', 599*cda5da8dSAndroid Build Coastguard Worker bytes: 'Bytes', 600*cda5da8dSAndroid Build Coastguard Worker type(...): 'Ellipsis', 601*cda5da8dSAndroid Build Coastguard Worker} 602*cda5da8dSAndroid Build Coastguard Worker 603*cda5da8dSAndroid Build Coastguard Workerclass slice(AST): 604*cda5da8dSAndroid Build Coastguard Worker """Deprecated AST node class.""" 605*cda5da8dSAndroid Build Coastguard Worker 606*cda5da8dSAndroid Build Coastguard Workerclass Index(slice): 607*cda5da8dSAndroid Build Coastguard Worker """Deprecated AST node class. Use the index value directly instead.""" 608*cda5da8dSAndroid Build Coastguard Worker def __new__(cls, value, **kwargs): 609*cda5da8dSAndroid Build Coastguard Worker return value 610*cda5da8dSAndroid Build Coastguard Worker 611*cda5da8dSAndroid Build Coastguard Workerclass ExtSlice(slice): 612*cda5da8dSAndroid Build Coastguard Worker """Deprecated AST node class. Use ast.Tuple instead.""" 613*cda5da8dSAndroid Build Coastguard Worker def __new__(cls, dims=(), **kwargs): 614*cda5da8dSAndroid Build Coastguard Worker return Tuple(list(dims), Load(), **kwargs) 615*cda5da8dSAndroid Build Coastguard Worker 616*cda5da8dSAndroid Build Coastguard Worker# If the ast module is loaded more than once, only add deprecated methods once 617*cda5da8dSAndroid Build Coastguard Workerif not hasattr(Tuple, 'dims'): 618*cda5da8dSAndroid Build Coastguard Worker # The following code is for backward compatibility. 619*cda5da8dSAndroid Build Coastguard Worker # It will be removed in future. 620*cda5da8dSAndroid Build Coastguard Worker 621*cda5da8dSAndroid Build Coastguard Worker def _dims_getter(self): 622*cda5da8dSAndroid Build Coastguard Worker """Deprecated. Use elts instead.""" 623*cda5da8dSAndroid Build Coastguard Worker return self.elts 624*cda5da8dSAndroid Build Coastguard Worker 625*cda5da8dSAndroid Build Coastguard Worker def _dims_setter(self, value): 626*cda5da8dSAndroid Build Coastguard Worker self.elts = value 627*cda5da8dSAndroid Build Coastguard Worker 628*cda5da8dSAndroid Build Coastguard Worker Tuple.dims = property(_dims_getter, _dims_setter) 629*cda5da8dSAndroid Build Coastguard Worker 630*cda5da8dSAndroid Build Coastguard Workerclass Suite(mod): 631*cda5da8dSAndroid Build Coastguard Worker """Deprecated AST node class. Unused in Python 3.""" 632*cda5da8dSAndroid Build Coastguard Worker 633*cda5da8dSAndroid Build Coastguard Workerclass AugLoad(expr_context): 634*cda5da8dSAndroid Build Coastguard Worker """Deprecated AST node class. Unused in Python 3.""" 635*cda5da8dSAndroid Build Coastguard Worker 636*cda5da8dSAndroid Build Coastguard Workerclass AugStore(expr_context): 637*cda5da8dSAndroid Build Coastguard Worker """Deprecated AST node class. Unused in Python 3.""" 638*cda5da8dSAndroid Build Coastguard Worker 639*cda5da8dSAndroid Build Coastguard Workerclass Param(expr_context): 640*cda5da8dSAndroid Build Coastguard Worker """Deprecated AST node class. Unused in Python 3.""" 641*cda5da8dSAndroid Build Coastguard Worker 642*cda5da8dSAndroid Build Coastguard Worker 643*cda5da8dSAndroid Build Coastguard Worker# Large float and imaginary literals get turned into infinities in the AST. 644*cda5da8dSAndroid Build Coastguard Worker# We unparse those infinities to INFSTR. 645*cda5da8dSAndroid Build Coastguard Worker_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1) 646*cda5da8dSAndroid Build Coastguard Worker 647*cda5da8dSAndroid Build Coastguard Worker@_simple_enum(IntEnum) 648*cda5da8dSAndroid Build Coastguard Workerclass _Precedence: 649*cda5da8dSAndroid Build Coastguard Worker """Precedence table that originated from python grammar.""" 650*cda5da8dSAndroid Build Coastguard Worker 651*cda5da8dSAndroid Build Coastguard Worker NAMED_EXPR = auto() # <target> := <expr1> 652*cda5da8dSAndroid Build Coastguard Worker TUPLE = auto() # <expr1>, <expr2> 653*cda5da8dSAndroid Build Coastguard Worker YIELD = auto() # 'yield', 'yield from' 654*cda5da8dSAndroid Build Coastguard Worker TEST = auto() # 'if'-'else', 'lambda' 655*cda5da8dSAndroid Build Coastguard Worker OR = auto() # 'or' 656*cda5da8dSAndroid Build Coastguard Worker AND = auto() # 'and' 657*cda5da8dSAndroid Build Coastguard Worker NOT = auto() # 'not' 658*cda5da8dSAndroid Build Coastguard Worker CMP = auto() # '<', '>', '==', '>=', '<=', '!=', 659*cda5da8dSAndroid Build Coastguard Worker # 'in', 'not in', 'is', 'is not' 660*cda5da8dSAndroid Build Coastguard Worker EXPR = auto() 661*cda5da8dSAndroid Build Coastguard Worker BOR = EXPR # '|' 662*cda5da8dSAndroid Build Coastguard Worker BXOR = auto() # '^' 663*cda5da8dSAndroid Build Coastguard Worker BAND = auto() # '&' 664*cda5da8dSAndroid Build Coastguard Worker SHIFT = auto() # '<<', '>>' 665*cda5da8dSAndroid Build Coastguard Worker ARITH = auto() # '+', '-' 666*cda5da8dSAndroid Build Coastguard Worker TERM = auto() # '*', '@', '/', '%', '//' 667*cda5da8dSAndroid Build Coastguard Worker FACTOR = auto() # unary '+', '-', '~' 668*cda5da8dSAndroid Build Coastguard Worker POWER = auto() # '**' 669*cda5da8dSAndroid Build Coastguard Worker AWAIT = auto() # 'await' 670*cda5da8dSAndroid Build Coastguard Worker ATOM = auto() 671*cda5da8dSAndroid Build Coastguard Worker 672*cda5da8dSAndroid Build Coastguard Worker def next(self): 673*cda5da8dSAndroid Build Coastguard Worker try: 674*cda5da8dSAndroid Build Coastguard Worker return self.__class__(self + 1) 675*cda5da8dSAndroid Build Coastguard Worker except ValueError: 676*cda5da8dSAndroid Build Coastguard Worker return self 677*cda5da8dSAndroid Build Coastguard Worker 678*cda5da8dSAndroid Build Coastguard Worker 679*cda5da8dSAndroid Build Coastguard Worker_SINGLE_QUOTES = ("'", '"') 680*cda5da8dSAndroid Build Coastguard Worker_MULTI_QUOTES = ('"""', "'''") 681*cda5da8dSAndroid Build Coastguard Worker_ALL_QUOTES = (*_SINGLE_QUOTES, *_MULTI_QUOTES) 682*cda5da8dSAndroid Build Coastguard Worker 683*cda5da8dSAndroid Build Coastguard Workerclass _Unparser(NodeVisitor): 684*cda5da8dSAndroid Build Coastguard Worker """Methods in this class recursively traverse an AST and 685*cda5da8dSAndroid Build Coastguard Worker output source code for the abstract syntax; original formatting 686*cda5da8dSAndroid Build Coastguard Worker is disregarded.""" 687*cda5da8dSAndroid Build Coastguard Worker 688*cda5da8dSAndroid Build Coastguard Worker def __init__(self, *, _avoid_backslashes=False): 689*cda5da8dSAndroid Build Coastguard Worker self._source = [] 690*cda5da8dSAndroid Build Coastguard Worker self._precedences = {} 691*cda5da8dSAndroid Build Coastguard Worker self._type_ignores = {} 692*cda5da8dSAndroid Build Coastguard Worker self._indent = 0 693*cda5da8dSAndroid Build Coastguard Worker self._avoid_backslashes = _avoid_backslashes 694*cda5da8dSAndroid Build Coastguard Worker self._in_try_star = False 695*cda5da8dSAndroid Build Coastguard Worker 696*cda5da8dSAndroid Build Coastguard Worker def interleave(self, inter, f, seq): 697*cda5da8dSAndroid Build Coastguard Worker """Call f on each item in seq, calling inter() in between.""" 698*cda5da8dSAndroid Build Coastguard Worker seq = iter(seq) 699*cda5da8dSAndroid Build Coastguard Worker try: 700*cda5da8dSAndroid Build Coastguard Worker f(next(seq)) 701*cda5da8dSAndroid Build Coastguard Worker except StopIteration: 702*cda5da8dSAndroid Build Coastguard Worker pass 703*cda5da8dSAndroid Build Coastguard Worker else: 704*cda5da8dSAndroid Build Coastguard Worker for x in seq: 705*cda5da8dSAndroid Build Coastguard Worker inter() 706*cda5da8dSAndroid Build Coastguard Worker f(x) 707*cda5da8dSAndroid Build Coastguard Worker 708*cda5da8dSAndroid Build Coastguard Worker def items_view(self, traverser, items): 709*cda5da8dSAndroid Build Coastguard Worker """Traverse and separate the given *items* with a comma and append it to 710*cda5da8dSAndroid Build Coastguard Worker the buffer. If *items* is a single item sequence, a trailing comma 711*cda5da8dSAndroid Build Coastguard Worker will be added.""" 712*cda5da8dSAndroid Build Coastguard Worker if len(items) == 1: 713*cda5da8dSAndroid Build Coastguard Worker traverser(items[0]) 714*cda5da8dSAndroid Build Coastguard Worker self.write(",") 715*cda5da8dSAndroid Build Coastguard Worker else: 716*cda5da8dSAndroid Build Coastguard Worker self.interleave(lambda: self.write(", "), traverser, items) 717*cda5da8dSAndroid Build Coastguard Worker 718*cda5da8dSAndroid Build Coastguard Worker def maybe_newline(self): 719*cda5da8dSAndroid Build Coastguard Worker """Adds a newline if it isn't the start of generated source""" 720*cda5da8dSAndroid Build Coastguard Worker if self._source: 721*cda5da8dSAndroid Build Coastguard Worker self.write("\n") 722*cda5da8dSAndroid Build Coastguard Worker 723*cda5da8dSAndroid Build Coastguard Worker def fill(self, text=""): 724*cda5da8dSAndroid Build Coastguard Worker """Indent a piece of text and append it, according to the current 725*cda5da8dSAndroid Build Coastguard Worker indentation level""" 726*cda5da8dSAndroid Build Coastguard Worker self.maybe_newline() 727*cda5da8dSAndroid Build Coastguard Worker self.write(" " * self._indent + text) 728*cda5da8dSAndroid Build Coastguard Worker 729*cda5da8dSAndroid Build Coastguard Worker def write(self, *text): 730*cda5da8dSAndroid Build Coastguard Worker """Add new source parts""" 731*cda5da8dSAndroid Build Coastguard Worker self._source.extend(text) 732*cda5da8dSAndroid Build Coastguard Worker 733*cda5da8dSAndroid Build Coastguard Worker @contextmanager 734*cda5da8dSAndroid Build Coastguard Worker def buffered(self, buffer = None): 735*cda5da8dSAndroid Build Coastguard Worker if buffer is None: 736*cda5da8dSAndroid Build Coastguard Worker buffer = [] 737*cda5da8dSAndroid Build Coastguard Worker 738*cda5da8dSAndroid Build Coastguard Worker original_source = self._source 739*cda5da8dSAndroid Build Coastguard Worker self._source = buffer 740*cda5da8dSAndroid Build Coastguard Worker yield buffer 741*cda5da8dSAndroid Build Coastguard Worker self._source = original_source 742*cda5da8dSAndroid Build Coastguard Worker 743*cda5da8dSAndroid Build Coastguard Worker @contextmanager 744*cda5da8dSAndroid Build Coastguard Worker def block(self, *, extra = None): 745*cda5da8dSAndroid Build Coastguard Worker """A context manager for preparing the source for blocks. It adds 746*cda5da8dSAndroid Build Coastguard Worker the character':', increases the indentation on enter and decreases 747*cda5da8dSAndroid Build Coastguard Worker the indentation on exit. If *extra* is given, it will be directly 748*cda5da8dSAndroid Build Coastguard Worker appended after the colon character. 749*cda5da8dSAndroid Build Coastguard Worker """ 750*cda5da8dSAndroid Build Coastguard Worker self.write(":") 751*cda5da8dSAndroid Build Coastguard Worker if extra: 752*cda5da8dSAndroid Build Coastguard Worker self.write(extra) 753*cda5da8dSAndroid Build Coastguard Worker self._indent += 1 754*cda5da8dSAndroid Build Coastguard Worker yield 755*cda5da8dSAndroid Build Coastguard Worker self._indent -= 1 756*cda5da8dSAndroid Build Coastguard Worker 757*cda5da8dSAndroid Build Coastguard Worker @contextmanager 758*cda5da8dSAndroid Build Coastguard Worker def delimit(self, start, end): 759*cda5da8dSAndroid Build Coastguard Worker """A context manager for preparing the source for expressions. It adds 760*cda5da8dSAndroid Build Coastguard Worker *start* to the buffer and enters, after exit it adds *end*.""" 761*cda5da8dSAndroid Build Coastguard Worker 762*cda5da8dSAndroid Build Coastguard Worker self.write(start) 763*cda5da8dSAndroid Build Coastguard Worker yield 764*cda5da8dSAndroid Build Coastguard Worker self.write(end) 765*cda5da8dSAndroid Build Coastguard Worker 766*cda5da8dSAndroid Build Coastguard Worker def delimit_if(self, start, end, condition): 767*cda5da8dSAndroid Build Coastguard Worker if condition: 768*cda5da8dSAndroid Build Coastguard Worker return self.delimit(start, end) 769*cda5da8dSAndroid Build Coastguard Worker else: 770*cda5da8dSAndroid Build Coastguard Worker return nullcontext() 771*cda5da8dSAndroid Build Coastguard Worker 772*cda5da8dSAndroid Build Coastguard Worker def require_parens(self, precedence, node): 773*cda5da8dSAndroid Build Coastguard Worker """Shortcut to adding precedence related parens""" 774*cda5da8dSAndroid Build Coastguard Worker return self.delimit_if("(", ")", self.get_precedence(node) > precedence) 775*cda5da8dSAndroid Build Coastguard Worker 776*cda5da8dSAndroid Build Coastguard Worker def get_precedence(self, node): 777*cda5da8dSAndroid Build Coastguard Worker return self._precedences.get(node, _Precedence.TEST) 778*cda5da8dSAndroid Build Coastguard Worker 779*cda5da8dSAndroid Build Coastguard Worker def set_precedence(self, precedence, *nodes): 780*cda5da8dSAndroid Build Coastguard Worker for node in nodes: 781*cda5da8dSAndroid Build Coastguard Worker self._precedences[node] = precedence 782*cda5da8dSAndroid Build Coastguard Worker 783*cda5da8dSAndroid Build Coastguard Worker def get_raw_docstring(self, node): 784*cda5da8dSAndroid Build Coastguard Worker """If a docstring node is found in the body of the *node* parameter, 785*cda5da8dSAndroid Build Coastguard Worker return that docstring node, None otherwise. 786*cda5da8dSAndroid Build Coastguard Worker 787*cda5da8dSAndroid Build Coastguard Worker Logic mirrored from ``_PyAST_GetDocString``.""" 788*cda5da8dSAndroid Build Coastguard Worker if not isinstance( 789*cda5da8dSAndroid Build Coastguard Worker node, (AsyncFunctionDef, FunctionDef, ClassDef, Module) 790*cda5da8dSAndroid Build Coastguard Worker ) or len(node.body) < 1: 791*cda5da8dSAndroid Build Coastguard Worker return None 792*cda5da8dSAndroid Build Coastguard Worker node = node.body[0] 793*cda5da8dSAndroid Build Coastguard Worker if not isinstance(node, Expr): 794*cda5da8dSAndroid Build Coastguard Worker return None 795*cda5da8dSAndroid Build Coastguard Worker node = node.value 796*cda5da8dSAndroid Build Coastguard Worker if isinstance(node, Constant) and isinstance(node.value, str): 797*cda5da8dSAndroid Build Coastguard Worker return node 798*cda5da8dSAndroid Build Coastguard Worker 799*cda5da8dSAndroid Build Coastguard Worker def get_type_comment(self, node): 800*cda5da8dSAndroid Build Coastguard Worker comment = self._type_ignores.get(node.lineno) or node.type_comment 801*cda5da8dSAndroid Build Coastguard Worker if comment is not None: 802*cda5da8dSAndroid Build Coastguard Worker return f" # type: {comment}" 803*cda5da8dSAndroid Build Coastguard Worker 804*cda5da8dSAndroid Build Coastguard Worker def traverse(self, node): 805*cda5da8dSAndroid Build Coastguard Worker if isinstance(node, list): 806*cda5da8dSAndroid Build Coastguard Worker for item in node: 807*cda5da8dSAndroid Build Coastguard Worker self.traverse(item) 808*cda5da8dSAndroid Build Coastguard Worker else: 809*cda5da8dSAndroid Build Coastguard Worker super().visit(node) 810*cda5da8dSAndroid Build Coastguard Worker 811*cda5da8dSAndroid Build Coastguard Worker # Note: as visit() resets the output text, do NOT rely on 812*cda5da8dSAndroid Build Coastguard Worker # NodeVisitor.generic_visit to handle any nodes (as it calls back in to 813*cda5da8dSAndroid Build Coastguard Worker # the subclass visit() method, which resets self._source to an empty list) 814*cda5da8dSAndroid Build Coastguard Worker def visit(self, node): 815*cda5da8dSAndroid Build Coastguard Worker """Outputs a source code string that, if converted back to an ast 816*cda5da8dSAndroid Build Coastguard Worker (using ast.parse) will generate an AST equivalent to *node*""" 817*cda5da8dSAndroid Build Coastguard Worker self._source = [] 818*cda5da8dSAndroid Build Coastguard Worker self.traverse(node) 819*cda5da8dSAndroid Build Coastguard Worker return "".join(self._source) 820*cda5da8dSAndroid Build Coastguard Worker 821*cda5da8dSAndroid Build Coastguard Worker def _write_docstring_and_traverse_body(self, node): 822*cda5da8dSAndroid Build Coastguard Worker if (docstring := self.get_raw_docstring(node)): 823*cda5da8dSAndroid Build Coastguard Worker self._write_docstring(docstring) 824*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.body[1:]) 825*cda5da8dSAndroid Build Coastguard Worker else: 826*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.body) 827*cda5da8dSAndroid Build Coastguard Worker 828*cda5da8dSAndroid Build Coastguard Worker def visit_Module(self, node): 829*cda5da8dSAndroid Build Coastguard Worker self._type_ignores = { 830*cda5da8dSAndroid Build Coastguard Worker ignore.lineno: f"ignore{ignore.tag}" 831*cda5da8dSAndroid Build Coastguard Worker for ignore in node.type_ignores 832*cda5da8dSAndroid Build Coastguard Worker } 833*cda5da8dSAndroid Build Coastguard Worker self._write_docstring_and_traverse_body(node) 834*cda5da8dSAndroid Build Coastguard Worker self._type_ignores.clear() 835*cda5da8dSAndroid Build Coastguard Worker 836*cda5da8dSAndroid Build Coastguard Worker def visit_FunctionType(self, node): 837*cda5da8dSAndroid Build Coastguard Worker with self.delimit("(", ")"): 838*cda5da8dSAndroid Build Coastguard Worker self.interleave( 839*cda5da8dSAndroid Build Coastguard Worker lambda: self.write(", "), self.traverse, node.argtypes 840*cda5da8dSAndroid Build Coastguard Worker ) 841*cda5da8dSAndroid Build Coastguard Worker 842*cda5da8dSAndroid Build Coastguard Worker self.write(" -> ") 843*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.returns) 844*cda5da8dSAndroid Build Coastguard Worker 845*cda5da8dSAndroid Build Coastguard Worker def visit_Expr(self, node): 846*cda5da8dSAndroid Build Coastguard Worker self.fill() 847*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.YIELD, node.value) 848*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 849*cda5da8dSAndroid Build Coastguard Worker 850*cda5da8dSAndroid Build Coastguard Worker def visit_NamedExpr(self, node): 851*cda5da8dSAndroid Build Coastguard Worker with self.require_parens(_Precedence.NAMED_EXPR, node): 852*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.ATOM, node.target, node.value) 853*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.target) 854*cda5da8dSAndroid Build Coastguard Worker self.write(" := ") 855*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 856*cda5da8dSAndroid Build Coastguard Worker 857*cda5da8dSAndroid Build Coastguard Worker def visit_Import(self, node): 858*cda5da8dSAndroid Build Coastguard Worker self.fill("import ") 859*cda5da8dSAndroid Build Coastguard Worker self.interleave(lambda: self.write(", "), self.traverse, node.names) 860*cda5da8dSAndroid Build Coastguard Worker 861*cda5da8dSAndroid Build Coastguard Worker def visit_ImportFrom(self, node): 862*cda5da8dSAndroid Build Coastguard Worker self.fill("from ") 863*cda5da8dSAndroid Build Coastguard Worker self.write("." * (node.level or 0)) 864*cda5da8dSAndroid Build Coastguard Worker if node.module: 865*cda5da8dSAndroid Build Coastguard Worker self.write(node.module) 866*cda5da8dSAndroid Build Coastguard Worker self.write(" import ") 867*cda5da8dSAndroid Build Coastguard Worker self.interleave(lambda: self.write(", "), self.traverse, node.names) 868*cda5da8dSAndroid Build Coastguard Worker 869*cda5da8dSAndroid Build Coastguard Worker def visit_Assign(self, node): 870*cda5da8dSAndroid Build Coastguard Worker self.fill() 871*cda5da8dSAndroid Build Coastguard Worker for target in node.targets: 872*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.TUPLE, target) 873*cda5da8dSAndroid Build Coastguard Worker self.traverse(target) 874*cda5da8dSAndroid Build Coastguard Worker self.write(" = ") 875*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 876*cda5da8dSAndroid Build Coastguard Worker if type_comment := self.get_type_comment(node): 877*cda5da8dSAndroid Build Coastguard Worker self.write(type_comment) 878*cda5da8dSAndroid Build Coastguard Worker 879*cda5da8dSAndroid Build Coastguard Worker def visit_AugAssign(self, node): 880*cda5da8dSAndroid Build Coastguard Worker self.fill() 881*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.target) 882*cda5da8dSAndroid Build Coastguard Worker self.write(" " + self.binop[node.op.__class__.__name__] + "= ") 883*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 884*cda5da8dSAndroid Build Coastguard Worker 885*cda5da8dSAndroid Build Coastguard Worker def visit_AnnAssign(self, node): 886*cda5da8dSAndroid Build Coastguard Worker self.fill() 887*cda5da8dSAndroid Build Coastguard Worker with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)): 888*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.target) 889*cda5da8dSAndroid Build Coastguard Worker self.write(": ") 890*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.annotation) 891*cda5da8dSAndroid Build Coastguard Worker if node.value: 892*cda5da8dSAndroid Build Coastguard Worker self.write(" = ") 893*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 894*cda5da8dSAndroid Build Coastguard Worker 895*cda5da8dSAndroid Build Coastguard Worker def visit_Return(self, node): 896*cda5da8dSAndroid Build Coastguard Worker self.fill("return") 897*cda5da8dSAndroid Build Coastguard Worker if node.value: 898*cda5da8dSAndroid Build Coastguard Worker self.write(" ") 899*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 900*cda5da8dSAndroid Build Coastguard Worker 901*cda5da8dSAndroid Build Coastguard Worker def visit_Pass(self, node): 902*cda5da8dSAndroid Build Coastguard Worker self.fill("pass") 903*cda5da8dSAndroid Build Coastguard Worker 904*cda5da8dSAndroid Build Coastguard Worker def visit_Break(self, node): 905*cda5da8dSAndroid Build Coastguard Worker self.fill("break") 906*cda5da8dSAndroid Build Coastguard Worker 907*cda5da8dSAndroid Build Coastguard Worker def visit_Continue(self, node): 908*cda5da8dSAndroid Build Coastguard Worker self.fill("continue") 909*cda5da8dSAndroid Build Coastguard Worker 910*cda5da8dSAndroid Build Coastguard Worker def visit_Delete(self, node): 911*cda5da8dSAndroid Build Coastguard Worker self.fill("del ") 912*cda5da8dSAndroid Build Coastguard Worker self.interleave(lambda: self.write(", "), self.traverse, node.targets) 913*cda5da8dSAndroid Build Coastguard Worker 914*cda5da8dSAndroid Build Coastguard Worker def visit_Assert(self, node): 915*cda5da8dSAndroid Build Coastguard Worker self.fill("assert ") 916*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.test) 917*cda5da8dSAndroid Build Coastguard Worker if node.msg: 918*cda5da8dSAndroid Build Coastguard Worker self.write(", ") 919*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.msg) 920*cda5da8dSAndroid Build Coastguard Worker 921*cda5da8dSAndroid Build Coastguard Worker def visit_Global(self, node): 922*cda5da8dSAndroid Build Coastguard Worker self.fill("global ") 923*cda5da8dSAndroid Build Coastguard Worker self.interleave(lambda: self.write(", "), self.write, node.names) 924*cda5da8dSAndroid Build Coastguard Worker 925*cda5da8dSAndroid Build Coastguard Worker def visit_Nonlocal(self, node): 926*cda5da8dSAndroid Build Coastguard Worker self.fill("nonlocal ") 927*cda5da8dSAndroid Build Coastguard Worker self.interleave(lambda: self.write(", "), self.write, node.names) 928*cda5da8dSAndroid Build Coastguard Worker 929*cda5da8dSAndroid Build Coastguard Worker def visit_Await(self, node): 930*cda5da8dSAndroid Build Coastguard Worker with self.require_parens(_Precedence.AWAIT, node): 931*cda5da8dSAndroid Build Coastguard Worker self.write("await") 932*cda5da8dSAndroid Build Coastguard Worker if node.value: 933*cda5da8dSAndroid Build Coastguard Worker self.write(" ") 934*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.ATOM, node.value) 935*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 936*cda5da8dSAndroid Build Coastguard Worker 937*cda5da8dSAndroid Build Coastguard Worker def visit_Yield(self, node): 938*cda5da8dSAndroid Build Coastguard Worker with self.require_parens(_Precedence.YIELD, node): 939*cda5da8dSAndroid Build Coastguard Worker self.write("yield") 940*cda5da8dSAndroid Build Coastguard Worker if node.value: 941*cda5da8dSAndroid Build Coastguard Worker self.write(" ") 942*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.ATOM, node.value) 943*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 944*cda5da8dSAndroid Build Coastguard Worker 945*cda5da8dSAndroid Build Coastguard Worker def visit_YieldFrom(self, node): 946*cda5da8dSAndroid Build Coastguard Worker with self.require_parens(_Precedence.YIELD, node): 947*cda5da8dSAndroid Build Coastguard Worker self.write("yield from ") 948*cda5da8dSAndroid Build Coastguard Worker if not node.value: 949*cda5da8dSAndroid Build Coastguard Worker raise ValueError("Node can't be used without a value attribute.") 950*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.ATOM, node.value) 951*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 952*cda5da8dSAndroid Build Coastguard Worker 953*cda5da8dSAndroid Build Coastguard Worker def visit_Raise(self, node): 954*cda5da8dSAndroid Build Coastguard Worker self.fill("raise") 955*cda5da8dSAndroid Build Coastguard Worker if not node.exc: 956*cda5da8dSAndroid Build Coastguard Worker if node.cause: 957*cda5da8dSAndroid Build Coastguard Worker raise ValueError(f"Node can't use cause without an exception.") 958*cda5da8dSAndroid Build Coastguard Worker return 959*cda5da8dSAndroid Build Coastguard Worker self.write(" ") 960*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.exc) 961*cda5da8dSAndroid Build Coastguard Worker if node.cause: 962*cda5da8dSAndroid Build Coastguard Worker self.write(" from ") 963*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.cause) 964*cda5da8dSAndroid Build Coastguard Worker 965*cda5da8dSAndroid Build Coastguard Worker def do_visit_try(self, node): 966*cda5da8dSAndroid Build Coastguard Worker self.fill("try") 967*cda5da8dSAndroid Build Coastguard Worker with self.block(): 968*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.body) 969*cda5da8dSAndroid Build Coastguard Worker for ex in node.handlers: 970*cda5da8dSAndroid Build Coastguard Worker self.traverse(ex) 971*cda5da8dSAndroid Build Coastguard Worker if node.orelse: 972*cda5da8dSAndroid Build Coastguard Worker self.fill("else") 973*cda5da8dSAndroid Build Coastguard Worker with self.block(): 974*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.orelse) 975*cda5da8dSAndroid Build Coastguard Worker if node.finalbody: 976*cda5da8dSAndroid Build Coastguard Worker self.fill("finally") 977*cda5da8dSAndroid Build Coastguard Worker with self.block(): 978*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.finalbody) 979*cda5da8dSAndroid Build Coastguard Worker 980*cda5da8dSAndroid Build Coastguard Worker def visit_Try(self, node): 981*cda5da8dSAndroid Build Coastguard Worker prev_in_try_star = self._in_try_star 982*cda5da8dSAndroid Build Coastguard Worker try: 983*cda5da8dSAndroid Build Coastguard Worker self._in_try_star = False 984*cda5da8dSAndroid Build Coastguard Worker self.do_visit_try(node) 985*cda5da8dSAndroid Build Coastguard Worker finally: 986*cda5da8dSAndroid Build Coastguard Worker self._in_try_star = prev_in_try_star 987*cda5da8dSAndroid Build Coastguard Worker 988*cda5da8dSAndroid Build Coastguard Worker def visit_TryStar(self, node): 989*cda5da8dSAndroid Build Coastguard Worker prev_in_try_star = self._in_try_star 990*cda5da8dSAndroid Build Coastguard Worker try: 991*cda5da8dSAndroid Build Coastguard Worker self._in_try_star = True 992*cda5da8dSAndroid Build Coastguard Worker self.do_visit_try(node) 993*cda5da8dSAndroid Build Coastguard Worker finally: 994*cda5da8dSAndroid Build Coastguard Worker self._in_try_star = prev_in_try_star 995*cda5da8dSAndroid Build Coastguard Worker 996*cda5da8dSAndroid Build Coastguard Worker def visit_ExceptHandler(self, node): 997*cda5da8dSAndroid Build Coastguard Worker self.fill("except*" if self._in_try_star else "except") 998*cda5da8dSAndroid Build Coastguard Worker if node.type: 999*cda5da8dSAndroid Build Coastguard Worker self.write(" ") 1000*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.type) 1001*cda5da8dSAndroid Build Coastguard Worker if node.name: 1002*cda5da8dSAndroid Build Coastguard Worker self.write(" as ") 1003*cda5da8dSAndroid Build Coastguard Worker self.write(node.name) 1004*cda5da8dSAndroid Build Coastguard Worker with self.block(): 1005*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.body) 1006*cda5da8dSAndroid Build Coastguard Worker 1007*cda5da8dSAndroid Build Coastguard Worker def visit_ClassDef(self, node): 1008*cda5da8dSAndroid Build Coastguard Worker self.maybe_newline() 1009*cda5da8dSAndroid Build Coastguard Worker for deco in node.decorator_list: 1010*cda5da8dSAndroid Build Coastguard Worker self.fill("@") 1011*cda5da8dSAndroid Build Coastguard Worker self.traverse(deco) 1012*cda5da8dSAndroid Build Coastguard Worker self.fill("class " + node.name) 1013*cda5da8dSAndroid Build Coastguard Worker with self.delimit_if("(", ")", condition = node.bases or node.keywords): 1014*cda5da8dSAndroid Build Coastguard Worker comma = False 1015*cda5da8dSAndroid Build Coastguard Worker for e in node.bases: 1016*cda5da8dSAndroid Build Coastguard Worker if comma: 1017*cda5da8dSAndroid Build Coastguard Worker self.write(", ") 1018*cda5da8dSAndroid Build Coastguard Worker else: 1019*cda5da8dSAndroid Build Coastguard Worker comma = True 1020*cda5da8dSAndroid Build Coastguard Worker self.traverse(e) 1021*cda5da8dSAndroid Build Coastguard Worker for e in node.keywords: 1022*cda5da8dSAndroid Build Coastguard Worker if comma: 1023*cda5da8dSAndroid Build Coastguard Worker self.write(", ") 1024*cda5da8dSAndroid Build Coastguard Worker else: 1025*cda5da8dSAndroid Build Coastguard Worker comma = True 1026*cda5da8dSAndroid Build Coastguard Worker self.traverse(e) 1027*cda5da8dSAndroid Build Coastguard Worker 1028*cda5da8dSAndroid Build Coastguard Worker with self.block(): 1029*cda5da8dSAndroid Build Coastguard Worker self._write_docstring_and_traverse_body(node) 1030*cda5da8dSAndroid Build Coastguard Worker 1031*cda5da8dSAndroid Build Coastguard Worker def visit_FunctionDef(self, node): 1032*cda5da8dSAndroid Build Coastguard Worker self._function_helper(node, "def") 1033*cda5da8dSAndroid Build Coastguard Worker 1034*cda5da8dSAndroid Build Coastguard Worker def visit_AsyncFunctionDef(self, node): 1035*cda5da8dSAndroid Build Coastguard Worker self._function_helper(node, "async def") 1036*cda5da8dSAndroid Build Coastguard Worker 1037*cda5da8dSAndroid Build Coastguard Worker def _function_helper(self, node, fill_suffix): 1038*cda5da8dSAndroid Build Coastguard Worker self.maybe_newline() 1039*cda5da8dSAndroid Build Coastguard Worker for deco in node.decorator_list: 1040*cda5da8dSAndroid Build Coastguard Worker self.fill("@") 1041*cda5da8dSAndroid Build Coastguard Worker self.traverse(deco) 1042*cda5da8dSAndroid Build Coastguard Worker def_str = fill_suffix + " " + node.name 1043*cda5da8dSAndroid Build Coastguard Worker self.fill(def_str) 1044*cda5da8dSAndroid Build Coastguard Worker with self.delimit("(", ")"): 1045*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.args) 1046*cda5da8dSAndroid Build Coastguard Worker if node.returns: 1047*cda5da8dSAndroid Build Coastguard Worker self.write(" -> ") 1048*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.returns) 1049*cda5da8dSAndroid Build Coastguard Worker with self.block(extra=self.get_type_comment(node)): 1050*cda5da8dSAndroid Build Coastguard Worker self._write_docstring_and_traverse_body(node) 1051*cda5da8dSAndroid Build Coastguard Worker 1052*cda5da8dSAndroid Build Coastguard Worker def visit_For(self, node): 1053*cda5da8dSAndroid Build Coastguard Worker self._for_helper("for ", node) 1054*cda5da8dSAndroid Build Coastguard Worker 1055*cda5da8dSAndroid Build Coastguard Worker def visit_AsyncFor(self, node): 1056*cda5da8dSAndroid Build Coastguard Worker self._for_helper("async for ", node) 1057*cda5da8dSAndroid Build Coastguard Worker 1058*cda5da8dSAndroid Build Coastguard Worker def _for_helper(self, fill, node): 1059*cda5da8dSAndroid Build Coastguard Worker self.fill(fill) 1060*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.TUPLE, node.target) 1061*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.target) 1062*cda5da8dSAndroid Build Coastguard Worker self.write(" in ") 1063*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.iter) 1064*cda5da8dSAndroid Build Coastguard Worker with self.block(extra=self.get_type_comment(node)): 1065*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.body) 1066*cda5da8dSAndroid Build Coastguard Worker if node.orelse: 1067*cda5da8dSAndroid Build Coastguard Worker self.fill("else") 1068*cda5da8dSAndroid Build Coastguard Worker with self.block(): 1069*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.orelse) 1070*cda5da8dSAndroid Build Coastguard Worker 1071*cda5da8dSAndroid Build Coastguard Worker def visit_If(self, node): 1072*cda5da8dSAndroid Build Coastguard Worker self.fill("if ") 1073*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.test) 1074*cda5da8dSAndroid Build Coastguard Worker with self.block(): 1075*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.body) 1076*cda5da8dSAndroid Build Coastguard Worker # collapse nested ifs into equivalent elifs. 1077*cda5da8dSAndroid Build Coastguard Worker while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If): 1078*cda5da8dSAndroid Build Coastguard Worker node = node.orelse[0] 1079*cda5da8dSAndroid Build Coastguard Worker self.fill("elif ") 1080*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.test) 1081*cda5da8dSAndroid Build Coastguard Worker with self.block(): 1082*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.body) 1083*cda5da8dSAndroid Build Coastguard Worker # final else 1084*cda5da8dSAndroid Build Coastguard Worker if node.orelse: 1085*cda5da8dSAndroid Build Coastguard Worker self.fill("else") 1086*cda5da8dSAndroid Build Coastguard Worker with self.block(): 1087*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.orelse) 1088*cda5da8dSAndroid Build Coastguard Worker 1089*cda5da8dSAndroid Build Coastguard Worker def visit_While(self, node): 1090*cda5da8dSAndroid Build Coastguard Worker self.fill("while ") 1091*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.test) 1092*cda5da8dSAndroid Build Coastguard Worker with self.block(): 1093*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.body) 1094*cda5da8dSAndroid Build Coastguard Worker if node.orelse: 1095*cda5da8dSAndroid Build Coastguard Worker self.fill("else") 1096*cda5da8dSAndroid Build Coastguard Worker with self.block(): 1097*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.orelse) 1098*cda5da8dSAndroid Build Coastguard Worker 1099*cda5da8dSAndroid Build Coastguard Worker def visit_With(self, node): 1100*cda5da8dSAndroid Build Coastguard Worker self.fill("with ") 1101*cda5da8dSAndroid Build Coastguard Worker self.interleave(lambda: self.write(", "), self.traverse, node.items) 1102*cda5da8dSAndroid Build Coastguard Worker with self.block(extra=self.get_type_comment(node)): 1103*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.body) 1104*cda5da8dSAndroid Build Coastguard Worker 1105*cda5da8dSAndroid Build Coastguard Worker def visit_AsyncWith(self, node): 1106*cda5da8dSAndroid Build Coastguard Worker self.fill("async with ") 1107*cda5da8dSAndroid Build Coastguard Worker self.interleave(lambda: self.write(", "), self.traverse, node.items) 1108*cda5da8dSAndroid Build Coastguard Worker with self.block(extra=self.get_type_comment(node)): 1109*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.body) 1110*cda5da8dSAndroid Build Coastguard Worker 1111*cda5da8dSAndroid Build Coastguard Worker def _str_literal_helper( 1112*cda5da8dSAndroid Build Coastguard Worker self, string, *, quote_types=_ALL_QUOTES, escape_special_whitespace=False 1113*cda5da8dSAndroid Build Coastguard Worker ): 1114*cda5da8dSAndroid Build Coastguard Worker """Helper for writing string literals, minimizing escapes. 1115*cda5da8dSAndroid Build Coastguard Worker Returns the tuple (string literal to write, possible quote types). 1116*cda5da8dSAndroid Build Coastguard Worker """ 1117*cda5da8dSAndroid Build Coastguard Worker def escape_char(c): 1118*cda5da8dSAndroid Build Coastguard Worker # \n and \t are non-printable, but we only escape them if 1119*cda5da8dSAndroid Build Coastguard Worker # escape_special_whitespace is True 1120*cda5da8dSAndroid Build Coastguard Worker if not escape_special_whitespace and c in "\n\t": 1121*cda5da8dSAndroid Build Coastguard Worker return c 1122*cda5da8dSAndroid Build Coastguard Worker # Always escape backslashes and other non-printable characters 1123*cda5da8dSAndroid Build Coastguard Worker if c == "\\" or not c.isprintable(): 1124*cda5da8dSAndroid Build Coastguard Worker return c.encode("unicode_escape").decode("ascii") 1125*cda5da8dSAndroid Build Coastguard Worker return c 1126*cda5da8dSAndroid Build Coastguard Worker 1127*cda5da8dSAndroid Build Coastguard Worker escaped_string = "".join(map(escape_char, string)) 1128*cda5da8dSAndroid Build Coastguard Worker possible_quotes = quote_types 1129*cda5da8dSAndroid Build Coastguard Worker if "\n" in escaped_string: 1130*cda5da8dSAndroid Build Coastguard Worker possible_quotes = [q for q in possible_quotes if q in _MULTI_QUOTES] 1131*cda5da8dSAndroid Build Coastguard Worker possible_quotes = [q for q in possible_quotes if q not in escaped_string] 1132*cda5da8dSAndroid Build Coastguard Worker if not possible_quotes: 1133*cda5da8dSAndroid Build Coastguard Worker # If there aren't any possible_quotes, fallback to using repr 1134*cda5da8dSAndroid Build Coastguard Worker # on the original string. Try to use a quote from quote_types, 1135*cda5da8dSAndroid Build Coastguard Worker # e.g., so that we use triple quotes for docstrings. 1136*cda5da8dSAndroid Build Coastguard Worker string = repr(string) 1137*cda5da8dSAndroid Build Coastguard Worker quote = next((q for q in quote_types if string[0] in q), string[0]) 1138*cda5da8dSAndroid Build Coastguard Worker return string[1:-1], [quote] 1139*cda5da8dSAndroid Build Coastguard Worker if escaped_string: 1140*cda5da8dSAndroid Build Coastguard Worker # Sort so that we prefer '''"''' over """\"""" 1141*cda5da8dSAndroid Build Coastguard Worker possible_quotes.sort(key=lambda q: q[0] == escaped_string[-1]) 1142*cda5da8dSAndroid Build Coastguard Worker # If we're using triple quotes and we'd need to escape a final 1143*cda5da8dSAndroid Build Coastguard Worker # quote, escape it 1144*cda5da8dSAndroid Build Coastguard Worker if possible_quotes[0][0] == escaped_string[-1]: 1145*cda5da8dSAndroid Build Coastguard Worker assert len(possible_quotes[0]) == 3 1146*cda5da8dSAndroid Build Coastguard Worker escaped_string = escaped_string[:-1] + "\\" + escaped_string[-1] 1147*cda5da8dSAndroid Build Coastguard Worker return escaped_string, possible_quotes 1148*cda5da8dSAndroid Build Coastguard Worker 1149*cda5da8dSAndroid Build Coastguard Worker def _write_str_avoiding_backslashes(self, string, *, quote_types=_ALL_QUOTES): 1150*cda5da8dSAndroid Build Coastguard Worker """Write string literal value with a best effort attempt to avoid backslashes.""" 1151*cda5da8dSAndroid Build Coastguard Worker string, quote_types = self._str_literal_helper(string, quote_types=quote_types) 1152*cda5da8dSAndroid Build Coastguard Worker quote_type = quote_types[0] 1153*cda5da8dSAndroid Build Coastguard Worker self.write(f"{quote_type}{string}{quote_type}") 1154*cda5da8dSAndroid Build Coastguard Worker 1155*cda5da8dSAndroid Build Coastguard Worker def visit_JoinedStr(self, node): 1156*cda5da8dSAndroid Build Coastguard Worker self.write("f") 1157*cda5da8dSAndroid Build Coastguard Worker if self._avoid_backslashes: 1158*cda5da8dSAndroid Build Coastguard Worker with self.buffered() as buffer: 1159*cda5da8dSAndroid Build Coastguard Worker self._write_fstring_inner(node) 1160*cda5da8dSAndroid Build Coastguard Worker return self._write_str_avoiding_backslashes("".join(buffer)) 1161*cda5da8dSAndroid Build Coastguard Worker 1162*cda5da8dSAndroid Build Coastguard Worker # If we don't need to avoid backslashes globally (i.e., we only need 1163*cda5da8dSAndroid Build Coastguard Worker # to avoid them inside FormattedValues), it's cosmetically preferred 1164*cda5da8dSAndroid Build Coastguard Worker # to use escaped whitespace. That is, it's preferred to use backslashes 1165*cda5da8dSAndroid Build Coastguard Worker # for cases like: f"{x}\n". To accomplish this, we keep track of what 1166*cda5da8dSAndroid Build Coastguard Worker # in our buffer corresponds to FormattedValues and what corresponds to 1167*cda5da8dSAndroid Build Coastguard Worker # Constant parts of the f-string, and allow escapes accordingly. 1168*cda5da8dSAndroid Build Coastguard Worker fstring_parts = [] 1169*cda5da8dSAndroid Build Coastguard Worker for value in node.values: 1170*cda5da8dSAndroid Build Coastguard Worker with self.buffered() as buffer: 1171*cda5da8dSAndroid Build Coastguard Worker self._write_fstring_inner(value) 1172*cda5da8dSAndroid Build Coastguard Worker fstring_parts.append( 1173*cda5da8dSAndroid Build Coastguard Worker ("".join(buffer), isinstance(value, Constant)) 1174*cda5da8dSAndroid Build Coastguard Worker ) 1175*cda5da8dSAndroid Build Coastguard Worker 1176*cda5da8dSAndroid Build Coastguard Worker new_fstring_parts = [] 1177*cda5da8dSAndroid Build Coastguard Worker quote_types = list(_ALL_QUOTES) 1178*cda5da8dSAndroid Build Coastguard Worker for value, is_constant in fstring_parts: 1179*cda5da8dSAndroid Build Coastguard Worker value, quote_types = self._str_literal_helper( 1180*cda5da8dSAndroid Build Coastguard Worker value, 1181*cda5da8dSAndroid Build Coastguard Worker quote_types=quote_types, 1182*cda5da8dSAndroid Build Coastguard Worker escape_special_whitespace=is_constant, 1183*cda5da8dSAndroid Build Coastguard Worker ) 1184*cda5da8dSAndroid Build Coastguard Worker new_fstring_parts.append(value) 1185*cda5da8dSAndroid Build Coastguard Worker 1186*cda5da8dSAndroid Build Coastguard Worker value = "".join(new_fstring_parts) 1187*cda5da8dSAndroid Build Coastguard Worker quote_type = quote_types[0] 1188*cda5da8dSAndroid Build Coastguard Worker self.write(f"{quote_type}{value}{quote_type}") 1189*cda5da8dSAndroid Build Coastguard Worker 1190*cda5da8dSAndroid Build Coastguard Worker def _write_fstring_inner(self, node): 1191*cda5da8dSAndroid Build Coastguard Worker if isinstance(node, JoinedStr): 1192*cda5da8dSAndroid Build Coastguard Worker # for both the f-string itself, and format_spec 1193*cda5da8dSAndroid Build Coastguard Worker for value in node.values: 1194*cda5da8dSAndroid Build Coastguard Worker self._write_fstring_inner(value) 1195*cda5da8dSAndroid Build Coastguard Worker elif isinstance(node, Constant) and isinstance(node.value, str): 1196*cda5da8dSAndroid Build Coastguard Worker value = node.value.replace("{", "{{").replace("}", "}}") 1197*cda5da8dSAndroid Build Coastguard Worker self.write(value) 1198*cda5da8dSAndroid Build Coastguard Worker elif isinstance(node, FormattedValue): 1199*cda5da8dSAndroid Build Coastguard Worker self.visit_FormattedValue(node) 1200*cda5da8dSAndroid Build Coastguard Worker else: 1201*cda5da8dSAndroid Build Coastguard Worker raise ValueError(f"Unexpected node inside JoinedStr, {node!r}") 1202*cda5da8dSAndroid Build Coastguard Worker 1203*cda5da8dSAndroid Build Coastguard Worker def visit_FormattedValue(self, node): 1204*cda5da8dSAndroid Build Coastguard Worker def unparse_inner(inner): 1205*cda5da8dSAndroid Build Coastguard Worker unparser = type(self)(_avoid_backslashes=True) 1206*cda5da8dSAndroid Build Coastguard Worker unparser.set_precedence(_Precedence.TEST.next(), inner) 1207*cda5da8dSAndroid Build Coastguard Worker return unparser.visit(inner) 1208*cda5da8dSAndroid Build Coastguard Worker 1209*cda5da8dSAndroid Build Coastguard Worker with self.delimit("{", "}"): 1210*cda5da8dSAndroid Build Coastguard Worker expr = unparse_inner(node.value) 1211*cda5da8dSAndroid Build Coastguard Worker if "\\" in expr: 1212*cda5da8dSAndroid Build Coastguard Worker raise ValueError( 1213*cda5da8dSAndroid Build Coastguard Worker "Unable to avoid backslash in f-string expression part" 1214*cda5da8dSAndroid Build Coastguard Worker ) 1215*cda5da8dSAndroid Build Coastguard Worker if expr.startswith("{"): 1216*cda5da8dSAndroid Build Coastguard Worker # Separate pair of opening brackets as "{ {" 1217*cda5da8dSAndroid Build Coastguard Worker self.write(" ") 1218*cda5da8dSAndroid Build Coastguard Worker self.write(expr) 1219*cda5da8dSAndroid Build Coastguard Worker if node.conversion != -1: 1220*cda5da8dSAndroid Build Coastguard Worker self.write(f"!{chr(node.conversion)}") 1221*cda5da8dSAndroid Build Coastguard Worker if node.format_spec: 1222*cda5da8dSAndroid Build Coastguard Worker self.write(":") 1223*cda5da8dSAndroid Build Coastguard Worker self._write_fstring_inner(node.format_spec) 1224*cda5da8dSAndroid Build Coastguard Worker 1225*cda5da8dSAndroid Build Coastguard Worker def visit_Name(self, node): 1226*cda5da8dSAndroid Build Coastguard Worker self.write(node.id) 1227*cda5da8dSAndroid Build Coastguard Worker 1228*cda5da8dSAndroid Build Coastguard Worker def _write_docstring(self, node): 1229*cda5da8dSAndroid Build Coastguard Worker self.fill() 1230*cda5da8dSAndroid Build Coastguard Worker if node.kind == "u": 1231*cda5da8dSAndroid Build Coastguard Worker self.write("u") 1232*cda5da8dSAndroid Build Coastguard Worker self._write_str_avoiding_backslashes(node.value, quote_types=_MULTI_QUOTES) 1233*cda5da8dSAndroid Build Coastguard Worker 1234*cda5da8dSAndroid Build Coastguard Worker def _write_constant(self, value): 1235*cda5da8dSAndroid Build Coastguard Worker if isinstance(value, (float, complex)): 1236*cda5da8dSAndroid Build Coastguard Worker # Substitute overflowing decimal literal for AST infinities, 1237*cda5da8dSAndroid Build Coastguard Worker # and inf - inf for NaNs. 1238*cda5da8dSAndroid Build Coastguard Worker self.write( 1239*cda5da8dSAndroid Build Coastguard Worker repr(value) 1240*cda5da8dSAndroid Build Coastguard Worker .replace("inf", _INFSTR) 1241*cda5da8dSAndroid Build Coastguard Worker .replace("nan", f"({_INFSTR}-{_INFSTR})") 1242*cda5da8dSAndroid Build Coastguard Worker ) 1243*cda5da8dSAndroid Build Coastguard Worker elif self._avoid_backslashes and isinstance(value, str): 1244*cda5da8dSAndroid Build Coastguard Worker self._write_str_avoiding_backslashes(value) 1245*cda5da8dSAndroid Build Coastguard Worker else: 1246*cda5da8dSAndroid Build Coastguard Worker self.write(repr(value)) 1247*cda5da8dSAndroid Build Coastguard Worker 1248*cda5da8dSAndroid Build Coastguard Worker def visit_Constant(self, node): 1249*cda5da8dSAndroid Build Coastguard Worker value = node.value 1250*cda5da8dSAndroid Build Coastguard Worker if isinstance(value, tuple): 1251*cda5da8dSAndroid Build Coastguard Worker with self.delimit("(", ")"): 1252*cda5da8dSAndroid Build Coastguard Worker self.items_view(self._write_constant, value) 1253*cda5da8dSAndroid Build Coastguard Worker elif value is ...: 1254*cda5da8dSAndroid Build Coastguard Worker self.write("...") 1255*cda5da8dSAndroid Build Coastguard Worker else: 1256*cda5da8dSAndroid Build Coastguard Worker if node.kind == "u": 1257*cda5da8dSAndroid Build Coastguard Worker self.write("u") 1258*cda5da8dSAndroid Build Coastguard Worker self._write_constant(node.value) 1259*cda5da8dSAndroid Build Coastguard Worker 1260*cda5da8dSAndroid Build Coastguard Worker def visit_List(self, node): 1261*cda5da8dSAndroid Build Coastguard Worker with self.delimit("[", "]"): 1262*cda5da8dSAndroid Build Coastguard Worker self.interleave(lambda: self.write(", "), self.traverse, node.elts) 1263*cda5da8dSAndroid Build Coastguard Worker 1264*cda5da8dSAndroid Build Coastguard Worker def visit_ListComp(self, node): 1265*cda5da8dSAndroid Build Coastguard Worker with self.delimit("[", "]"): 1266*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.elt) 1267*cda5da8dSAndroid Build Coastguard Worker for gen in node.generators: 1268*cda5da8dSAndroid Build Coastguard Worker self.traverse(gen) 1269*cda5da8dSAndroid Build Coastguard Worker 1270*cda5da8dSAndroid Build Coastguard Worker def visit_GeneratorExp(self, node): 1271*cda5da8dSAndroid Build Coastguard Worker with self.delimit("(", ")"): 1272*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.elt) 1273*cda5da8dSAndroid Build Coastguard Worker for gen in node.generators: 1274*cda5da8dSAndroid Build Coastguard Worker self.traverse(gen) 1275*cda5da8dSAndroid Build Coastguard Worker 1276*cda5da8dSAndroid Build Coastguard Worker def visit_SetComp(self, node): 1277*cda5da8dSAndroid Build Coastguard Worker with self.delimit("{", "}"): 1278*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.elt) 1279*cda5da8dSAndroid Build Coastguard Worker for gen in node.generators: 1280*cda5da8dSAndroid Build Coastguard Worker self.traverse(gen) 1281*cda5da8dSAndroid Build Coastguard Worker 1282*cda5da8dSAndroid Build Coastguard Worker def visit_DictComp(self, node): 1283*cda5da8dSAndroid Build Coastguard Worker with self.delimit("{", "}"): 1284*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.key) 1285*cda5da8dSAndroid Build Coastguard Worker self.write(": ") 1286*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 1287*cda5da8dSAndroid Build Coastguard Worker for gen in node.generators: 1288*cda5da8dSAndroid Build Coastguard Worker self.traverse(gen) 1289*cda5da8dSAndroid Build Coastguard Worker 1290*cda5da8dSAndroid Build Coastguard Worker def visit_comprehension(self, node): 1291*cda5da8dSAndroid Build Coastguard Worker if node.is_async: 1292*cda5da8dSAndroid Build Coastguard Worker self.write(" async for ") 1293*cda5da8dSAndroid Build Coastguard Worker else: 1294*cda5da8dSAndroid Build Coastguard Worker self.write(" for ") 1295*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.TUPLE, node.target) 1296*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.target) 1297*cda5da8dSAndroid Build Coastguard Worker self.write(" in ") 1298*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.TEST.next(), node.iter, *node.ifs) 1299*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.iter) 1300*cda5da8dSAndroid Build Coastguard Worker for if_clause in node.ifs: 1301*cda5da8dSAndroid Build Coastguard Worker self.write(" if ") 1302*cda5da8dSAndroid Build Coastguard Worker self.traverse(if_clause) 1303*cda5da8dSAndroid Build Coastguard Worker 1304*cda5da8dSAndroid Build Coastguard Worker def visit_IfExp(self, node): 1305*cda5da8dSAndroid Build Coastguard Worker with self.require_parens(_Precedence.TEST, node): 1306*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.TEST.next(), node.body, node.test) 1307*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.body) 1308*cda5da8dSAndroid Build Coastguard Worker self.write(" if ") 1309*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.test) 1310*cda5da8dSAndroid Build Coastguard Worker self.write(" else ") 1311*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.TEST, node.orelse) 1312*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.orelse) 1313*cda5da8dSAndroid Build Coastguard Worker 1314*cda5da8dSAndroid Build Coastguard Worker def visit_Set(self, node): 1315*cda5da8dSAndroid Build Coastguard Worker if node.elts: 1316*cda5da8dSAndroid Build Coastguard Worker with self.delimit("{", "}"): 1317*cda5da8dSAndroid Build Coastguard Worker self.interleave(lambda: self.write(", "), self.traverse, node.elts) 1318*cda5da8dSAndroid Build Coastguard Worker else: 1319*cda5da8dSAndroid Build Coastguard Worker # `{}` would be interpreted as a dictionary literal, and 1320*cda5da8dSAndroid Build Coastguard Worker # `set` might be shadowed. Thus: 1321*cda5da8dSAndroid Build Coastguard Worker self.write('{*()}') 1322*cda5da8dSAndroid Build Coastguard Worker 1323*cda5da8dSAndroid Build Coastguard Worker def visit_Dict(self, node): 1324*cda5da8dSAndroid Build Coastguard Worker def write_key_value_pair(k, v): 1325*cda5da8dSAndroid Build Coastguard Worker self.traverse(k) 1326*cda5da8dSAndroid Build Coastguard Worker self.write(": ") 1327*cda5da8dSAndroid Build Coastguard Worker self.traverse(v) 1328*cda5da8dSAndroid Build Coastguard Worker 1329*cda5da8dSAndroid Build Coastguard Worker def write_item(item): 1330*cda5da8dSAndroid Build Coastguard Worker k, v = item 1331*cda5da8dSAndroid Build Coastguard Worker if k is None: 1332*cda5da8dSAndroid Build Coastguard Worker # for dictionary unpacking operator in dicts {**{'y': 2}} 1333*cda5da8dSAndroid Build Coastguard Worker # see PEP 448 for details 1334*cda5da8dSAndroid Build Coastguard Worker self.write("**") 1335*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.EXPR, v) 1336*cda5da8dSAndroid Build Coastguard Worker self.traverse(v) 1337*cda5da8dSAndroid Build Coastguard Worker else: 1338*cda5da8dSAndroid Build Coastguard Worker write_key_value_pair(k, v) 1339*cda5da8dSAndroid Build Coastguard Worker 1340*cda5da8dSAndroid Build Coastguard Worker with self.delimit("{", "}"): 1341*cda5da8dSAndroid Build Coastguard Worker self.interleave( 1342*cda5da8dSAndroid Build Coastguard Worker lambda: self.write(", "), write_item, zip(node.keys, node.values) 1343*cda5da8dSAndroid Build Coastguard Worker ) 1344*cda5da8dSAndroid Build Coastguard Worker 1345*cda5da8dSAndroid Build Coastguard Worker def visit_Tuple(self, node): 1346*cda5da8dSAndroid Build Coastguard Worker with self.delimit_if( 1347*cda5da8dSAndroid Build Coastguard Worker "(", 1348*cda5da8dSAndroid Build Coastguard Worker ")", 1349*cda5da8dSAndroid Build Coastguard Worker len(node.elts) == 0 or self.get_precedence(node) > _Precedence.TUPLE 1350*cda5da8dSAndroid Build Coastguard Worker ): 1351*cda5da8dSAndroid Build Coastguard Worker self.items_view(self.traverse, node.elts) 1352*cda5da8dSAndroid Build Coastguard Worker 1353*cda5da8dSAndroid Build Coastguard Worker unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"} 1354*cda5da8dSAndroid Build Coastguard Worker unop_precedence = { 1355*cda5da8dSAndroid Build Coastguard Worker "not": _Precedence.NOT, 1356*cda5da8dSAndroid Build Coastguard Worker "~": _Precedence.FACTOR, 1357*cda5da8dSAndroid Build Coastguard Worker "+": _Precedence.FACTOR, 1358*cda5da8dSAndroid Build Coastguard Worker "-": _Precedence.FACTOR, 1359*cda5da8dSAndroid Build Coastguard Worker } 1360*cda5da8dSAndroid Build Coastguard Worker 1361*cda5da8dSAndroid Build Coastguard Worker def visit_UnaryOp(self, node): 1362*cda5da8dSAndroid Build Coastguard Worker operator = self.unop[node.op.__class__.__name__] 1363*cda5da8dSAndroid Build Coastguard Worker operator_precedence = self.unop_precedence[operator] 1364*cda5da8dSAndroid Build Coastguard Worker with self.require_parens(operator_precedence, node): 1365*cda5da8dSAndroid Build Coastguard Worker self.write(operator) 1366*cda5da8dSAndroid Build Coastguard Worker # factor prefixes (+, -, ~) shouldn't be separated 1367*cda5da8dSAndroid Build Coastguard Worker # from the value they belong, (e.g: +1 instead of + 1) 1368*cda5da8dSAndroid Build Coastguard Worker if operator_precedence is not _Precedence.FACTOR: 1369*cda5da8dSAndroid Build Coastguard Worker self.write(" ") 1370*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(operator_precedence, node.operand) 1371*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.operand) 1372*cda5da8dSAndroid Build Coastguard Worker 1373*cda5da8dSAndroid Build Coastguard Worker binop = { 1374*cda5da8dSAndroid Build Coastguard Worker "Add": "+", 1375*cda5da8dSAndroid Build Coastguard Worker "Sub": "-", 1376*cda5da8dSAndroid Build Coastguard Worker "Mult": "*", 1377*cda5da8dSAndroid Build Coastguard Worker "MatMult": "@", 1378*cda5da8dSAndroid Build Coastguard Worker "Div": "/", 1379*cda5da8dSAndroid Build Coastguard Worker "Mod": "%", 1380*cda5da8dSAndroid Build Coastguard Worker "LShift": "<<", 1381*cda5da8dSAndroid Build Coastguard Worker "RShift": ">>", 1382*cda5da8dSAndroid Build Coastguard Worker "BitOr": "|", 1383*cda5da8dSAndroid Build Coastguard Worker "BitXor": "^", 1384*cda5da8dSAndroid Build Coastguard Worker "BitAnd": "&", 1385*cda5da8dSAndroid Build Coastguard Worker "FloorDiv": "//", 1386*cda5da8dSAndroid Build Coastguard Worker "Pow": "**", 1387*cda5da8dSAndroid Build Coastguard Worker } 1388*cda5da8dSAndroid Build Coastguard Worker 1389*cda5da8dSAndroid Build Coastguard Worker binop_precedence = { 1390*cda5da8dSAndroid Build Coastguard Worker "+": _Precedence.ARITH, 1391*cda5da8dSAndroid Build Coastguard Worker "-": _Precedence.ARITH, 1392*cda5da8dSAndroid Build Coastguard Worker "*": _Precedence.TERM, 1393*cda5da8dSAndroid Build Coastguard Worker "@": _Precedence.TERM, 1394*cda5da8dSAndroid Build Coastguard Worker "/": _Precedence.TERM, 1395*cda5da8dSAndroid Build Coastguard Worker "%": _Precedence.TERM, 1396*cda5da8dSAndroid Build Coastguard Worker "<<": _Precedence.SHIFT, 1397*cda5da8dSAndroid Build Coastguard Worker ">>": _Precedence.SHIFT, 1398*cda5da8dSAndroid Build Coastguard Worker "|": _Precedence.BOR, 1399*cda5da8dSAndroid Build Coastguard Worker "^": _Precedence.BXOR, 1400*cda5da8dSAndroid Build Coastguard Worker "&": _Precedence.BAND, 1401*cda5da8dSAndroid Build Coastguard Worker "//": _Precedence.TERM, 1402*cda5da8dSAndroid Build Coastguard Worker "**": _Precedence.POWER, 1403*cda5da8dSAndroid Build Coastguard Worker } 1404*cda5da8dSAndroid Build Coastguard Worker 1405*cda5da8dSAndroid Build Coastguard Worker binop_rassoc = frozenset(("**",)) 1406*cda5da8dSAndroid Build Coastguard Worker def visit_BinOp(self, node): 1407*cda5da8dSAndroid Build Coastguard Worker operator = self.binop[node.op.__class__.__name__] 1408*cda5da8dSAndroid Build Coastguard Worker operator_precedence = self.binop_precedence[operator] 1409*cda5da8dSAndroid Build Coastguard Worker with self.require_parens(operator_precedence, node): 1410*cda5da8dSAndroid Build Coastguard Worker if operator in self.binop_rassoc: 1411*cda5da8dSAndroid Build Coastguard Worker left_precedence = operator_precedence.next() 1412*cda5da8dSAndroid Build Coastguard Worker right_precedence = operator_precedence 1413*cda5da8dSAndroid Build Coastguard Worker else: 1414*cda5da8dSAndroid Build Coastguard Worker left_precedence = operator_precedence 1415*cda5da8dSAndroid Build Coastguard Worker right_precedence = operator_precedence.next() 1416*cda5da8dSAndroid Build Coastguard Worker 1417*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(left_precedence, node.left) 1418*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.left) 1419*cda5da8dSAndroid Build Coastguard Worker self.write(f" {operator} ") 1420*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(right_precedence, node.right) 1421*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.right) 1422*cda5da8dSAndroid Build Coastguard Worker 1423*cda5da8dSAndroid Build Coastguard Worker cmpops = { 1424*cda5da8dSAndroid Build Coastguard Worker "Eq": "==", 1425*cda5da8dSAndroid Build Coastguard Worker "NotEq": "!=", 1426*cda5da8dSAndroid Build Coastguard Worker "Lt": "<", 1427*cda5da8dSAndroid Build Coastguard Worker "LtE": "<=", 1428*cda5da8dSAndroid Build Coastguard Worker "Gt": ">", 1429*cda5da8dSAndroid Build Coastguard Worker "GtE": ">=", 1430*cda5da8dSAndroid Build Coastguard Worker "Is": "is", 1431*cda5da8dSAndroid Build Coastguard Worker "IsNot": "is not", 1432*cda5da8dSAndroid Build Coastguard Worker "In": "in", 1433*cda5da8dSAndroid Build Coastguard Worker "NotIn": "not in", 1434*cda5da8dSAndroid Build Coastguard Worker } 1435*cda5da8dSAndroid Build Coastguard Worker 1436*cda5da8dSAndroid Build Coastguard Worker def visit_Compare(self, node): 1437*cda5da8dSAndroid Build Coastguard Worker with self.require_parens(_Precedence.CMP, node): 1438*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.CMP.next(), node.left, *node.comparators) 1439*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.left) 1440*cda5da8dSAndroid Build Coastguard Worker for o, e in zip(node.ops, node.comparators): 1441*cda5da8dSAndroid Build Coastguard Worker self.write(" " + self.cmpops[o.__class__.__name__] + " ") 1442*cda5da8dSAndroid Build Coastguard Worker self.traverse(e) 1443*cda5da8dSAndroid Build Coastguard Worker 1444*cda5da8dSAndroid Build Coastguard Worker boolops = {"And": "and", "Or": "or"} 1445*cda5da8dSAndroid Build Coastguard Worker boolop_precedence = {"and": _Precedence.AND, "or": _Precedence.OR} 1446*cda5da8dSAndroid Build Coastguard Worker 1447*cda5da8dSAndroid Build Coastguard Worker def visit_BoolOp(self, node): 1448*cda5da8dSAndroid Build Coastguard Worker operator = self.boolops[node.op.__class__.__name__] 1449*cda5da8dSAndroid Build Coastguard Worker operator_precedence = self.boolop_precedence[operator] 1450*cda5da8dSAndroid Build Coastguard Worker 1451*cda5da8dSAndroid Build Coastguard Worker def increasing_level_traverse(node): 1452*cda5da8dSAndroid Build Coastguard Worker nonlocal operator_precedence 1453*cda5da8dSAndroid Build Coastguard Worker operator_precedence = operator_precedence.next() 1454*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(operator_precedence, node) 1455*cda5da8dSAndroid Build Coastguard Worker self.traverse(node) 1456*cda5da8dSAndroid Build Coastguard Worker 1457*cda5da8dSAndroid Build Coastguard Worker with self.require_parens(operator_precedence, node): 1458*cda5da8dSAndroid Build Coastguard Worker s = f" {operator} " 1459*cda5da8dSAndroid Build Coastguard Worker self.interleave(lambda: self.write(s), increasing_level_traverse, node.values) 1460*cda5da8dSAndroid Build Coastguard Worker 1461*cda5da8dSAndroid Build Coastguard Worker def visit_Attribute(self, node): 1462*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.ATOM, node.value) 1463*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 1464*cda5da8dSAndroid Build Coastguard Worker # Special case: 3.__abs__() is a syntax error, so if node.value 1465*cda5da8dSAndroid Build Coastguard Worker # is an integer literal then we need to either parenthesize 1466*cda5da8dSAndroid Build Coastguard Worker # it or add an extra space to get 3 .__abs__(). 1467*cda5da8dSAndroid Build Coastguard Worker if isinstance(node.value, Constant) and isinstance(node.value.value, int): 1468*cda5da8dSAndroid Build Coastguard Worker self.write(" ") 1469*cda5da8dSAndroid Build Coastguard Worker self.write(".") 1470*cda5da8dSAndroid Build Coastguard Worker self.write(node.attr) 1471*cda5da8dSAndroid Build Coastguard Worker 1472*cda5da8dSAndroid Build Coastguard Worker def visit_Call(self, node): 1473*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.ATOM, node.func) 1474*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.func) 1475*cda5da8dSAndroid Build Coastguard Worker with self.delimit("(", ")"): 1476*cda5da8dSAndroid Build Coastguard Worker comma = False 1477*cda5da8dSAndroid Build Coastguard Worker for e in node.args: 1478*cda5da8dSAndroid Build Coastguard Worker if comma: 1479*cda5da8dSAndroid Build Coastguard Worker self.write(", ") 1480*cda5da8dSAndroid Build Coastguard Worker else: 1481*cda5da8dSAndroid Build Coastguard Worker comma = True 1482*cda5da8dSAndroid Build Coastguard Worker self.traverse(e) 1483*cda5da8dSAndroid Build Coastguard Worker for e in node.keywords: 1484*cda5da8dSAndroid Build Coastguard Worker if comma: 1485*cda5da8dSAndroid Build Coastguard Worker self.write(", ") 1486*cda5da8dSAndroid Build Coastguard Worker else: 1487*cda5da8dSAndroid Build Coastguard Worker comma = True 1488*cda5da8dSAndroid Build Coastguard Worker self.traverse(e) 1489*cda5da8dSAndroid Build Coastguard Worker 1490*cda5da8dSAndroid Build Coastguard Worker def visit_Subscript(self, node): 1491*cda5da8dSAndroid Build Coastguard Worker def is_non_empty_tuple(slice_value): 1492*cda5da8dSAndroid Build Coastguard Worker return ( 1493*cda5da8dSAndroid Build Coastguard Worker isinstance(slice_value, Tuple) 1494*cda5da8dSAndroid Build Coastguard Worker and slice_value.elts 1495*cda5da8dSAndroid Build Coastguard Worker ) 1496*cda5da8dSAndroid Build Coastguard Worker 1497*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.ATOM, node.value) 1498*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 1499*cda5da8dSAndroid Build Coastguard Worker with self.delimit("[", "]"): 1500*cda5da8dSAndroid Build Coastguard Worker if is_non_empty_tuple(node.slice): 1501*cda5da8dSAndroid Build Coastguard Worker # parentheses can be omitted if the tuple isn't empty 1502*cda5da8dSAndroid Build Coastguard Worker self.items_view(self.traverse, node.slice.elts) 1503*cda5da8dSAndroid Build Coastguard Worker else: 1504*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.slice) 1505*cda5da8dSAndroid Build Coastguard Worker 1506*cda5da8dSAndroid Build Coastguard Worker def visit_Starred(self, node): 1507*cda5da8dSAndroid Build Coastguard Worker self.write("*") 1508*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.EXPR, node.value) 1509*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 1510*cda5da8dSAndroid Build Coastguard Worker 1511*cda5da8dSAndroid Build Coastguard Worker def visit_Ellipsis(self, node): 1512*cda5da8dSAndroid Build Coastguard Worker self.write("...") 1513*cda5da8dSAndroid Build Coastguard Worker 1514*cda5da8dSAndroid Build Coastguard Worker def visit_Slice(self, node): 1515*cda5da8dSAndroid Build Coastguard Worker if node.lower: 1516*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.lower) 1517*cda5da8dSAndroid Build Coastguard Worker self.write(":") 1518*cda5da8dSAndroid Build Coastguard Worker if node.upper: 1519*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.upper) 1520*cda5da8dSAndroid Build Coastguard Worker if node.step: 1521*cda5da8dSAndroid Build Coastguard Worker self.write(":") 1522*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.step) 1523*cda5da8dSAndroid Build Coastguard Worker 1524*cda5da8dSAndroid Build Coastguard Worker def visit_Match(self, node): 1525*cda5da8dSAndroid Build Coastguard Worker self.fill("match ") 1526*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.subject) 1527*cda5da8dSAndroid Build Coastguard Worker with self.block(): 1528*cda5da8dSAndroid Build Coastguard Worker for case in node.cases: 1529*cda5da8dSAndroid Build Coastguard Worker self.traverse(case) 1530*cda5da8dSAndroid Build Coastguard Worker 1531*cda5da8dSAndroid Build Coastguard Worker def visit_arg(self, node): 1532*cda5da8dSAndroid Build Coastguard Worker self.write(node.arg) 1533*cda5da8dSAndroid Build Coastguard Worker if node.annotation: 1534*cda5da8dSAndroid Build Coastguard Worker self.write(": ") 1535*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.annotation) 1536*cda5da8dSAndroid Build Coastguard Worker 1537*cda5da8dSAndroid Build Coastguard Worker def visit_arguments(self, node): 1538*cda5da8dSAndroid Build Coastguard Worker first = True 1539*cda5da8dSAndroid Build Coastguard Worker # normal arguments 1540*cda5da8dSAndroid Build Coastguard Worker all_args = node.posonlyargs + node.args 1541*cda5da8dSAndroid Build Coastguard Worker defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults 1542*cda5da8dSAndroid Build Coastguard Worker for index, elements in enumerate(zip(all_args, defaults), 1): 1543*cda5da8dSAndroid Build Coastguard Worker a, d = elements 1544*cda5da8dSAndroid Build Coastguard Worker if first: 1545*cda5da8dSAndroid Build Coastguard Worker first = False 1546*cda5da8dSAndroid Build Coastguard Worker else: 1547*cda5da8dSAndroid Build Coastguard Worker self.write(", ") 1548*cda5da8dSAndroid Build Coastguard Worker self.traverse(a) 1549*cda5da8dSAndroid Build Coastguard Worker if d: 1550*cda5da8dSAndroid Build Coastguard Worker self.write("=") 1551*cda5da8dSAndroid Build Coastguard Worker self.traverse(d) 1552*cda5da8dSAndroid Build Coastguard Worker if index == len(node.posonlyargs): 1553*cda5da8dSAndroid Build Coastguard Worker self.write(", /") 1554*cda5da8dSAndroid Build Coastguard Worker 1555*cda5da8dSAndroid Build Coastguard Worker # varargs, or bare '*' if no varargs but keyword-only arguments present 1556*cda5da8dSAndroid Build Coastguard Worker if node.vararg or node.kwonlyargs: 1557*cda5da8dSAndroid Build Coastguard Worker if first: 1558*cda5da8dSAndroid Build Coastguard Worker first = False 1559*cda5da8dSAndroid Build Coastguard Worker else: 1560*cda5da8dSAndroid Build Coastguard Worker self.write(", ") 1561*cda5da8dSAndroid Build Coastguard Worker self.write("*") 1562*cda5da8dSAndroid Build Coastguard Worker if node.vararg: 1563*cda5da8dSAndroid Build Coastguard Worker self.write(node.vararg.arg) 1564*cda5da8dSAndroid Build Coastguard Worker if node.vararg.annotation: 1565*cda5da8dSAndroid Build Coastguard Worker self.write(": ") 1566*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.vararg.annotation) 1567*cda5da8dSAndroid Build Coastguard Worker 1568*cda5da8dSAndroid Build Coastguard Worker # keyword-only arguments 1569*cda5da8dSAndroid Build Coastguard Worker if node.kwonlyargs: 1570*cda5da8dSAndroid Build Coastguard Worker for a, d in zip(node.kwonlyargs, node.kw_defaults): 1571*cda5da8dSAndroid Build Coastguard Worker self.write(", ") 1572*cda5da8dSAndroid Build Coastguard Worker self.traverse(a) 1573*cda5da8dSAndroid Build Coastguard Worker if d: 1574*cda5da8dSAndroid Build Coastguard Worker self.write("=") 1575*cda5da8dSAndroid Build Coastguard Worker self.traverse(d) 1576*cda5da8dSAndroid Build Coastguard Worker 1577*cda5da8dSAndroid Build Coastguard Worker # kwargs 1578*cda5da8dSAndroid Build Coastguard Worker if node.kwarg: 1579*cda5da8dSAndroid Build Coastguard Worker if first: 1580*cda5da8dSAndroid Build Coastguard Worker first = False 1581*cda5da8dSAndroid Build Coastguard Worker else: 1582*cda5da8dSAndroid Build Coastguard Worker self.write(", ") 1583*cda5da8dSAndroid Build Coastguard Worker self.write("**" + node.kwarg.arg) 1584*cda5da8dSAndroid Build Coastguard Worker if node.kwarg.annotation: 1585*cda5da8dSAndroid Build Coastguard Worker self.write(": ") 1586*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.kwarg.annotation) 1587*cda5da8dSAndroid Build Coastguard Worker 1588*cda5da8dSAndroid Build Coastguard Worker def visit_keyword(self, node): 1589*cda5da8dSAndroid Build Coastguard Worker if node.arg is None: 1590*cda5da8dSAndroid Build Coastguard Worker self.write("**") 1591*cda5da8dSAndroid Build Coastguard Worker else: 1592*cda5da8dSAndroid Build Coastguard Worker self.write(node.arg) 1593*cda5da8dSAndroid Build Coastguard Worker self.write("=") 1594*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 1595*cda5da8dSAndroid Build Coastguard Worker 1596*cda5da8dSAndroid Build Coastguard Worker def visit_Lambda(self, node): 1597*cda5da8dSAndroid Build Coastguard Worker with self.require_parens(_Precedence.TEST, node): 1598*cda5da8dSAndroid Build Coastguard Worker self.write("lambda") 1599*cda5da8dSAndroid Build Coastguard Worker with self.buffered() as buffer: 1600*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.args) 1601*cda5da8dSAndroid Build Coastguard Worker if buffer: 1602*cda5da8dSAndroid Build Coastguard Worker self.write(" ", *buffer) 1603*cda5da8dSAndroid Build Coastguard Worker self.write(": ") 1604*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.TEST, node.body) 1605*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.body) 1606*cda5da8dSAndroid Build Coastguard Worker 1607*cda5da8dSAndroid Build Coastguard Worker def visit_alias(self, node): 1608*cda5da8dSAndroid Build Coastguard Worker self.write(node.name) 1609*cda5da8dSAndroid Build Coastguard Worker if node.asname: 1610*cda5da8dSAndroid Build Coastguard Worker self.write(" as " + node.asname) 1611*cda5da8dSAndroid Build Coastguard Worker 1612*cda5da8dSAndroid Build Coastguard Worker def visit_withitem(self, node): 1613*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.context_expr) 1614*cda5da8dSAndroid Build Coastguard Worker if node.optional_vars: 1615*cda5da8dSAndroid Build Coastguard Worker self.write(" as ") 1616*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.optional_vars) 1617*cda5da8dSAndroid Build Coastguard Worker 1618*cda5da8dSAndroid Build Coastguard Worker def visit_match_case(self, node): 1619*cda5da8dSAndroid Build Coastguard Worker self.fill("case ") 1620*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.pattern) 1621*cda5da8dSAndroid Build Coastguard Worker if node.guard: 1622*cda5da8dSAndroid Build Coastguard Worker self.write(" if ") 1623*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.guard) 1624*cda5da8dSAndroid Build Coastguard Worker with self.block(): 1625*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.body) 1626*cda5da8dSAndroid Build Coastguard Worker 1627*cda5da8dSAndroid Build Coastguard Worker def visit_MatchValue(self, node): 1628*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.value) 1629*cda5da8dSAndroid Build Coastguard Worker 1630*cda5da8dSAndroid Build Coastguard Worker def visit_MatchSingleton(self, node): 1631*cda5da8dSAndroid Build Coastguard Worker self._write_constant(node.value) 1632*cda5da8dSAndroid Build Coastguard Worker 1633*cda5da8dSAndroid Build Coastguard Worker def visit_MatchSequence(self, node): 1634*cda5da8dSAndroid Build Coastguard Worker with self.delimit("[", "]"): 1635*cda5da8dSAndroid Build Coastguard Worker self.interleave( 1636*cda5da8dSAndroid Build Coastguard Worker lambda: self.write(", "), self.traverse, node.patterns 1637*cda5da8dSAndroid Build Coastguard Worker ) 1638*cda5da8dSAndroid Build Coastguard Worker 1639*cda5da8dSAndroid Build Coastguard Worker def visit_MatchStar(self, node): 1640*cda5da8dSAndroid Build Coastguard Worker name = node.name 1641*cda5da8dSAndroid Build Coastguard Worker if name is None: 1642*cda5da8dSAndroid Build Coastguard Worker name = "_" 1643*cda5da8dSAndroid Build Coastguard Worker self.write(f"*{name}") 1644*cda5da8dSAndroid Build Coastguard Worker 1645*cda5da8dSAndroid Build Coastguard Worker def visit_MatchMapping(self, node): 1646*cda5da8dSAndroid Build Coastguard Worker def write_key_pattern_pair(pair): 1647*cda5da8dSAndroid Build Coastguard Worker k, p = pair 1648*cda5da8dSAndroid Build Coastguard Worker self.traverse(k) 1649*cda5da8dSAndroid Build Coastguard Worker self.write(": ") 1650*cda5da8dSAndroid Build Coastguard Worker self.traverse(p) 1651*cda5da8dSAndroid Build Coastguard Worker 1652*cda5da8dSAndroid Build Coastguard Worker with self.delimit("{", "}"): 1653*cda5da8dSAndroid Build Coastguard Worker keys = node.keys 1654*cda5da8dSAndroid Build Coastguard Worker self.interleave( 1655*cda5da8dSAndroid Build Coastguard Worker lambda: self.write(", "), 1656*cda5da8dSAndroid Build Coastguard Worker write_key_pattern_pair, 1657*cda5da8dSAndroid Build Coastguard Worker zip(keys, node.patterns, strict=True), 1658*cda5da8dSAndroid Build Coastguard Worker ) 1659*cda5da8dSAndroid Build Coastguard Worker rest = node.rest 1660*cda5da8dSAndroid Build Coastguard Worker if rest is not None: 1661*cda5da8dSAndroid Build Coastguard Worker if keys: 1662*cda5da8dSAndroid Build Coastguard Worker self.write(", ") 1663*cda5da8dSAndroid Build Coastguard Worker self.write(f"**{rest}") 1664*cda5da8dSAndroid Build Coastguard Worker 1665*cda5da8dSAndroid Build Coastguard Worker def visit_MatchClass(self, node): 1666*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.ATOM, node.cls) 1667*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.cls) 1668*cda5da8dSAndroid Build Coastguard Worker with self.delimit("(", ")"): 1669*cda5da8dSAndroid Build Coastguard Worker patterns = node.patterns 1670*cda5da8dSAndroid Build Coastguard Worker self.interleave( 1671*cda5da8dSAndroid Build Coastguard Worker lambda: self.write(", "), self.traverse, patterns 1672*cda5da8dSAndroid Build Coastguard Worker ) 1673*cda5da8dSAndroid Build Coastguard Worker attrs = node.kwd_attrs 1674*cda5da8dSAndroid Build Coastguard Worker if attrs: 1675*cda5da8dSAndroid Build Coastguard Worker def write_attr_pattern(pair): 1676*cda5da8dSAndroid Build Coastguard Worker attr, pattern = pair 1677*cda5da8dSAndroid Build Coastguard Worker self.write(f"{attr}=") 1678*cda5da8dSAndroid Build Coastguard Worker self.traverse(pattern) 1679*cda5da8dSAndroid Build Coastguard Worker 1680*cda5da8dSAndroid Build Coastguard Worker if patterns: 1681*cda5da8dSAndroid Build Coastguard Worker self.write(", ") 1682*cda5da8dSAndroid Build Coastguard Worker self.interleave( 1683*cda5da8dSAndroid Build Coastguard Worker lambda: self.write(", "), 1684*cda5da8dSAndroid Build Coastguard Worker write_attr_pattern, 1685*cda5da8dSAndroid Build Coastguard Worker zip(attrs, node.kwd_patterns, strict=True), 1686*cda5da8dSAndroid Build Coastguard Worker ) 1687*cda5da8dSAndroid Build Coastguard Worker 1688*cda5da8dSAndroid Build Coastguard Worker def visit_MatchAs(self, node): 1689*cda5da8dSAndroid Build Coastguard Worker name = node.name 1690*cda5da8dSAndroid Build Coastguard Worker pattern = node.pattern 1691*cda5da8dSAndroid Build Coastguard Worker if name is None: 1692*cda5da8dSAndroid Build Coastguard Worker self.write("_") 1693*cda5da8dSAndroid Build Coastguard Worker elif pattern is None: 1694*cda5da8dSAndroid Build Coastguard Worker self.write(node.name) 1695*cda5da8dSAndroid Build Coastguard Worker else: 1696*cda5da8dSAndroid Build Coastguard Worker with self.require_parens(_Precedence.TEST, node): 1697*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.BOR, node.pattern) 1698*cda5da8dSAndroid Build Coastguard Worker self.traverse(node.pattern) 1699*cda5da8dSAndroid Build Coastguard Worker self.write(f" as {node.name}") 1700*cda5da8dSAndroid Build Coastguard Worker 1701*cda5da8dSAndroid Build Coastguard Worker def visit_MatchOr(self, node): 1702*cda5da8dSAndroid Build Coastguard Worker with self.require_parens(_Precedence.BOR, node): 1703*cda5da8dSAndroid Build Coastguard Worker self.set_precedence(_Precedence.BOR.next(), *node.patterns) 1704*cda5da8dSAndroid Build Coastguard Worker self.interleave(lambda: self.write(" | "), self.traverse, node.patterns) 1705*cda5da8dSAndroid Build Coastguard Worker 1706*cda5da8dSAndroid Build Coastguard Workerdef unparse(ast_obj): 1707*cda5da8dSAndroid Build Coastguard Worker unparser = _Unparser() 1708*cda5da8dSAndroid Build Coastguard Worker return unparser.visit(ast_obj) 1709*cda5da8dSAndroid Build Coastguard Worker 1710*cda5da8dSAndroid Build Coastguard Worker 1711*cda5da8dSAndroid Build Coastguard Workerdef main(): 1712*cda5da8dSAndroid Build Coastguard Worker import argparse 1713*cda5da8dSAndroid Build Coastguard Worker 1714*cda5da8dSAndroid Build Coastguard Worker parser = argparse.ArgumentParser(prog='python -m ast') 1715*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?', 1716*cda5da8dSAndroid Build Coastguard Worker default='-', 1717*cda5da8dSAndroid Build Coastguard Worker help='the file to parse; defaults to stdin') 1718*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-m', '--mode', default='exec', 1719*cda5da8dSAndroid Build Coastguard Worker choices=('exec', 'single', 'eval', 'func_type'), 1720*cda5da8dSAndroid Build Coastguard Worker help='specify what kind of code must be parsed') 1721*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('--no-type-comments', default=True, action='store_false', 1722*cda5da8dSAndroid Build Coastguard Worker help="don't add information about type comments") 1723*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-a', '--include-attributes', action='store_true', 1724*cda5da8dSAndroid Build Coastguard Worker help='include attributes such as line numbers and ' 1725*cda5da8dSAndroid Build Coastguard Worker 'column offsets') 1726*cda5da8dSAndroid Build Coastguard Worker parser.add_argument('-i', '--indent', type=int, default=3, 1727*cda5da8dSAndroid Build Coastguard Worker help='indentation of nodes (number of spaces)') 1728*cda5da8dSAndroid Build Coastguard Worker args = parser.parse_args() 1729*cda5da8dSAndroid Build Coastguard Worker 1730*cda5da8dSAndroid Build Coastguard Worker with args.infile as infile: 1731*cda5da8dSAndroid Build Coastguard Worker source = infile.read() 1732*cda5da8dSAndroid Build Coastguard Worker tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments) 1733*cda5da8dSAndroid Build Coastguard Worker print(dump(tree, include_attributes=args.include_attributes, indent=args.indent)) 1734*cda5da8dSAndroid Build Coastguard Worker 1735*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__': 1736*cda5da8dSAndroid Build Coastguard Worker main() 1737