blob: 401af5647a240ceb9d0afad045f7e5c1921b79ee [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
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +030030from enum import IntEnum, auto
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):
62 node_or_string = parse(node_or_string, mode='eval')
63 if isinstance(node_or_string, Expression):
64 node_or_string = node_or_string.body
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020065 def _convert_num(node):
66 if isinstance(node, Constant):
Serhiy Storchaka3f228112018-09-27 17:42:37 +030067 if type(node.value) in (int, float, complex):
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020068 return node.value
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020069 raise ValueError('malformed node or string: ' + repr(node))
70 def _convert_signed_num(node):
71 if isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)):
72 operand = _convert_num(node.operand)
73 if isinstance(node.op, UAdd):
74 return + operand
75 else:
76 return - operand
77 return _convert_num(node)
Georg Brandl0c77a822008-06-10 16:37:50 +000078 def _convert(node):
Victor Stinnerf2c1aa12016-01-26 00:40:57 +010079 if isinstance(node, Constant):
80 return node.value
Georg Brandl0c77a822008-06-10 16:37:50 +000081 elif isinstance(node, Tuple):
82 return tuple(map(_convert, node.elts))
83 elif isinstance(node, List):
84 return list(map(_convert, node.elts))
Georg Brandl492f3fc2010-07-11 09:41:21 +000085 elif isinstance(node, Set):
86 return set(map(_convert, node.elts))
Raymond Hettinger4fcf5c12020-01-02 22:21:18 -070087 elif (isinstance(node, Call) and isinstance(node.func, Name) and
88 node.func.id == 'set' and node.args == node.keywords == []):
89 return set()
Georg Brandl0c77a822008-06-10 16:37:50 +000090 elif isinstance(node, Dict):
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020091 return dict(zip(map(_convert, node.keys),
92 map(_convert, node.values)))
Victor Stinnerf2c1aa12016-01-26 00:40:57 +010093 elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)):
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020094 left = _convert_signed_num(node.left)
95 right = _convert_num(node.right)
96 if isinstance(left, (int, float)) and isinstance(right, complex):
Victor Stinnerf2c1aa12016-01-26 00:40:57 +010097 if isinstance(node.op, Add):
98 return left + right
99 else:
100 return left - right
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +0200101 return _convert_signed_num(node)
Georg Brandl0c77a822008-06-10 16:37:50 +0000102 return _convert(node_or_string)
103
104
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300105def dump(node, annotate_fields=True, include_attributes=False, *, indent=None):
Georg Brandl0c77a822008-06-10 16:37:50 +0000106 """
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300107 Return a formatted dump of the tree in node. This is mainly useful for
108 debugging purposes. If annotate_fields is true (by default),
109 the returned string will show the names and the values for fields.
110 If annotate_fields is false, the result string will be more compact by
111 omitting unambiguous field names. Attributes such as line
Benjamin Petersondcf97b92008-07-02 17:30:14 +0000112 numbers and column offsets are not dumped by default. If this is wanted,
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300113 include_attributes can be set to true. If indent is a non-negative
114 integer or string, then the tree will be pretty-printed with that indent
115 level. None (the default) selects the single line representation.
Georg Brandl0c77a822008-06-10 16:37:50 +0000116 """
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300117 def _format(node, level=0):
118 if indent is not None:
119 level += 1
120 prefix = '\n' + indent * level
121 sep = ',\n' + indent * level
122 else:
123 prefix = ''
124 sep = ', '
Georg Brandl0c77a822008-06-10 16:37:50 +0000125 if isinstance(node, AST):
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200126 cls = type(node)
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300127 args = []
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300128 allsimple = True
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300129 keywords = annotate_fields
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200130 for name in node._fields:
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300131 try:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200132 value = getattr(node, name)
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300133 except AttributeError:
134 keywords = True
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200135 continue
136 if value is None and getattr(cls, name, ...) is None:
137 keywords = True
138 continue
139 value, simple = _format(value, level)
140 allsimple = allsimple and simple
141 if keywords:
142 args.append('%s=%s' % (name, value))
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300143 else:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200144 args.append(value)
145 if include_attributes and node._attributes:
146 for name in node._attributes:
147 try:
148 value = getattr(node, name)
149 except AttributeError:
150 continue
151 if value is None and getattr(cls, name, ...) is None:
152 continue
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300153 value, simple = _format(value, level)
154 allsimple = allsimple and simple
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200155 args.append('%s=%s' % (name, value))
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300156 if allsimple and len(args) <= 3:
157 return '%s(%s)' % (node.__class__.__name__, ', '.join(args)), not args
158 return '%s(%s%s)' % (node.__class__.__name__, prefix, sep.join(args)), False
Georg Brandl0c77a822008-06-10 16:37:50 +0000159 elif isinstance(node, list):
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300160 if not node:
161 return '[]', True
162 return '[%s%s]' % (prefix, sep.join(_format(x, level)[0] for x in node)), False
163 return repr(node), True
164
Georg Brandl0c77a822008-06-10 16:37:50 +0000165 if not isinstance(node, AST):
166 raise TypeError('expected AST, got %r' % node.__class__.__name__)
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300167 if indent is not None and not isinstance(indent, str):
168 indent = ' ' * indent
169 return _format(node)[0]
Georg Brandl0c77a822008-06-10 16:37:50 +0000170
171
172def copy_location(new_node, old_node):
173 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000174 Copy source location (`lineno`, `col_offset`, `end_lineno`, and `end_col_offset`
175 attributes) from *old_node* to *new_node* if possible, and return *new_node*.
Georg Brandl0c77a822008-06-10 16:37:50 +0000176 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000177 for attr in 'lineno', 'col_offset', 'end_lineno', 'end_col_offset':
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200178 if attr in old_node._attributes and attr in new_node._attributes:
179 value = getattr(old_node, attr, None)
180 if value is not None:
181 setattr(new_node, attr, value)
Georg Brandl0c77a822008-06-10 16:37:50 +0000182 return new_node
183
184
185def fix_missing_locations(node):
186 """
187 When you compile a node tree with compile(), the compiler expects lineno and
188 col_offset attributes for every node that supports them. This is rather
189 tedious to fill in for generated nodes, so this helper adds these attributes
190 recursively where not already set, by setting them to the values of the
191 parent node. It works recursively starting at *node*.
192 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000193 def _fix(node, lineno, col_offset, end_lineno, end_col_offset):
Georg Brandl0c77a822008-06-10 16:37:50 +0000194 if 'lineno' in node._attributes:
195 if not hasattr(node, 'lineno'):
196 node.lineno = lineno
197 else:
198 lineno = node.lineno
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000199 if 'end_lineno' in node._attributes:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200200 if getattr(node, 'end_lineno', None) is None:
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000201 node.end_lineno = end_lineno
202 else:
203 end_lineno = node.end_lineno
Georg Brandl0c77a822008-06-10 16:37:50 +0000204 if 'col_offset' in node._attributes:
205 if not hasattr(node, 'col_offset'):
206 node.col_offset = col_offset
207 else:
208 col_offset = node.col_offset
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000209 if 'end_col_offset' in node._attributes:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200210 if getattr(node, 'end_col_offset', None) is None:
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000211 node.end_col_offset = end_col_offset
212 else:
213 end_col_offset = node.end_col_offset
Georg Brandl0c77a822008-06-10 16:37:50 +0000214 for child in iter_child_nodes(node):
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000215 _fix(child, lineno, col_offset, end_lineno, end_col_offset)
216 _fix(node, 1, 0, 1, 0)
Georg Brandl0c77a822008-06-10 16:37:50 +0000217 return node
218
219
220def increment_lineno(node, n=1):
221 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000222 Increment the line number and end line number of each node in the tree
223 starting at *node* by *n*. This is useful to "move code" to a different
224 location in a file.
Georg Brandl0c77a822008-06-10 16:37:50 +0000225 """
Georg Brandl0c77a822008-06-10 16:37:50 +0000226 for child in walk(node):
227 if 'lineno' in child._attributes:
228 child.lineno = getattr(child, 'lineno', 0) + n
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000229 if 'end_lineno' in child._attributes:
230 child.end_lineno = getattr(child, 'end_lineno', 0) + n
Georg Brandl0c77a822008-06-10 16:37:50 +0000231 return node
232
233
234def iter_fields(node):
235 """
236 Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
237 that is present on *node*.
238 """
239 for field in node._fields:
240 try:
241 yield field, getattr(node, field)
242 except AttributeError:
243 pass
244
245
246def iter_child_nodes(node):
247 """
248 Yield all direct child nodes of *node*, that is, all fields that are nodes
249 and all items of fields that are lists of nodes.
250 """
251 for name, field in iter_fields(node):
252 if isinstance(field, AST):
253 yield field
254 elif isinstance(field, list):
255 for item in field:
256 if isinstance(item, AST):
257 yield item
258
259
260def get_docstring(node, clean=True):
261 """
262 Return the docstring for the given node or None if no docstring can
263 be found. If the node provided does not have docstrings a TypeError
264 will be raised.
Matthias Bussonnier41cea702017-02-23 22:44:19 -0800265
266 If *clean* is `True`, all tabs are expanded to spaces and any whitespace
267 that can be uniformly removed from the second line onwards is removed.
Georg Brandl0c77a822008-06-10 16:37:50 +0000268 """
Yury Selivanov2f07a662015-07-23 08:54:35 +0300269 if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
Georg Brandl0c77a822008-06-10 16:37:50 +0000270 raise TypeError("%r can't have docstrings" % node.__class__.__name__)
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300271 if not(node.body and isinstance(node.body[0], Expr)):
Serhiy Storchaka73cbe7a2018-05-29 12:04:55 +0300272 return None
273 node = node.body[0].value
274 if isinstance(node, Str):
275 text = node.s
276 elif isinstance(node, Constant) and isinstance(node.value, str):
277 text = node.value
278 else:
279 return None
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300280 if clean:
Victor Stinnerf2c1aa12016-01-26 00:40:57 +0100281 import inspect
282 text = inspect.cleandoc(text)
283 return text
Georg Brandl0c77a822008-06-10 16:37:50 +0000284
285
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000286def _splitlines_no_ff(source):
287 """Split a string into lines ignoring form feed and other chars.
288
289 This mimics how the Python parser splits source code.
290 """
291 idx = 0
292 lines = []
293 next_line = ''
294 while idx < len(source):
295 c = source[idx]
296 next_line += c
297 idx += 1
298 # Keep \r\n together
299 if c == '\r' and idx < len(source) and source[idx] == '\n':
300 next_line += '\n'
301 idx += 1
302 if c in '\r\n':
303 lines.append(next_line)
304 next_line = ''
305
306 if next_line:
307 lines.append(next_line)
308 return lines
309
310
311def _pad_whitespace(source):
mpheathfbeba8f2020-02-14 04:32:09 +1000312 r"""Replace all chars except '\f\t' in a line with spaces."""
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000313 result = ''
314 for c in source:
315 if c in '\f\t':
316 result += c
317 else:
318 result += ' '
319 return result
320
321
322def get_source_segment(source, node, *, padded=False):
323 """Get source code segment of the *source* that generated *node*.
324
325 If some location information (`lineno`, `end_lineno`, `col_offset`,
326 or `end_col_offset`) is missing, return None.
327
328 If *padded* is `True`, the first line of a multi-line statement will
329 be padded with spaces to match its original position.
330 """
331 try:
332 lineno = node.lineno - 1
333 end_lineno = node.end_lineno - 1
334 col_offset = node.col_offset
335 end_col_offset = node.end_col_offset
336 except AttributeError:
337 return None
338
339 lines = _splitlines_no_ff(source)
340 if end_lineno == lineno:
341 return lines[lineno].encode()[col_offset:end_col_offset].decode()
342
343 if padded:
344 padding = _pad_whitespace(lines[lineno].encode()[:col_offset].decode())
345 else:
346 padding = ''
347
348 first = padding + lines[lineno].encode()[col_offset:].decode()
349 last = lines[end_lineno].encode()[:end_col_offset].decode()
350 lines = lines[lineno+1:end_lineno]
351
352 lines.insert(0, first)
353 lines.append(last)
354 return ''.join(lines)
355
356
Georg Brandl0c77a822008-06-10 16:37:50 +0000357def walk(node):
358 """
Georg Brandl619e7ba2011-01-09 07:38:51 +0000359 Recursively yield all descendant nodes in the tree starting at *node*
360 (including *node* itself), in no specified order. This is useful if you
361 only want to modify nodes in place and don't care about the context.
Georg Brandl0c77a822008-06-10 16:37:50 +0000362 """
363 from collections import deque
364 todo = deque([node])
365 while todo:
366 node = todo.popleft()
367 todo.extend(iter_child_nodes(node))
368 yield node
369
370
371class NodeVisitor(object):
372 """
373 A node visitor base class that walks the abstract syntax tree and calls a
374 visitor function for every node found. This function may return a value
375 which is forwarded by the `visit` method.
376
377 This class is meant to be subclassed, with the subclass adding visitor
378 methods.
379
380 Per default the visitor functions for the nodes are ``'visit_'`` +
381 class name of the node. So a `TryFinally` node visit function would
382 be `visit_TryFinally`. This behavior can be changed by overriding
383 the `visit` method. If no visitor function exists for a node
384 (return value `None`) the `generic_visit` visitor is used instead.
385
386 Don't use the `NodeVisitor` if you want to apply changes to nodes during
387 traversing. For this a special visitor exists (`NodeTransformer`) that
388 allows modifications.
389 """
390
391 def visit(self, node):
392 """Visit a node."""
393 method = 'visit_' + node.__class__.__name__
394 visitor = getattr(self, method, self.generic_visit)
395 return visitor(node)
396
397 def generic_visit(self, node):
398 """Called if no explicit visitor function exists for a node."""
399 for field, value in iter_fields(node):
400 if isinstance(value, list):
401 for item in value:
402 if isinstance(item, AST):
403 self.visit(item)
404 elif isinstance(value, AST):
405 self.visit(value)
406
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300407 def visit_Constant(self, node):
408 value = node.value
409 type_name = _const_node_type_names.get(type(value))
410 if type_name is None:
411 for cls, name in _const_node_type_names.items():
412 if isinstance(value, cls):
413 type_name = name
414 break
415 if type_name is not None:
416 method = 'visit_' + type_name
417 try:
418 visitor = getattr(self, method)
419 except AttributeError:
420 pass
421 else:
422 import warnings
423 warnings.warn(f"{method} is deprecated; add visit_Constant",
424 DeprecationWarning, 2)
425 return visitor(node)
426 return self.generic_visit(node)
427
Georg Brandl0c77a822008-06-10 16:37:50 +0000428
429class NodeTransformer(NodeVisitor):
430 """
431 A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
432 allows modification of nodes.
433
434 The `NodeTransformer` will walk the AST and use the return value of the
435 visitor methods to replace or remove the old node. If the return value of
436 the visitor method is ``None``, the node will be removed from its location,
437 otherwise it is replaced with the return value. The return value may be the
438 original node in which case no replacement takes place.
439
440 Here is an example transformer that rewrites all occurrences of name lookups
441 (``foo``) to ``data['foo']``::
442
443 class RewriteName(NodeTransformer):
444
445 def visit_Name(self, node):
Pablo Galindoc00c86b2020-03-12 00:48:19 +0000446 return Subscript(
Georg Brandl0c77a822008-06-10 16:37:50 +0000447 value=Name(id='data', ctx=Load()),
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200448 slice=Constant(value=node.id),
Georg Brandl0c77a822008-06-10 16:37:50 +0000449 ctx=node.ctx
Pablo Galindoc00c86b2020-03-12 00:48:19 +0000450 )
Georg Brandl0c77a822008-06-10 16:37:50 +0000451
452 Keep in mind that if the node you're operating on has child nodes you must
453 either transform the child nodes yourself or call the :meth:`generic_visit`
454 method for the node first.
455
456 For nodes that were part of a collection of statements (that applies to all
457 statement nodes), the visitor may also return a list of nodes rather than
458 just a single node.
459
460 Usually you use the transformer like this::
461
462 node = YourTransformer().visit(node)
463 """
464
465 def generic_visit(self, node):
466 for field, old_value in iter_fields(node):
Georg Brandl0c77a822008-06-10 16:37:50 +0000467 if isinstance(old_value, list):
468 new_values = []
469 for value in old_value:
470 if isinstance(value, AST):
471 value = self.visit(value)
472 if value is None:
473 continue
474 elif not isinstance(value, AST):
475 new_values.extend(value)
476 continue
477 new_values.append(value)
478 old_value[:] = new_values
479 elif isinstance(old_value, AST):
480 new_node = self.visit(old_value)
481 if new_node is None:
482 delattr(node, field)
483 else:
484 setattr(node, field, new_node)
485 return node
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300486
487
488# The following code is for backward compatibility.
489# It will be removed in future.
490
491def _getter(self):
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200492 """Deprecated. Use value instead."""
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300493 return self.value
494
495def _setter(self, value):
496 self.value = value
497
498Constant.n = property(_getter, _setter)
499Constant.s = property(_getter, _setter)
500
501class _ABC(type):
502
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200503 def __init__(cls, *args):
504 cls.__doc__ = """Deprecated AST node class. Use ast.Constant instead"""
505
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300506 def __instancecheck__(cls, inst):
507 if not isinstance(inst, Constant):
508 return False
509 if cls in _const_types:
510 try:
511 value = inst.value
512 except AttributeError:
513 return False
514 else:
Anthony Sottile74176222019-01-18 11:30:28 -0800515 return (
516 isinstance(value, _const_types[cls]) and
517 not isinstance(value, _const_types_not.get(cls, ()))
518 )
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300519 return type.__instancecheck__(cls, inst)
520
521def _new(cls, *args, **kwargs):
522 if cls in _const_types:
523 return Constant(*args, **kwargs)
524 return Constant.__new__(cls, *args, **kwargs)
525
526class Num(Constant, metaclass=_ABC):
527 _fields = ('n',)
528 __new__ = _new
529
530class Str(Constant, metaclass=_ABC):
531 _fields = ('s',)
532 __new__ = _new
533
534class Bytes(Constant, metaclass=_ABC):
535 _fields = ('s',)
536 __new__ = _new
537
538class NameConstant(Constant, metaclass=_ABC):
539 __new__ = _new
540
541class Ellipsis(Constant, metaclass=_ABC):
542 _fields = ()
543
544 def __new__(cls, *args, **kwargs):
545 if cls is Ellipsis:
546 return Constant(..., *args, **kwargs)
547 return Constant.__new__(cls, *args, **kwargs)
548
549_const_types = {
550 Num: (int, float, complex),
551 Str: (str,),
552 Bytes: (bytes,),
553 NameConstant: (type(None), bool),
554 Ellipsis: (type(...),),
555}
Anthony Sottile74176222019-01-18 11:30:28 -0800556_const_types_not = {
557 Num: (bool,),
558}
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200559
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300560_const_node_type_names = {
561 bool: 'NameConstant', # should be before int
562 type(None): 'NameConstant',
563 int: 'Num',
564 float: 'Num',
565 complex: 'Num',
566 str: 'Str',
567 bytes: 'Bytes',
568 type(...): 'Ellipsis',
569}
Serhiy Storchaka832e8642019-09-09 23:36:13 +0300570
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200571class slice(AST):
572 """Deprecated AST node class."""
573
574class Index(slice):
575 """Deprecated AST node class. Use the index value directly instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200576 def __new__(cls, value, **kwargs):
577 return value
578
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200579class ExtSlice(slice):
580 """Deprecated AST node class. Use ast.Tuple instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200581 def __new__(cls, dims=(), **kwargs):
582 return Tuple(list(dims), Load(), **kwargs)
583
584def _dims_getter(self):
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200585 """Deprecated. Use elts instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200586 return self.elts
587
588def _dims_setter(self, value):
589 self.elts = value
590
591Tuple.dims = property(_dims_getter, _dims_setter)
592
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200593class Suite(mod):
594 """Deprecated AST node class. Unused in Python 3."""
595
596class AugLoad(expr_context):
597 """Deprecated AST node class. Unused in Python 3."""
598
599class AugStore(expr_context):
600 """Deprecated AST node class. Unused in Python 3."""
601
602class Param(expr_context):
603 """Deprecated AST node class. Unused in Python 3."""
604
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200605
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000606# Large float and imaginary literals get turned into infinities in the AST.
607# We unparse those infinities to INFSTR.
608_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
609
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300610class _Precedence(IntEnum):
611 """Precedence table that originated from python grammar."""
612
613 TUPLE = auto()
614 YIELD = auto() # 'yield', 'yield from'
615 TEST = auto() # 'if'-'else', 'lambda'
616 OR = auto() # 'or'
617 AND = auto() # 'and'
618 NOT = auto() # 'not'
619 CMP = auto() # '<', '>', '==', '>=', '<=', '!=',
620 # 'in', 'not in', 'is', 'is not'
621 EXPR = auto()
622 BOR = EXPR # '|'
623 BXOR = auto() # '^'
624 BAND = auto() # '&'
625 SHIFT = auto() # '<<', '>>'
626 ARITH = auto() # '+', '-'
627 TERM = auto() # '*', '@', '/', '%', '//'
628 FACTOR = auto() # unary '+', '-', '~'
629 POWER = auto() # '**'
630 AWAIT = auto() # 'await'
631 ATOM = auto()
632
633 def next(self):
634 try:
635 return self.__class__(self + 1)
636 except ValueError:
637 return self
638
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000639class _Unparser(NodeVisitor):
640 """Methods in this class recursively traverse an AST and
641 output source code for the abstract syntax; original formatting
642 is disregarded."""
643
644 def __init__(self):
645 self._source = []
646 self._buffer = []
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300647 self._precedences = {}
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000648 self._indent = 0
649
650 def interleave(self, inter, f, seq):
651 """Call f on each item in seq, calling inter() in between."""
652 seq = iter(seq)
653 try:
654 f(next(seq))
655 except StopIteration:
656 pass
657 else:
658 for x in seq:
659 inter()
660 f(x)
661
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +0300662 def items_view(self, traverser, items):
663 """Traverse and separate the given *items* with a comma and append it to
664 the buffer. If *items* is a single item sequence, a trailing comma
665 will be added."""
666 if len(items) == 1:
667 traverser(items[0])
668 self.write(",")
669 else:
670 self.interleave(lambda: self.write(", "), traverser, items)
671
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000672 def fill(self, text=""):
673 """Indent a piece of text and append it, according to the current
674 indentation level"""
675 self.write("\n" + " " * self._indent + text)
676
677 def write(self, text):
678 """Append a piece of text"""
679 self._source.append(text)
680
681 def buffer_writer(self, text):
682 self._buffer.append(text)
683
684 @property
685 def buffer(self):
686 value = "".join(self._buffer)
687 self._buffer.clear()
688 return value
689
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000690 @contextmanager
691 def block(self):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000692 """A context manager for preparing the source for blocks. It adds
693 the character':', increases the indentation on enter and decreases
694 the indentation on exit."""
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000695 self.write(":")
696 self._indent += 1
697 yield
698 self._indent -= 1
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000699
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300700 @contextmanager
701 def delimit(self, start, end):
702 """A context manager for preparing the source for expressions. It adds
703 *start* to the buffer and enters, after exit it adds *end*."""
704
705 self.write(start)
706 yield
707 self.write(end)
708
709 def delimit_if(self, start, end, condition):
710 if condition:
711 return self.delimit(start, end)
712 else:
713 return nullcontext()
714
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300715 def require_parens(self, precedence, node):
716 """Shortcut to adding precedence related parens"""
717 return self.delimit_if("(", ")", self.get_precedence(node) > precedence)
718
719 def get_precedence(self, node):
720 return self._precedences.get(node, _Precedence.TEST)
721
722 def set_precedence(self, precedence, *nodes):
723 for node in nodes:
724 self._precedences[node] = precedence
725
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300726 def get_raw_docstring(self, node):
727 """If a docstring node is found in the body of the *node* parameter,
728 return that docstring node, None otherwise.
729
730 Logic mirrored from ``_PyAST_GetDocString``."""
731 if not isinstance(
732 node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)
733 ) or len(node.body) < 1:
734 return None
735 node = node.body[0]
736 if not isinstance(node, Expr):
737 return None
738 node = node.value
739 if isinstance(node, Constant) and isinstance(node.value, str):
740 return node
741
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000742 def traverse(self, node):
743 if isinstance(node, list):
744 for item in node:
745 self.traverse(item)
746 else:
747 super().visit(node)
748
749 def visit(self, node):
750 """Outputs a source code string that, if converted back to an ast
751 (using ast.parse) will generate an AST equivalent to *node*"""
752 self._source = []
753 self.traverse(node)
754 return "".join(self._source)
755
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300756 def _write_docstring_and_traverse_body(self, node):
757 if (docstring := self.get_raw_docstring(node)):
758 self._write_docstring(docstring)
759 self.traverse(node.body[1:])
760 else:
761 self.traverse(node.body)
762
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000763 def visit_Module(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300764 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000765
Batuhan Taşkaya5b66ec12020-03-15 22:56:57 +0300766 def visit_FunctionType(self, node):
767 with self.delimit("(", ")"):
768 self.interleave(
769 lambda: self.write(", "), self.traverse, node.argtypes
770 )
771
772 self.write(" -> ")
773 self.traverse(node.returns)
774
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000775 def visit_Expr(self, node):
776 self.fill()
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300777 self.set_precedence(_Precedence.YIELD, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000778 self.traverse(node.value)
779
780 def visit_NamedExpr(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300781 with self.require_parens(_Precedence.TUPLE, node):
782 self.set_precedence(_Precedence.ATOM, node.target, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300783 self.traverse(node.target)
784 self.write(" := ")
785 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000786
787 def visit_Import(self, node):
788 self.fill("import ")
789 self.interleave(lambda: self.write(", "), self.traverse, node.names)
790
791 def visit_ImportFrom(self, node):
792 self.fill("from ")
793 self.write("." * node.level)
794 if node.module:
795 self.write(node.module)
796 self.write(" import ")
797 self.interleave(lambda: self.write(", "), self.traverse, node.names)
798
799 def visit_Assign(self, node):
800 self.fill()
801 for target in node.targets:
802 self.traverse(target)
803 self.write(" = ")
804 self.traverse(node.value)
805
806 def visit_AugAssign(self, node):
807 self.fill()
808 self.traverse(node.target)
809 self.write(" " + self.binop[node.op.__class__.__name__] + "= ")
810 self.traverse(node.value)
811
812 def visit_AnnAssign(self, node):
813 self.fill()
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300814 with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)):
815 self.traverse(node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000816 self.write(": ")
817 self.traverse(node.annotation)
818 if node.value:
819 self.write(" = ")
820 self.traverse(node.value)
821
822 def visit_Return(self, node):
823 self.fill("return")
824 if node.value:
825 self.write(" ")
826 self.traverse(node.value)
827
828 def visit_Pass(self, node):
829 self.fill("pass")
830
831 def visit_Break(self, node):
832 self.fill("break")
833
834 def visit_Continue(self, node):
835 self.fill("continue")
836
837 def visit_Delete(self, node):
838 self.fill("del ")
839 self.interleave(lambda: self.write(", "), self.traverse, node.targets)
840
841 def visit_Assert(self, node):
842 self.fill("assert ")
843 self.traverse(node.test)
844 if node.msg:
845 self.write(", ")
846 self.traverse(node.msg)
847
848 def visit_Global(self, node):
849 self.fill("global ")
850 self.interleave(lambda: self.write(", "), self.write, node.names)
851
852 def visit_Nonlocal(self, node):
853 self.fill("nonlocal ")
854 self.interleave(lambda: self.write(", "), self.write, node.names)
855
856 def visit_Await(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300857 with self.require_parens(_Precedence.AWAIT, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300858 self.write("await")
859 if node.value:
860 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300861 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300862 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000863
864 def visit_Yield(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300865 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300866 self.write("yield")
867 if node.value:
868 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300869 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300870 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000871
872 def visit_YieldFrom(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300873 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300874 self.write("yield from ")
875 if not node.value:
876 raise ValueError("Node can't be used without a value attribute.")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300877 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300878 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000879
880 def visit_Raise(self, node):
881 self.fill("raise")
882 if not node.exc:
883 if node.cause:
884 raise ValueError(f"Node can't use cause without an exception.")
885 return
886 self.write(" ")
887 self.traverse(node.exc)
888 if node.cause:
889 self.write(" from ")
890 self.traverse(node.cause)
891
892 def visit_Try(self, node):
893 self.fill("try")
894 with self.block():
895 self.traverse(node.body)
896 for ex in node.handlers:
897 self.traverse(ex)
898 if node.orelse:
899 self.fill("else")
900 with self.block():
901 self.traverse(node.orelse)
902 if node.finalbody:
903 self.fill("finally")
904 with self.block():
905 self.traverse(node.finalbody)
906
907 def visit_ExceptHandler(self, node):
908 self.fill("except")
909 if node.type:
910 self.write(" ")
911 self.traverse(node.type)
912 if node.name:
913 self.write(" as ")
914 self.write(node.name)
915 with self.block():
916 self.traverse(node.body)
917
918 def visit_ClassDef(self, node):
919 self.write("\n")
920 for deco in node.decorator_list:
921 self.fill("@")
922 self.traverse(deco)
923 self.fill("class " + node.name)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300924 with self.delimit("(", ")"):
925 comma = False
926 for e in node.bases:
927 if comma:
928 self.write(", ")
929 else:
930 comma = True
931 self.traverse(e)
932 for e in node.keywords:
933 if comma:
934 self.write(", ")
935 else:
936 comma = True
937 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000938
939 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300940 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000941
942 def visit_FunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300943 self._function_helper(node, "def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000944
945 def visit_AsyncFunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300946 self._function_helper(node, "async def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000947
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300948 def _function_helper(self, node, fill_suffix):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000949 self.write("\n")
950 for deco in node.decorator_list:
951 self.fill("@")
952 self.traverse(deco)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300953 def_str = fill_suffix + " " + node.name
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000954 self.fill(def_str)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300955 with self.delimit("(", ")"):
956 self.traverse(node.args)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000957 if node.returns:
958 self.write(" -> ")
959 self.traverse(node.returns)
960 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300961 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000962
963 def visit_For(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300964 self._for_helper("for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000965
966 def visit_AsyncFor(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300967 self._for_helper("async for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000968
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300969 def _for_helper(self, fill, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000970 self.fill(fill)
971 self.traverse(node.target)
972 self.write(" in ")
973 self.traverse(node.iter)
974 with self.block():
975 self.traverse(node.body)
976 if node.orelse:
977 self.fill("else")
978 with self.block():
979 self.traverse(node.orelse)
980
981 def visit_If(self, node):
982 self.fill("if ")
983 self.traverse(node.test)
984 with self.block():
985 self.traverse(node.body)
986 # collapse nested ifs into equivalent elifs.
987 while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If):
988 node = node.orelse[0]
989 self.fill("elif ")
990 self.traverse(node.test)
991 with self.block():
992 self.traverse(node.body)
993 # final else
994 if node.orelse:
995 self.fill("else")
996 with self.block():
997 self.traverse(node.orelse)
998
999 def visit_While(self, node):
1000 self.fill("while ")
1001 self.traverse(node.test)
1002 with self.block():
1003 self.traverse(node.body)
1004 if node.orelse:
1005 self.fill("else")
1006 with self.block():
1007 self.traverse(node.orelse)
1008
1009 def visit_With(self, node):
1010 self.fill("with ")
1011 self.interleave(lambda: self.write(", "), self.traverse, node.items)
1012 with self.block():
1013 self.traverse(node.body)
1014
1015 def visit_AsyncWith(self, node):
1016 self.fill("async with ")
1017 self.interleave(lambda: self.write(", "), self.traverse, node.items)
1018 with self.block():
1019 self.traverse(node.body)
1020
1021 def visit_JoinedStr(self, node):
1022 self.write("f")
1023 self._fstring_JoinedStr(node, self.buffer_writer)
1024 self.write(repr(self.buffer))
1025
1026 def visit_FormattedValue(self, node):
1027 self.write("f")
1028 self._fstring_FormattedValue(node, self.buffer_writer)
1029 self.write(repr(self.buffer))
1030
1031 def _fstring_JoinedStr(self, node, write):
1032 for value in node.values:
1033 meth = getattr(self, "_fstring_" + type(value).__name__)
1034 meth(value, write)
1035
1036 def _fstring_Constant(self, node, write):
1037 if not isinstance(node.value, str):
1038 raise ValueError("Constants inside JoinedStr should be a string.")
1039 value = node.value.replace("{", "{{").replace("}", "}}")
1040 write(value)
1041
1042 def _fstring_FormattedValue(self, node, write):
1043 write("{")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001044 unparser = type(self)()
1045 unparser.set_precedence(_Precedence.TEST.next(), node.value)
1046 expr = unparser.visit(node.value).rstrip("\n")
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001047 if expr.startswith("{"):
1048 write(" ") # Separate pair of opening brackets as "{ {"
1049 write(expr)
1050 if node.conversion != -1:
1051 conversion = chr(node.conversion)
1052 if conversion not in "sra":
1053 raise ValueError("Unknown f-string conversion.")
1054 write(f"!{conversion}")
1055 if node.format_spec:
1056 write(":")
1057 meth = getattr(self, "_fstring_" + type(node.format_spec).__name__)
1058 meth(node.format_spec, write)
1059 write("}")
1060
1061 def visit_Name(self, node):
1062 self.write(node.id)
1063
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001064 def _write_docstring(self, node):
1065 self.fill()
1066 if node.kind == "u":
1067 self.write("u")
1068
1069 # Preserve quotes in the docstring by escaping them
1070 value = node.value.replace("\\", "\\\\")
1071 value = value.replace('"""', '""\"')
1072 if value[-1] == '"':
1073 value = value.replace('"', '\\"', -1)
1074
1075 self.write(f'"""{value}"""')
1076
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001077 def _write_constant(self, value):
1078 if isinstance(value, (float, complex)):
1079 # Substitute overflowing decimal literal for AST infinities.
1080 self.write(repr(value).replace("inf", _INFSTR))
1081 else:
1082 self.write(repr(value))
1083
1084 def visit_Constant(self, node):
1085 value = node.value
1086 if isinstance(value, tuple):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001087 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001088 self.items_view(self._write_constant, value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001089 elif value is ...:
1090 self.write("...")
1091 else:
1092 if node.kind == "u":
1093 self.write("u")
1094 self._write_constant(node.value)
1095
1096 def visit_List(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001097 with self.delimit("[", "]"):
1098 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001099
1100 def visit_ListComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001101 with self.delimit("[", "]"):
1102 self.traverse(node.elt)
1103 for gen in node.generators:
1104 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001105
1106 def visit_GeneratorExp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001107 with self.delimit("(", ")"):
1108 self.traverse(node.elt)
1109 for gen in node.generators:
1110 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001111
1112 def visit_SetComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001113 with self.delimit("{", "}"):
1114 self.traverse(node.elt)
1115 for gen in node.generators:
1116 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001117
1118 def visit_DictComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001119 with self.delimit("{", "}"):
1120 self.traverse(node.key)
1121 self.write(": ")
1122 self.traverse(node.value)
1123 for gen in node.generators:
1124 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001125
1126 def visit_comprehension(self, node):
1127 if node.is_async:
1128 self.write(" async for ")
1129 else:
1130 self.write(" for ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001131 self.set_precedence(_Precedence.TUPLE, node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001132 self.traverse(node.target)
1133 self.write(" in ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001134 self.set_precedence(_Precedence.TEST.next(), node.iter, *node.ifs)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001135 self.traverse(node.iter)
1136 for if_clause in node.ifs:
1137 self.write(" if ")
1138 self.traverse(if_clause)
1139
1140 def visit_IfExp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001141 with self.require_parens(_Precedence.TEST, node):
1142 self.set_precedence(_Precedence.TEST.next(), node.body, node.test)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001143 self.traverse(node.body)
1144 self.write(" if ")
1145 self.traverse(node.test)
1146 self.write(" else ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001147 self.set_precedence(_Precedence.TEST, node.orelse)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001148 self.traverse(node.orelse)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001149
1150 def visit_Set(self, node):
1151 if not node.elts:
Shantanu01508dc2020-04-16 03:10:12 -07001152 raise ValueError("Set node should have at least one item")
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001153 with self.delimit("{", "}"):
1154 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001155
1156 def visit_Dict(self, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001157 def write_key_value_pair(k, v):
1158 self.traverse(k)
1159 self.write(": ")
1160 self.traverse(v)
1161
1162 def write_item(item):
1163 k, v = item
1164 if k is None:
1165 # for dictionary unpacking operator in dicts {**{'y': 2}}
1166 # see PEP 448 for details
1167 self.write("**")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001168 self.set_precedence(_Precedence.EXPR, v)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001169 self.traverse(v)
1170 else:
1171 write_key_value_pair(k, v)
1172
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001173 with self.delimit("{", "}"):
1174 self.interleave(
1175 lambda: self.write(", "), write_item, zip(node.keys, node.values)
1176 )
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001177
1178 def visit_Tuple(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001179 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001180 self.items_view(self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001181
1182 unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001183 unop_precedence = {
1184 "~": _Precedence.FACTOR,
1185 "not": _Precedence.NOT,
1186 "+": _Precedence.FACTOR,
1187 "-": _Precedence.FACTOR
1188 }
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001189
1190 def visit_UnaryOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001191 operator = self.unop[node.op.__class__.__name__]
1192 operator_precedence = self.unop_precedence[operator]
1193 with self.require_parens(operator_precedence, node):
1194 self.write(operator)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001195 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001196 self.set_precedence(operator_precedence, node.operand)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001197 self.traverse(node.operand)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001198
1199 binop = {
1200 "Add": "+",
1201 "Sub": "-",
1202 "Mult": "*",
1203 "MatMult": "@",
1204 "Div": "/",
1205 "Mod": "%",
1206 "LShift": "<<",
1207 "RShift": ">>",
1208 "BitOr": "|",
1209 "BitXor": "^",
1210 "BitAnd": "&",
1211 "FloorDiv": "//",
1212 "Pow": "**",
1213 }
1214
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001215 binop_precedence = {
1216 "+": _Precedence.ARITH,
1217 "-": _Precedence.ARITH,
1218 "*": _Precedence.TERM,
1219 "@": _Precedence.TERM,
1220 "/": _Precedence.TERM,
1221 "%": _Precedence.TERM,
1222 "<<": _Precedence.SHIFT,
1223 ">>": _Precedence.SHIFT,
1224 "|": _Precedence.BOR,
1225 "^": _Precedence.BXOR,
1226 "&": _Precedence.BAND,
1227 "//": _Precedence.TERM,
1228 "**": _Precedence.POWER,
1229 }
1230
1231 binop_rassoc = frozenset(("**",))
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001232 def visit_BinOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001233 operator = self.binop[node.op.__class__.__name__]
1234 operator_precedence = self.binop_precedence[operator]
1235 with self.require_parens(operator_precedence, node):
1236 if operator in self.binop_rassoc:
1237 left_precedence = operator_precedence.next()
1238 right_precedence = operator_precedence
1239 else:
1240 left_precedence = operator_precedence
1241 right_precedence = operator_precedence.next()
1242
1243 self.set_precedence(left_precedence, node.left)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001244 self.traverse(node.left)
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001245 self.write(f" {operator} ")
1246 self.set_precedence(right_precedence, node.right)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001247 self.traverse(node.right)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001248
1249 cmpops = {
1250 "Eq": "==",
1251 "NotEq": "!=",
1252 "Lt": "<",
1253 "LtE": "<=",
1254 "Gt": ">",
1255 "GtE": ">=",
1256 "Is": "is",
1257 "IsNot": "is not",
1258 "In": "in",
1259 "NotIn": "not in",
1260 }
1261
1262 def visit_Compare(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001263 with self.require_parens(_Precedence.CMP, node):
1264 self.set_precedence(_Precedence.CMP.next(), node.left, *node.comparators)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001265 self.traverse(node.left)
1266 for o, e in zip(node.ops, node.comparators):
1267 self.write(" " + self.cmpops[o.__class__.__name__] + " ")
1268 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001269
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001270 boolops = {"And": "and", "Or": "or"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001271 boolop_precedence = {"and": _Precedence.AND, "or": _Precedence.OR}
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001272
1273 def visit_BoolOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001274 operator = self.boolops[node.op.__class__.__name__]
1275 operator_precedence = self.boolop_precedence[operator]
1276
1277 def increasing_level_traverse(node):
1278 nonlocal operator_precedence
1279 operator_precedence = operator_precedence.next()
1280 self.set_precedence(operator_precedence, node)
1281 self.traverse(node)
1282
1283 with self.require_parens(operator_precedence, node):
1284 s = f" {operator} "
1285 self.interleave(lambda: self.write(s), increasing_level_traverse, node.values)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001286
1287 def visit_Attribute(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001288 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001289 self.traverse(node.value)
1290 # Special case: 3.__abs__() is a syntax error, so if node.value
1291 # is an integer literal then we need to either parenthesize
1292 # it or add an extra space to get 3 .__abs__().
1293 if isinstance(node.value, Constant) and isinstance(node.value.value, int):
1294 self.write(" ")
1295 self.write(".")
1296 self.write(node.attr)
1297
1298 def visit_Call(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001299 self.set_precedence(_Precedence.ATOM, node.func)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001300 self.traverse(node.func)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001301 with self.delimit("(", ")"):
1302 comma = False
1303 for e in node.args:
1304 if comma:
1305 self.write(", ")
1306 else:
1307 comma = True
1308 self.traverse(e)
1309 for e in node.keywords:
1310 if comma:
1311 self.write(", ")
1312 else:
1313 comma = True
1314 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001315
1316 def visit_Subscript(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001317 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001318 self.traverse(node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001319 with self.delimit("[", "]"):
Serhiy Storchaka13d52c22020-03-10 18:52:34 +02001320 if isinstance(node.slice, Tuple) and node.slice.elts:
1321 self.items_view(self.traverse, node.slice.elts)
Serhiy Storchakac4928fc2020-03-07 17:25:32 +02001322 else:
1323 self.traverse(node.slice)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001324
1325 def visit_Starred(self, node):
1326 self.write("*")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001327 self.set_precedence(_Precedence.EXPR, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001328 self.traverse(node.value)
1329
1330 def visit_Ellipsis(self, node):
1331 self.write("...")
1332
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001333 def visit_Slice(self, node):
1334 if node.lower:
1335 self.traverse(node.lower)
1336 self.write(":")
1337 if node.upper:
1338 self.traverse(node.upper)
1339 if node.step:
1340 self.write(":")
1341 self.traverse(node.step)
1342
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001343 def visit_arg(self, node):
1344 self.write(node.arg)
1345 if node.annotation:
1346 self.write(": ")
1347 self.traverse(node.annotation)
1348
1349 def visit_arguments(self, node):
1350 first = True
1351 # normal arguments
1352 all_args = node.posonlyargs + node.args
1353 defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults
1354 for index, elements in enumerate(zip(all_args, defaults), 1):
1355 a, d = elements
1356 if first:
1357 first = False
1358 else:
1359 self.write(", ")
1360 self.traverse(a)
1361 if d:
1362 self.write("=")
1363 self.traverse(d)
1364 if index == len(node.posonlyargs):
1365 self.write(", /")
1366
1367 # varargs, or bare '*' if no varargs but keyword-only arguments present
1368 if node.vararg or node.kwonlyargs:
1369 if first:
1370 first = False
1371 else:
1372 self.write(", ")
1373 self.write("*")
1374 if node.vararg:
1375 self.write(node.vararg.arg)
1376 if node.vararg.annotation:
1377 self.write(": ")
1378 self.traverse(node.vararg.annotation)
1379
1380 # keyword-only arguments
1381 if node.kwonlyargs:
1382 for a, d in zip(node.kwonlyargs, node.kw_defaults):
Batuhan Taşkayaa322f502019-12-16 15:26:58 +03001383 self.write(", ")
1384 self.traverse(a)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001385 if d:
1386 self.write("=")
1387 self.traverse(d)
1388
1389 # kwargs
1390 if node.kwarg:
1391 if first:
1392 first = False
1393 else:
1394 self.write(", ")
1395 self.write("**" + node.kwarg.arg)
1396 if node.kwarg.annotation:
1397 self.write(": ")
1398 self.traverse(node.kwarg.annotation)
1399
1400 def visit_keyword(self, node):
1401 if node.arg is None:
1402 self.write("**")
1403 else:
1404 self.write(node.arg)
1405 self.write("=")
1406 self.traverse(node.value)
1407
1408 def visit_Lambda(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001409 with self.require_parens(_Precedence.TEST, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001410 self.write("lambda ")
1411 self.traverse(node.args)
1412 self.write(": ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001413 self.set_precedence(_Precedence.TEST, node.body)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001414 self.traverse(node.body)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001415
1416 def visit_alias(self, node):
1417 self.write(node.name)
1418 if node.asname:
1419 self.write(" as " + node.asname)
1420
1421 def visit_withitem(self, node):
1422 self.traverse(node.context_expr)
1423 if node.optional_vars:
1424 self.write(" as ")
1425 self.traverse(node.optional_vars)
1426
1427def unparse(ast_obj):
1428 unparser = _Unparser()
1429 return unparser.visit(ast_obj)
1430
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001431
1432def main():
1433 import argparse
1434
1435 parser = argparse.ArgumentParser(prog='python -m ast')
1436 parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?',
1437 default='-',
1438 help='the file to parse; defaults to stdin')
1439 parser.add_argument('-m', '--mode', default='exec',
1440 choices=('exec', 'single', 'eval', 'func_type'),
1441 help='specify what kind of code must be parsed')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001442 parser.add_argument('--no-type-comments', default=True, action='store_false',
1443 help="don't add information about type comments")
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001444 parser.add_argument('-a', '--include-attributes', action='store_true',
1445 help='include attributes such as line numbers and '
1446 'column offsets')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001447 parser.add_argument('-i', '--indent', type=int, default=3,
1448 help='indentation of nodes (number of spaces)')
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001449 args = parser.parse_args()
1450
1451 with args.infile as infile:
1452 source = infile.read()
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001453 tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
1454 print(dump(tree, include_attributes=args.include_attributes, indent=args.indent))
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001455
1456if __name__ == '__main__':
1457 main()