blob: 9a3d3806eb8ca7519a625d974e234f52790a14f9 [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 Storchakae64f9482019-08-29 09:30:23 +0300126 args = []
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300127 allsimple = True
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300128 keywords = annotate_fields
129 for field in node._fields:
130 try:
131 value = getattr(node, field)
132 except AttributeError:
133 keywords = True
134 else:
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300135 value, simple = _format(value, level)
136 allsimple = allsimple and simple
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300137 if keywords:
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300138 args.append('%s=%s' % (field, value))
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300139 else:
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300140 args.append(value)
Georg Brandl0c77a822008-06-10 16:37:50 +0000141 if include_attributes and node._attributes:
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300142 for attr in node._attributes:
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300143 try:
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300144 value = getattr(node, attr)
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300145 except AttributeError:
146 pass
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300147 else:
148 value, simple = _format(value, level)
149 allsimple = allsimple and simple
150 args.append('%s=%s' % (attr, value))
151 if allsimple and len(args) <= 3:
152 return '%s(%s)' % (node.__class__.__name__, ', '.join(args)), not args
153 return '%s(%s%s)' % (node.__class__.__name__, prefix, sep.join(args)), False
Georg Brandl0c77a822008-06-10 16:37:50 +0000154 elif isinstance(node, list):
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300155 if not node:
156 return '[]', True
157 return '[%s%s]' % (prefix, sep.join(_format(x, level)[0] for x in node)), False
158 return repr(node), True
159
Georg Brandl0c77a822008-06-10 16:37:50 +0000160 if not isinstance(node, AST):
161 raise TypeError('expected AST, got %r' % node.__class__.__name__)
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300162 if indent is not None and not isinstance(indent, str):
163 indent = ' ' * indent
164 return _format(node)[0]
Georg Brandl0c77a822008-06-10 16:37:50 +0000165
166
167def copy_location(new_node, old_node):
168 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000169 Copy source location (`lineno`, `col_offset`, `end_lineno`, and `end_col_offset`
170 attributes) from *old_node* to *new_node* if possible, and return *new_node*.
Georg Brandl0c77a822008-06-10 16:37:50 +0000171 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000172 for attr in 'lineno', 'col_offset', 'end_lineno', 'end_col_offset':
Georg Brandl0c77a822008-06-10 16:37:50 +0000173 if attr in old_node._attributes and attr in new_node._attributes \
174 and hasattr(old_node, attr):
175 setattr(new_node, attr, getattr(old_node, attr))
176 return new_node
177
178
179def fix_missing_locations(node):
180 """
181 When you compile a node tree with compile(), the compiler expects lineno and
182 col_offset attributes for every node that supports them. This is rather
183 tedious to fill in for generated nodes, so this helper adds these attributes
184 recursively where not already set, by setting them to the values of the
185 parent node. It works recursively starting at *node*.
186 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000187 def _fix(node, lineno, col_offset, end_lineno, end_col_offset):
Georg Brandl0c77a822008-06-10 16:37:50 +0000188 if 'lineno' in node._attributes:
189 if not hasattr(node, 'lineno'):
190 node.lineno = lineno
191 else:
192 lineno = node.lineno
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000193 if 'end_lineno' in node._attributes:
194 if not hasattr(node, 'end_lineno'):
195 node.end_lineno = end_lineno
196 else:
197 end_lineno = node.end_lineno
Georg Brandl0c77a822008-06-10 16:37:50 +0000198 if 'col_offset' in node._attributes:
199 if not hasattr(node, 'col_offset'):
200 node.col_offset = col_offset
201 else:
202 col_offset = node.col_offset
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000203 if 'end_col_offset' in node._attributes:
204 if not hasattr(node, 'end_col_offset'):
205 node.end_col_offset = end_col_offset
206 else:
207 end_col_offset = node.end_col_offset
Georg Brandl0c77a822008-06-10 16:37:50 +0000208 for child in iter_child_nodes(node):
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000209 _fix(child, lineno, col_offset, end_lineno, end_col_offset)
210 _fix(node, 1, 0, 1, 0)
Georg Brandl0c77a822008-06-10 16:37:50 +0000211 return node
212
213
214def increment_lineno(node, n=1):
215 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000216 Increment the line number and end line number of each node in the tree
217 starting at *node* by *n*. This is useful to "move code" to a different
218 location in a file.
Georg Brandl0c77a822008-06-10 16:37:50 +0000219 """
Georg Brandl0c77a822008-06-10 16:37:50 +0000220 for child in walk(node):
221 if 'lineno' in child._attributes:
222 child.lineno = getattr(child, 'lineno', 0) + n
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000223 if 'end_lineno' in child._attributes:
224 child.end_lineno = getattr(child, 'end_lineno', 0) + n
Georg Brandl0c77a822008-06-10 16:37:50 +0000225 return node
226
227
228def iter_fields(node):
229 """
230 Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
231 that is present on *node*.
232 """
233 for field in node._fields:
234 try:
235 yield field, getattr(node, field)
236 except AttributeError:
237 pass
238
239
240def iter_child_nodes(node):
241 """
242 Yield all direct child nodes of *node*, that is, all fields that are nodes
243 and all items of fields that are lists of nodes.
244 """
245 for name, field in iter_fields(node):
246 if isinstance(field, AST):
247 yield field
248 elif isinstance(field, list):
249 for item in field:
250 if isinstance(item, AST):
251 yield item
252
253
254def get_docstring(node, clean=True):
255 """
256 Return the docstring for the given node or None if no docstring can
257 be found. If the node provided does not have docstrings a TypeError
258 will be raised.
Matthias Bussonnier41cea702017-02-23 22:44:19 -0800259
260 If *clean* is `True`, all tabs are expanded to spaces and any whitespace
261 that can be uniformly removed from the second line onwards is removed.
Georg Brandl0c77a822008-06-10 16:37:50 +0000262 """
Yury Selivanov2f07a662015-07-23 08:54:35 +0300263 if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
Georg Brandl0c77a822008-06-10 16:37:50 +0000264 raise TypeError("%r can't have docstrings" % node.__class__.__name__)
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300265 if not(node.body and isinstance(node.body[0], Expr)):
Serhiy Storchaka73cbe7a2018-05-29 12:04:55 +0300266 return None
267 node = node.body[0].value
268 if isinstance(node, Str):
269 text = node.s
270 elif isinstance(node, Constant) and isinstance(node.value, str):
271 text = node.value
272 else:
273 return None
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300274 if clean:
Victor Stinnerf2c1aa12016-01-26 00:40:57 +0100275 import inspect
276 text = inspect.cleandoc(text)
277 return text
Georg Brandl0c77a822008-06-10 16:37:50 +0000278
279
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000280def _splitlines_no_ff(source):
281 """Split a string into lines ignoring form feed and other chars.
282
283 This mimics how the Python parser splits source code.
284 """
285 idx = 0
286 lines = []
287 next_line = ''
288 while idx < len(source):
289 c = source[idx]
290 next_line += c
291 idx += 1
292 # Keep \r\n together
293 if c == '\r' and idx < len(source) and source[idx] == '\n':
294 next_line += '\n'
295 idx += 1
296 if c in '\r\n':
297 lines.append(next_line)
298 next_line = ''
299
300 if next_line:
301 lines.append(next_line)
302 return lines
303
304
305def _pad_whitespace(source):
mpheathfbeba8f2020-02-14 04:32:09 +1000306 r"""Replace all chars except '\f\t' in a line with spaces."""
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000307 result = ''
308 for c in source:
309 if c in '\f\t':
310 result += c
311 else:
312 result += ' '
313 return result
314
315
316def get_source_segment(source, node, *, padded=False):
317 """Get source code segment of the *source* that generated *node*.
318
319 If some location information (`lineno`, `end_lineno`, `col_offset`,
320 or `end_col_offset`) is missing, return None.
321
322 If *padded* is `True`, the first line of a multi-line statement will
323 be padded with spaces to match its original position.
324 """
325 try:
326 lineno = node.lineno - 1
327 end_lineno = node.end_lineno - 1
328 col_offset = node.col_offset
329 end_col_offset = node.end_col_offset
330 except AttributeError:
331 return None
332
333 lines = _splitlines_no_ff(source)
334 if end_lineno == lineno:
335 return lines[lineno].encode()[col_offset:end_col_offset].decode()
336
337 if padded:
338 padding = _pad_whitespace(lines[lineno].encode()[:col_offset].decode())
339 else:
340 padding = ''
341
342 first = padding + lines[lineno].encode()[col_offset:].decode()
343 last = lines[end_lineno].encode()[:end_col_offset].decode()
344 lines = lines[lineno+1:end_lineno]
345
346 lines.insert(0, first)
347 lines.append(last)
348 return ''.join(lines)
349
350
Georg Brandl0c77a822008-06-10 16:37:50 +0000351def walk(node):
352 """
Georg Brandl619e7ba2011-01-09 07:38:51 +0000353 Recursively yield all descendant nodes in the tree starting at *node*
354 (including *node* itself), in no specified order. This is useful if you
355 only want to modify nodes in place and don't care about the context.
Georg Brandl0c77a822008-06-10 16:37:50 +0000356 """
357 from collections import deque
358 todo = deque([node])
359 while todo:
360 node = todo.popleft()
361 todo.extend(iter_child_nodes(node))
362 yield node
363
364
365class NodeVisitor(object):
366 """
367 A node visitor base class that walks the abstract syntax tree and calls a
368 visitor function for every node found. This function may return a value
369 which is forwarded by the `visit` method.
370
371 This class is meant to be subclassed, with the subclass adding visitor
372 methods.
373
374 Per default the visitor functions for the nodes are ``'visit_'`` +
375 class name of the node. So a `TryFinally` node visit function would
376 be `visit_TryFinally`. This behavior can be changed by overriding
377 the `visit` method. If no visitor function exists for a node
378 (return value `None`) the `generic_visit` visitor is used instead.
379
380 Don't use the `NodeVisitor` if you want to apply changes to nodes during
381 traversing. For this a special visitor exists (`NodeTransformer`) that
382 allows modifications.
383 """
384
385 def visit(self, node):
386 """Visit a node."""
387 method = 'visit_' + node.__class__.__name__
388 visitor = getattr(self, method, self.generic_visit)
389 return visitor(node)
390
391 def generic_visit(self, node):
392 """Called if no explicit visitor function exists for a node."""
393 for field, value in iter_fields(node):
394 if isinstance(value, list):
395 for item in value:
396 if isinstance(item, AST):
397 self.visit(item)
398 elif isinstance(value, AST):
399 self.visit(value)
400
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300401 def visit_Constant(self, node):
402 value = node.value
403 type_name = _const_node_type_names.get(type(value))
404 if type_name is None:
405 for cls, name in _const_node_type_names.items():
406 if isinstance(value, cls):
407 type_name = name
408 break
409 if type_name is not None:
410 method = 'visit_' + type_name
411 try:
412 visitor = getattr(self, method)
413 except AttributeError:
414 pass
415 else:
416 import warnings
417 warnings.warn(f"{method} is deprecated; add visit_Constant",
418 DeprecationWarning, 2)
419 return visitor(node)
420 return self.generic_visit(node)
421
Georg Brandl0c77a822008-06-10 16:37:50 +0000422
423class NodeTransformer(NodeVisitor):
424 """
425 A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
426 allows modification of nodes.
427
428 The `NodeTransformer` will walk the AST and use the return value of the
429 visitor methods to replace or remove the old node. If the return value of
430 the visitor method is ``None``, the node will be removed from its location,
431 otherwise it is replaced with the return value. The return value may be the
432 original node in which case no replacement takes place.
433
434 Here is an example transformer that rewrites all occurrences of name lookups
435 (``foo``) to ``data['foo']``::
436
437 class RewriteName(NodeTransformer):
438
439 def visit_Name(self, node):
440 return copy_location(Subscript(
441 value=Name(id='data', ctx=Load()),
442 slice=Index(value=Str(s=node.id)),
443 ctx=node.ctx
444 ), node)
445
446 Keep in mind that if the node you're operating on has child nodes you must
447 either transform the child nodes yourself or call the :meth:`generic_visit`
448 method for the node first.
449
450 For nodes that were part of a collection of statements (that applies to all
451 statement nodes), the visitor may also return a list of nodes rather than
452 just a single node.
453
454 Usually you use the transformer like this::
455
456 node = YourTransformer().visit(node)
457 """
458
459 def generic_visit(self, node):
460 for field, old_value in iter_fields(node):
Georg Brandl0c77a822008-06-10 16:37:50 +0000461 if isinstance(old_value, list):
462 new_values = []
463 for value in old_value:
464 if isinstance(value, AST):
465 value = self.visit(value)
466 if value is None:
467 continue
468 elif not isinstance(value, AST):
469 new_values.extend(value)
470 continue
471 new_values.append(value)
472 old_value[:] = new_values
473 elif isinstance(old_value, AST):
474 new_node = self.visit(old_value)
475 if new_node is None:
476 delattr(node, field)
477 else:
478 setattr(node, field, new_node)
479 return node
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300480
481
482# The following code is for backward compatibility.
483# It will be removed in future.
484
485def _getter(self):
486 return self.value
487
488def _setter(self, value):
489 self.value = value
490
491Constant.n = property(_getter, _setter)
492Constant.s = property(_getter, _setter)
493
494class _ABC(type):
495
496 def __instancecheck__(cls, inst):
497 if not isinstance(inst, Constant):
498 return False
499 if cls in _const_types:
500 try:
501 value = inst.value
502 except AttributeError:
503 return False
504 else:
Anthony Sottile74176222019-01-18 11:30:28 -0800505 return (
506 isinstance(value, _const_types[cls]) and
507 not isinstance(value, _const_types_not.get(cls, ()))
508 )
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300509 return type.__instancecheck__(cls, inst)
510
511def _new(cls, *args, **kwargs):
512 if cls in _const_types:
513 return Constant(*args, **kwargs)
514 return Constant.__new__(cls, *args, **kwargs)
515
516class Num(Constant, metaclass=_ABC):
517 _fields = ('n',)
518 __new__ = _new
519
520class Str(Constant, metaclass=_ABC):
521 _fields = ('s',)
522 __new__ = _new
523
524class Bytes(Constant, metaclass=_ABC):
525 _fields = ('s',)
526 __new__ = _new
527
528class NameConstant(Constant, metaclass=_ABC):
529 __new__ = _new
530
531class Ellipsis(Constant, metaclass=_ABC):
532 _fields = ()
533
534 def __new__(cls, *args, **kwargs):
535 if cls is Ellipsis:
536 return Constant(..., *args, **kwargs)
537 return Constant.__new__(cls, *args, **kwargs)
538
539_const_types = {
540 Num: (int, float, complex),
541 Str: (str,),
542 Bytes: (bytes,),
543 NameConstant: (type(None), bool),
544 Ellipsis: (type(...),),
545}
Anthony Sottile74176222019-01-18 11:30:28 -0800546_const_types_not = {
547 Num: (bool,),
548}
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300549_const_node_type_names = {
550 bool: 'NameConstant', # should be before int
551 type(None): 'NameConstant',
552 int: 'Num',
553 float: 'Num',
554 complex: 'Num',
555 str: 'Str',
556 bytes: 'Bytes',
557 type(...): 'Ellipsis',
558}
Serhiy Storchaka832e8642019-09-09 23:36:13 +0300559
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000560# Large float and imaginary literals get turned into infinities in the AST.
561# We unparse those infinities to INFSTR.
562_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
563
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300564class _Precedence(IntEnum):
565 """Precedence table that originated from python grammar."""
566
567 TUPLE = auto()
568 YIELD = auto() # 'yield', 'yield from'
569 TEST = auto() # 'if'-'else', 'lambda'
570 OR = auto() # 'or'
571 AND = auto() # 'and'
572 NOT = auto() # 'not'
573 CMP = auto() # '<', '>', '==', '>=', '<=', '!=',
574 # 'in', 'not in', 'is', 'is not'
575 EXPR = auto()
576 BOR = EXPR # '|'
577 BXOR = auto() # '^'
578 BAND = auto() # '&'
579 SHIFT = auto() # '<<', '>>'
580 ARITH = auto() # '+', '-'
581 TERM = auto() # '*', '@', '/', '%', '//'
582 FACTOR = auto() # unary '+', '-', '~'
583 POWER = auto() # '**'
584 AWAIT = auto() # 'await'
585 ATOM = auto()
586
587 def next(self):
588 try:
589 return self.__class__(self + 1)
590 except ValueError:
591 return self
592
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000593class _Unparser(NodeVisitor):
594 """Methods in this class recursively traverse an AST and
595 output source code for the abstract syntax; original formatting
596 is disregarded."""
597
598 def __init__(self):
599 self._source = []
600 self._buffer = []
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300601 self._precedences = {}
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000602 self._indent = 0
603
604 def interleave(self, inter, f, seq):
605 """Call f on each item in seq, calling inter() in between."""
606 seq = iter(seq)
607 try:
608 f(next(seq))
609 except StopIteration:
610 pass
611 else:
612 for x in seq:
613 inter()
614 f(x)
615
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +0300616 def items_view(self, traverser, items):
617 """Traverse and separate the given *items* with a comma and append it to
618 the buffer. If *items* is a single item sequence, a trailing comma
619 will be added."""
620 if len(items) == 1:
621 traverser(items[0])
622 self.write(",")
623 else:
624 self.interleave(lambda: self.write(", "), traverser, items)
625
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000626 def fill(self, text=""):
627 """Indent a piece of text and append it, according to the current
628 indentation level"""
629 self.write("\n" + " " * self._indent + text)
630
631 def write(self, text):
632 """Append a piece of text"""
633 self._source.append(text)
634
635 def buffer_writer(self, text):
636 self._buffer.append(text)
637
638 @property
639 def buffer(self):
640 value = "".join(self._buffer)
641 self._buffer.clear()
642 return value
643
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000644 @contextmanager
645 def block(self):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000646 """A context manager for preparing the source for blocks. It adds
647 the character':', increases the indentation on enter and decreases
648 the indentation on exit."""
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000649 self.write(":")
650 self._indent += 1
651 yield
652 self._indent -= 1
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000653
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300654 @contextmanager
655 def delimit(self, start, end):
656 """A context manager for preparing the source for expressions. It adds
657 *start* to the buffer and enters, after exit it adds *end*."""
658
659 self.write(start)
660 yield
661 self.write(end)
662
663 def delimit_if(self, start, end, condition):
664 if condition:
665 return self.delimit(start, end)
666 else:
667 return nullcontext()
668
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300669 def require_parens(self, precedence, node):
670 """Shortcut to adding precedence related parens"""
671 return self.delimit_if("(", ")", self.get_precedence(node) > precedence)
672
673 def get_precedence(self, node):
674 return self._precedences.get(node, _Precedence.TEST)
675
676 def set_precedence(self, precedence, *nodes):
677 for node in nodes:
678 self._precedences[node] = precedence
679
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300680 def get_raw_docstring(self, node):
681 """If a docstring node is found in the body of the *node* parameter,
682 return that docstring node, None otherwise.
683
684 Logic mirrored from ``_PyAST_GetDocString``."""
685 if not isinstance(
686 node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)
687 ) or len(node.body) < 1:
688 return None
689 node = node.body[0]
690 if not isinstance(node, Expr):
691 return None
692 node = node.value
693 if isinstance(node, Constant) and isinstance(node.value, str):
694 return node
695
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000696 def traverse(self, node):
697 if isinstance(node, list):
698 for item in node:
699 self.traverse(item)
700 else:
701 super().visit(node)
702
703 def visit(self, node):
704 """Outputs a source code string that, if converted back to an ast
705 (using ast.parse) will generate an AST equivalent to *node*"""
706 self._source = []
707 self.traverse(node)
708 return "".join(self._source)
709
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300710 def _write_docstring_and_traverse_body(self, node):
711 if (docstring := self.get_raw_docstring(node)):
712 self._write_docstring(docstring)
713 self.traverse(node.body[1:])
714 else:
715 self.traverse(node.body)
716
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000717 def visit_Module(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300718 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000719
720 def visit_Expr(self, node):
721 self.fill()
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300722 self.set_precedence(_Precedence.YIELD, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000723 self.traverse(node.value)
724
725 def visit_NamedExpr(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300726 with self.require_parens(_Precedence.TUPLE, node):
727 self.set_precedence(_Precedence.ATOM, node.target, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300728 self.traverse(node.target)
729 self.write(" := ")
730 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000731
732 def visit_Import(self, node):
733 self.fill("import ")
734 self.interleave(lambda: self.write(", "), self.traverse, node.names)
735
736 def visit_ImportFrom(self, node):
737 self.fill("from ")
738 self.write("." * node.level)
739 if node.module:
740 self.write(node.module)
741 self.write(" import ")
742 self.interleave(lambda: self.write(", "), self.traverse, node.names)
743
744 def visit_Assign(self, node):
745 self.fill()
746 for target in node.targets:
747 self.traverse(target)
748 self.write(" = ")
749 self.traverse(node.value)
750
751 def visit_AugAssign(self, node):
752 self.fill()
753 self.traverse(node.target)
754 self.write(" " + self.binop[node.op.__class__.__name__] + "= ")
755 self.traverse(node.value)
756
757 def visit_AnnAssign(self, node):
758 self.fill()
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300759 with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)):
760 self.traverse(node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000761 self.write(": ")
762 self.traverse(node.annotation)
763 if node.value:
764 self.write(" = ")
765 self.traverse(node.value)
766
767 def visit_Return(self, node):
768 self.fill("return")
769 if node.value:
770 self.write(" ")
771 self.traverse(node.value)
772
773 def visit_Pass(self, node):
774 self.fill("pass")
775
776 def visit_Break(self, node):
777 self.fill("break")
778
779 def visit_Continue(self, node):
780 self.fill("continue")
781
782 def visit_Delete(self, node):
783 self.fill("del ")
784 self.interleave(lambda: self.write(", "), self.traverse, node.targets)
785
786 def visit_Assert(self, node):
787 self.fill("assert ")
788 self.traverse(node.test)
789 if node.msg:
790 self.write(", ")
791 self.traverse(node.msg)
792
793 def visit_Global(self, node):
794 self.fill("global ")
795 self.interleave(lambda: self.write(", "), self.write, node.names)
796
797 def visit_Nonlocal(self, node):
798 self.fill("nonlocal ")
799 self.interleave(lambda: self.write(", "), self.write, node.names)
800
801 def visit_Await(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300802 with self.require_parens(_Precedence.AWAIT, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300803 self.write("await")
804 if node.value:
805 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300806 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300807 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000808
809 def visit_Yield(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300810 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300811 self.write("yield")
812 if node.value:
813 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300814 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300815 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000816
817 def visit_YieldFrom(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300818 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300819 self.write("yield from ")
820 if not node.value:
821 raise ValueError("Node can't be used without a value attribute.")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300822 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300823 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000824
825 def visit_Raise(self, node):
826 self.fill("raise")
827 if not node.exc:
828 if node.cause:
829 raise ValueError(f"Node can't use cause without an exception.")
830 return
831 self.write(" ")
832 self.traverse(node.exc)
833 if node.cause:
834 self.write(" from ")
835 self.traverse(node.cause)
836
837 def visit_Try(self, node):
838 self.fill("try")
839 with self.block():
840 self.traverse(node.body)
841 for ex in node.handlers:
842 self.traverse(ex)
843 if node.orelse:
844 self.fill("else")
845 with self.block():
846 self.traverse(node.orelse)
847 if node.finalbody:
848 self.fill("finally")
849 with self.block():
850 self.traverse(node.finalbody)
851
852 def visit_ExceptHandler(self, node):
853 self.fill("except")
854 if node.type:
855 self.write(" ")
856 self.traverse(node.type)
857 if node.name:
858 self.write(" as ")
859 self.write(node.name)
860 with self.block():
861 self.traverse(node.body)
862
863 def visit_ClassDef(self, node):
864 self.write("\n")
865 for deco in node.decorator_list:
866 self.fill("@")
867 self.traverse(deco)
868 self.fill("class " + node.name)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300869 with self.delimit("(", ")"):
870 comma = False
871 for e in node.bases:
872 if comma:
873 self.write(", ")
874 else:
875 comma = True
876 self.traverse(e)
877 for e in node.keywords:
878 if comma:
879 self.write(", ")
880 else:
881 comma = True
882 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000883
884 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300885 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000886
887 def visit_FunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300888 self._function_helper(node, "def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000889
890 def visit_AsyncFunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300891 self._function_helper(node, "async def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000892
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300893 def _function_helper(self, node, fill_suffix):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000894 self.write("\n")
895 for deco in node.decorator_list:
896 self.fill("@")
897 self.traverse(deco)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300898 def_str = fill_suffix + " " + node.name
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000899 self.fill(def_str)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300900 with self.delimit("(", ")"):
901 self.traverse(node.args)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000902 if node.returns:
903 self.write(" -> ")
904 self.traverse(node.returns)
905 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300906 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000907
908 def visit_For(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300909 self._for_helper("for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000910
911 def visit_AsyncFor(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300912 self._for_helper("async for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000913
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300914 def _for_helper(self, fill, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000915 self.fill(fill)
916 self.traverse(node.target)
917 self.write(" in ")
918 self.traverse(node.iter)
919 with self.block():
920 self.traverse(node.body)
921 if node.orelse:
922 self.fill("else")
923 with self.block():
924 self.traverse(node.orelse)
925
926 def visit_If(self, node):
927 self.fill("if ")
928 self.traverse(node.test)
929 with self.block():
930 self.traverse(node.body)
931 # collapse nested ifs into equivalent elifs.
932 while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If):
933 node = node.orelse[0]
934 self.fill("elif ")
935 self.traverse(node.test)
936 with self.block():
937 self.traverse(node.body)
938 # final else
939 if node.orelse:
940 self.fill("else")
941 with self.block():
942 self.traverse(node.orelse)
943
944 def visit_While(self, node):
945 self.fill("while ")
946 self.traverse(node.test)
947 with self.block():
948 self.traverse(node.body)
949 if node.orelse:
950 self.fill("else")
951 with self.block():
952 self.traverse(node.orelse)
953
954 def visit_With(self, node):
955 self.fill("with ")
956 self.interleave(lambda: self.write(", "), self.traverse, node.items)
957 with self.block():
958 self.traverse(node.body)
959
960 def visit_AsyncWith(self, node):
961 self.fill("async with ")
962 self.interleave(lambda: self.write(", "), self.traverse, node.items)
963 with self.block():
964 self.traverse(node.body)
965
966 def visit_JoinedStr(self, node):
967 self.write("f")
968 self._fstring_JoinedStr(node, self.buffer_writer)
969 self.write(repr(self.buffer))
970
971 def visit_FormattedValue(self, node):
972 self.write("f")
973 self._fstring_FormattedValue(node, self.buffer_writer)
974 self.write(repr(self.buffer))
975
976 def _fstring_JoinedStr(self, node, write):
977 for value in node.values:
978 meth = getattr(self, "_fstring_" + type(value).__name__)
979 meth(value, write)
980
981 def _fstring_Constant(self, node, write):
982 if not isinstance(node.value, str):
983 raise ValueError("Constants inside JoinedStr should be a string.")
984 value = node.value.replace("{", "{{").replace("}", "}}")
985 write(value)
986
987 def _fstring_FormattedValue(self, node, write):
988 write("{")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300989 unparser = type(self)()
990 unparser.set_precedence(_Precedence.TEST.next(), node.value)
991 expr = unparser.visit(node.value).rstrip("\n")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000992 if expr.startswith("{"):
993 write(" ") # Separate pair of opening brackets as "{ {"
994 write(expr)
995 if node.conversion != -1:
996 conversion = chr(node.conversion)
997 if conversion not in "sra":
998 raise ValueError("Unknown f-string conversion.")
999 write(f"!{conversion}")
1000 if node.format_spec:
1001 write(":")
1002 meth = getattr(self, "_fstring_" + type(node.format_spec).__name__)
1003 meth(node.format_spec, write)
1004 write("}")
1005
1006 def visit_Name(self, node):
1007 self.write(node.id)
1008
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001009 def _write_docstring(self, node):
1010 self.fill()
1011 if node.kind == "u":
1012 self.write("u")
1013
1014 # Preserve quotes in the docstring by escaping them
1015 value = node.value.replace("\\", "\\\\")
1016 value = value.replace('"""', '""\"')
1017 if value[-1] == '"':
1018 value = value.replace('"', '\\"', -1)
1019
1020 self.write(f'"""{value}"""')
1021
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001022 def _write_constant(self, value):
1023 if isinstance(value, (float, complex)):
1024 # Substitute overflowing decimal literal for AST infinities.
1025 self.write(repr(value).replace("inf", _INFSTR))
1026 else:
1027 self.write(repr(value))
1028
1029 def visit_Constant(self, node):
1030 value = node.value
1031 if isinstance(value, tuple):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001032 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001033 self.items_view(self._write_constant, value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001034 elif value is ...:
1035 self.write("...")
1036 else:
1037 if node.kind == "u":
1038 self.write("u")
1039 self._write_constant(node.value)
1040
1041 def visit_List(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001042 with self.delimit("[", "]"):
1043 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001044
1045 def visit_ListComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001046 with self.delimit("[", "]"):
1047 self.traverse(node.elt)
1048 for gen in node.generators:
1049 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001050
1051 def visit_GeneratorExp(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_SetComp(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_DictComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001064 with self.delimit("{", "}"):
1065 self.traverse(node.key)
1066 self.write(": ")
1067 self.traverse(node.value)
1068 for gen in node.generators:
1069 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001070
1071 def visit_comprehension(self, node):
1072 if node.is_async:
1073 self.write(" async for ")
1074 else:
1075 self.write(" for ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001076 self.set_precedence(_Precedence.TUPLE, node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001077 self.traverse(node.target)
1078 self.write(" in ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001079 self.set_precedence(_Precedence.TEST.next(), node.iter, *node.ifs)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001080 self.traverse(node.iter)
1081 for if_clause in node.ifs:
1082 self.write(" if ")
1083 self.traverse(if_clause)
1084
1085 def visit_IfExp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001086 with self.require_parens(_Precedence.TEST, node):
1087 self.set_precedence(_Precedence.TEST.next(), node.body, node.test)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001088 self.traverse(node.body)
1089 self.write(" if ")
1090 self.traverse(node.test)
1091 self.write(" else ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001092 self.set_precedence(_Precedence.TEST, node.orelse)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001093 self.traverse(node.orelse)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001094
1095 def visit_Set(self, node):
1096 if not node.elts:
1097 raise ValueError("Set node should has at least one item")
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001098 with self.delimit("{", "}"):
1099 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001100
1101 def visit_Dict(self, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001102 def write_key_value_pair(k, v):
1103 self.traverse(k)
1104 self.write(": ")
1105 self.traverse(v)
1106
1107 def write_item(item):
1108 k, v = item
1109 if k is None:
1110 # for dictionary unpacking operator in dicts {**{'y': 2}}
1111 # see PEP 448 for details
1112 self.write("**")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001113 self.set_precedence(_Precedence.EXPR, v)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001114 self.traverse(v)
1115 else:
1116 write_key_value_pair(k, v)
1117
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001118 with self.delimit("{", "}"):
1119 self.interleave(
1120 lambda: self.write(", "), write_item, zip(node.keys, node.values)
1121 )
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001122
1123 def visit_Tuple(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001124 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001125 self.items_view(self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001126
1127 unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001128 unop_precedence = {
1129 "~": _Precedence.FACTOR,
1130 "not": _Precedence.NOT,
1131 "+": _Precedence.FACTOR,
1132 "-": _Precedence.FACTOR
1133 }
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001134
1135 def visit_UnaryOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001136 operator = self.unop[node.op.__class__.__name__]
1137 operator_precedence = self.unop_precedence[operator]
1138 with self.require_parens(operator_precedence, node):
1139 self.write(operator)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001140 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001141 self.set_precedence(operator_precedence, node.operand)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001142 self.traverse(node.operand)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001143
1144 binop = {
1145 "Add": "+",
1146 "Sub": "-",
1147 "Mult": "*",
1148 "MatMult": "@",
1149 "Div": "/",
1150 "Mod": "%",
1151 "LShift": "<<",
1152 "RShift": ">>",
1153 "BitOr": "|",
1154 "BitXor": "^",
1155 "BitAnd": "&",
1156 "FloorDiv": "//",
1157 "Pow": "**",
1158 }
1159
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001160 binop_precedence = {
1161 "+": _Precedence.ARITH,
1162 "-": _Precedence.ARITH,
1163 "*": _Precedence.TERM,
1164 "@": _Precedence.TERM,
1165 "/": _Precedence.TERM,
1166 "%": _Precedence.TERM,
1167 "<<": _Precedence.SHIFT,
1168 ">>": _Precedence.SHIFT,
1169 "|": _Precedence.BOR,
1170 "^": _Precedence.BXOR,
1171 "&": _Precedence.BAND,
1172 "//": _Precedence.TERM,
1173 "**": _Precedence.POWER,
1174 }
1175
1176 binop_rassoc = frozenset(("**",))
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001177 def visit_BinOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001178 operator = self.binop[node.op.__class__.__name__]
1179 operator_precedence = self.binop_precedence[operator]
1180 with self.require_parens(operator_precedence, node):
1181 if operator in self.binop_rassoc:
1182 left_precedence = operator_precedence.next()
1183 right_precedence = operator_precedence
1184 else:
1185 left_precedence = operator_precedence
1186 right_precedence = operator_precedence.next()
1187
1188 self.set_precedence(left_precedence, node.left)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001189 self.traverse(node.left)
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001190 self.write(f" {operator} ")
1191 self.set_precedence(right_precedence, node.right)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001192 self.traverse(node.right)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001193
1194 cmpops = {
1195 "Eq": "==",
1196 "NotEq": "!=",
1197 "Lt": "<",
1198 "LtE": "<=",
1199 "Gt": ">",
1200 "GtE": ">=",
1201 "Is": "is",
1202 "IsNot": "is not",
1203 "In": "in",
1204 "NotIn": "not in",
1205 }
1206
1207 def visit_Compare(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001208 with self.require_parens(_Precedence.CMP, node):
1209 self.set_precedence(_Precedence.CMP.next(), node.left, *node.comparators)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001210 self.traverse(node.left)
1211 for o, e in zip(node.ops, node.comparators):
1212 self.write(" " + self.cmpops[o.__class__.__name__] + " ")
1213 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001214
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001215 boolops = {"And": "and", "Or": "or"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001216 boolop_precedence = {"and": _Precedence.AND, "or": _Precedence.OR}
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001217
1218 def visit_BoolOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001219 operator = self.boolops[node.op.__class__.__name__]
1220 operator_precedence = self.boolop_precedence[operator]
1221
1222 def increasing_level_traverse(node):
1223 nonlocal operator_precedence
1224 operator_precedence = operator_precedence.next()
1225 self.set_precedence(operator_precedence, node)
1226 self.traverse(node)
1227
1228 with self.require_parens(operator_precedence, node):
1229 s = f" {operator} "
1230 self.interleave(lambda: self.write(s), increasing_level_traverse, node.values)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001231
1232 def visit_Attribute(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001233 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001234 self.traverse(node.value)
1235 # Special case: 3.__abs__() is a syntax error, so if node.value
1236 # is an integer literal then we need to either parenthesize
1237 # it or add an extra space to get 3 .__abs__().
1238 if isinstance(node.value, Constant) and isinstance(node.value.value, int):
1239 self.write(" ")
1240 self.write(".")
1241 self.write(node.attr)
1242
1243 def visit_Call(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001244 self.set_precedence(_Precedence.ATOM, node.func)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001245 self.traverse(node.func)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001246 with self.delimit("(", ")"):
1247 comma = False
1248 for e in node.args:
1249 if comma:
1250 self.write(", ")
1251 else:
1252 comma = True
1253 self.traverse(e)
1254 for e in node.keywords:
1255 if comma:
1256 self.write(", ")
1257 else:
1258 comma = True
1259 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001260
1261 def visit_Subscript(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001262 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001263 self.traverse(node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001264 with self.delimit("[", "]"):
Serhiy Storchakac4928fc2020-03-07 17:25:32 +02001265 if (isinstance(node.slice, Index)
1266 and isinstance(node.slice.value, Tuple)
1267 and node.slice.value.elts):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001268 self.items_view(self.traverse, node.slice.value.elts)
Serhiy Storchakac4928fc2020-03-07 17:25:32 +02001269 else:
1270 self.traverse(node.slice)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001271
1272 def visit_Starred(self, node):
1273 self.write("*")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001274 self.set_precedence(_Precedence.EXPR, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001275 self.traverse(node.value)
1276
1277 def visit_Ellipsis(self, node):
1278 self.write("...")
1279
1280 def visit_Index(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001281 self.set_precedence(_Precedence.TUPLE, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001282 self.traverse(node.value)
1283
1284 def visit_Slice(self, node):
1285 if node.lower:
1286 self.traverse(node.lower)
1287 self.write(":")
1288 if node.upper:
1289 self.traverse(node.upper)
1290 if node.step:
1291 self.write(":")
1292 self.traverse(node.step)
1293
1294 def visit_ExtSlice(self, node):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001295 self.items_view(self.traverse, node.dims)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001296
1297 def visit_arg(self, node):
1298 self.write(node.arg)
1299 if node.annotation:
1300 self.write(": ")
1301 self.traverse(node.annotation)
1302
1303 def visit_arguments(self, node):
1304 first = True
1305 # normal arguments
1306 all_args = node.posonlyargs + node.args
1307 defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults
1308 for index, elements in enumerate(zip(all_args, defaults), 1):
1309 a, d = elements
1310 if first:
1311 first = False
1312 else:
1313 self.write(", ")
1314 self.traverse(a)
1315 if d:
1316 self.write("=")
1317 self.traverse(d)
1318 if index == len(node.posonlyargs):
1319 self.write(", /")
1320
1321 # varargs, or bare '*' if no varargs but keyword-only arguments present
1322 if node.vararg or node.kwonlyargs:
1323 if first:
1324 first = False
1325 else:
1326 self.write(", ")
1327 self.write("*")
1328 if node.vararg:
1329 self.write(node.vararg.arg)
1330 if node.vararg.annotation:
1331 self.write(": ")
1332 self.traverse(node.vararg.annotation)
1333
1334 # keyword-only arguments
1335 if node.kwonlyargs:
1336 for a, d in zip(node.kwonlyargs, node.kw_defaults):
Batuhan Taşkayaa322f502019-12-16 15:26:58 +03001337 self.write(", ")
1338 self.traverse(a)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001339 if d:
1340 self.write("=")
1341 self.traverse(d)
1342
1343 # kwargs
1344 if node.kwarg:
1345 if first:
1346 first = False
1347 else:
1348 self.write(", ")
1349 self.write("**" + node.kwarg.arg)
1350 if node.kwarg.annotation:
1351 self.write(": ")
1352 self.traverse(node.kwarg.annotation)
1353
1354 def visit_keyword(self, node):
1355 if node.arg is None:
1356 self.write("**")
1357 else:
1358 self.write(node.arg)
1359 self.write("=")
1360 self.traverse(node.value)
1361
1362 def visit_Lambda(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001363 with self.require_parens(_Precedence.TEST, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001364 self.write("lambda ")
1365 self.traverse(node.args)
1366 self.write(": ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001367 self.set_precedence(_Precedence.TEST, node.body)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001368 self.traverse(node.body)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001369
1370 def visit_alias(self, node):
1371 self.write(node.name)
1372 if node.asname:
1373 self.write(" as " + node.asname)
1374
1375 def visit_withitem(self, node):
1376 self.traverse(node.context_expr)
1377 if node.optional_vars:
1378 self.write(" as ")
1379 self.traverse(node.optional_vars)
1380
1381def unparse(ast_obj):
1382 unparser = _Unparser()
1383 return unparser.visit(ast_obj)
1384
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001385
1386def main():
1387 import argparse
1388
1389 parser = argparse.ArgumentParser(prog='python -m ast')
1390 parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?',
1391 default='-',
1392 help='the file to parse; defaults to stdin')
1393 parser.add_argument('-m', '--mode', default='exec',
1394 choices=('exec', 'single', 'eval', 'func_type'),
1395 help='specify what kind of code must be parsed')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001396 parser.add_argument('--no-type-comments', default=True, action='store_false',
1397 help="don't add information about type comments")
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001398 parser.add_argument('-a', '--include-attributes', action='store_true',
1399 help='include attributes such as line numbers and '
1400 'column offsets')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001401 parser.add_argument('-i', '--indent', type=int, default=3,
1402 help='indentation of nodes (number of spaces)')
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001403 args = parser.parse_args()
1404
1405 with args.infile as infile:
1406 source = infile.read()
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001407 tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
1408 print(dump(tree, include_attributes=args.include_attributes, indent=args.indent))
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001409
1410if __name__ == '__main__':
1411 main()