blob: 077eb92abb40284d961ce2f2485ec7d36dbe459c [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):
492 return self.value
493
494def _setter(self, value):
495 self.value = value
496
497Constant.n = property(_getter, _setter)
498Constant.s = property(_getter, _setter)
499
500class _ABC(type):
501
502 def __instancecheck__(cls, inst):
503 if not isinstance(inst, Constant):
504 return False
505 if cls in _const_types:
506 try:
507 value = inst.value
508 except AttributeError:
509 return False
510 else:
Anthony Sottile74176222019-01-18 11:30:28 -0800511 return (
512 isinstance(value, _const_types[cls]) and
513 not isinstance(value, _const_types_not.get(cls, ()))
514 )
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300515 return type.__instancecheck__(cls, inst)
516
517def _new(cls, *args, **kwargs):
518 if cls in _const_types:
519 return Constant(*args, **kwargs)
520 return Constant.__new__(cls, *args, **kwargs)
521
522class Num(Constant, metaclass=_ABC):
523 _fields = ('n',)
524 __new__ = _new
525
526class Str(Constant, metaclass=_ABC):
527 _fields = ('s',)
528 __new__ = _new
529
530class Bytes(Constant, metaclass=_ABC):
531 _fields = ('s',)
532 __new__ = _new
533
534class NameConstant(Constant, metaclass=_ABC):
535 __new__ = _new
536
537class Ellipsis(Constant, metaclass=_ABC):
538 _fields = ()
539
540 def __new__(cls, *args, **kwargs):
541 if cls is Ellipsis:
542 return Constant(..., *args, **kwargs)
543 return Constant.__new__(cls, *args, **kwargs)
544
545_const_types = {
546 Num: (int, float, complex),
547 Str: (str,),
548 Bytes: (bytes,),
549 NameConstant: (type(None), bool),
550 Ellipsis: (type(...),),
551}
Anthony Sottile74176222019-01-18 11:30:28 -0800552_const_types_not = {
553 Num: (bool,),
554}
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200555
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300556_const_node_type_names = {
557 bool: 'NameConstant', # should be before int
558 type(None): 'NameConstant',
559 int: 'Num',
560 float: 'Num',
561 complex: 'Num',
562 str: 'Str',
563 bytes: 'Bytes',
564 type(...): 'Ellipsis',
565}
Serhiy Storchaka832e8642019-09-09 23:36:13 +0300566
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200567class Index(AST):
568 def __new__(cls, value, **kwargs):
569 return value
570
571class ExtSlice(AST):
572 def __new__(cls, dims=(), **kwargs):
573 return Tuple(list(dims), Load(), **kwargs)
574
575def _dims_getter(self):
576 return self.elts
577
578def _dims_setter(self, value):
579 self.elts = value
580
581Tuple.dims = property(_dims_getter, _dims_setter)
582
583
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000584# Large float and imaginary literals get turned into infinities in the AST.
585# We unparse those infinities to INFSTR.
586_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
587
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300588class _Precedence(IntEnum):
589 """Precedence table that originated from python grammar."""
590
591 TUPLE = auto()
592 YIELD = auto() # 'yield', 'yield from'
593 TEST = auto() # 'if'-'else', 'lambda'
594 OR = auto() # 'or'
595 AND = auto() # 'and'
596 NOT = auto() # 'not'
597 CMP = auto() # '<', '>', '==', '>=', '<=', '!=',
598 # 'in', 'not in', 'is', 'is not'
599 EXPR = auto()
600 BOR = EXPR # '|'
601 BXOR = auto() # '^'
602 BAND = auto() # '&'
603 SHIFT = auto() # '<<', '>>'
604 ARITH = auto() # '+', '-'
605 TERM = auto() # '*', '@', '/', '%', '//'
606 FACTOR = auto() # unary '+', '-', '~'
607 POWER = auto() # '**'
608 AWAIT = auto() # 'await'
609 ATOM = auto()
610
611 def next(self):
612 try:
613 return self.__class__(self + 1)
614 except ValueError:
615 return self
616
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000617class _Unparser(NodeVisitor):
618 """Methods in this class recursively traverse an AST and
619 output source code for the abstract syntax; original formatting
620 is disregarded."""
621
622 def __init__(self):
623 self._source = []
624 self._buffer = []
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300625 self._precedences = {}
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000626 self._indent = 0
627
628 def interleave(self, inter, f, seq):
629 """Call f on each item in seq, calling inter() in between."""
630 seq = iter(seq)
631 try:
632 f(next(seq))
633 except StopIteration:
634 pass
635 else:
636 for x in seq:
637 inter()
638 f(x)
639
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +0300640 def items_view(self, traverser, items):
641 """Traverse and separate the given *items* with a comma and append it to
642 the buffer. If *items* is a single item sequence, a trailing comma
643 will be added."""
644 if len(items) == 1:
645 traverser(items[0])
646 self.write(",")
647 else:
648 self.interleave(lambda: self.write(", "), traverser, items)
649
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000650 def fill(self, text=""):
651 """Indent a piece of text and append it, according to the current
652 indentation level"""
653 self.write("\n" + " " * self._indent + text)
654
655 def write(self, text):
656 """Append a piece of text"""
657 self._source.append(text)
658
659 def buffer_writer(self, text):
660 self._buffer.append(text)
661
662 @property
663 def buffer(self):
664 value = "".join(self._buffer)
665 self._buffer.clear()
666 return value
667
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000668 @contextmanager
669 def block(self):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000670 """A context manager for preparing the source for blocks. It adds
671 the character':', increases the indentation on enter and decreases
672 the indentation on exit."""
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000673 self.write(":")
674 self._indent += 1
675 yield
676 self._indent -= 1
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000677
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300678 @contextmanager
679 def delimit(self, start, end):
680 """A context manager for preparing the source for expressions. It adds
681 *start* to the buffer and enters, after exit it adds *end*."""
682
683 self.write(start)
684 yield
685 self.write(end)
686
687 def delimit_if(self, start, end, condition):
688 if condition:
689 return self.delimit(start, end)
690 else:
691 return nullcontext()
692
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300693 def require_parens(self, precedence, node):
694 """Shortcut to adding precedence related parens"""
695 return self.delimit_if("(", ")", self.get_precedence(node) > precedence)
696
697 def get_precedence(self, node):
698 return self._precedences.get(node, _Precedence.TEST)
699
700 def set_precedence(self, precedence, *nodes):
701 for node in nodes:
702 self._precedences[node] = precedence
703
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300704 def get_raw_docstring(self, node):
705 """If a docstring node is found in the body of the *node* parameter,
706 return that docstring node, None otherwise.
707
708 Logic mirrored from ``_PyAST_GetDocString``."""
709 if not isinstance(
710 node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)
711 ) or len(node.body) < 1:
712 return None
713 node = node.body[0]
714 if not isinstance(node, Expr):
715 return None
716 node = node.value
717 if isinstance(node, Constant) and isinstance(node.value, str):
718 return node
719
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000720 def traverse(self, node):
721 if isinstance(node, list):
722 for item in node:
723 self.traverse(item)
724 else:
725 super().visit(node)
726
727 def visit(self, node):
728 """Outputs a source code string that, if converted back to an ast
729 (using ast.parse) will generate an AST equivalent to *node*"""
730 self._source = []
731 self.traverse(node)
732 return "".join(self._source)
733
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300734 def _write_docstring_and_traverse_body(self, node):
735 if (docstring := self.get_raw_docstring(node)):
736 self._write_docstring(docstring)
737 self.traverse(node.body[1:])
738 else:
739 self.traverse(node.body)
740
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000741 def visit_Module(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300742 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000743
744 def visit_Expr(self, node):
745 self.fill()
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300746 self.set_precedence(_Precedence.YIELD, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000747 self.traverse(node.value)
748
749 def visit_NamedExpr(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300750 with self.require_parens(_Precedence.TUPLE, node):
751 self.set_precedence(_Precedence.ATOM, node.target, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300752 self.traverse(node.target)
753 self.write(" := ")
754 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000755
756 def visit_Import(self, node):
757 self.fill("import ")
758 self.interleave(lambda: self.write(", "), self.traverse, node.names)
759
760 def visit_ImportFrom(self, node):
761 self.fill("from ")
762 self.write("." * node.level)
763 if node.module:
764 self.write(node.module)
765 self.write(" import ")
766 self.interleave(lambda: self.write(", "), self.traverse, node.names)
767
768 def visit_Assign(self, node):
769 self.fill()
770 for target in node.targets:
771 self.traverse(target)
772 self.write(" = ")
773 self.traverse(node.value)
774
775 def visit_AugAssign(self, node):
776 self.fill()
777 self.traverse(node.target)
778 self.write(" " + self.binop[node.op.__class__.__name__] + "= ")
779 self.traverse(node.value)
780
781 def visit_AnnAssign(self, node):
782 self.fill()
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300783 with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)):
784 self.traverse(node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000785 self.write(": ")
786 self.traverse(node.annotation)
787 if node.value:
788 self.write(" = ")
789 self.traverse(node.value)
790
791 def visit_Return(self, node):
792 self.fill("return")
793 if node.value:
794 self.write(" ")
795 self.traverse(node.value)
796
797 def visit_Pass(self, node):
798 self.fill("pass")
799
800 def visit_Break(self, node):
801 self.fill("break")
802
803 def visit_Continue(self, node):
804 self.fill("continue")
805
806 def visit_Delete(self, node):
807 self.fill("del ")
808 self.interleave(lambda: self.write(", "), self.traverse, node.targets)
809
810 def visit_Assert(self, node):
811 self.fill("assert ")
812 self.traverse(node.test)
813 if node.msg:
814 self.write(", ")
815 self.traverse(node.msg)
816
817 def visit_Global(self, node):
818 self.fill("global ")
819 self.interleave(lambda: self.write(", "), self.write, node.names)
820
821 def visit_Nonlocal(self, node):
822 self.fill("nonlocal ")
823 self.interleave(lambda: self.write(", "), self.write, node.names)
824
825 def visit_Await(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300826 with self.require_parens(_Precedence.AWAIT, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300827 self.write("await")
828 if node.value:
829 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300830 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300831 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000832
833 def visit_Yield(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300834 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300835 self.write("yield")
836 if node.value:
837 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300838 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300839 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000840
841 def visit_YieldFrom(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300842 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300843 self.write("yield from ")
844 if not node.value:
845 raise ValueError("Node can't be used without a value attribute.")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300846 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300847 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000848
849 def visit_Raise(self, node):
850 self.fill("raise")
851 if not node.exc:
852 if node.cause:
853 raise ValueError(f"Node can't use cause without an exception.")
854 return
855 self.write(" ")
856 self.traverse(node.exc)
857 if node.cause:
858 self.write(" from ")
859 self.traverse(node.cause)
860
861 def visit_Try(self, node):
862 self.fill("try")
863 with self.block():
864 self.traverse(node.body)
865 for ex in node.handlers:
866 self.traverse(ex)
867 if node.orelse:
868 self.fill("else")
869 with self.block():
870 self.traverse(node.orelse)
871 if node.finalbody:
872 self.fill("finally")
873 with self.block():
874 self.traverse(node.finalbody)
875
876 def visit_ExceptHandler(self, node):
877 self.fill("except")
878 if node.type:
879 self.write(" ")
880 self.traverse(node.type)
881 if node.name:
882 self.write(" as ")
883 self.write(node.name)
884 with self.block():
885 self.traverse(node.body)
886
887 def visit_ClassDef(self, node):
888 self.write("\n")
889 for deco in node.decorator_list:
890 self.fill("@")
891 self.traverse(deco)
892 self.fill("class " + node.name)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300893 with self.delimit("(", ")"):
894 comma = False
895 for e in node.bases:
896 if comma:
897 self.write(", ")
898 else:
899 comma = True
900 self.traverse(e)
901 for e in node.keywords:
902 if comma:
903 self.write(", ")
904 else:
905 comma = True
906 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000907
908 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300909 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000910
911 def visit_FunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300912 self._function_helper(node, "def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000913
914 def visit_AsyncFunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300915 self._function_helper(node, "async def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000916
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300917 def _function_helper(self, node, fill_suffix):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000918 self.write("\n")
919 for deco in node.decorator_list:
920 self.fill("@")
921 self.traverse(deco)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300922 def_str = fill_suffix + " " + node.name
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000923 self.fill(def_str)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300924 with self.delimit("(", ")"):
925 self.traverse(node.args)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000926 if node.returns:
927 self.write(" -> ")
928 self.traverse(node.returns)
929 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300930 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000931
932 def visit_For(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300933 self._for_helper("for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000934
935 def visit_AsyncFor(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300936 self._for_helper("async for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000937
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300938 def _for_helper(self, fill, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000939 self.fill(fill)
940 self.traverse(node.target)
941 self.write(" in ")
942 self.traverse(node.iter)
943 with self.block():
944 self.traverse(node.body)
945 if node.orelse:
946 self.fill("else")
947 with self.block():
948 self.traverse(node.orelse)
949
950 def visit_If(self, node):
951 self.fill("if ")
952 self.traverse(node.test)
953 with self.block():
954 self.traverse(node.body)
955 # collapse nested ifs into equivalent elifs.
956 while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If):
957 node = node.orelse[0]
958 self.fill("elif ")
959 self.traverse(node.test)
960 with self.block():
961 self.traverse(node.body)
962 # final else
963 if node.orelse:
964 self.fill("else")
965 with self.block():
966 self.traverse(node.orelse)
967
968 def visit_While(self, node):
969 self.fill("while ")
970 self.traverse(node.test)
971 with self.block():
972 self.traverse(node.body)
973 if node.orelse:
974 self.fill("else")
975 with self.block():
976 self.traverse(node.orelse)
977
978 def visit_With(self, node):
979 self.fill("with ")
980 self.interleave(lambda: self.write(", "), self.traverse, node.items)
981 with self.block():
982 self.traverse(node.body)
983
984 def visit_AsyncWith(self, node):
985 self.fill("async with ")
986 self.interleave(lambda: self.write(", "), self.traverse, node.items)
987 with self.block():
988 self.traverse(node.body)
989
990 def visit_JoinedStr(self, node):
991 self.write("f")
992 self._fstring_JoinedStr(node, self.buffer_writer)
993 self.write(repr(self.buffer))
994
995 def visit_FormattedValue(self, node):
996 self.write("f")
997 self._fstring_FormattedValue(node, self.buffer_writer)
998 self.write(repr(self.buffer))
999
1000 def _fstring_JoinedStr(self, node, write):
1001 for value in node.values:
1002 meth = getattr(self, "_fstring_" + type(value).__name__)
1003 meth(value, write)
1004
1005 def _fstring_Constant(self, node, write):
1006 if not isinstance(node.value, str):
1007 raise ValueError("Constants inside JoinedStr should be a string.")
1008 value = node.value.replace("{", "{{").replace("}", "}}")
1009 write(value)
1010
1011 def _fstring_FormattedValue(self, node, write):
1012 write("{")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001013 unparser = type(self)()
1014 unparser.set_precedence(_Precedence.TEST.next(), node.value)
1015 expr = unparser.visit(node.value).rstrip("\n")
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001016 if expr.startswith("{"):
1017 write(" ") # Separate pair of opening brackets as "{ {"
1018 write(expr)
1019 if node.conversion != -1:
1020 conversion = chr(node.conversion)
1021 if conversion not in "sra":
1022 raise ValueError("Unknown f-string conversion.")
1023 write(f"!{conversion}")
1024 if node.format_spec:
1025 write(":")
1026 meth = getattr(self, "_fstring_" + type(node.format_spec).__name__)
1027 meth(node.format_spec, write)
1028 write("}")
1029
1030 def visit_Name(self, node):
1031 self.write(node.id)
1032
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001033 def _write_docstring(self, node):
1034 self.fill()
1035 if node.kind == "u":
1036 self.write("u")
1037
1038 # Preserve quotes in the docstring by escaping them
1039 value = node.value.replace("\\", "\\\\")
1040 value = value.replace('"""', '""\"')
1041 if value[-1] == '"':
1042 value = value.replace('"', '\\"', -1)
1043
1044 self.write(f'"""{value}"""')
1045
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001046 def _write_constant(self, value):
1047 if isinstance(value, (float, complex)):
1048 # Substitute overflowing decimal literal for AST infinities.
1049 self.write(repr(value).replace("inf", _INFSTR))
1050 else:
1051 self.write(repr(value))
1052
1053 def visit_Constant(self, node):
1054 value = node.value
1055 if isinstance(value, tuple):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001056 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001057 self.items_view(self._write_constant, value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001058 elif value is ...:
1059 self.write("...")
1060 else:
1061 if node.kind == "u":
1062 self.write("u")
1063 self._write_constant(node.value)
1064
1065 def visit_List(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001066 with self.delimit("[", "]"):
1067 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001068
1069 def visit_ListComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001070 with self.delimit("[", "]"):
1071 self.traverse(node.elt)
1072 for gen in node.generators:
1073 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001074
1075 def visit_GeneratorExp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001076 with self.delimit("(", ")"):
1077 self.traverse(node.elt)
1078 for gen in node.generators:
1079 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001080
1081 def visit_SetComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001082 with self.delimit("{", "}"):
1083 self.traverse(node.elt)
1084 for gen in node.generators:
1085 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001086
1087 def visit_DictComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001088 with self.delimit("{", "}"):
1089 self.traverse(node.key)
1090 self.write(": ")
1091 self.traverse(node.value)
1092 for gen in node.generators:
1093 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001094
1095 def visit_comprehension(self, node):
1096 if node.is_async:
1097 self.write(" async for ")
1098 else:
1099 self.write(" for ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001100 self.set_precedence(_Precedence.TUPLE, node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001101 self.traverse(node.target)
1102 self.write(" in ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001103 self.set_precedence(_Precedence.TEST.next(), node.iter, *node.ifs)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001104 self.traverse(node.iter)
1105 for if_clause in node.ifs:
1106 self.write(" if ")
1107 self.traverse(if_clause)
1108
1109 def visit_IfExp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001110 with self.require_parens(_Precedence.TEST, node):
1111 self.set_precedence(_Precedence.TEST.next(), node.body, node.test)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001112 self.traverse(node.body)
1113 self.write(" if ")
1114 self.traverse(node.test)
1115 self.write(" else ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001116 self.set_precedence(_Precedence.TEST, node.orelse)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001117 self.traverse(node.orelse)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001118
1119 def visit_Set(self, node):
1120 if not node.elts:
1121 raise ValueError("Set node should has at least one item")
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001122 with self.delimit("{", "}"):
1123 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001124
1125 def visit_Dict(self, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001126 def write_key_value_pair(k, v):
1127 self.traverse(k)
1128 self.write(": ")
1129 self.traverse(v)
1130
1131 def write_item(item):
1132 k, v = item
1133 if k is None:
1134 # for dictionary unpacking operator in dicts {**{'y': 2}}
1135 # see PEP 448 for details
1136 self.write("**")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001137 self.set_precedence(_Precedence.EXPR, v)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001138 self.traverse(v)
1139 else:
1140 write_key_value_pair(k, v)
1141
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001142 with self.delimit("{", "}"):
1143 self.interleave(
1144 lambda: self.write(", "), write_item, zip(node.keys, node.values)
1145 )
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001146
1147 def visit_Tuple(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001148 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001149 self.items_view(self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001150
1151 unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001152 unop_precedence = {
1153 "~": _Precedence.FACTOR,
1154 "not": _Precedence.NOT,
1155 "+": _Precedence.FACTOR,
1156 "-": _Precedence.FACTOR
1157 }
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001158
1159 def visit_UnaryOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001160 operator = self.unop[node.op.__class__.__name__]
1161 operator_precedence = self.unop_precedence[operator]
1162 with self.require_parens(operator_precedence, node):
1163 self.write(operator)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001164 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001165 self.set_precedence(operator_precedence, node.operand)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001166 self.traverse(node.operand)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001167
1168 binop = {
1169 "Add": "+",
1170 "Sub": "-",
1171 "Mult": "*",
1172 "MatMult": "@",
1173 "Div": "/",
1174 "Mod": "%",
1175 "LShift": "<<",
1176 "RShift": ">>",
1177 "BitOr": "|",
1178 "BitXor": "^",
1179 "BitAnd": "&",
1180 "FloorDiv": "//",
1181 "Pow": "**",
1182 }
1183
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001184 binop_precedence = {
1185 "+": _Precedence.ARITH,
1186 "-": _Precedence.ARITH,
1187 "*": _Precedence.TERM,
1188 "@": _Precedence.TERM,
1189 "/": _Precedence.TERM,
1190 "%": _Precedence.TERM,
1191 "<<": _Precedence.SHIFT,
1192 ">>": _Precedence.SHIFT,
1193 "|": _Precedence.BOR,
1194 "^": _Precedence.BXOR,
1195 "&": _Precedence.BAND,
1196 "//": _Precedence.TERM,
1197 "**": _Precedence.POWER,
1198 }
1199
1200 binop_rassoc = frozenset(("**",))
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001201 def visit_BinOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001202 operator = self.binop[node.op.__class__.__name__]
1203 operator_precedence = self.binop_precedence[operator]
1204 with self.require_parens(operator_precedence, node):
1205 if operator in self.binop_rassoc:
1206 left_precedence = operator_precedence.next()
1207 right_precedence = operator_precedence
1208 else:
1209 left_precedence = operator_precedence
1210 right_precedence = operator_precedence.next()
1211
1212 self.set_precedence(left_precedence, node.left)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001213 self.traverse(node.left)
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001214 self.write(f" {operator} ")
1215 self.set_precedence(right_precedence, node.right)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001216 self.traverse(node.right)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001217
1218 cmpops = {
1219 "Eq": "==",
1220 "NotEq": "!=",
1221 "Lt": "<",
1222 "LtE": "<=",
1223 "Gt": ">",
1224 "GtE": ">=",
1225 "Is": "is",
1226 "IsNot": "is not",
1227 "In": "in",
1228 "NotIn": "not in",
1229 }
1230
1231 def visit_Compare(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001232 with self.require_parens(_Precedence.CMP, node):
1233 self.set_precedence(_Precedence.CMP.next(), node.left, *node.comparators)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001234 self.traverse(node.left)
1235 for o, e in zip(node.ops, node.comparators):
1236 self.write(" " + self.cmpops[o.__class__.__name__] + " ")
1237 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001238
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001239 boolops = {"And": "and", "Or": "or"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001240 boolop_precedence = {"and": _Precedence.AND, "or": _Precedence.OR}
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001241
1242 def visit_BoolOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001243 operator = self.boolops[node.op.__class__.__name__]
1244 operator_precedence = self.boolop_precedence[operator]
1245
1246 def increasing_level_traverse(node):
1247 nonlocal operator_precedence
1248 operator_precedence = operator_precedence.next()
1249 self.set_precedence(operator_precedence, node)
1250 self.traverse(node)
1251
1252 with self.require_parens(operator_precedence, node):
1253 s = f" {operator} "
1254 self.interleave(lambda: self.write(s), increasing_level_traverse, node.values)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001255
1256 def visit_Attribute(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001257 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001258 self.traverse(node.value)
1259 # Special case: 3.__abs__() is a syntax error, so if node.value
1260 # is an integer literal then we need to either parenthesize
1261 # it or add an extra space to get 3 .__abs__().
1262 if isinstance(node.value, Constant) and isinstance(node.value.value, int):
1263 self.write(" ")
1264 self.write(".")
1265 self.write(node.attr)
1266
1267 def visit_Call(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001268 self.set_precedence(_Precedence.ATOM, node.func)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001269 self.traverse(node.func)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001270 with self.delimit("(", ")"):
1271 comma = False
1272 for e in node.args:
1273 if comma:
1274 self.write(", ")
1275 else:
1276 comma = True
1277 self.traverse(e)
1278 for e in node.keywords:
1279 if comma:
1280 self.write(", ")
1281 else:
1282 comma = True
1283 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001284
1285 def visit_Subscript(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001286 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001287 self.traverse(node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001288 with self.delimit("[", "]"):
Serhiy Storchaka13d52c22020-03-10 18:52:34 +02001289 if isinstance(node.slice, Tuple) and node.slice.elts:
1290 self.items_view(self.traverse, node.slice.elts)
Serhiy Storchakac4928fc2020-03-07 17:25:32 +02001291 else:
1292 self.traverse(node.slice)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001293
1294 def visit_Starred(self, node):
1295 self.write("*")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001296 self.set_precedence(_Precedence.EXPR, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001297 self.traverse(node.value)
1298
1299 def visit_Ellipsis(self, node):
1300 self.write("...")
1301
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001302 def visit_Slice(self, node):
1303 if node.lower:
1304 self.traverse(node.lower)
1305 self.write(":")
1306 if node.upper:
1307 self.traverse(node.upper)
1308 if node.step:
1309 self.write(":")
1310 self.traverse(node.step)
1311
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001312 def visit_arg(self, node):
1313 self.write(node.arg)
1314 if node.annotation:
1315 self.write(": ")
1316 self.traverse(node.annotation)
1317
1318 def visit_arguments(self, node):
1319 first = True
1320 # normal arguments
1321 all_args = node.posonlyargs + node.args
1322 defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults
1323 for index, elements in enumerate(zip(all_args, defaults), 1):
1324 a, d = elements
1325 if first:
1326 first = False
1327 else:
1328 self.write(", ")
1329 self.traverse(a)
1330 if d:
1331 self.write("=")
1332 self.traverse(d)
1333 if index == len(node.posonlyargs):
1334 self.write(", /")
1335
1336 # varargs, or bare '*' if no varargs but keyword-only arguments present
1337 if node.vararg or node.kwonlyargs:
1338 if first:
1339 first = False
1340 else:
1341 self.write(", ")
1342 self.write("*")
1343 if node.vararg:
1344 self.write(node.vararg.arg)
1345 if node.vararg.annotation:
1346 self.write(": ")
1347 self.traverse(node.vararg.annotation)
1348
1349 # keyword-only arguments
1350 if node.kwonlyargs:
1351 for a, d in zip(node.kwonlyargs, node.kw_defaults):
Batuhan Taşkayaa322f502019-12-16 15:26:58 +03001352 self.write(", ")
1353 self.traverse(a)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001354 if d:
1355 self.write("=")
1356 self.traverse(d)
1357
1358 # kwargs
1359 if node.kwarg:
1360 if first:
1361 first = False
1362 else:
1363 self.write(", ")
1364 self.write("**" + node.kwarg.arg)
1365 if node.kwarg.annotation:
1366 self.write(": ")
1367 self.traverse(node.kwarg.annotation)
1368
1369 def visit_keyword(self, node):
1370 if node.arg is None:
1371 self.write("**")
1372 else:
1373 self.write(node.arg)
1374 self.write("=")
1375 self.traverse(node.value)
1376
1377 def visit_Lambda(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001378 with self.require_parens(_Precedence.TEST, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001379 self.write("lambda ")
1380 self.traverse(node.args)
1381 self.write(": ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001382 self.set_precedence(_Precedence.TEST, node.body)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001383 self.traverse(node.body)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001384
1385 def visit_alias(self, node):
1386 self.write(node.name)
1387 if node.asname:
1388 self.write(" as " + node.asname)
1389
1390 def visit_withitem(self, node):
1391 self.traverse(node.context_expr)
1392 if node.optional_vars:
1393 self.write(" as ")
1394 self.traverse(node.optional_vars)
1395
1396def unparse(ast_obj):
1397 unparser = _Unparser()
1398 return unparser.visit(ast_obj)
1399
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001400
1401def main():
1402 import argparse
1403
1404 parser = argparse.ArgumentParser(prog='python -m ast')
1405 parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?',
1406 default='-',
1407 help='the file to parse; defaults to stdin')
1408 parser.add_argument('-m', '--mode', default='exec',
1409 choices=('exec', 'single', 'eval', 'func_type'),
1410 help='specify what kind of code must be parsed')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001411 parser.add_argument('--no-type-comments', default=True, action='store_false',
1412 help="don't add information about type comments")
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001413 parser.add_argument('-a', '--include-attributes', action='store_true',
1414 help='include attributes such as line numbers and '
1415 'column offsets')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001416 parser.add_argument('-i', '--indent', type=int, default=3,
1417 help='indentation of nodes (number of spaces)')
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001418 args = parser.parse_args()
1419
1420 with args.infile as infile:
1421 source = infile.read()
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001422 tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
1423 print(dump(tree, include_attributes=args.include_attributes, indent=args.indent))
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001424
1425if __name__ == '__main__':
1426 main()