blob: 703f68ace6068ffb91dc306d09151c2172c7354f [file] [log] [blame]
Georg Brandl0c77a822008-06-10 16:37:50 +00001"""
2 ast
3 ~~~
4
5 The `ast` module helps Python applications to process trees of the Python
6 abstract syntax grammar. The abstract syntax itself might change with
7 each Python release; this module helps to find out programmatically what
8 the current grammar looks like and allows modifications of it.
9
10 An abstract syntax tree can be generated by passing `ast.PyCF_ONLY_AST` as
11 a flag to the `compile()` builtin function or by using the `parse()`
12 function from this module. The result will be a tree of objects whose
13 classes all inherit from `ast.AST`.
14
15 A modified abstract syntax tree can be compiled into a Python code object
16 using the built-in `compile()` function.
17
18 Additionally various helper functions are provided that make working with
19 the trees simpler. The main intention of the helper functions and this
20 module in general is to provide an easy to use interface for libraries
21 that work tightly with the python syntax (template engines for example).
22
23
24 :copyright: Copyright 2008 by Armin Ronacher.
25 :license: Python License.
26"""
Pablo Galindo27fc3b62019-11-24 23:02:40 +000027import sys
Georg Brandl0c77a822008-06-10 16:37:50 +000028from _ast import *
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +030029from contextlib import contextmanager, nullcontext
Ethan Furmana02cb472021-04-21 10:20:44 -070030from enum import IntEnum, auto, _simple_enum
Georg Brandl0c77a822008-06-10 16:37:50 +000031
32
Guido van Rossum495da292019-03-07 12:38:08 -080033def parse(source, filename='<unknown>', mode='exec', *,
Guido van Rossum10b55c12019-06-11 17:23:12 -070034 type_comments=False, feature_version=None):
Georg Brandl0c77a822008-06-10 16:37:50 +000035 """
Terry Reedyfeac6242011-01-24 21:36:03 +000036 Parse the source into an AST node.
37 Equivalent to compile(source, filename, mode, PyCF_ONLY_AST).
Guido van Rossumdcfcd142019-01-31 03:40:27 -080038 Pass type_comments=True to get back type comments where the syntax allows.
Georg Brandl0c77a822008-06-10 16:37:50 +000039 """
Guido van Rossumdcfcd142019-01-31 03:40:27 -080040 flags = PyCF_ONLY_AST
41 if type_comments:
42 flags |= PyCF_TYPE_COMMENTS
Guido van Rossum10b55c12019-06-11 17:23:12 -070043 if isinstance(feature_version, tuple):
44 major, minor = feature_version # Should be a 2-tuple.
45 assert major == 3
46 feature_version = minor
47 elif feature_version is None:
48 feature_version = -1
49 # Else it should be an int giving the minor version for 3.x.
Guido van Rossum495da292019-03-07 12:38:08 -080050 return compile(source, filename, mode, flags,
Victor Stinnerefdf6ca2019-06-12 02:52:16 +020051 _feature_version=feature_version)
Georg Brandl0c77a822008-06-10 16:37:50 +000052
53
54def literal_eval(node_or_string):
55 """
56 Safely evaluate an expression node or a string containing a Python
57 expression. The string or node provided may only consist of the following
Éric Araujo2a83cc62011-04-17 19:10:27 +020058 Python literal structures: strings, bytes, numbers, tuples, lists, dicts,
59 sets, booleans, and None.
Georg Brandl0c77a822008-06-10 16:37:50 +000060 """
Georg Brandl0c77a822008-06-10 16:37:50 +000061 if isinstance(node_or_string, str):
Batuhan Taskayae799aa82020-10-04 03:46:44 +030062 node_or_string = parse(node_or_string.lstrip(" \t"), mode='eval')
Georg Brandl0c77a822008-06-10 16:37:50 +000063 if isinstance(node_or_string, Expression):
64 node_or_string = node_or_string.body
Curtis Bucherc21c5122020-05-05 12:40:56 -070065 def _raise_malformed_node(node):
Irit Katriel586f3db2020-12-25 17:04:31 +000066 msg = "malformed node or string"
67 if lno := getattr(node, 'lineno', None):
68 msg += f' on line {lno}'
69 raise ValueError(msg + f': {node!r}')
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020070 def _convert_num(node):
Curtis Bucherc21c5122020-05-05 12:40:56 -070071 if not isinstance(node, Constant) or type(node.value) not in (int, float, complex):
72 _raise_malformed_node(node)
73 return node.value
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020074 def _convert_signed_num(node):
75 if isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)):
76 operand = _convert_num(node.operand)
77 if isinstance(node.op, UAdd):
78 return + operand
79 else:
80 return - operand
81 return _convert_num(node)
Georg Brandl0c77a822008-06-10 16:37:50 +000082 def _convert(node):
Victor Stinnerf2c1aa12016-01-26 00:40:57 +010083 if isinstance(node, Constant):
84 return node.value
Georg Brandl0c77a822008-06-10 16:37:50 +000085 elif isinstance(node, Tuple):
86 return tuple(map(_convert, node.elts))
87 elif isinstance(node, List):
88 return list(map(_convert, node.elts))
Georg Brandl492f3fc2010-07-11 09:41:21 +000089 elif isinstance(node, Set):
90 return set(map(_convert, node.elts))
Raymond Hettinger4fcf5c12020-01-02 22:21:18 -070091 elif (isinstance(node, Call) and isinstance(node.func, Name) and
92 node.func.id == 'set' and node.args == node.keywords == []):
93 return set()
Georg Brandl0c77a822008-06-10 16:37:50 +000094 elif isinstance(node, Dict):
Curtis Bucherc21c5122020-05-05 12:40:56 -070095 if len(node.keys) != len(node.values):
96 _raise_malformed_node(node)
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020097 return dict(zip(map(_convert, node.keys),
98 map(_convert, node.values)))
Victor Stinnerf2c1aa12016-01-26 00:40:57 +010099 elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)):
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +0200100 left = _convert_signed_num(node.left)
101 right = _convert_num(node.right)
102 if isinstance(left, (int, float)) and isinstance(right, complex):
Victor Stinnerf2c1aa12016-01-26 00:40:57 +0100103 if isinstance(node.op, Add):
104 return left + right
105 else:
106 return left - right
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +0200107 return _convert_signed_num(node)
Georg Brandl0c77a822008-06-10 16:37:50 +0000108 return _convert(node_or_string)
109
110
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300111def dump(node, annotate_fields=True, include_attributes=False, *, indent=None):
Georg Brandl0c77a822008-06-10 16:37:50 +0000112 """
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300113 Return a formatted dump of the tree in node. This is mainly useful for
114 debugging purposes. If annotate_fields is true (by default),
115 the returned string will show the names and the values for fields.
116 If annotate_fields is false, the result string will be more compact by
117 omitting unambiguous field names. Attributes such as line
Benjamin Petersondcf97b92008-07-02 17:30:14 +0000118 numbers and column offsets are not dumped by default. If this is wanted,
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300119 include_attributes can be set to true. If indent is a non-negative
120 integer or string, then the tree will be pretty-printed with that indent
121 level. None (the default) selects the single line representation.
Georg Brandl0c77a822008-06-10 16:37:50 +0000122 """
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300123 def _format(node, level=0):
124 if indent is not None:
125 level += 1
126 prefix = '\n' + indent * level
127 sep = ',\n' + indent * level
128 else:
129 prefix = ''
130 sep = ', '
Georg Brandl0c77a822008-06-10 16:37:50 +0000131 if isinstance(node, AST):
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200132 cls = type(node)
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300133 args = []
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300134 allsimple = True
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300135 keywords = annotate_fields
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200136 for name in node._fields:
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300137 try:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200138 value = getattr(node, name)
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300139 except AttributeError:
140 keywords = True
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200141 continue
142 if value is None and getattr(cls, name, ...) is None:
143 keywords = True
144 continue
145 value, simple = _format(value, level)
146 allsimple = allsimple and simple
147 if keywords:
148 args.append('%s=%s' % (name, value))
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300149 else:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200150 args.append(value)
151 if include_attributes and node._attributes:
152 for name in node._attributes:
153 try:
154 value = getattr(node, name)
155 except AttributeError:
156 continue
157 if value is None and getattr(cls, name, ...) is None:
158 continue
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300159 value, simple = _format(value, level)
160 allsimple = allsimple and simple
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200161 args.append('%s=%s' % (name, value))
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300162 if allsimple and len(args) <= 3:
163 return '%s(%s)' % (node.__class__.__name__, ', '.join(args)), not args
164 return '%s(%s%s)' % (node.__class__.__name__, prefix, sep.join(args)), False
Georg Brandl0c77a822008-06-10 16:37:50 +0000165 elif isinstance(node, list):
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300166 if not node:
167 return '[]', True
168 return '[%s%s]' % (prefix, sep.join(_format(x, level)[0] for x in node)), False
169 return repr(node), True
170
Georg Brandl0c77a822008-06-10 16:37:50 +0000171 if not isinstance(node, AST):
172 raise TypeError('expected AST, got %r' % node.__class__.__name__)
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300173 if indent is not None and not isinstance(indent, str):
174 indent = ' ' * indent
175 return _format(node)[0]
Georg Brandl0c77a822008-06-10 16:37:50 +0000176
177
178def copy_location(new_node, old_node):
179 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000180 Copy source location (`lineno`, `col_offset`, `end_lineno`, and `end_col_offset`
181 attributes) from *old_node* to *new_node* if possible, and return *new_node*.
Georg Brandl0c77a822008-06-10 16:37:50 +0000182 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000183 for attr in 'lineno', 'col_offset', 'end_lineno', 'end_col_offset':
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200184 if attr in old_node._attributes and attr in new_node._attributes:
185 value = getattr(old_node, attr, None)
Batuhan Taskaya8f4380d2020-08-05 16:32:32 +0300186 # end_lineno and end_col_offset are optional attributes, and they
187 # should be copied whether the value is None or not.
188 if value is not None or (
189 hasattr(old_node, attr) and attr.startswith("end_")
190 ):
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200191 setattr(new_node, attr, value)
Georg Brandl0c77a822008-06-10 16:37:50 +0000192 return new_node
193
194
195def fix_missing_locations(node):
196 """
197 When you compile a node tree with compile(), the compiler expects lineno and
198 col_offset attributes for every node that supports them. This is rather
199 tedious to fill in for generated nodes, so this helper adds these attributes
200 recursively where not already set, by setting them to the values of the
201 parent node. It works recursively starting at *node*.
202 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000203 def _fix(node, lineno, col_offset, end_lineno, end_col_offset):
Georg Brandl0c77a822008-06-10 16:37:50 +0000204 if 'lineno' in node._attributes:
205 if not hasattr(node, 'lineno'):
206 node.lineno = lineno
207 else:
208 lineno = node.lineno
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000209 if 'end_lineno' in node._attributes:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200210 if getattr(node, 'end_lineno', None) is None:
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000211 node.end_lineno = end_lineno
212 else:
213 end_lineno = node.end_lineno
Georg Brandl0c77a822008-06-10 16:37:50 +0000214 if 'col_offset' in node._attributes:
215 if not hasattr(node, 'col_offset'):
216 node.col_offset = col_offset
217 else:
218 col_offset = node.col_offset
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000219 if 'end_col_offset' in node._attributes:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200220 if getattr(node, 'end_col_offset', None) is None:
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000221 node.end_col_offset = end_col_offset
222 else:
223 end_col_offset = node.end_col_offset
Georg Brandl0c77a822008-06-10 16:37:50 +0000224 for child in iter_child_nodes(node):
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000225 _fix(child, lineno, col_offset, end_lineno, end_col_offset)
226 _fix(node, 1, 0, 1, 0)
Georg Brandl0c77a822008-06-10 16:37:50 +0000227 return node
228
229
230def increment_lineno(node, n=1):
231 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000232 Increment the line number and end line number of each node in the tree
233 starting at *node* by *n*. This is useful to "move code" to a different
234 location in a file.
Georg Brandl0c77a822008-06-10 16:37:50 +0000235 """
Georg Brandl0c77a822008-06-10 16:37:50 +0000236 for child in walk(node):
237 if 'lineno' in child._attributes:
238 child.lineno = getattr(child, 'lineno', 0) + n
Batuhan Taskaya8f4380d2020-08-05 16:32:32 +0300239 if (
240 "end_lineno" in child._attributes
241 and (end_lineno := getattr(child, "end_lineno", 0)) is not None
242 ):
243 child.end_lineno = end_lineno + n
Georg Brandl0c77a822008-06-10 16:37:50 +0000244 return node
245
246
247def iter_fields(node):
248 """
249 Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
250 that is present on *node*.
251 """
252 for field in node._fields:
253 try:
254 yield field, getattr(node, field)
255 except AttributeError:
256 pass
257
258
259def iter_child_nodes(node):
260 """
261 Yield all direct child nodes of *node*, that is, all fields that are nodes
262 and all items of fields that are lists of nodes.
263 """
264 for name, field in iter_fields(node):
265 if isinstance(field, AST):
266 yield field
267 elif isinstance(field, list):
268 for item in field:
269 if isinstance(item, AST):
270 yield item
271
272
273def get_docstring(node, clean=True):
274 """
275 Return the docstring for the given node or None if no docstring can
276 be found. If the node provided does not have docstrings a TypeError
277 will be raised.
Matthias Bussonnier41cea702017-02-23 22:44:19 -0800278
279 If *clean* is `True`, all tabs are expanded to spaces and any whitespace
280 that can be uniformly removed from the second line onwards is removed.
Georg Brandl0c77a822008-06-10 16:37:50 +0000281 """
Yury Selivanov2f07a662015-07-23 08:54:35 +0300282 if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
Georg Brandl0c77a822008-06-10 16:37:50 +0000283 raise TypeError("%r can't have docstrings" % node.__class__.__name__)
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300284 if not(node.body and isinstance(node.body[0], Expr)):
Serhiy Storchaka73cbe7a2018-05-29 12:04:55 +0300285 return None
286 node = node.body[0].value
287 if isinstance(node, Str):
288 text = node.s
289 elif isinstance(node, Constant) and isinstance(node.value, str):
290 text = node.value
291 else:
292 return None
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300293 if clean:
Victor Stinnerf2c1aa12016-01-26 00:40:57 +0100294 import inspect
295 text = inspect.cleandoc(text)
296 return text
Georg Brandl0c77a822008-06-10 16:37:50 +0000297
298
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000299def _splitlines_no_ff(source):
300 """Split a string into lines ignoring form feed and other chars.
301
302 This mimics how the Python parser splits source code.
303 """
304 idx = 0
305 lines = []
306 next_line = ''
307 while idx < len(source):
308 c = source[idx]
309 next_line += c
310 idx += 1
311 # Keep \r\n together
312 if c == '\r' and idx < len(source) and source[idx] == '\n':
313 next_line += '\n'
314 idx += 1
315 if c in '\r\n':
316 lines.append(next_line)
317 next_line = ''
318
319 if next_line:
320 lines.append(next_line)
321 return lines
322
323
324def _pad_whitespace(source):
mpheathfbeba8f2020-02-14 04:32:09 +1000325 r"""Replace all chars except '\f\t' in a line with spaces."""
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000326 result = ''
327 for c in source:
328 if c in '\f\t':
329 result += c
330 else:
331 result += ' '
332 return result
333
334
335def get_source_segment(source, node, *, padded=False):
336 """Get source code segment of the *source* that generated *node*.
337
338 If some location information (`lineno`, `end_lineno`, `col_offset`,
339 or `end_col_offset`) is missing, return None.
340
341 If *padded* is `True`, the first line of a multi-line statement will
342 be padded with spaces to match its original position.
343 """
344 try:
Irit Katriele6578a22020-05-18 19:14:12 +0100345 if node.end_lineno is None or node.end_col_offset is None:
346 return None
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000347 lineno = node.lineno - 1
348 end_lineno = node.end_lineno - 1
349 col_offset = node.col_offset
350 end_col_offset = node.end_col_offset
351 except AttributeError:
352 return None
353
354 lines = _splitlines_no_ff(source)
355 if end_lineno == lineno:
356 return lines[lineno].encode()[col_offset:end_col_offset].decode()
357
358 if padded:
359 padding = _pad_whitespace(lines[lineno].encode()[:col_offset].decode())
360 else:
361 padding = ''
362
363 first = padding + lines[lineno].encode()[col_offset:].decode()
364 last = lines[end_lineno].encode()[:end_col_offset].decode()
365 lines = lines[lineno+1:end_lineno]
366
367 lines.insert(0, first)
368 lines.append(last)
369 return ''.join(lines)
370
371
Georg Brandl0c77a822008-06-10 16:37:50 +0000372def walk(node):
373 """
Georg Brandl619e7ba2011-01-09 07:38:51 +0000374 Recursively yield all descendant nodes in the tree starting at *node*
375 (including *node* itself), in no specified order. This is useful if you
376 only want to modify nodes in place and don't care about the context.
Georg Brandl0c77a822008-06-10 16:37:50 +0000377 """
378 from collections import deque
379 todo = deque([node])
380 while todo:
381 node = todo.popleft()
382 todo.extend(iter_child_nodes(node))
383 yield node
384
385
386class NodeVisitor(object):
387 """
388 A node visitor base class that walks the abstract syntax tree and calls a
389 visitor function for every node found. This function may return a value
390 which is forwarded by the `visit` method.
391
392 This class is meant to be subclassed, with the subclass adding visitor
393 methods.
394
395 Per default the visitor functions for the nodes are ``'visit_'`` +
396 class name of the node. So a `TryFinally` node visit function would
397 be `visit_TryFinally`. This behavior can be changed by overriding
398 the `visit` method. If no visitor function exists for a node
399 (return value `None`) the `generic_visit` visitor is used instead.
400
401 Don't use the `NodeVisitor` if you want to apply changes to nodes during
402 traversing. For this a special visitor exists (`NodeTransformer`) that
403 allows modifications.
404 """
405
406 def visit(self, node):
407 """Visit a node."""
408 method = 'visit_' + node.__class__.__name__
409 visitor = getattr(self, method, self.generic_visit)
410 return visitor(node)
411
412 def generic_visit(self, node):
413 """Called if no explicit visitor function exists for a node."""
414 for field, value in iter_fields(node):
415 if isinstance(value, list):
416 for item in value:
417 if isinstance(item, AST):
418 self.visit(item)
419 elif isinstance(value, AST):
420 self.visit(value)
421
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300422 def visit_Constant(self, node):
423 value = node.value
424 type_name = _const_node_type_names.get(type(value))
425 if type_name is None:
426 for cls, name in _const_node_type_names.items():
427 if isinstance(value, cls):
428 type_name = name
429 break
430 if type_name is not None:
431 method = 'visit_' + type_name
432 try:
433 visitor = getattr(self, method)
434 except AttributeError:
435 pass
436 else:
437 import warnings
438 warnings.warn(f"{method} is deprecated; add visit_Constant",
439 DeprecationWarning, 2)
440 return visitor(node)
441 return self.generic_visit(node)
442
Georg Brandl0c77a822008-06-10 16:37:50 +0000443
444class NodeTransformer(NodeVisitor):
445 """
446 A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
447 allows modification of nodes.
448
449 The `NodeTransformer` will walk the AST and use the return value of the
450 visitor methods to replace or remove the old node. If the return value of
451 the visitor method is ``None``, the node will be removed from its location,
452 otherwise it is replaced with the return value. The return value may be the
453 original node in which case no replacement takes place.
454
455 Here is an example transformer that rewrites all occurrences of name lookups
456 (``foo``) to ``data['foo']``::
457
458 class RewriteName(NodeTransformer):
459
460 def visit_Name(self, node):
Pablo Galindoc00c86b2020-03-12 00:48:19 +0000461 return Subscript(
Georg Brandl0c77a822008-06-10 16:37:50 +0000462 value=Name(id='data', ctx=Load()),
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200463 slice=Constant(value=node.id),
Georg Brandl0c77a822008-06-10 16:37:50 +0000464 ctx=node.ctx
Pablo Galindoc00c86b2020-03-12 00:48:19 +0000465 )
Georg Brandl0c77a822008-06-10 16:37:50 +0000466
467 Keep in mind that if the node you're operating on has child nodes you must
468 either transform the child nodes yourself or call the :meth:`generic_visit`
469 method for the node first.
470
471 For nodes that were part of a collection of statements (that applies to all
472 statement nodes), the visitor may also return a list of nodes rather than
473 just a single node.
474
475 Usually you use the transformer like this::
476
477 node = YourTransformer().visit(node)
478 """
479
480 def generic_visit(self, node):
481 for field, old_value in iter_fields(node):
Georg Brandl0c77a822008-06-10 16:37:50 +0000482 if isinstance(old_value, list):
483 new_values = []
484 for value in old_value:
485 if isinstance(value, AST):
486 value = self.visit(value)
487 if value is None:
488 continue
489 elif not isinstance(value, AST):
490 new_values.extend(value)
491 continue
492 new_values.append(value)
493 old_value[:] = new_values
494 elif isinstance(old_value, AST):
495 new_node = self.visit(old_value)
496 if new_node is None:
497 delattr(node, field)
498 else:
499 setattr(node, field, new_node)
500 return node
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300501
502
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200503# If the ast module is loaded more than once, only add deprecated methods once
504if not hasattr(Constant, 'n'):
505 # The following code is for backward compatibility.
506 # It will be removed in future.
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300507
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200508 def _getter(self):
509 """Deprecated. Use value instead."""
510 return self.value
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300511
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200512 def _setter(self, value):
513 self.value = value
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300514
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200515 Constant.n = property(_getter, _setter)
516 Constant.s = property(_getter, _setter)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300517
518class _ABC(type):
519
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200520 def __init__(cls, *args):
521 cls.__doc__ = """Deprecated AST node class. Use ast.Constant instead"""
522
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300523 def __instancecheck__(cls, inst):
524 if not isinstance(inst, Constant):
525 return False
526 if cls in _const_types:
527 try:
528 value = inst.value
529 except AttributeError:
530 return False
531 else:
Anthony Sottile74176222019-01-18 11:30:28 -0800532 return (
533 isinstance(value, _const_types[cls]) and
534 not isinstance(value, _const_types_not.get(cls, ()))
535 )
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300536 return type.__instancecheck__(cls, inst)
537
538def _new(cls, *args, **kwargs):
Rémi Lapeyrec73914a2020-05-24 23:12:57 +0200539 for key in kwargs:
540 if key not in cls._fields:
541 # arbitrary keyword arguments are accepted
542 continue
543 pos = cls._fields.index(key)
544 if pos < len(args):
545 raise TypeError(f"{cls.__name__} got multiple values for argument {key!r}")
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300546 if cls in _const_types:
547 return Constant(*args, **kwargs)
548 return Constant.__new__(cls, *args, **kwargs)
549
550class Num(Constant, metaclass=_ABC):
551 _fields = ('n',)
552 __new__ = _new
553
554class Str(Constant, metaclass=_ABC):
555 _fields = ('s',)
556 __new__ = _new
557
558class Bytes(Constant, metaclass=_ABC):
559 _fields = ('s',)
560 __new__ = _new
561
562class NameConstant(Constant, metaclass=_ABC):
563 __new__ = _new
564
565class Ellipsis(Constant, metaclass=_ABC):
566 _fields = ()
567
568 def __new__(cls, *args, **kwargs):
569 if cls is Ellipsis:
570 return Constant(..., *args, **kwargs)
571 return Constant.__new__(cls, *args, **kwargs)
572
573_const_types = {
574 Num: (int, float, complex),
575 Str: (str,),
576 Bytes: (bytes,),
577 NameConstant: (type(None), bool),
578 Ellipsis: (type(...),),
579}
Anthony Sottile74176222019-01-18 11:30:28 -0800580_const_types_not = {
581 Num: (bool,),
582}
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200583
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300584_const_node_type_names = {
585 bool: 'NameConstant', # should be before int
586 type(None): 'NameConstant',
587 int: 'Num',
588 float: 'Num',
589 complex: 'Num',
590 str: 'Str',
591 bytes: 'Bytes',
592 type(...): 'Ellipsis',
593}
Serhiy Storchaka832e8642019-09-09 23:36:13 +0300594
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200595class slice(AST):
596 """Deprecated AST node class."""
597
598class Index(slice):
599 """Deprecated AST node class. Use the index value directly instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200600 def __new__(cls, value, **kwargs):
601 return value
602
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200603class ExtSlice(slice):
604 """Deprecated AST node class. Use ast.Tuple instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200605 def __new__(cls, dims=(), **kwargs):
606 return Tuple(list(dims), Load(), **kwargs)
607
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200608# If the ast module is loaded more than once, only add deprecated methods once
609if not hasattr(Tuple, 'dims'):
610 # The following code is for backward compatibility.
611 # It will be removed in future.
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200612
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200613 def _dims_getter(self):
614 """Deprecated. Use elts instead."""
615 return self.elts
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200616
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200617 def _dims_setter(self, value):
618 self.elts = value
619
620 Tuple.dims = property(_dims_getter, _dims_setter)
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200621
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200622class Suite(mod):
623 """Deprecated AST node class. Unused in Python 3."""
624
625class AugLoad(expr_context):
626 """Deprecated AST node class. Unused in Python 3."""
627
628class AugStore(expr_context):
629 """Deprecated AST node class. Unused in Python 3."""
630
631class Param(expr_context):
632 """Deprecated AST node class. Unused in Python 3."""
633
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200634
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000635# Large float and imaginary literals get turned into infinities in the AST.
636# We unparse those infinities to INFSTR.
637_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
638
Ethan Furmana02cb472021-04-21 10:20:44 -0700639@_simple_enum(IntEnum)
640class _Precedence:
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300641 """Precedence table that originated from python grammar."""
642
643 TUPLE = auto()
644 YIELD = auto() # 'yield', 'yield from'
645 TEST = auto() # 'if'-'else', 'lambda'
646 OR = auto() # 'or'
647 AND = auto() # 'and'
648 NOT = auto() # 'not'
649 CMP = auto() # '<', '>', '==', '>=', '<=', '!=',
650 # 'in', 'not in', 'is', 'is not'
651 EXPR = auto()
652 BOR = EXPR # '|'
653 BXOR = auto() # '^'
654 BAND = auto() # '&'
655 SHIFT = auto() # '<<', '>>'
656 ARITH = auto() # '+', '-'
657 TERM = auto() # '*', '@', '/', '%', '//'
658 FACTOR = auto() # unary '+', '-', '~'
659 POWER = auto() # '**'
660 AWAIT = auto() # 'await'
661 ATOM = auto()
662
663 def next(self):
664 try:
665 return self.__class__(self + 1)
666 except ValueError:
667 return self
668
Shantanua993e902020-11-20 13:16:42 -0800669
670_SINGLE_QUOTES = ("'", '"')
671_MULTI_QUOTES = ('"""', "'''")
672_ALL_QUOTES = (*_SINGLE_QUOTES, *_MULTI_QUOTES)
673
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000674class _Unparser(NodeVisitor):
675 """Methods in this class recursively traverse an AST and
676 output source code for the abstract syntax; original formatting
677 is disregarded."""
678
Shantanua993e902020-11-20 13:16:42 -0800679 def __init__(self, *, _avoid_backslashes=False):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000680 self._source = []
681 self._buffer = []
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300682 self._precedences = {}
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300683 self._type_ignores = {}
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000684 self._indent = 0
Shantanua993e902020-11-20 13:16:42 -0800685 self._avoid_backslashes = _avoid_backslashes
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000686
687 def interleave(self, inter, f, seq):
688 """Call f on each item in seq, calling inter() in between."""
689 seq = iter(seq)
690 try:
691 f(next(seq))
692 except StopIteration:
693 pass
694 else:
695 for x in seq:
696 inter()
697 f(x)
698
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +0300699 def items_view(self, traverser, items):
700 """Traverse and separate the given *items* with a comma and append it to
701 the buffer. If *items* is a single item sequence, a trailing comma
702 will be added."""
703 if len(items) == 1:
704 traverser(items[0])
705 self.write(",")
706 else:
707 self.interleave(lambda: self.write(", "), traverser, items)
708
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300709 def maybe_newline(self):
710 """Adds a newline if it isn't the start of generated source"""
711 if self._source:
712 self.write("\n")
713
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000714 def fill(self, text=""):
715 """Indent a piece of text and append it, according to the current
716 indentation level"""
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300717 self.maybe_newline()
718 self.write(" " * self._indent + text)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000719
720 def write(self, text):
721 """Append a piece of text"""
722 self._source.append(text)
723
724 def buffer_writer(self, text):
725 self._buffer.append(text)
726
727 @property
728 def buffer(self):
729 value = "".join(self._buffer)
730 self._buffer.clear()
731 return value
732
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000733 @contextmanager
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300734 def block(self, *, extra = None):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000735 """A context manager for preparing the source for blocks. It adds
736 the character':', increases the indentation on enter and decreases
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300737 the indentation on exit. If *extra* is given, it will be directly
738 appended after the colon character.
739 """
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000740 self.write(":")
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300741 if extra:
742 self.write(extra)
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000743 self._indent += 1
744 yield
745 self._indent -= 1
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000746
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300747 @contextmanager
748 def delimit(self, start, end):
749 """A context manager for preparing the source for expressions. It adds
750 *start* to the buffer and enters, after exit it adds *end*."""
751
752 self.write(start)
753 yield
754 self.write(end)
755
756 def delimit_if(self, start, end, condition):
757 if condition:
758 return self.delimit(start, end)
759 else:
760 return nullcontext()
761
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300762 def require_parens(self, precedence, node):
763 """Shortcut to adding precedence related parens"""
764 return self.delimit_if("(", ")", self.get_precedence(node) > precedence)
765
766 def get_precedence(self, node):
767 return self._precedences.get(node, _Precedence.TEST)
768
769 def set_precedence(self, precedence, *nodes):
770 for node in nodes:
771 self._precedences[node] = precedence
772
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300773 def get_raw_docstring(self, node):
774 """If a docstring node is found in the body of the *node* parameter,
775 return that docstring node, None otherwise.
776
777 Logic mirrored from ``_PyAST_GetDocString``."""
778 if not isinstance(
779 node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)
780 ) or len(node.body) < 1:
781 return None
782 node = node.body[0]
783 if not isinstance(node, Expr):
784 return None
785 node = node.value
786 if isinstance(node, Constant) and isinstance(node.value, str):
787 return node
788
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300789 def get_type_comment(self, node):
790 comment = self._type_ignores.get(node.lineno) or node.type_comment
791 if comment is not None:
792 return f" # type: {comment}"
793
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000794 def traverse(self, node):
795 if isinstance(node, list):
796 for item in node:
797 self.traverse(item)
798 else:
799 super().visit(node)
800
801 def visit(self, node):
802 """Outputs a source code string that, if converted back to an ast
803 (using ast.parse) will generate an AST equivalent to *node*"""
804 self._source = []
805 self.traverse(node)
806 return "".join(self._source)
807
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300808 def _write_docstring_and_traverse_body(self, node):
809 if (docstring := self.get_raw_docstring(node)):
810 self._write_docstring(docstring)
811 self.traverse(node.body[1:])
812 else:
813 self.traverse(node.body)
814
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000815 def visit_Module(self, node):
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300816 self._type_ignores = {
817 ignore.lineno: f"ignore{ignore.tag}"
818 for ignore in node.type_ignores
819 }
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300820 self._write_docstring_and_traverse_body(node)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300821 self._type_ignores.clear()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000822
Batuhan Taşkaya5b66ec12020-03-15 22:56:57 +0300823 def visit_FunctionType(self, node):
824 with self.delimit("(", ")"):
825 self.interleave(
826 lambda: self.write(", "), self.traverse, node.argtypes
827 )
828
829 self.write(" -> ")
830 self.traverse(node.returns)
831
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000832 def visit_Expr(self, node):
833 self.fill()
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300834 self.set_precedence(_Precedence.YIELD, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000835 self.traverse(node.value)
836
837 def visit_NamedExpr(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300838 with self.require_parens(_Precedence.TUPLE, node):
839 self.set_precedence(_Precedence.ATOM, node.target, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300840 self.traverse(node.target)
841 self.write(" := ")
842 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000843
844 def visit_Import(self, node):
845 self.fill("import ")
846 self.interleave(lambda: self.write(", "), self.traverse, node.names)
847
848 def visit_ImportFrom(self, node):
849 self.fill("from ")
850 self.write("." * node.level)
851 if node.module:
852 self.write(node.module)
853 self.write(" import ")
854 self.interleave(lambda: self.write(", "), self.traverse, node.names)
855
856 def visit_Assign(self, node):
857 self.fill()
858 for target in node.targets:
859 self.traverse(target)
860 self.write(" = ")
861 self.traverse(node.value)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300862 if type_comment := self.get_type_comment(node):
863 self.write(type_comment)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000864
865 def visit_AugAssign(self, node):
866 self.fill()
867 self.traverse(node.target)
868 self.write(" " + self.binop[node.op.__class__.__name__] + "= ")
869 self.traverse(node.value)
870
871 def visit_AnnAssign(self, node):
872 self.fill()
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300873 with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)):
874 self.traverse(node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000875 self.write(": ")
876 self.traverse(node.annotation)
877 if node.value:
878 self.write(" = ")
879 self.traverse(node.value)
880
881 def visit_Return(self, node):
882 self.fill("return")
883 if node.value:
884 self.write(" ")
885 self.traverse(node.value)
886
887 def visit_Pass(self, node):
888 self.fill("pass")
889
890 def visit_Break(self, node):
891 self.fill("break")
892
893 def visit_Continue(self, node):
894 self.fill("continue")
895
896 def visit_Delete(self, node):
897 self.fill("del ")
898 self.interleave(lambda: self.write(", "), self.traverse, node.targets)
899
900 def visit_Assert(self, node):
901 self.fill("assert ")
902 self.traverse(node.test)
903 if node.msg:
904 self.write(", ")
905 self.traverse(node.msg)
906
907 def visit_Global(self, node):
908 self.fill("global ")
909 self.interleave(lambda: self.write(", "), self.write, node.names)
910
911 def visit_Nonlocal(self, node):
912 self.fill("nonlocal ")
913 self.interleave(lambda: self.write(", "), self.write, node.names)
914
915 def visit_Await(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300916 with self.require_parens(_Precedence.AWAIT, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300917 self.write("await")
918 if node.value:
919 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300920 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300921 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000922
923 def visit_Yield(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300924 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300925 self.write("yield")
926 if node.value:
927 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300928 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300929 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000930
931 def visit_YieldFrom(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300932 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300933 self.write("yield from ")
934 if not node.value:
935 raise ValueError("Node can't be used without a value attribute.")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300936 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300937 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000938
939 def visit_Raise(self, node):
940 self.fill("raise")
941 if not node.exc:
942 if node.cause:
943 raise ValueError(f"Node can't use cause without an exception.")
944 return
945 self.write(" ")
946 self.traverse(node.exc)
947 if node.cause:
948 self.write(" from ")
949 self.traverse(node.cause)
950
951 def visit_Try(self, node):
952 self.fill("try")
953 with self.block():
954 self.traverse(node.body)
955 for ex in node.handlers:
956 self.traverse(ex)
957 if node.orelse:
958 self.fill("else")
959 with self.block():
960 self.traverse(node.orelse)
961 if node.finalbody:
962 self.fill("finally")
963 with self.block():
964 self.traverse(node.finalbody)
965
966 def visit_ExceptHandler(self, node):
967 self.fill("except")
968 if node.type:
969 self.write(" ")
970 self.traverse(node.type)
971 if node.name:
972 self.write(" as ")
973 self.write(node.name)
974 with self.block():
975 self.traverse(node.body)
976
977 def visit_ClassDef(self, node):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300978 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000979 for deco in node.decorator_list:
980 self.fill("@")
981 self.traverse(deco)
982 self.fill("class " + node.name)
Batuhan Taskaya25160cd2020-05-17 00:53:25 +0300983 with self.delimit_if("(", ")", condition = node.bases or node.keywords):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300984 comma = False
985 for e in node.bases:
986 if comma:
987 self.write(", ")
988 else:
989 comma = True
990 self.traverse(e)
991 for e in node.keywords:
992 if comma:
993 self.write(", ")
994 else:
995 comma = True
996 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000997
998 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300999 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001000
1001 def visit_FunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001002 self._function_helper(node, "def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001003
1004 def visit_AsyncFunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001005 self._function_helper(node, "async def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001006
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001007 def _function_helper(self, node, fill_suffix):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +03001008 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001009 for deco in node.decorator_list:
1010 self.fill("@")
1011 self.traverse(deco)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001012 def_str = fill_suffix + " " + node.name
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001013 self.fill(def_str)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001014 with self.delimit("(", ")"):
1015 self.traverse(node.args)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001016 if node.returns:
1017 self.write(" -> ")
1018 self.traverse(node.returns)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001019 with self.block(extra=self.get_type_comment(node)):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001020 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001021
1022 def visit_For(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001023 self._for_helper("for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001024
1025 def visit_AsyncFor(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001026 self._for_helper("async for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001027
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001028 def _for_helper(self, fill, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001029 self.fill(fill)
1030 self.traverse(node.target)
1031 self.write(" in ")
1032 self.traverse(node.iter)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001033 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001034 self.traverse(node.body)
1035 if node.orelse:
1036 self.fill("else")
1037 with self.block():
1038 self.traverse(node.orelse)
1039
1040 def visit_If(self, node):
1041 self.fill("if ")
1042 self.traverse(node.test)
1043 with self.block():
1044 self.traverse(node.body)
1045 # collapse nested ifs into equivalent elifs.
1046 while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If):
1047 node = node.orelse[0]
1048 self.fill("elif ")
1049 self.traverse(node.test)
1050 with self.block():
1051 self.traverse(node.body)
1052 # final else
1053 if node.orelse:
1054 self.fill("else")
1055 with self.block():
1056 self.traverse(node.orelse)
1057
1058 def visit_While(self, node):
1059 self.fill("while ")
1060 self.traverse(node.test)
1061 with self.block():
1062 self.traverse(node.body)
1063 if node.orelse:
1064 self.fill("else")
1065 with self.block():
1066 self.traverse(node.orelse)
1067
1068 def visit_With(self, node):
1069 self.fill("with ")
1070 self.interleave(lambda: self.write(", "), self.traverse, node.items)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001071 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001072 self.traverse(node.body)
1073
1074 def visit_AsyncWith(self, node):
1075 self.fill("async with ")
1076 self.interleave(lambda: self.write(", "), self.traverse, node.items)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001077 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001078 self.traverse(node.body)
1079
Shantanua993e902020-11-20 13:16:42 -08001080 def _str_literal_helper(
1081 self, string, *, quote_types=_ALL_QUOTES, escape_special_whitespace=False
1082 ):
1083 """Helper for writing string literals, minimizing escapes.
1084 Returns the tuple (string literal to write, possible quote types).
1085 """
1086 def escape_char(c):
1087 # \n and \t are non-printable, but we only escape them if
1088 # escape_special_whitespace is True
1089 if not escape_special_whitespace and c in "\n\t":
1090 return c
1091 # Always escape backslashes and other non-printable characters
1092 if c == "\\" or not c.isprintable():
1093 return c.encode("unicode_escape").decode("ascii")
1094 return c
1095
1096 escaped_string = "".join(map(escape_char, string))
1097 possible_quotes = quote_types
1098 if "\n" in escaped_string:
1099 possible_quotes = [q for q in possible_quotes if q in _MULTI_QUOTES]
1100 possible_quotes = [q for q in possible_quotes if q not in escaped_string]
1101 if not possible_quotes:
1102 # If there aren't any possible_quotes, fallback to using repr
1103 # on the original string. Try to use a quote from quote_types,
1104 # e.g., so that we use triple quotes for docstrings.
1105 string = repr(string)
1106 quote = next((q for q in quote_types if string[0] in q), string[0])
1107 return string[1:-1], [quote]
1108 if escaped_string:
1109 # Sort so that we prefer '''"''' over """\""""
1110 possible_quotes.sort(key=lambda q: q[0] == escaped_string[-1])
1111 # If we're using triple quotes and we'd need to escape a final
1112 # quote, escape it
1113 if possible_quotes[0][0] == escaped_string[-1]:
1114 assert len(possible_quotes[0]) == 3
1115 escaped_string = escaped_string[:-1] + "\\" + escaped_string[-1]
1116 return escaped_string, possible_quotes
1117
1118 def _write_str_avoiding_backslashes(self, string, *, quote_types=_ALL_QUOTES):
1119 """Write string literal value with a best effort attempt to avoid backslashes."""
1120 string, quote_types = self._str_literal_helper(string, quote_types=quote_types)
1121 quote_type = quote_types[0]
1122 self.write(f"{quote_type}{string}{quote_type}")
1123
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001124 def visit_JoinedStr(self, node):
1125 self.write("f")
Shantanua993e902020-11-20 13:16:42 -08001126 if self._avoid_backslashes:
1127 self._fstring_JoinedStr(node, self.buffer_writer)
1128 self._write_str_avoiding_backslashes(self.buffer)
1129 return
1130
1131 # If we don't need to avoid backslashes globally (i.e., we only need
1132 # to avoid them inside FormattedValues), it's cosmetically preferred
1133 # to use escaped whitespace. That is, it's preferred to use backslashes
1134 # for cases like: f"{x}\n". To accomplish this, we keep track of what
1135 # in our buffer corresponds to FormattedValues and what corresponds to
1136 # Constant parts of the f-string, and allow escapes accordingly.
1137 buffer = []
1138 for value in node.values:
1139 meth = getattr(self, "_fstring_" + type(value).__name__)
1140 meth(value, self.buffer_writer)
1141 buffer.append((self.buffer, isinstance(value, Constant)))
1142 new_buffer = []
1143 quote_types = _ALL_QUOTES
1144 for value, is_constant in buffer:
1145 # Repeatedly narrow down the list of possible quote_types
1146 value, quote_types = self._str_literal_helper(
1147 value, quote_types=quote_types,
1148 escape_special_whitespace=is_constant
1149 )
1150 new_buffer.append(value)
1151 value = "".join(new_buffer)
1152 quote_type = quote_types[0]
1153 self.write(f"{quote_type}{value}{quote_type}")
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001154
1155 def visit_FormattedValue(self, node):
1156 self.write("f")
1157 self._fstring_FormattedValue(node, self.buffer_writer)
Shantanua993e902020-11-20 13:16:42 -08001158 self._write_str_avoiding_backslashes(self.buffer)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001159
1160 def _fstring_JoinedStr(self, node, write):
1161 for value in node.values:
1162 meth = getattr(self, "_fstring_" + type(value).__name__)
1163 meth(value, write)
1164
1165 def _fstring_Constant(self, node, write):
1166 if not isinstance(node.value, str):
1167 raise ValueError("Constants inside JoinedStr should be a string.")
1168 value = node.value.replace("{", "{{").replace("}", "}}")
1169 write(value)
1170
1171 def _fstring_FormattedValue(self, node, write):
1172 write("{")
Shantanua993e902020-11-20 13:16:42 -08001173 unparser = type(self)(_avoid_backslashes=True)
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001174 unparser.set_precedence(_Precedence.TEST.next(), node.value)
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +03001175 expr = unparser.visit(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001176 if expr.startswith("{"):
1177 write(" ") # Separate pair of opening brackets as "{ {"
Shantanua993e902020-11-20 13:16:42 -08001178 if "\\" in expr:
1179 raise ValueError("Unable to avoid backslash in f-string expression part")
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001180 write(expr)
1181 if node.conversion != -1:
1182 conversion = chr(node.conversion)
1183 if conversion not in "sra":
1184 raise ValueError("Unknown f-string conversion.")
1185 write(f"!{conversion}")
1186 if node.format_spec:
1187 write(":")
1188 meth = getattr(self, "_fstring_" + type(node.format_spec).__name__)
1189 meth(node.format_spec, write)
1190 write("}")
1191
1192 def visit_Name(self, node):
1193 self.write(node.id)
1194
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001195 def _write_docstring(self, node):
1196 self.fill()
1197 if node.kind == "u":
1198 self.write("u")
Shantanua993e902020-11-20 13:16:42 -08001199 self._write_str_avoiding_backslashes(node.value, quote_types=_MULTI_QUOTES)
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001200
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001201 def _write_constant(self, value):
1202 if isinstance(value, (float, complex)):
Kodi Arfer08ff4362021-03-18 13:36:06 -04001203 # Substitute overflowing decimal literal for AST infinities,
1204 # and inf - inf for NaNs.
1205 self.write(
1206 repr(value)
1207 .replace("inf", _INFSTR)
1208 .replace("nan", f"({_INFSTR}-{_INFSTR})")
1209 )
Shantanua993e902020-11-20 13:16:42 -08001210 elif self._avoid_backslashes and isinstance(value, str):
1211 self._write_str_avoiding_backslashes(value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001212 else:
1213 self.write(repr(value))
1214
1215 def visit_Constant(self, node):
1216 value = node.value
1217 if isinstance(value, tuple):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001218 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001219 self.items_view(self._write_constant, value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001220 elif value is ...:
1221 self.write("...")
1222 else:
1223 if node.kind == "u":
1224 self.write("u")
1225 self._write_constant(node.value)
1226
1227 def visit_List(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001228 with self.delimit("[", "]"):
1229 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001230
1231 def visit_ListComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001232 with self.delimit("[", "]"):
1233 self.traverse(node.elt)
1234 for gen in node.generators:
1235 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001236
1237 def visit_GeneratorExp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001238 with self.delimit("(", ")"):
1239 self.traverse(node.elt)
1240 for gen in node.generators:
1241 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001242
1243 def visit_SetComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001244 with self.delimit("{", "}"):
1245 self.traverse(node.elt)
1246 for gen in node.generators:
1247 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001248
1249 def visit_DictComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001250 with self.delimit("{", "}"):
1251 self.traverse(node.key)
1252 self.write(": ")
1253 self.traverse(node.value)
1254 for gen in node.generators:
1255 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001256
1257 def visit_comprehension(self, node):
1258 if node.is_async:
1259 self.write(" async for ")
1260 else:
1261 self.write(" for ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001262 self.set_precedence(_Precedence.TUPLE, node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001263 self.traverse(node.target)
1264 self.write(" in ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001265 self.set_precedence(_Precedence.TEST.next(), node.iter, *node.ifs)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001266 self.traverse(node.iter)
1267 for if_clause in node.ifs:
1268 self.write(" if ")
1269 self.traverse(if_clause)
1270
1271 def visit_IfExp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001272 with self.require_parens(_Precedence.TEST, node):
1273 self.set_precedence(_Precedence.TEST.next(), node.body, node.test)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001274 self.traverse(node.body)
1275 self.write(" if ")
1276 self.traverse(node.test)
1277 self.write(" else ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001278 self.set_precedence(_Precedence.TEST, node.orelse)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001279 self.traverse(node.orelse)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001280
1281 def visit_Set(self, node):
Kodi Arfer08ff4362021-03-18 13:36:06 -04001282 if node.elts:
1283 with self.delimit("{", "}"):
1284 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
1285 else:
1286 # `{}` would be interpreted as a dictionary literal, and
1287 # `set` might be shadowed. Thus:
1288 self.write('{*()}')
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001289
1290 def visit_Dict(self, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001291 def write_key_value_pair(k, v):
1292 self.traverse(k)
1293 self.write(": ")
1294 self.traverse(v)
1295
1296 def write_item(item):
1297 k, v = item
1298 if k is None:
1299 # for dictionary unpacking operator in dicts {**{'y': 2}}
1300 # see PEP 448 for details
1301 self.write("**")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001302 self.set_precedence(_Precedence.EXPR, v)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001303 self.traverse(v)
1304 else:
1305 write_key_value_pair(k, v)
1306
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001307 with self.delimit("{", "}"):
1308 self.interleave(
1309 lambda: self.write(", "), write_item, zip(node.keys, node.values)
1310 )
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001311
1312 def visit_Tuple(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001313 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001314 self.items_view(self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001315
1316 unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001317 unop_precedence = {
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001318 "not": _Precedence.NOT,
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001319 "~": _Precedence.FACTOR,
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001320 "+": _Precedence.FACTOR,
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001321 "-": _Precedence.FACTOR,
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001322 }
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001323
1324 def visit_UnaryOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001325 operator = self.unop[node.op.__class__.__name__]
1326 operator_precedence = self.unop_precedence[operator]
1327 with self.require_parens(operator_precedence, node):
1328 self.write(operator)
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001329 # factor prefixes (+, -, ~) shouldn't be seperated
1330 # from the value they belong, (e.g: +1 instead of + 1)
1331 if operator_precedence is not _Precedence.FACTOR:
1332 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001333 self.set_precedence(operator_precedence, node.operand)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001334 self.traverse(node.operand)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001335
1336 binop = {
1337 "Add": "+",
1338 "Sub": "-",
1339 "Mult": "*",
1340 "MatMult": "@",
1341 "Div": "/",
1342 "Mod": "%",
1343 "LShift": "<<",
1344 "RShift": ">>",
1345 "BitOr": "|",
1346 "BitXor": "^",
1347 "BitAnd": "&",
1348 "FloorDiv": "//",
1349 "Pow": "**",
1350 }
1351
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001352 binop_precedence = {
1353 "+": _Precedence.ARITH,
1354 "-": _Precedence.ARITH,
1355 "*": _Precedence.TERM,
1356 "@": _Precedence.TERM,
1357 "/": _Precedence.TERM,
1358 "%": _Precedence.TERM,
1359 "<<": _Precedence.SHIFT,
1360 ">>": _Precedence.SHIFT,
1361 "|": _Precedence.BOR,
1362 "^": _Precedence.BXOR,
1363 "&": _Precedence.BAND,
1364 "//": _Precedence.TERM,
1365 "**": _Precedence.POWER,
1366 }
1367
1368 binop_rassoc = frozenset(("**",))
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001369 def visit_BinOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001370 operator = self.binop[node.op.__class__.__name__]
1371 operator_precedence = self.binop_precedence[operator]
1372 with self.require_parens(operator_precedence, node):
1373 if operator in self.binop_rassoc:
1374 left_precedence = operator_precedence.next()
1375 right_precedence = operator_precedence
1376 else:
1377 left_precedence = operator_precedence
1378 right_precedence = operator_precedence.next()
1379
1380 self.set_precedence(left_precedence, node.left)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001381 self.traverse(node.left)
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001382 self.write(f" {operator} ")
1383 self.set_precedence(right_precedence, node.right)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001384 self.traverse(node.right)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001385
1386 cmpops = {
1387 "Eq": "==",
1388 "NotEq": "!=",
1389 "Lt": "<",
1390 "LtE": "<=",
1391 "Gt": ">",
1392 "GtE": ">=",
1393 "Is": "is",
1394 "IsNot": "is not",
1395 "In": "in",
1396 "NotIn": "not in",
1397 }
1398
1399 def visit_Compare(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001400 with self.require_parens(_Precedence.CMP, node):
1401 self.set_precedence(_Precedence.CMP.next(), node.left, *node.comparators)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001402 self.traverse(node.left)
1403 for o, e in zip(node.ops, node.comparators):
1404 self.write(" " + self.cmpops[o.__class__.__name__] + " ")
1405 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001406
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001407 boolops = {"And": "and", "Or": "or"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001408 boolop_precedence = {"and": _Precedence.AND, "or": _Precedence.OR}
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001409
1410 def visit_BoolOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001411 operator = self.boolops[node.op.__class__.__name__]
1412 operator_precedence = self.boolop_precedence[operator]
1413
1414 def increasing_level_traverse(node):
1415 nonlocal operator_precedence
1416 operator_precedence = operator_precedence.next()
1417 self.set_precedence(operator_precedence, node)
1418 self.traverse(node)
1419
1420 with self.require_parens(operator_precedence, node):
1421 s = f" {operator} "
1422 self.interleave(lambda: self.write(s), increasing_level_traverse, node.values)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001423
1424 def visit_Attribute(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001425 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001426 self.traverse(node.value)
1427 # Special case: 3.__abs__() is a syntax error, so if node.value
1428 # is an integer literal then we need to either parenthesize
1429 # it or add an extra space to get 3 .__abs__().
1430 if isinstance(node.value, Constant) and isinstance(node.value.value, int):
1431 self.write(" ")
1432 self.write(".")
1433 self.write(node.attr)
1434
1435 def visit_Call(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001436 self.set_precedence(_Precedence.ATOM, node.func)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001437 self.traverse(node.func)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001438 with self.delimit("(", ")"):
1439 comma = False
1440 for e in node.args:
1441 if comma:
1442 self.write(", ")
1443 else:
1444 comma = True
1445 self.traverse(e)
1446 for e in node.keywords:
1447 if comma:
1448 self.write(", ")
1449 else:
1450 comma = True
1451 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001452
1453 def visit_Subscript(self, node):
Batuhan Taskayac102a142020-05-18 23:48:49 +03001454 def is_simple_tuple(slice_value):
1455 # when unparsing a non-empty tuple, the parantheses can be safely
1456 # omitted if there aren't any elements that explicitly requires
1457 # parantheses (such as starred expressions).
1458 return (
1459 isinstance(slice_value, Tuple)
1460 and slice_value.elts
1461 and not any(isinstance(elt, Starred) for elt in slice_value.elts)
1462 )
1463
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001464 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001465 self.traverse(node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001466 with self.delimit("[", "]"):
Batuhan Taskayac102a142020-05-18 23:48:49 +03001467 if is_simple_tuple(node.slice):
Serhiy Storchaka13d52c22020-03-10 18:52:34 +02001468 self.items_view(self.traverse, node.slice.elts)
Serhiy Storchakac4928fc2020-03-07 17:25:32 +02001469 else:
1470 self.traverse(node.slice)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001471
1472 def visit_Starred(self, node):
1473 self.write("*")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001474 self.set_precedence(_Precedence.EXPR, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001475 self.traverse(node.value)
1476
1477 def visit_Ellipsis(self, node):
1478 self.write("...")
1479
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001480 def visit_Slice(self, node):
1481 if node.lower:
1482 self.traverse(node.lower)
1483 self.write(":")
1484 if node.upper:
1485 self.traverse(node.upper)
1486 if node.step:
1487 self.write(":")
1488 self.traverse(node.step)
1489
Brandt Bucher145bf262021-02-26 14:51:55 -08001490 def visit_Match(self, node):
1491 self.fill("match ")
1492 self.traverse(node.subject)
1493 with self.block():
1494 for case in node.cases:
1495 self.traverse(case)
1496
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001497 def visit_arg(self, node):
1498 self.write(node.arg)
1499 if node.annotation:
1500 self.write(": ")
1501 self.traverse(node.annotation)
1502
1503 def visit_arguments(self, node):
1504 first = True
1505 # normal arguments
1506 all_args = node.posonlyargs + node.args
1507 defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults
1508 for index, elements in enumerate(zip(all_args, defaults), 1):
1509 a, d = elements
1510 if first:
1511 first = False
1512 else:
1513 self.write(", ")
1514 self.traverse(a)
1515 if d:
1516 self.write("=")
1517 self.traverse(d)
1518 if index == len(node.posonlyargs):
1519 self.write(", /")
1520
1521 # varargs, or bare '*' if no varargs but keyword-only arguments present
1522 if node.vararg or node.kwonlyargs:
1523 if first:
1524 first = False
1525 else:
1526 self.write(", ")
1527 self.write("*")
1528 if node.vararg:
1529 self.write(node.vararg.arg)
1530 if node.vararg.annotation:
1531 self.write(": ")
1532 self.traverse(node.vararg.annotation)
1533
1534 # keyword-only arguments
1535 if node.kwonlyargs:
1536 for a, d in zip(node.kwonlyargs, node.kw_defaults):
Batuhan Taşkayaa322f502019-12-16 15:26:58 +03001537 self.write(", ")
1538 self.traverse(a)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001539 if d:
1540 self.write("=")
1541 self.traverse(d)
1542
1543 # kwargs
1544 if node.kwarg:
1545 if first:
1546 first = False
1547 else:
1548 self.write(", ")
1549 self.write("**" + node.kwarg.arg)
1550 if node.kwarg.annotation:
1551 self.write(": ")
1552 self.traverse(node.kwarg.annotation)
1553
1554 def visit_keyword(self, node):
1555 if node.arg is None:
1556 self.write("**")
1557 else:
1558 self.write(node.arg)
1559 self.write("=")
1560 self.traverse(node.value)
1561
1562 def visit_Lambda(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001563 with self.require_parens(_Precedence.TEST, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001564 self.write("lambda ")
1565 self.traverse(node.args)
1566 self.write(": ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001567 self.set_precedence(_Precedence.TEST, node.body)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001568 self.traverse(node.body)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001569
1570 def visit_alias(self, node):
1571 self.write(node.name)
1572 if node.asname:
1573 self.write(" as " + node.asname)
1574
1575 def visit_withitem(self, node):
1576 self.traverse(node.context_expr)
1577 if node.optional_vars:
1578 self.write(" as ")
1579 self.traverse(node.optional_vars)
1580
Brandt Bucher145bf262021-02-26 14:51:55 -08001581 def visit_match_case(self, node):
1582 self.fill("case ")
1583 self.traverse(node.pattern)
1584 if node.guard:
1585 self.write(" if ")
1586 self.traverse(node.guard)
1587 with self.block():
1588 self.traverse(node.body)
1589
1590 def visit_MatchAs(self, node):
1591 with self.require_parens(_Precedence.TEST, node):
1592 self.set_precedence(_Precedence.BOR, node.pattern)
1593 self.traverse(node.pattern)
1594 self.write(f" as {node.name}")
1595
1596 def visit_MatchOr(self, node):
1597 with self.require_parens(_Precedence.BOR, node):
1598 self.set_precedence(_Precedence.BOR.next(), *node.patterns)
1599 self.interleave(lambda: self.write(" | "), self.traverse, node.patterns)
1600
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001601def unparse(ast_obj):
1602 unparser = _Unparser()
1603 return unparser.visit(ast_obj)
1604
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001605
1606def main():
1607 import argparse
1608
1609 parser = argparse.ArgumentParser(prog='python -m ast')
1610 parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?',
1611 default='-',
1612 help='the file to parse; defaults to stdin')
1613 parser.add_argument('-m', '--mode', default='exec',
1614 choices=('exec', 'single', 'eval', 'func_type'),
1615 help='specify what kind of code must be parsed')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001616 parser.add_argument('--no-type-comments', default=True, action='store_false',
1617 help="don't add information about type comments")
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001618 parser.add_argument('-a', '--include-attributes', action='store_true',
1619 help='include attributes such as line numbers and '
1620 'column offsets')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001621 parser.add_argument('-i', '--indent', type=int, default=3,
1622 help='indentation of nodes (number of spaces)')
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001623 args = parser.parse_args()
1624
1625 with args.infile as infile:
1626 source = infile.read()
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001627 tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
1628 print(dump(tree, include_attributes=args.include_attributes, indent=args.indent))
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001629
1630if __name__ == '__main__':
1631 main()