blob: 0bce4a49dc77a0f3702a679ee75576df8b03e81d [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):
446 return copy_location(Subscript(
447 value=Name(id='data', ctx=Load()),
448 slice=Index(value=Str(s=node.id)),
449 ctx=node.ctx
450 ), node)
451
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 Storchakac3ea41e2019-08-26 10:13:19 +0300555_const_node_type_names = {
556 bool: 'NameConstant', # should be before int
557 type(None): 'NameConstant',
558 int: 'Num',
559 float: 'Num',
560 complex: 'Num',
561 str: 'Str',
562 bytes: 'Bytes',
563 type(...): 'Ellipsis',
564}
Serhiy Storchaka832e8642019-09-09 23:36:13 +0300565
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000566# Large float and imaginary literals get turned into infinities in the AST.
567# We unparse those infinities to INFSTR.
568_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
569
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300570class _Precedence(IntEnum):
571 """Precedence table that originated from python grammar."""
572
573 TUPLE = auto()
574 YIELD = auto() # 'yield', 'yield from'
575 TEST = auto() # 'if'-'else', 'lambda'
576 OR = auto() # 'or'
577 AND = auto() # 'and'
578 NOT = auto() # 'not'
579 CMP = auto() # '<', '>', '==', '>=', '<=', '!=',
580 # 'in', 'not in', 'is', 'is not'
581 EXPR = auto()
582 BOR = EXPR # '|'
583 BXOR = auto() # '^'
584 BAND = auto() # '&'
585 SHIFT = auto() # '<<', '>>'
586 ARITH = auto() # '+', '-'
587 TERM = auto() # '*', '@', '/', '%', '//'
588 FACTOR = auto() # unary '+', '-', '~'
589 POWER = auto() # '**'
590 AWAIT = auto() # 'await'
591 ATOM = auto()
592
593 def next(self):
594 try:
595 return self.__class__(self + 1)
596 except ValueError:
597 return self
598
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000599class _Unparser(NodeVisitor):
600 """Methods in this class recursively traverse an AST and
601 output source code for the abstract syntax; original formatting
602 is disregarded."""
603
604 def __init__(self):
605 self._source = []
606 self._buffer = []
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300607 self._precedences = {}
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000608 self._indent = 0
609
610 def interleave(self, inter, f, seq):
611 """Call f on each item in seq, calling inter() in between."""
612 seq = iter(seq)
613 try:
614 f(next(seq))
615 except StopIteration:
616 pass
617 else:
618 for x in seq:
619 inter()
620 f(x)
621
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +0300622 def items_view(self, traverser, items):
623 """Traverse and separate the given *items* with a comma and append it to
624 the buffer. If *items* is a single item sequence, a trailing comma
625 will be added."""
626 if len(items) == 1:
627 traverser(items[0])
628 self.write(",")
629 else:
630 self.interleave(lambda: self.write(", "), traverser, items)
631
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000632 def fill(self, text=""):
633 """Indent a piece of text and append it, according to the current
634 indentation level"""
635 self.write("\n" + " " * self._indent + text)
636
637 def write(self, text):
638 """Append a piece of text"""
639 self._source.append(text)
640
641 def buffer_writer(self, text):
642 self._buffer.append(text)
643
644 @property
645 def buffer(self):
646 value = "".join(self._buffer)
647 self._buffer.clear()
648 return value
649
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000650 @contextmanager
651 def block(self):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000652 """A context manager for preparing the source for blocks. It adds
653 the character':', increases the indentation on enter and decreases
654 the indentation on exit."""
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000655 self.write(":")
656 self._indent += 1
657 yield
658 self._indent -= 1
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000659
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300660 @contextmanager
661 def delimit(self, start, end):
662 """A context manager for preparing the source for expressions. It adds
663 *start* to the buffer and enters, after exit it adds *end*."""
664
665 self.write(start)
666 yield
667 self.write(end)
668
669 def delimit_if(self, start, end, condition):
670 if condition:
671 return self.delimit(start, end)
672 else:
673 return nullcontext()
674
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300675 def require_parens(self, precedence, node):
676 """Shortcut to adding precedence related parens"""
677 return self.delimit_if("(", ")", self.get_precedence(node) > precedence)
678
679 def get_precedence(self, node):
680 return self._precedences.get(node, _Precedence.TEST)
681
682 def set_precedence(self, precedence, *nodes):
683 for node in nodes:
684 self._precedences[node] = precedence
685
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300686 def get_raw_docstring(self, node):
687 """If a docstring node is found in the body of the *node* parameter,
688 return that docstring node, None otherwise.
689
690 Logic mirrored from ``_PyAST_GetDocString``."""
691 if not isinstance(
692 node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)
693 ) or len(node.body) < 1:
694 return None
695 node = node.body[0]
696 if not isinstance(node, Expr):
697 return None
698 node = node.value
699 if isinstance(node, Constant) and isinstance(node.value, str):
700 return node
701
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000702 def traverse(self, node):
703 if isinstance(node, list):
704 for item in node:
705 self.traverse(item)
706 else:
707 super().visit(node)
708
709 def visit(self, node):
710 """Outputs a source code string that, if converted back to an ast
711 (using ast.parse) will generate an AST equivalent to *node*"""
712 self._source = []
713 self.traverse(node)
714 return "".join(self._source)
715
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300716 def _write_docstring_and_traverse_body(self, node):
717 if (docstring := self.get_raw_docstring(node)):
718 self._write_docstring(docstring)
719 self.traverse(node.body[1:])
720 else:
721 self.traverse(node.body)
722
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000723 def visit_Module(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300724 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000725
726 def visit_Expr(self, node):
727 self.fill()
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300728 self.set_precedence(_Precedence.YIELD, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000729 self.traverse(node.value)
730
731 def visit_NamedExpr(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300732 with self.require_parens(_Precedence.TUPLE, node):
733 self.set_precedence(_Precedence.ATOM, node.target, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300734 self.traverse(node.target)
735 self.write(" := ")
736 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000737
738 def visit_Import(self, node):
739 self.fill("import ")
740 self.interleave(lambda: self.write(", "), self.traverse, node.names)
741
742 def visit_ImportFrom(self, node):
743 self.fill("from ")
744 self.write("." * node.level)
745 if node.module:
746 self.write(node.module)
747 self.write(" import ")
748 self.interleave(lambda: self.write(", "), self.traverse, node.names)
749
750 def visit_Assign(self, node):
751 self.fill()
752 for target in node.targets:
753 self.traverse(target)
754 self.write(" = ")
755 self.traverse(node.value)
756
757 def visit_AugAssign(self, node):
758 self.fill()
759 self.traverse(node.target)
760 self.write(" " + self.binop[node.op.__class__.__name__] + "= ")
761 self.traverse(node.value)
762
763 def visit_AnnAssign(self, node):
764 self.fill()
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300765 with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)):
766 self.traverse(node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000767 self.write(": ")
768 self.traverse(node.annotation)
769 if node.value:
770 self.write(" = ")
771 self.traverse(node.value)
772
773 def visit_Return(self, node):
774 self.fill("return")
775 if node.value:
776 self.write(" ")
777 self.traverse(node.value)
778
779 def visit_Pass(self, node):
780 self.fill("pass")
781
782 def visit_Break(self, node):
783 self.fill("break")
784
785 def visit_Continue(self, node):
786 self.fill("continue")
787
788 def visit_Delete(self, node):
789 self.fill("del ")
790 self.interleave(lambda: self.write(", "), self.traverse, node.targets)
791
792 def visit_Assert(self, node):
793 self.fill("assert ")
794 self.traverse(node.test)
795 if node.msg:
796 self.write(", ")
797 self.traverse(node.msg)
798
799 def visit_Global(self, node):
800 self.fill("global ")
801 self.interleave(lambda: self.write(", "), self.write, node.names)
802
803 def visit_Nonlocal(self, node):
804 self.fill("nonlocal ")
805 self.interleave(lambda: self.write(", "), self.write, node.names)
806
807 def visit_Await(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300808 with self.require_parens(_Precedence.AWAIT, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300809 self.write("await")
810 if node.value:
811 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300812 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300813 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000814
815 def visit_Yield(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300816 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300817 self.write("yield")
818 if node.value:
819 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300820 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300821 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000822
823 def visit_YieldFrom(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300824 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300825 self.write("yield from ")
826 if not node.value:
827 raise ValueError("Node can't be used without a value attribute.")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300828 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300829 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000830
831 def visit_Raise(self, node):
832 self.fill("raise")
833 if not node.exc:
834 if node.cause:
835 raise ValueError(f"Node can't use cause without an exception.")
836 return
837 self.write(" ")
838 self.traverse(node.exc)
839 if node.cause:
840 self.write(" from ")
841 self.traverse(node.cause)
842
843 def visit_Try(self, node):
844 self.fill("try")
845 with self.block():
846 self.traverse(node.body)
847 for ex in node.handlers:
848 self.traverse(ex)
849 if node.orelse:
850 self.fill("else")
851 with self.block():
852 self.traverse(node.orelse)
853 if node.finalbody:
854 self.fill("finally")
855 with self.block():
856 self.traverse(node.finalbody)
857
858 def visit_ExceptHandler(self, node):
859 self.fill("except")
860 if node.type:
861 self.write(" ")
862 self.traverse(node.type)
863 if node.name:
864 self.write(" as ")
865 self.write(node.name)
866 with self.block():
867 self.traverse(node.body)
868
869 def visit_ClassDef(self, node):
870 self.write("\n")
871 for deco in node.decorator_list:
872 self.fill("@")
873 self.traverse(deco)
874 self.fill("class " + node.name)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300875 with self.delimit("(", ")"):
876 comma = False
877 for e in node.bases:
878 if comma:
879 self.write(", ")
880 else:
881 comma = True
882 self.traverse(e)
883 for e in node.keywords:
884 if comma:
885 self.write(", ")
886 else:
887 comma = True
888 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000889
890 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300891 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000892
893 def visit_FunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300894 self._function_helper(node, "def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000895
896 def visit_AsyncFunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300897 self._function_helper(node, "async def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000898
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300899 def _function_helper(self, node, fill_suffix):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000900 self.write("\n")
901 for deco in node.decorator_list:
902 self.fill("@")
903 self.traverse(deco)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300904 def_str = fill_suffix + " " + node.name
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000905 self.fill(def_str)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300906 with self.delimit("(", ")"):
907 self.traverse(node.args)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000908 if node.returns:
909 self.write(" -> ")
910 self.traverse(node.returns)
911 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300912 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000913
914 def visit_For(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300915 self._for_helper("for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000916
917 def visit_AsyncFor(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300918 self._for_helper("async for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000919
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300920 def _for_helper(self, fill, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000921 self.fill(fill)
922 self.traverse(node.target)
923 self.write(" in ")
924 self.traverse(node.iter)
925 with self.block():
926 self.traverse(node.body)
927 if node.orelse:
928 self.fill("else")
929 with self.block():
930 self.traverse(node.orelse)
931
932 def visit_If(self, node):
933 self.fill("if ")
934 self.traverse(node.test)
935 with self.block():
936 self.traverse(node.body)
937 # collapse nested ifs into equivalent elifs.
938 while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If):
939 node = node.orelse[0]
940 self.fill("elif ")
941 self.traverse(node.test)
942 with self.block():
943 self.traverse(node.body)
944 # final else
945 if node.orelse:
946 self.fill("else")
947 with self.block():
948 self.traverse(node.orelse)
949
950 def visit_While(self, node):
951 self.fill("while ")
952 self.traverse(node.test)
953 with self.block():
954 self.traverse(node.body)
955 if node.orelse:
956 self.fill("else")
957 with self.block():
958 self.traverse(node.orelse)
959
960 def visit_With(self, node):
961 self.fill("with ")
962 self.interleave(lambda: self.write(", "), self.traverse, node.items)
963 with self.block():
964 self.traverse(node.body)
965
966 def visit_AsyncWith(self, node):
967 self.fill("async with ")
968 self.interleave(lambda: self.write(", "), self.traverse, node.items)
969 with self.block():
970 self.traverse(node.body)
971
972 def visit_JoinedStr(self, node):
973 self.write("f")
974 self._fstring_JoinedStr(node, self.buffer_writer)
975 self.write(repr(self.buffer))
976
977 def visit_FormattedValue(self, node):
978 self.write("f")
979 self._fstring_FormattedValue(node, self.buffer_writer)
980 self.write(repr(self.buffer))
981
982 def _fstring_JoinedStr(self, node, write):
983 for value in node.values:
984 meth = getattr(self, "_fstring_" + type(value).__name__)
985 meth(value, write)
986
987 def _fstring_Constant(self, node, write):
988 if not isinstance(node.value, str):
989 raise ValueError("Constants inside JoinedStr should be a string.")
990 value = node.value.replace("{", "{{").replace("}", "}}")
991 write(value)
992
993 def _fstring_FormattedValue(self, node, write):
994 write("{")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300995 unparser = type(self)()
996 unparser.set_precedence(_Precedence.TEST.next(), node.value)
997 expr = unparser.visit(node.value).rstrip("\n")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000998 if expr.startswith("{"):
999 write(" ") # Separate pair of opening brackets as "{ {"
1000 write(expr)
1001 if node.conversion != -1:
1002 conversion = chr(node.conversion)
1003 if conversion not in "sra":
1004 raise ValueError("Unknown f-string conversion.")
1005 write(f"!{conversion}")
1006 if node.format_spec:
1007 write(":")
1008 meth = getattr(self, "_fstring_" + type(node.format_spec).__name__)
1009 meth(node.format_spec, write)
1010 write("}")
1011
1012 def visit_Name(self, node):
1013 self.write(node.id)
1014
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001015 def _write_docstring(self, node):
1016 self.fill()
1017 if node.kind == "u":
1018 self.write("u")
1019
1020 # Preserve quotes in the docstring by escaping them
1021 value = node.value.replace("\\", "\\\\")
1022 value = value.replace('"""', '""\"')
1023 if value[-1] == '"':
1024 value = value.replace('"', '\\"', -1)
1025
1026 self.write(f'"""{value}"""')
1027
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001028 def _write_constant(self, value):
1029 if isinstance(value, (float, complex)):
1030 # Substitute overflowing decimal literal for AST infinities.
1031 self.write(repr(value).replace("inf", _INFSTR))
1032 else:
1033 self.write(repr(value))
1034
1035 def visit_Constant(self, node):
1036 value = node.value
1037 if isinstance(value, tuple):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001038 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001039 self.items_view(self._write_constant, value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001040 elif value is ...:
1041 self.write("...")
1042 else:
1043 if node.kind == "u":
1044 self.write("u")
1045 self._write_constant(node.value)
1046
1047 def visit_List(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001048 with self.delimit("[", "]"):
1049 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001050
1051 def visit_ListComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001052 with self.delimit("[", "]"):
1053 self.traverse(node.elt)
1054 for gen in node.generators:
1055 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001056
1057 def visit_GeneratorExp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001058 with self.delimit("(", ")"):
1059 self.traverse(node.elt)
1060 for gen in node.generators:
1061 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001062
1063 def visit_SetComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001064 with self.delimit("{", "}"):
1065 self.traverse(node.elt)
1066 for gen in node.generators:
1067 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001068
1069 def visit_DictComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001070 with self.delimit("{", "}"):
1071 self.traverse(node.key)
1072 self.write(": ")
1073 self.traverse(node.value)
1074 for gen in node.generators:
1075 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001076
1077 def visit_comprehension(self, node):
1078 if node.is_async:
1079 self.write(" async for ")
1080 else:
1081 self.write(" for ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001082 self.set_precedence(_Precedence.TUPLE, node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001083 self.traverse(node.target)
1084 self.write(" in ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001085 self.set_precedence(_Precedence.TEST.next(), node.iter, *node.ifs)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001086 self.traverse(node.iter)
1087 for if_clause in node.ifs:
1088 self.write(" if ")
1089 self.traverse(if_clause)
1090
1091 def visit_IfExp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001092 with self.require_parens(_Precedence.TEST, node):
1093 self.set_precedence(_Precedence.TEST.next(), node.body, node.test)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001094 self.traverse(node.body)
1095 self.write(" if ")
1096 self.traverse(node.test)
1097 self.write(" else ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001098 self.set_precedence(_Precedence.TEST, node.orelse)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001099 self.traverse(node.orelse)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001100
1101 def visit_Set(self, node):
1102 if not node.elts:
1103 raise ValueError("Set node should has at least one item")
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001104 with self.delimit("{", "}"):
1105 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001106
1107 def visit_Dict(self, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001108 def write_key_value_pair(k, v):
1109 self.traverse(k)
1110 self.write(": ")
1111 self.traverse(v)
1112
1113 def write_item(item):
1114 k, v = item
1115 if k is None:
1116 # for dictionary unpacking operator in dicts {**{'y': 2}}
1117 # see PEP 448 for details
1118 self.write("**")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001119 self.set_precedence(_Precedence.EXPR, v)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001120 self.traverse(v)
1121 else:
1122 write_key_value_pair(k, v)
1123
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001124 with self.delimit("{", "}"):
1125 self.interleave(
1126 lambda: self.write(", "), write_item, zip(node.keys, node.values)
1127 )
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001128
1129 def visit_Tuple(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001130 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001131 self.items_view(self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001132
1133 unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001134 unop_precedence = {
1135 "~": _Precedence.FACTOR,
1136 "not": _Precedence.NOT,
1137 "+": _Precedence.FACTOR,
1138 "-": _Precedence.FACTOR
1139 }
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001140
1141 def visit_UnaryOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001142 operator = self.unop[node.op.__class__.__name__]
1143 operator_precedence = self.unop_precedence[operator]
1144 with self.require_parens(operator_precedence, node):
1145 self.write(operator)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001146 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001147 self.set_precedence(operator_precedence, node.operand)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001148 self.traverse(node.operand)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001149
1150 binop = {
1151 "Add": "+",
1152 "Sub": "-",
1153 "Mult": "*",
1154 "MatMult": "@",
1155 "Div": "/",
1156 "Mod": "%",
1157 "LShift": "<<",
1158 "RShift": ">>",
1159 "BitOr": "|",
1160 "BitXor": "^",
1161 "BitAnd": "&",
1162 "FloorDiv": "//",
1163 "Pow": "**",
1164 }
1165
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001166 binop_precedence = {
1167 "+": _Precedence.ARITH,
1168 "-": _Precedence.ARITH,
1169 "*": _Precedence.TERM,
1170 "@": _Precedence.TERM,
1171 "/": _Precedence.TERM,
1172 "%": _Precedence.TERM,
1173 "<<": _Precedence.SHIFT,
1174 ">>": _Precedence.SHIFT,
1175 "|": _Precedence.BOR,
1176 "^": _Precedence.BXOR,
1177 "&": _Precedence.BAND,
1178 "//": _Precedence.TERM,
1179 "**": _Precedence.POWER,
1180 }
1181
1182 binop_rassoc = frozenset(("**",))
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001183 def visit_BinOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001184 operator = self.binop[node.op.__class__.__name__]
1185 operator_precedence = self.binop_precedence[operator]
1186 with self.require_parens(operator_precedence, node):
1187 if operator in self.binop_rassoc:
1188 left_precedence = operator_precedence.next()
1189 right_precedence = operator_precedence
1190 else:
1191 left_precedence = operator_precedence
1192 right_precedence = operator_precedence.next()
1193
1194 self.set_precedence(left_precedence, node.left)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001195 self.traverse(node.left)
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001196 self.write(f" {operator} ")
1197 self.set_precedence(right_precedence, node.right)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001198 self.traverse(node.right)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001199
1200 cmpops = {
1201 "Eq": "==",
1202 "NotEq": "!=",
1203 "Lt": "<",
1204 "LtE": "<=",
1205 "Gt": ">",
1206 "GtE": ">=",
1207 "Is": "is",
1208 "IsNot": "is not",
1209 "In": "in",
1210 "NotIn": "not in",
1211 }
1212
1213 def visit_Compare(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001214 with self.require_parens(_Precedence.CMP, node):
1215 self.set_precedence(_Precedence.CMP.next(), node.left, *node.comparators)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001216 self.traverse(node.left)
1217 for o, e in zip(node.ops, node.comparators):
1218 self.write(" " + self.cmpops[o.__class__.__name__] + " ")
1219 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001220
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001221 boolops = {"And": "and", "Or": "or"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001222 boolop_precedence = {"and": _Precedence.AND, "or": _Precedence.OR}
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001223
1224 def visit_BoolOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001225 operator = self.boolops[node.op.__class__.__name__]
1226 operator_precedence = self.boolop_precedence[operator]
1227
1228 def increasing_level_traverse(node):
1229 nonlocal operator_precedence
1230 operator_precedence = operator_precedence.next()
1231 self.set_precedence(operator_precedence, node)
1232 self.traverse(node)
1233
1234 with self.require_parens(operator_precedence, node):
1235 s = f" {operator} "
1236 self.interleave(lambda: self.write(s), increasing_level_traverse, node.values)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001237
1238 def visit_Attribute(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001239 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001240 self.traverse(node.value)
1241 # Special case: 3.__abs__() is a syntax error, so if node.value
1242 # is an integer literal then we need to either parenthesize
1243 # it or add an extra space to get 3 .__abs__().
1244 if isinstance(node.value, Constant) and isinstance(node.value.value, int):
1245 self.write(" ")
1246 self.write(".")
1247 self.write(node.attr)
1248
1249 def visit_Call(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001250 self.set_precedence(_Precedence.ATOM, node.func)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001251 self.traverse(node.func)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001252 with self.delimit("(", ")"):
1253 comma = False
1254 for e in node.args:
1255 if comma:
1256 self.write(", ")
1257 else:
1258 comma = True
1259 self.traverse(e)
1260 for e in node.keywords:
1261 if comma:
1262 self.write(", ")
1263 else:
1264 comma = True
1265 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001266
1267 def visit_Subscript(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001268 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001269 self.traverse(node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001270 with self.delimit("[", "]"):
Serhiy Storchakac4928fc2020-03-07 17:25:32 +02001271 if (isinstance(node.slice, Index)
1272 and isinstance(node.slice.value, Tuple)
1273 and node.slice.value.elts):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001274 self.items_view(self.traverse, node.slice.value.elts)
Serhiy Storchakac4928fc2020-03-07 17:25:32 +02001275 else:
1276 self.traverse(node.slice)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001277
1278 def visit_Starred(self, node):
1279 self.write("*")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001280 self.set_precedence(_Precedence.EXPR, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001281 self.traverse(node.value)
1282
1283 def visit_Ellipsis(self, node):
1284 self.write("...")
1285
1286 def visit_Index(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001287 self.set_precedence(_Precedence.TUPLE, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001288 self.traverse(node.value)
1289
1290 def visit_Slice(self, node):
1291 if node.lower:
1292 self.traverse(node.lower)
1293 self.write(":")
1294 if node.upper:
1295 self.traverse(node.upper)
1296 if node.step:
1297 self.write(":")
1298 self.traverse(node.step)
1299
1300 def visit_ExtSlice(self, node):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001301 self.items_view(self.traverse, node.dims)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001302
1303 def visit_arg(self, node):
1304 self.write(node.arg)
1305 if node.annotation:
1306 self.write(": ")
1307 self.traverse(node.annotation)
1308
1309 def visit_arguments(self, node):
1310 first = True
1311 # normal arguments
1312 all_args = node.posonlyargs + node.args
1313 defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults
1314 for index, elements in enumerate(zip(all_args, defaults), 1):
1315 a, d = elements
1316 if first:
1317 first = False
1318 else:
1319 self.write(", ")
1320 self.traverse(a)
1321 if d:
1322 self.write("=")
1323 self.traverse(d)
1324 if index == len(node.posonlyargs):
1325 self.write(", /")
1326
1327 # varargs, or bare '*' if no varargs but keyword-only arguments present
1328 if node.vararg or node.kwonlyargs:
1329 if first:
1330 first = False
1331 else:
1332 self.write(", ")
1333 self.write("*")
1334 if node.vararg:
1335 self.write(node.vararg.arg)
1336 if node.vararg.annotation:
1337 self.write(": ")
1338 self.traverse(node.vararg.annotation)
1339
1340 # keyword-only arguments
1341 if node.kwonlyargs:
1342 for a, d in zip(node.kwonlyargs, node.kw_defaults):
Batuhan Taşkayaa322f502019-12-16 15:26:58 +03001343 self.write(", ")
1344 self.traverse(a)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001345 if d:
1346 self.write("=")
1347 self.traverse(d)
1348
1349 # kwargs
1350 if node.kwarg:
1351 if first:
1352 first = False
1353 else:
1354 self.write(", ")
1355 self.write("**" + node.kwarg.arg)
1356 if node.kwarg.annotation:
1357 self.write(": ")
1358 self.traverse(node.kwarg.annotation)
1359
1360 def visit_keyword(self, node):
1361 if node.arg is None:
1362 self.write("**")
1363 else:
1364 self.write(node.arg)
1365 self.write("=")
1366 self.traverse(node.value)
1367
1368 def visit_Lambda(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001369 with self.require_parens(_Precedence.TEST, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001370 self.write("lambda ")
1371 self.traverse(node.args)
1372 self.write(": ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001373 self.set_precedence(_Precedence.TEST, node.body)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001374 self.traverse(node.body)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001375
1376 def visit_alias(self, node):
1377 self.write(node.name)
1378 if node.asname:
1379 self.write(" as " + node.asname)
1380
1381 def visit_withitem(self, node):
1382 self.traverse(node.context_expr)
1383 if node.optional_vars:
1384 self.write(" as ")
1385 self.traverse(node.optional_vars)
1386
1387def unparse(ast_obj):
1388 unparser = _Unparser()
1389 return unparser.visit(ast_obj)
1390
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001391
1392def main():
1393 import argparse
1394
1395 parser = argparse.ArgumentParser(prog='python -m ast')
1396 parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?',
1397 default='-',
1398 help='the file to parse; defaults to stdin')
1399 parser.add_argument('-m', '--mode', default='exec',
1400 choices=('exec', 'single', 'eval', 'func_type'),
1401 help='specify what kind of code must be parsed')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001402 parser.add_argument('--no-type-comments', default=True, action='store_false',
1403 help="don't add information about type comments")
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001404 parser.add_argument('-a', '--include-attributes', action='store_true',
1405 help='include attributes such as line numbers and '
1406 'column offsets')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001407 parser.add_argument('-i', '--indent', type=int, default=3,
1408 help='indentation of nodes (number of spaces)')
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001409 args = parser.parse_args()
1410
1411 with args.infile as infile:
1412 source = infile.read()
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001413 tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
1414 print(dump(tree, include_attributes=args.include_attributes, indent=args.indent))
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001415
1416if __name__ == '__main__':
1417 main()