blob: 5c68c4a66e1dd06d1aa6fdaba9d084bc61a76a7a [file] [log] [blame]
Georg Brandl0c77a822008-06-10 16:37:50 +00001"""
2 ast
3 ~~~
4
5 The `ast` module helps Python applications to process trees of the Python
6 abstract syntax grammar. The abstract syntax itself might change with
7 each Python release; this module helps to find out programmatically what
8 the current grammar looks like and allows modifications of it.
9
10 An abstract syntax tree can be generated by passing `ast.PyCF_ONLY_AST` as
11 a flag to the `compile()` builtin function or by using the `parse()`
12 function from this module. The result will be a tree of objects whose
13 classes all inherit from `ast.AST`.
14
15 A modified abstract syntax tree can be compiled into a Python code object
16 using the built-in `compile()` function.
17
18 Additionally various helper functions are provided that make working with
19 the trees simpler. The main intention of the helper functions and this
20 module in general is to provide an easy to use interface for libraries
21 that work tightly with the python syntax (template engines for example).
22
23
24 :copyright: Copyright 2008 by Armin Ronacher.
25 :license: Python License.
26"""
Pablo Galindo27fc3b62019-11-24 23:02:40 +000027import sys
Georg Brandl0c77a822008-06-10 16:37:50 +000028from _ast import *
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +030029from contextlib import contextmanager, nullcontext
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +030030from enum import IntEnum, auto
Georg Brandl0c77a822008-06-10 16:37:50 +000031
32
Guido van Rossum495da292019-03-07 12:38:08 -080033def parse(source, filename='<unknown>', mode='exec', *,
Guido van Rossum10b55c12019-06-11 17:23:12 -070034 type_comments=False, feature_version=None):
Georg Brandl0c77a822008-06-10 16:37:50 +000035 """
Terry Reedyfeac6242011-01-24 21:36:03 +000036 Parse the source into an AST node.
37 Equivalent to compile(source, filename, mode, PyCF_ONLY_AST).
Guido van Rossumdcfcd142019-01-31 03:40:27 -080038 Pass type_comments=True to get back type comments where the syntax allows.
Georg Brandl0c77a822008-06-10 16:37:50 +000039 """
Guido van Rossumdcfcd142019-01-31 03:40:27 -080040 flags = PyCF_ONLY_AST
41 if type_comments:
42 flags |= PyCF_TYPE_COMMENTS
Guido van Rossum10b55c12019-06-11 17:23:12 -070043 if isinstance(feature_version, tuple):
44 major, minor = feature_version # Should be a 2-tuple.
45 assert major == 3
46 feature_version = minor
47 elif feature_version is None:
48 feature_version = -1
49 # Else it should be an int giving the minor version for 3.x.
Guido van Rossum495da292019-03-07 12:38:08 -080050 return compile(source, filename, mode, flags,
Victor Stinnerefdf6ca2019-06-12 02:52:16 +020051 _feature_version=feature_version)
Georg Brandl0c77a822008-06-10 16:37:50 +000052
53
54def literal_eval(node_or_string):
55 """
56 Safely evaluate an expression node or a string containing a Python
57 expression. The string or node provided may only consist of the following
Éric Araujo2a83cc62011-04-17 19:10:27 +020058 Python literal structures: strings, bytes, numbers, tuples, lists, dicts,
59 sets, booleans, and None.
Georg Brandl0c77a822008-06-10 16:37:50 +000060 """
Georg Brandl0c77a822008-06-10 16:37:50 +000061 if isinstance(node_or_string, str):
62 node_or_string = parse(node_or_string, mode='eval')
63 if isinstance(node_or_string, Expression):
64 node_or_string = node_or_string.body
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020065 def _convert_num(node):
66 if isinstance(node, Constant):
Serhiy Storchaka3f228112018-09-27 17:42:37 +030067 if type(node.value) in (int, float, complex):
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020068 return node.value
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020069 raise ValueError('malformed node or string: ' + repr(node))
70 def _convert_signed_num(node):
71 if isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)):
72 operand = _convert_num(node.operand)
73 if isinstance(node.op, UAdd):
74 return + operand
75 else:
76 return - operand
77 return _convert_num(node)
Georg Brandl0c77a822008-06-10 16:37:50 +000078 def _convert(node):
Victor Stinnerf2c1aa12016-01-26 00:40:57 +010079 if isinstance(node, Constant):
80 return node.value
Georg Brandl0c77a822008-06-10 16:37:50 +000081 elif isinstance(node, Tuple):
82 return tuple(map(_convert, node.elts))
83 elif isinstance(node, List):
84 return list(map(_convert, node.elts))
Georg Brandl492f3fc2010-07-11 09:41:21 +000085 elif isinstance(node, Set):
86 return set(map(_convert, node.elts))
Raymond Hettinger4fcf5c12020-01-02 22:21:18 -070087 elif (isinstance(node, Call) and isinstance(node.func, Name) and
88 node.func.id == 'set' and node.args == node.keywords == []):
89 return set()
Georg Brandl0c77a822008-06-10 16:37:50 +000090 elif isinstance(node, Dict):
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020091 return dict(zip(map(_convert, node.keys),
92 map(_convert, node.values)))
Victor Stinnerf2c1aa12016-01-26 00:40:57 +010093 elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)):
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020094 left = _convert_signed_num(node.left)
95 right = _convert_num(node.right)
96 if isinstance(left, (int, float)) and isinstance(right, complex):
Victor Stinnerf2c1aa12016-01-26 00:40:57 +010097 if isinstance(node.op, Add):
98 return left + right
99 else:
100 return left - right
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +0200101 return _convert_signed_num(node)
Georg Brandl0c77a822008-06-10 16:37:50 +0000102 return _convert(node_or_string)
103
104
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300105def dump(node, annotate_fields=True, include_attributes=False, *, indent=None):
Georg Brandl0c77a822008-06-10 16:37:50 +0000106 """
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300107 Return a formatted dump of the tree in node. This is mainly useful for
108 debugging purposes. If annotate_fields is true (by default),
109 the returned string will show the names and the values for fields.
110 If annotate_fields is false, the result string will be more compact by
111 omitting unambiguous field names. Attributes such as line
Benjamin Petersondcf97b92008-07-02 17:30:14 +0000112 numbers and column offsets are not dumped by default. If this is wanted,
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300113 include_attributes can be set to true. If indent is a non-negative
114 integer or string, then the tree will be pretty-printed with that indent
115 level. None (the default) selects the single line representation.
Georg Brandl0c77a822008-06-10 16:37:50 +0000116 """
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300117 def _format(node, level=0):
118 if indent is not None:
119 level += 1
120 prefix = '\n' + indent * level
121 sep = ',\n' + indent * level
122 else:
123 prefix = ''
124 sep = ', '
Georg Brandl0c77a822008-06-10 16:37:50 +0000125 if isinstance(node, AST):
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200126 cls = type(node)
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300127 args = []
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300128 allsimple = True
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300129 keywords = annotate_fields
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200130 for name in node._fields:
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300131 try:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200132 value = getattr(node, name)
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300133 except AttributeError:
134 keywords = True
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200135 continue
136 if value is None and getattr(cls, name, ...) is None:
137 keywords = True
138 continue
139 value, simple = _format(value, level)
140 allsimple = allsimple and simple
141 if keywords:
142 args.append('%s=%s' % (name, value))
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300143 else:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200144 args.append(value)
145 if include_attributes and node._attributes:
146 for name in node._attributes:
147 try:
148 value = getattr(node, name)
149 except AttributeError:
150 continue
151 if value is None and getattr(cls, name, ...) is None:
152 continue
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300153 value, simple = _format(value, level)
154 allsimple = allsimple and simple
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200155 args.append('%s=%s' % (name, value))
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300156 if allsimple and len(args) <= 3:
157 return '%s(%s)' % (node.__class__.__name__, ', '.join(args)), not args
158 return '%s(%s%s)' % (node.__class__.__name__, prefix, sep.join(args)), False
Georg Brandl0c77a822008-06-10 16:37:50 +0000159 elif isinstance(node, list):
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300160 if not node:
161 return '[]', True
162 return '[%s%s]' % (prefix, sep.join(_format(x, level)[0] for x in node)), False
163 return repr(node), True
164
Georg Brandl0c77a822008-06-10 16:37:50 +0000165 if not isinstance(node, AST):
166 raise TypeError('expected AST, got %r' % node.__class__.__name__)
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300167 if indent is not None and not isinstance(indent, str):
168 indent = ' ' * indent
169 return _format(node)[0]
Georg Brandl0c77a822008-06-10 16:37:50 +0000170
171
172def copy_location(new_node, old_node):
173 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000174 Copy source location (`lineno`, `col_offset`, `end_lineno`, and `end_col_offset`
175 attributes) from *old_node* to *new_node* if possible, and return *new_node*.
Georg Brandl0c77a822008-06-10 16:37:50 +0000176 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000177 for attr in 'lineno', 'col_offset', 'end_lineno', 'end_col_offset':
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200178 if attr in old_node._attributes and attr in new_node._attributes:
179 value = getattr(old_node, attr, None)
180 if value is not None:
181 setattr(new_node, attr, value)
Georg Brandl0c77a822008-06-10 16:37:50 +0000182 return new_node
183
184
185def fix_missing_locations(node):
186 """
187 When you compile a node tree with compile(), the compiler expects lineno and
188 col_offset attributes for every node that supports them. This is rather
189 tedious to fill in for generated nodes, so this helper adds these attributes
190 recursively where not already set, by setting them to the values of the
191 parent node. It works recursively starting at *node*.
192 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000193 def _fix(node, lineno, col_offset, end_lineno, end_col_offset):
Georg Brandl0c77a822008-06-10 16:37:50 +0000194 if 'lineno' in node._attributes:
195 if not hasattr(node, 'lineno'):
196 node.lineno = lineno
197 else:
198 lineno = node.lineno
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000199 if 'end_lineno' in node._attributes:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200200 if getattr(node, 'end_lineno', None) is None:
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000201 node.end_lineno = end_lineno
202 else:
203 end_lineno = node.end_lineno
Georg Brandl0c77a822008-06-10 16:37:50 +0000204 if 'col_offset' in node._attributes:
205 if not hasattr(node, 'col_offset'):
206 node.col_offset = col_offset
207 else:
208 col_offset = node.col_offset
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000209 if 'end_col_offset' in node._attributes:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200210 if getattr(node, 'end_col_offset', None) is None:
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000211 node.end_col_offset = end_col_offset
212 else:
213 end_col_offset = node.end_col_offset
Georg Brandl0c77a822008-06-10 16:37:50 +0000214 for child in iter_child_nodes(node):
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000215 _fix(child, lineno, col_offset, end_lineno, end_col_offset)
216 _fix(node, 1, 0, 1, 0)
Georg Brandl0c77a822008-06-10 16:37:50 +0000217 return node
218
219
220def increment_lineno(node, n=1):
221 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000222 Increment the line number and end line number of each node in the tree
223 starting at *node* by *n*. This is useful to "move code" to a different
224 location in a file.
Georg Brandl0c77a822008-06-10 16:37:50 +0000225 """
Georg Brandl0c77a822008-06-10 16:37:50 +0000226 for child in walk(node):
227 if 'lineno' in child._attributes:
228 child.lineno = getattr(child, 'lineno', 0) + n
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000229 if 'end_lineno' in child._attributes:
230 child.end_lineno = getattr(child, 'end_lineno', 0) + n
Georg Brandl0c77a822008-06-10 16:37:50 +0000231 return node
232
233
234def iter_fields(node):
235 """
236 Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
237 that is present on *node*.
238 """
239 for field in node._fields:
240 try:
241 yield field, getattr(node, field)
242 except AttributeError:
243 pass
244
245
246def iter_child_nodes(node):
247 """
248 Yield all direct child nodes of *node*, that is, all fields that are nodes
249 and all items of fields that are lists of nodes.
250 """
251 for name, field in iter_fields(node):
252 if isinstance(field, AST):
253 yield field
254 elif isinstance(field, list):
255 for item in field:
256 if isinstance(item, AST):
257 yield item
258
259
260def get_docstring(node, clean=True):
261 """
262 Return the docstring for the given node or None if no docstring can
263 be found. If the node provided does not have docstrings a TypeError
264 will be raised.
Matthias Bussonnier41cea702017-02-23 22:44:19 -0800265
266 If *clean* is `True`, all tabs are expanded to spaces and any whitespace
267 that can be uniformly removed from the second line onwards is removed.
Georg Brandl0c77a822008-06-10 16:37:50 +0000268 """
Yury Selivanov2f07a662015-07-23 08:54:35 +0300269 if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
Georg Brandl0c77a822008-06-10 16:37:50 +0000270 raise TypeError("%r can't have docstrings" % node.__class__.__name__)
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300271 if not(node.body and isinstance(node.body[0], Expr)):
Serhiy Storchaka73cbe7a2018-05-29 12:04:55 +0300272 return None
273 node = node.body[0].value
274 if isinstance(node, Str):
275 text = node.s
276 elif isinstance(node, Constant) and isinstance(node.value, str):
277 text = node.value
278 else:
279 return None
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300280 if clean:
Victor Stinnerf2c1aa12016-01-26 00:40:57 +0100281 import inspect
282 text = inspect.cleandoc(text)
283 return text
Georg Brandl0c77a822008-06-10 16:37:50 +0000284
285
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000286def _splitlines_no_ff(source):
287 """Split a string into lines ignoring form feed and other chars.
288
289 This mimics how the Python parser splits source code.
290 """
291 idx = 0
292 lines = []
293 next_line = ''
294 while idx < len(source):
295 c = source[idx]
296 next_line += c
297 idx += 1
298 # Keep \r\n together
299 if c == '\r' and idx < len(source) and source[idx] == '\n':
300 next_line += '\n'
301 idx += 1
302 if c in '\r\n':
303 lines.append(next_line)
304 next_line = ''
305
306 if next_line:
307 lines.append(next_line)
308 return lines
309
310
311def _pad_whitespace(source):
mpheathfbeba8f2020-02-14 04:32:09 +1000312 r"""Replace all chars except '\f\t' in a line with spaces."""
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000313 result = ''
314 for c in source:
315 if c in '\f\t':
316 result += c
317 else:
318 result += ' '
319 return result
320
321
322def get_source_segment(source, node, *, padded=False):
323 """Get source code segment of the *source* that generated *node*.
324
325 If some location information (`lineno`, `end_lineno`, `col_offset`,
326 or `end_col_offset`) is missing, return None.
327
328 If *padded* is `True`, the first line of a multi-line statement will
329 be padded with spaces to match its original position.
330 """
331 try:
332 lineno = node.lineno - 1
333 end_lineno = node.end_lineno - 1
334 col_offset = node.col_offset
335 end_col_offset = node.end_col_offset
336 except AttributeError:
337 return None
338
339 lines = _splitlines_no_ff(source)
340 if end_lineno == lineno:
341 return lines[lineno].encode()[col_offset:end_col_offset].decode()
342
343 if padded:
344 padding = _pad_whitespace(lines[lineno].encode()[:col_offset].decode())
345 else:
346 padding = ''
347
348 first = padding + lines[lineno].encode()[col_offset:].decode()
349 last = lines[end_lineno].encode()[:end_col_offset].decode()
350 lines = lines[lineno+1:end_lineno]
351
352 lines.insert(0, first)
353 lines.append(last)
354 return ''.join(lines)
355
356
Georg Brandl0c77a822008-06-10 16:37:50 +0000357def walk(node):
358 """
Georg Brandl619e7ba2011-01-09 07:38:51 +0000359 Recursively yield all descendant nodes in the tree starting at *node*
360 (including *node* itself), in no specified order. This is useful if you
361 only want to modify nodes in place and don't care about the context.
Georg Brandl0c77a822008-06-10 16:37:50 +0000362 """
363 from collections import deque
364 todo = deque([node])
365 while todo:
366 node = todo.popleft()
367 todo.extend(iter_child_nodes(node))
368 yield node
369
370
371class NodeVisitor(object):
372 """
373 A node visitor base class that walks the abstract syntax tree and calls a
374 visitor function for every node found. This function may return a value
375 which is forwarded by the `visit` method.
376
377 This class is meant to be subclassed, with the subclass adding visitor
378 methods.
379
380 Per default the visitor functions for the nodes are ``'visit_'`` +
381 class name of the node. So a `TryFinally` node visit function would
382 be `visit_TryFinally`. This behavior can be changed by overriding
383 the `visit` method. If no visitor function exists for a node
384 (return value `None`) the `generic_visit` visitor is used instead.
385
386 Don't use the `NodeVisitor` if you want to apply changes to nodes during
387 traversing. For this a special visitor exists (`NodeTransformer`) that
388 allows modifications.
389 """
390
391 def visit(self, node):
392 """Visit a node."""
393 method = 'visit_' + node.__class__.__name__
394 visitor = getattr(self, method, self.generic_visit)
395 return visitor(node)
396
397 def generic_visit(self, node):
398 """Called if no explicit visitor function exists for a node."""
399 for field, value in iter_fields(node):
400 if isinstance(value, list):
401 for item in value:
402 if isinstance(item, AST):
403 self.visit(item)
404 elif isinstance(value, AST):
405 self.visit(value)
406
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300407 def visit_Constant(self, node):
408 value = node.value
409 type_name = _const_node_type_names.get(type(value))
410 if type_name is None:
411 for cls, name in _const_node_type_names.items():
412 if isinstance(value, cls):
413 type_name = name
414 break
415 if type_name is not None:
416 method = 'visit_' + type_name
417 try:
418 visitor = getattr(self, method)
419 except AttributeError:
420 pass
421 else:
422 import warnings
423 warnings.warn(f"{method} is deprecated; add visit_Constant",
424 DeprecationWarning, 2)
425 return visitor(node)
426 return self.generic_visit(node)
427
Georg Brandl0c77a822008-06-10 16:37:50 +0000428
429class NodeTransformer(NodeVisitor):
430 """
431 A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
432 allows modification of nodes.
433
434 The `NodeTransformer` will walk the AST and use the return value of the
435 visitor methods to replace or remove the old node. If the return value of
436 the visitor method is ``None``, the node will be removed from its location,
437 otherwise it is replaced with the return value. The return value may be the
438 original node in which case no replacement takes place.
439
440 Here is an example transformer that rewrites all occurrences of name lookups
441 (``foo``) to ``data['foo']``::
442
443 class RewriteName(NodeTransformer):
444
445 def visit_Name(self, node):
Pablo Galindoc00c86b2020-03-12 00:48:19 +0000446 return Subscript(
Georg Brandl0c77a822008-06-10 16:37:50 +0000447 value=Name(id='data', ctx=Load()),
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200448 slice=Constant(value=node.id),
Georg Brandl0c77a822008-06-10 16:37:50 +0000449 ctx=node.ctx
Pablo Galindoc00c86b2020-03-12 00:48:19 +0000450 )
Georg Brandl0c77a822008-06-10 16:37:50 +0000451
452 Keep in mind that if the node you're operating on has child nodes you must
453 either transform the child nodes yourself or call the :meth:`generic_visit`
454 method for the node first.
455
456 For nodes that were part of a collection of statements (that applies to all
457 statement nodes), the visitor may also return a list of nodes rather than
458 just a single node.
459
460 Usually you use the transformer like this::
461
462 node = YourTransformer().visit(node)
463 """
464
465 def generic_visit(self, node):
466 for field, old_value in iter_fields(node):
Georg Brandl0c77a822008-06-10 16:37:50 +0000467 if isinstance(old_value, list):
468 new_values = []
469 for value in old_value:
470 if isinstance(value, AST):
471 value = self.visit(value)
472 if value is None:
473 continue
474 elif not isinstance(value, AST):
475 new_values.extend(value)
476 continue
477 new_values.append(value)
478 old_value[:] = new_values
479 elif isinstance(old_value, AST):
480 new_node = self.visit(old_value)
481 if new_node is None:
482 delattr(node, field)
483 else:
484 setattr(node, field, new_node)
485 return node
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300486
487
488# The following code is for backward compatibility.
489# It will be removed in future.
490
491def _getter(self):
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200492 """Deprecated. Use value instead."""
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300493 return self.value
494
495def _setter(self, value):
496 self.value = value
497
498Constant.n = property(_getter, _setter)
499Constant.s = property(_getter, _setter)
500
501class _ABC(type):
502
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200503 def __init__(cls, *args):
504 cls.__doc__ = """Deprecated AST node class. Use ast.Constant instead"""
505
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300506 def __instancecheck__(cls, inst):
507 if not isinstance(inst, Constant):
508 return False
509 if cls in _const_types:
510 try:
511 value = inst.value
512 except AttributeError:
513 return False
514 else:
Anthony Sottile74176222019-01-18 11:30:28 -0800515 return (
516 isinstance(value, _const_types[cls]) and
517 not isinstance(value, _const_types_not.get(cls, ()))
518 )
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300519 return type.__instancecheck__(cls, inst)
520
521def _new(cls, *args, **kwargs):
522 if cls in _const_types:
523 return Constant(*args, **kwargs)
524 return Constant.__new__(cls, *args, **kwargs)
525
526class Num(Constant, metaclass=_ABC):
527 _fields = ('n',)
528 __new__ = _new
529
530class Str(Constant, metaclass=_ABC):
531 _fields = ('s',)
532 __new__ = _new
533
534class Bytes(Constant, metaclass=_ABC):
535 _fields = ('s',)
536 __new__ = _new
537
538class NameConstant(Constant, metaclass=_ABC):
539 __new__ = _new
540
541class Ellipsis(Constant, metaclass=_ABC):
542 _fields = ()
543
544 def __new__(cls, *args, **kwargs):
545 if cls is Ellipsis:
546 return Constant(..., *args, **kwargs)
547 return Constant.__new__(cls, *args, **kwargs)
548
549_const_types = {
550 Num: (int, float, complex),
551 Str: (str,),
552 Bytes: (bytes,),
553 NameConstant: (type(None), bool),
554 Ellipsis: (type(...),),
555}
Anthony Sottile74176222019-01-18 11:30:28 -0800556_const_types_not = {
557 Num: (bool,),
558}
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200559
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300560_const_node_type_names = {
561 bool: 'NameConstant', # should be before int
562 type(None): 'NameConstant',
563 int: 'Num',
564 float: 'Num',
565 complex: 'Num',
566 str: 'Str',
567 bytes: 'Bytes',
568 type(...): 'Ellipsis',
569}
Serhiy Storchaka832e8642019-09-09 23:36:13 +0300570
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200571class slice(AST):
572 """Deprecated AST node class."""
573
574class Index(slice):
575 """Deprecated AST node class. Use the index value directly instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200576 def __new__(cls, value, **kwargs):
577 return value
578
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200579class ExtSlice(slice):
580 """Deprecated AST node class. Use ast.Tuple instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200581 def __new__(cls, dims=(), **kwargs):
582 return Tuple(list(dims), Load(), **kwargs)
583
584def _dims_getter(self):
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200585 """Deprecated. Use elts instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200586 return self.elts
587
588def _dims_setter(self, value):
589 self.elts = value
590
591Tuple.dims = property(_dims_getter, _dims_setter)
592
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200593class Suite(mod):
594 """Deprecated AST node class. Unused in Python 3."""
595
596class AugLoad(expr_context):
597 """Deprecated AST node class. Unused in Python 3."""
598
599class AugStore(expr_context):
600 """Deprecated AST node class. Unused in Python 3."""
601
602class Param(expr_context):
603 """Deprecated AST node class. Unused in Python 3."""
604
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200605
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000606# Large float and imaginary literals get turned into infinities in the AST.
607# We unparse those infinities to INFSTR.
608_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
609
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300610class _Precedence(IntEnum):
611 """Precedence table that originated from python grammar."""
612
613 TUPLE = auto()
614 YIELD = auto() # 'yield', 'yield from'
615 TEST = auto() # 'if'-'else', 'lambda'
616 OR = auto() # 'or'
617 AND = auto() # 'and'
618 NOT = auto() # 'not'
619 CMP = auto() # '<', '>', '==', '>=', '<=', '!=',
620 # 'in', 'not in', 'is', 'is not'
621 EXPR = auto()
622 BOR = EXPR # '|'
623 BXOR = auto() # '^'
624 BAND = auto() # '&'
625 SHIFT = auto() # '<<', '>>'
626 ARITH = auto() # '+', '-'
627 TERM = auto() # '*', '@', '/', '%', '//'
628 FACTOR = auto() # unary '+', '-', '~'
629 POWER = auto() # '**'
630 AWAIT = auto() # 'await'
631 ATOM = auto()
632
633 def next(self):
634 try:
635 return self.__class__(self + 1)
636 except ValueError:
637 return self
638
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000639class _Unparser(NodeVisitor):
640 """Methods in this class recursively traverse an AST and
641 output source code for the abstract syntax; original formatting
642 is disregarded."""
643
644 def __init__(self):
645 self._source = []
646 self._buffer = []
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300647 self._precedences = {}
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000648 self._indent = 0
649
650 def interleave(self, inter, f, seq):
651 """Call f on each item in seq, calling inter() in between."""
652 seq = iter(seq)
653 try:
654 f(next(seq))
655 except StopIteration:
656 pass
657 else:
658 for x in seq:
659 inter()
660 f(x)
661
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +0300662 def items_view(self, traverser, items):
663 """Traverse and separate the given *items* with a comma and append it to
664 the buffer. If *items* is a single item sequence, a trailing comma
665 will be added."""
666 if len(items) == 1:
667 traverser(items[0])
668 self.write(",")
669 else:
670 self.interleave(lambda: self.write(", "), traverser, items)
671
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300672 def maybe_newline(self):
673 """Adds a newline if it isn't the start of generated source"""
674 if self._source:
675 self.write("\n")
676
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000677 def fill(self, text=""):
678 """Indent a piece of text and append it, according to the current
679 indentation level"""
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300680 self.maybe_newline()
681 self.write(" " * self._indent + text)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000682
683 def write(self, text):
684 """Append a piece of text"""
685 self._source.append(text)
686
687 def buffer_writer(self, text):
688 self._buffer.append(text)
689
690 @property
691 def buffer(self):
692 value = "".join(self._buffer)
693 self._buffer.clear()
694 return value
695
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000696 @contextmanager
697 def block(self):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000698 """A context manager for preparing the source for blocks. It adds
699 the character':', increases the indentation on enter and decreases
700 the indentation on exit."""
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000701 self.write(":")
702 self._indent += 1
703 yield
704 self._indent -= 1
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000705
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300706 @contextmanager
707 def delimit(self, start, end):
708 """A context manager for preparing the source for expressions. It adds
709 *start* to the buffer and enters, after exit it adds *end*."""
710
711 self.write(start)
712 yield
713 self.write(end)
714
715 def delimit_if(self, start, end, condition):
716 if condition:
717 return self.delimit(start, end)
718 else:
719 return nullcontext()
720
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300721 def require_parens(self, precedence, node):
722 """Shortcut to adding precedence related parens"""
723 return self.delimit_if("(", ")", self.get_precedence(node) > precedence)
724
725 def get_precedence(self, node):
726 return self._precedences.get(node, _Precedence.TEST)
727
728 def set_precedence(self, precedence, *nodes):
729 for node in nodes:
730 self._precedences[node] = precedence
731
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300732 def get_raw_docstring(self, node):
733 """If a docstring node is found in the body of the *node* parameter,
734 return that docstring node, None otherwise.
735
736 Logic mirrored from ``_PyAST_GetDocString``."""
737 if not isinstance(
738 node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)
739 ) or len(node.body) < 1:
740 return None
741 node = node.body[0]
742 if not isinstance(node, Expr):
743 return None
744 node = node.value
745 if isinstance(node, Constant) and isinstance(node.value, str):
746 return node
747
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000748 def traverse(self, node):
749 if isinstance(node, list):
750 for item in node:
751 self.traverse(item)
752 else:
753 super().visit(node)
754
755 def visit(self, node):
756 """Outputs a source code string that, if converted back to an ast
757 (using ast.parse) will generate an AST equivalent to *node*"""
758 self._source = []
759 self.traverse(node)
760 return "".join(self._source)
761
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300762 def _write_docstring_and_traverse_body(self, node):
763 if (docstring := self.get_raw_docstring(node)):
764 self._write_docstring(docstring)
765 self.traverse(node.body[1:])
766 else:
767 self.traverse(node.body)
768
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000769 def visit_Module(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300770 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000771
Batuhan Taşkaya5b66ec12020-03-15 22:56:57 +0300772 def visit_FunctionType(self, node):
773 with self.delimit("(", ")"):
774 self.interleave(
775 lambda: self.write(", "), self.traverse, node.argtypes
776 )
777
778 self.write(" -> ")
779 self.traverse(node.returns)
780
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000781 def visit_Expr(self, node):
782 self.fill()
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300783 self.set_precedence(_Precedence.YIELD, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000784 self.traverse(node.value)
785
786 def visit_NamedExpr(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300787 with self.require_parens(_Precedence.TUPLE, node):
788 self.set_precedence(_Precedence.ATOM, node.target, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300789 self.traverse(node.target)
790 self.write(" := ")
791 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000792
793 def visit_Import(self, node):
794 self.fill("import ")
795 self.interleave(lambda: self.write(", "), self.traverse, node.names)
796
797 def visit_ImportFrom(self, node):
798 self.fill("from ")
799 self.write("." * node.level)
800 if node.module:
801 self.write(node.module)
802 self.write(" import ")
803 self.interleave(lambda: self.write(", "), self.traverse, node.names)
804
805 def visit_Assign(self, node):
806 self.fill()
807 for target in node.targets:
808 self.traverse(target)
809 self.write(" = ")
810 self.traverse(node.value)
811
812 def visit_AugAssign(self, node):
813 self.fill()
814 self.traverse(node.target)
815 self.write(" " + self.binop[node.op.__class__.__name__] + "= ")
816 self.traverse(node.value)
817
818 def visit_AnnAssign(self, node):
819 self.fill()
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300820 with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)):
821 self.traverse(node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000822 self.write(": ")
823 self.traverse(node.annotation)
824 if node.value:
825 self.write(" = ")
826 self.traverse(node.value)
827
828 def visit_Return(self, node):
829 self.fill("return")
830 if node.value:
831 self.write(" ")
832 self.traverse(node.value)
833
834 def visit_Pass(self, node):
835 self.fill("pass")
836
837 def visit_Break(self, node):
838 self.fill("break")
839
840 def visit_Continue(self, node):
841 self.fill("continue")
842
843 def visit_Delete(self, node):
844 self.fill("del ")
845 self.interleave(lambda: self.write(", "), self.traverse, node.targets)
846
847 def visit_Assert(self, node):
848 self.fill("assert ")
849 self.traverse(node.test)
850 if node.msg:
851 self.write(", ")
852 self.traverse(node.msg)
853
854 def visit_Global(self, node):
855 self.fill("global ")
856 self.interleave(lambda: self.write(", "), self.write, node.names)
857
858 def visit_Nonlocal(self, node):
859 self.fill("nonlocal ")
860 self.interleave(lambda: self.write(", "), self.write, node.names)
861
862 def visit_Await(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300863 with self.require_parens(_Precedence.AWAIT, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300864 self.write("await")
865 if node.value:
866 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300867 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300868 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000869
870 def visit_Yield(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300871 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300872 self.write("yield")
873 if node.value:
874 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300875 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300876 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000877
878 def visit_YieldFrom(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300879 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300880 self.write("yield from ")
881 if not node.value:
882 raise ValueError("Node can't be used without a value attribute.")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300883 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300884 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000885
886 def visit_Raise(self, node):
887 self.fill("raise")
888 if not node.exc:
889 if node.cause:
890 raise ValueError(f"Node can't use cause without an exception.")
891 return
892 self.write(" ")
893 self.traverse(node.exc)
894 if node.cause:
895 self.write(" from ")
896 self.traverse(node.cause)
897
898 def visit_Try(self, node):
899 self.fill("try")
900 with self.block():
901 self.traverse(node.body)
902 for ex in node.handlers:
903 self.traverse(ex)
904 if node.orelse:
905 self.fill("else")
906 with self.block():
907 self.traverse(node.orelse)
908 if node.finalbody:
909 self.fill("finally")
910 with self.block():
911 self.traverse(node.finalbody)
912
913 def visit_ExceptHandler(self, node):
914 self.fill("except")
915 if node.type:
916 self.write(" ")
917 self.traverse(node.type)
918 if node.name:
919 self.write(" as ")
920 self.write(node.name)
921 with self.block():
922 self.traverse(node.body)
923
924 def visit_ClassDef(self, node):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300925 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000926 for deco in node.decorator_list:
927 self.fill("@")
928 self.traverse(deco)
929 self.fill("class " + node.name)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300930 with self.delimit("(", ")"):
931 comma = False
932 for e in node.bases:
933 if comma:
934 self.write(", ")
935 else:
936 comma = True
937 self.traverse(e)
938 for e in node.keywords:
939 if comma:
940 self.write(", ")
941 else:
942 comma = True
943 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000944
945 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300946 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000947
948 def visit_FunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300949 self._function_helper(node, "def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000950
951 def visit_AsyncFunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300952 self._function_helper(node, "async def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000953
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300954 def _function_helper(self, node, fill_suffix):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300955 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000956 for deco in node.decorator_list:
957 self.fill("@")
958 self.traverse(deco)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300959 def_str = fill_suffix + " " + node.name
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000960 self.fill(def_str)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300961 with self.delimit("(", ")"):
962 self.traverse(node.args)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000963 if node.returns:
964 self.write(" -> ")
965 self.traverse(node.returns)
966 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300967 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000968
969 def visit_For(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300970 self._for_helper("for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000971
972 def visit_AsyncFor(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300973 self._for_helper("async for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000974
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300975 def _for_helper(self, fill, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000976 self.fill(fill)
977 self.traverse(node.target)
978 self.write(" in ")
979 self.traverse(node.iter)
980 with self.block():
981 self.traverse(node.body)
982 if node.orelse:
983 self.fill("else")
984 with self.block():
985 self.traverse(node.orelse)
986
987 def visit_If(self, node):
988 self.fill("if ")
989 self.traverse(node.test)
990 with self.block():
991 self.traverse(node.body)
992 # collapse nested ifs into equivalent elifs.
993 while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If):
994 node = node.orelse[0]
995 self.fill("elif ")
996 self.traverse(node.test)
997 with self.block():
998 self.traverse(node.body)
999 # final else
1000 if node.orelse:
1001 self.fill("else")
1002 with self.block():
1003 self.traverse(node.orelse)
1004
1005 def visit_While(self, node):
1006 self.fill("while ")
1007 self.traverse(node.test)
1008 with self.block():
1009 self.traverse(node.body)
1010 if node.orelse:
1011 self.fill("else")
1012 with self.block():
1013 self.traverse(node.orelse)
1014
1015 def visit_With(self, node):
1016 self.fill("with ")
1017 self.interleave(lambda: self.write(", "), self.traverse, node.items)
1018 with self.block():
1019 self.traverse(node.body)
1020
1021 def visit_AsyncWith(self, node):
1022 self.fill("async with ")
1023 self.interleave(lambda: self.write(", "), self.traverse, node.items)
1024 with self.block():
1025 self.traverse(node.body)
1026
1027 def visit_JoinedStr(self, node):
1028 self.write("f")
1029 self._fstring_JoinedStr(node, self.buffer_writer)
1030 self.write(repr(self.buffer))
1031
1032 def visit_FormattedValue(self, node):
1033 self.write("f")
1034 self._fstring_FormattedValue(node, self.buffer_writer)
1035 self.write(repr(self.buffer))
1036
1037 def _fstring_JoinedStr(self, node, write):
1038 for value in node.values:
1039 meth = getattr(self, "_fstring_" + type(value).__name__)
1040 meth(value, write)
1041
1042 def _fstring_Constant(self, node, write):
1043 if not isinstance(node.value, str):
1044 raise ValueError("Constants inside JoinedStr should be a string.")
1045 value = node.value.replace("{", "{{").replace("}", "}}")
1046 write(value)
1047
1048 def _fstring_FormattedValue(self, node, write):
1049 write("{")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001050 unparser = type(self)()
1051 unparser.set_precedence(_Precedence.TEST.next(), node.value)
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +03001052 expr = unparser.visit(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001053 if expr.startswith("{"):
1054 write(" ") # Separate pair of opening brackets as "{ {"
1055 write(expr)
1056 if node.conversion != -1:
1057 conversion = chr(node.conversion)
1058 if conversion not in "sra":
1059 raise ValueError("Unknown f-string conversion.")
1060 write(f"!{conversion}")
1061 if node.format_spec:
1062 write(":")
1063 meth = getattr(self, "_fstring_" + type(node.format_spec).__name__)
1064 meth(node.format_spec, write)
1065 write("}")
1066
1067 def visit_Name(self, node):
1068 self.write(node.id)
1069
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001070 def _write_docstring(self, node):
1071 self.fill()
1072 if node.kind == "u":
1073 self.write("u")
1074
1075 # Preserve quotes in the docstring by escaping them
1076 value = node.value.replace("\\", "\\\\")
1077 value = value.replace('"""', '""\"')
1078 if value[-1] == '"':
1079 value = value.replace('"', '\\"', -1)
1080
1081 self.write(f'"""{value}"""')
1082
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001083 def _write_constant(self, value):
1084 if isinstance(value, (float, complex)):
1085 # Substitute overflowing decimal literal for AST infinities.
1086 self.write(repr(value).replace("inf", _INFSTR))
1087 else:
1088 self.write(repr(value))
1089
1090 def visit_Constant(self, node):
1091 value = node.value
1092 if isinstance(value, tuple):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001093 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001094 self.items_view(self._write_constant, value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001095 elif value is ...:
1096 self.write("...")
1097 else:
1098 if node.kind == "u":
1099 self.write("u")
1100 self._write_constant(node.value)
1101
1102 def visit_List(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001103 with self.delimit("[", "]"):
1104 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001105
1106 def visit_ListComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001107 with self.delimit("[", "]"):
1108 self.traverse(node.elt)
1109 for gen in node.generators:
1110 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001111
1112 def visit_GeneratorExp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001113 with self.delimit("(", ")"):
1114 self.traverse(node.elt)
1115 for gen in node.generators:
1116 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001117
1118 def visit_SetComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001119 with self.delimit("{", "}"):
1120 self.traverse(node.elt)
1121 for gen in node.generators:
1122 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001123
1124 def visit_DictComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001125 with self.delimit("{", "}"):
1126 self.traverse(node.key)
1127 self.write(": ")
1128 self.traverse(node.value)
1129 for gen in node.generators:
1130 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001131
1132 def visit_comprehension(self, node):
1133 if node.is_async:
1134 self.write(" async for ")
1135 else:
1136 self.write(" for ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001137 self.set_precedence(_Precedence.TUPLE, node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001138 self.traverse(node.target)
1139 self.write(" in ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001140 self.set_precedence(_Precedence.TEST.next(), node.iter, *node.ifs)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001141 self.traverse(node.iter)
1142 for if_clause in node.ifs:
1143 self.write(" if ")
1144 self.traverse(if_clause)
1145
1146 def visit_IfExp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001147 with self.require_parens(_Precedence.TEST, node):
1148 self.set_precedence(_Precedence.TEST.next(), node.body, node.test)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001149 self.traverse(node.body)
1150 self.write(" if ")
1151 self.traverse(node.test)
1152 self.write(" else ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001153 self.set_precedence(_Precedence.TEST, node.orelse)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001154 self.traverse(node.orelse)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001155
1156 def visit_Set(self, node):
1157 if not node.elts:
Shantanu01508dc2020-04-16 03:10:12 -07001158 raise ValueError("Set node should have at least one item")
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001159 with self.delimit("{", "}"):
1160 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001161
1162 def visit_Dict(self, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001163 def write_key_value_pair(k, v):
1164 self.traverse(k)
1165 self.write(": ")
1166 self.traverse(v)
1167
1168 def write_item(item):
1169 k, v = item
1170 if k is None:
1171 # for dictionary unpacking operator in dicts {**{'y': 2}}
1172 # see PEP 448 for details
1173 self.write("**")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001174 self.set_precedence(_Precedence.EXPR, v)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001175 self.traverse(v)
1176 else:
1177 write_key_value_pair(k, v)
1178
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001179 with self.delimit("{", "}"):
1180 self.interleave(
1181 lambda: self.write(", "), write_item, zip(node.keys, node.values)
1182 )
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001183
1184 def visit_Tuple(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001185 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001186 self.items_view(self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001187
1188 unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001189 unop_precedence = {
1190 "~": _Precedence.FACTOR,
1191 "not": _Precedence.NOT,
1192 "+": _Precedence.FACTOR,
1193 "-": _Precedence.FACTOR
1194 }
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001195
1196 def visit_UnaryOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001197 operator = self.unop[node.op.__class__.__name__]
1198 operator_precedence = self.unop_precedence[operator]
1199 with self.require_parens(operator_precedence, node):
1200 self.write(operator)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001201 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001202 self.set_precedence(operator_precedence, node.operand)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001203 self.traverse(node.operand)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001204
1205 binop = {
1206 "Add": "+",
1207 "Sub": "-",
1208 "Mult": "*",
1209 "MatMult": "@",
1210 "Div": "/",
1211 "Mod": "%",
1212 "LShift": "<<",
1213 "RShift": ">>",
1214 "BitOr": "|",
1215 "BitXor": "^",
1216 "BitAnd": "&",
1217 "FloorDiv": "//",
1218 "Pow": "**",
1219 }
1220
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001221 binop_precedence = {
1222 "+": _Precedence.ARITH,
1223 "-": _Precedence.ARITH,
1224 "*": _Precedence.TERM,
1225 "@": _Precedence.TERM,
1226 "/": _Precedence.TERM,
1227 "%": _Precedence.TERM,
1228 "<<": _Precedence.SHIFT,
1229 ">>": _Precedence.SHIFT,
1230 "|": _Precedence.BOR,
1231 "^": _Precedence.BXOR,
1232 "&": _Precedence.BAND,
1233 "//": _Precedence.TERM,
1234 "**": _Precedence.POWER,
1235 }
1236
1237 binop_rassoc = frozenset(("**",))
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001238 def visit_BinOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001239 operator = self.binop[node.op.__class__.__name__]
1240 operator_precedence = self.binop_precedence[operator]
1241 with self.require_parens(operator_precedence, node):
1242 if operator in self.binop_rassoc:
1243 left_precedence = operator_precedence.next()
1244 right_precedence = operator_precedence
1245 else:
1246 left_precedence = operator_precedence
1247 right_precedence = operator_precedence.next()
1248
1249 self.set_precedence(left_precedence, node.left)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001250 self.traverse(node.left)
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001251 self.write(f" {operator} ")
1252 self.set_precedence(right_precedence, node.right)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001253 self.traverse(node.right)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001254
1255 cmpops = {
1256 "Eq": "==",
1257 "NotEq": "!=",
1258 "Lt": "<",
1259 "LtE": "<=",
1260 "Gt": ">",
1261 "GtE": ">=",
1262 "Is": "is",
1263 "IsNot": "is not",
1264 "In": "in",
1265 "NotIn": "not in",
1266 }
1267
1268 def visit_Compare(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001269 with self.require_parens(_Precedence.CMP, node):
1270 self.set_precedence(_Precedence.CMP.next(), node.left, *node.comparators)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001271 self.traverse(node.left)
1272 for o, e in zip(node.ops, node.comparators):
1273 self.write(" " + self.cmpops[o.__class__.__name__] + " ")
1274 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001275
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001276 boolops = {"And": "and", "Or": "or"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001277 boolop_precedence = {"and": _Precedence.AND, "or": _Precedence.OR}
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001278
1279 def visit_BoolOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001280 operator = self.boolops[node.op.__class__.__name__]
1281 operator_precedence = self.boolop_precedence[operator]
1282
1283 def increasing_level_traverse(node):
1284 nonlocal operator_precedence
1285 operator_precedence = operator_precedence.next()
1286 self.set_precedence(operator_precedence, node)
1287 self.traverse(node)
1288
1289 with self.require_parens(operator_precedence, node):
1290 s = f" {operator} "
1291 self.interleave(lambda: self.write(s), increasing_level_traverse, node.values)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001292
1293 def visit_Attribute(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001294 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001295 self.traverse(node.value)
1296 # Special case: 3.__abs__() is a syntax error, so if node.value
1297 # is an integer literal then we need to either parenthesize
1298 # it or add an extra space to get 3 .__abs__().
1299 if isinstance(node.value, Constant) and isinstance(node.value.value, int):
1300 self.write(" ")
1301 self.write(".")
1302 self.write(node.attr)
1303
1304 def visit_Call(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001305 self.set_precedence(_Precedence.ATOM, node.func)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001306 self.traverse(node.func)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001307 with self.delimit("(", ")"):
1308 comma = False
1309 for e in node.args:
1310 if comma:
1311 self.write(", ")
1312 else:
1313 comma = True
1314 self.traverse(e)
1315 for e in node.keywords:
1316 if comma:
1317 self.write(", ")
1318 else:
1319 comma = True
1320 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001321
1322 def visit_Subscript(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001323 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001324 self.traverse(node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001325 with self.delimit("[", "]"):
Serhiy Storchaka13d52c22020-03-10 18:52:34 +02001326 if isinstance(node.slice, Tuple) and node.slice.elts:
1327 self.items_view(self.traverse, node.slice.elts)
Serhiy Storchakac4928fc2020-03-07 17:25:32 +02001328 else:
1329 self.traverse(node.slice)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001330
1331 def visit_Starred(self, node):
1332 self.write("*")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001333 self.set_precedence(_Precedence.EXPR, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001334 self.traverse(node.value)
1335
1336 def visit_Ellipsis(self, node):
1337 self.write("...")
1338
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001339 def visit_Slice(self, node):
1340 if node.lower:
1341 self.traverse(node.lower)
1342 self.write(":")
1343 if node.upper:
1344 self.traverse(node.upper)
1345 if node.step:
1346 self.write(":")
1347 self.traverse(node.step)
1348
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001349 def visit_arg(self, node):
1350 self.write(node.arg)
1351 if node.annotation:
1352 self.write(": ")
1353 self.traverse(node.annotation)
1354
1355 def visit_arguments(self, node):
1356 first = True
1357 # normal arguments
1358 all_args = node.posonlyargs + node.args
1359 defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults
1360 for index, elements in enumerate(zip(all_args, defaults), 1):
1361 a, d = elements
1362 if first:
1363 first = False
1364 else:
1365 self.write(", ")
1366 self.traverse(a)
1367 if d:
1368 self.write("=")
1369 self.traverse(d)
1370 if index == len(node.posonlyargs):
1371 self.write(", /")
1372
1373 # varargs, or bare '*' if no varargs but keyword-only arguments present
1374 if node.vararg or node.kwonlyargs:
1375 if first:
1376 first = False
1377 else:
1378 self.write(", ")
1379 self.write("*")
1380 if node.vararg:
1381 self.write(node.vararg.arg)
1382 if node.vararg.annotation:
1383 self.write(": ")
1384 self.traverse(node.vararg.annotation)
1385
1386 # keyword-only arguments
1387 if node.kwonlyargs:
1388 for a, d in zip(node.kwonlyargs, node.kw_defaults):
Batuhan Taşkayaa322f502019-12-16 15:26:58 +03001389 self.write(", ")
1390 self.traverse(a)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001391 if d:
1392 self.write("=")
1393 self.traverse(d)
1394
1395 # kwargs
1396 if node.kwarg:
1397 if first:
1398 first = False
1399 else:
1400 self.write(", ")
1401 self.write("**" + node.kwarg.arg)
1402 if node.kwarg.annotation:
1403 self.write(": ")
1404 self.traverse(node.kwarg.annotation)
1405
1406 def visit_keyword(self, node):
1407 if node.arg is None:
1408 self.write("**")
1409 else:
1410 self.write(node.arg)
1411 self.write("=")
1412 self.traverse(node.value)
1413
1414 def visit_Lambda(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001415 with self.require_parens(_Precedence.TEST, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001416 self.write("lambda ")
1417 self.traverse(node.args)
1418 self.write(": ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001419 self.set_precedence(_Precedence.TEST, node.body)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001420 self.traverse(node.body)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001421
1422 def visit_alias(self, node):
1423 self.write(node.name)
1424 if node.asname:
1425 self.write(" as " + node.asname)
1426
1427 def visit_withitem(self, node):
1428 self.traverse(node.context_expr)
1429 if node.optional_vars:
1430 self.write(" as ")
1431 self.traverse(node.optional_vars)
1432
1433def unparse(ast_obj):
1434 unparser = _Unparser()
1435 return unparser.visit(ast_obj)
1436
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001437
1438def main():
1439 import argparse
1440
1441 parser = argparse.ArgumentParser(prog='python -m ast')
1442 parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?',
1443 default='-',
1444 help='the file to parse; defaults to stdin')
1445 parser.add_argument('-m', '--mode', default='exec',
1446 choices=('exec', 'single', 'eval', 'func_type'),
1447 help='specify what kind of code must be parsed')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001448 parser.add_argument('--no-type-comments', default=True, action='store_false',
1449 help="don't add information about type comments")
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001450 parser.add_argument('-a', '--include-attributes', action='store_true',
1451 help='include attributes such as line numbers and '
1452 'column offsets')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001453 parser.add_argument('-i', '--indent', type=int, default=3,
1454 help='indentation of nodes (number of spaces)')
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001455 args = parser.parse_args()
1456
1457 with args.infile as infile:
1458 source = infile.read()
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001459 tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
1460 print(dump(tree, include_attributes=args.include_attributes, indent=args.indent))
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001461
1462if __name__ == '__main__':
1463 main()