blob: d8bd3373701dec60b5984ecfc2b0e4edb913a18d [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):
Batuhan Taskayae799aa82020-10-04 03:46:44 +030062 node_or_string = parse(node_or_string.lstrip(" \t"), mode='eval')
Georg Brandl0c77a822008-06-10 16:37:50 +000063 if isinstance(node_or_string, Expression):
64 node_or_string = node_or_string.body
Curtis Bucherc21c5122020-05-05 12:40:56 -070065 def _raise_malformed_node(node):
66 raise ValueError(f'malformed node or string: {node!r}')
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020067 def _convert_num(node):
Curtis Bucherc21c5122020-05-05 12:40:56 -070068 if not isinstance(node, Constant) or type(node.value) not in (int, float, complex):
69 _raise_malformed_node(node)
70 return node.value
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020071 def _convert_signed_num(node):
72 if isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)):
73 operand = _convert_num(node.operand)
74 if isinstance(node.op, UAdd):
75 return + operand
76 else:
77 return - operand
78 return _convert_num(node)
Georg Brandl0c77a822008-06-10 16:37:50 +000079 def _convert(node):
Victor Stinnerf2c1aa12016-01-26 00:40:57 +010080 if isinstance(node, Constant):
81 return node.value
Georg Brandl0c77a822008-06-10 16:37:50 +000082 elif isinstance(node, Tuple):
83 return tuple(map(_convert, node.elts))
84 elif isinstance(node, List):
85 return list(map(_convert, node.elts))
Georg Brandl492f3fc2010-07-11 09:41:21 +000086 elif isinstance(node, Set):
87 return set(map(_convert, node.elts))
Raymond Hettinger4fcf5c12020-01-02 22:21:18 -070088 elif (isinstance(node, Call) and isinstance(node.func, Name) and
89 node.func.id == 'set' and node.args == node.keywords == []):
90 return set()
Georg Brandl0c77a822008-06-10 16:37:50 +000091 elif isinstance(node, Dict):
Curtis Bucherc21c5122020-05-05 12:40:56 -070092 if len(node.keys) != len(node.values):
93 _raise_malformed_node(node)
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020094 return dict(zip(map(_convert, node.keys),
95 map(_convert, node.values)))
Victor Stinnerf2c1aa12016-01-26 00:40:57 +010096 elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)):
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020097 left = _convert_signed_num(node.left)
98 right = _convert_num(node.right)
99 if isinstance(left, (int, float)) and isinstance(right, complex):
Victor Stinnerf2c1aa12016-01-26 00:40:57 +0100100 if isinstance(node.op, Add):
101 return left + right
102 else:
103 return left - right
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +0200104 return _convert_signed_num(node)
Georg Brandl0c77a822008-06-10 16:37:50 +0000105 return _convert(node_or_string)
106
107
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300108def dump(node, annotate_fields=True, include_attributes=False, *, indent=None):
Georg Brandl0c77a822008-06-10 16:37:50 +0000109 """
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300110 Return a formatted dump of the tree in node. This is mainly useful for
111 debugging purposes. If annotate_fields is true (by default),
112 the returned string will show the names and the values for fields.
113 If annotate_fields is false, the result string will be more compact by
114 omitting unambiguous field names. Attributes such as line
Benjamin Petersondcf97b92008-07-02 17:30:14 +0000115 numbers and column offsets are not dumped by default. If this is wanted,
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300116 include_attributes can be set to true. If indent is a non-negative
117 integer or string, then the tree will be pretty-printed with that indent
118 level. None (the default) selects the single line representation.
Georg Brandl0c77a822008-06-10 16:37:50 +0000119 """
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300120 def _format(node, level=0):
121 if indent is not None:
122 level += 1
123 prefix = '\n' + indent * level
124 sep = ',\n' + indent * level
125 else:
126 prefix = ''
127 sep = ', '
Georg Brandl0c77a822008-06-10 16:37:50 +0000128 if isinstance(node, AST):
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200129 cls = type(node)
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300130 args = []
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300131 allsimple = True
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300132 keywords = annotate_fields
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200133 for name in node._fields:
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300134 try:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200135 value = getattr(node, name)
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300136 except AttributeError:
137 keywords = True
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200138 continue
139 if value is None and getattr(cls, name, ...) is None:
140 keywords = True
141 continue
142 value, simple = _format(value, level)
143 allsimple = allsimple and simple
144 if keywords:
145 args.append('%s=%s' % (name, value))
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300146 else:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200147 args.append(value)
148 if include_attributes and node._attributes:
149 for name in node._attributes:
150 try:
151 value = getattr(node, name)
152 except AttributeError:
153 continue
154 if value is None and getattr(cls, name, ...) is None:
155 continue
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300156 value, simple = _format(value, level)
157 allsimple = allsimple and simple
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200158 args.append('%s=%s' % (name, value))
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300159 if allsimple and len(args) <= 3:
160 return '%s(%s)' % (node.__class__.__name__, ', '.join(args)), not args
161 return '%s(%s%s)' % (node.__class__.__name__, prefix, sep.join(args)), False
Georg Brandl0c77a822008-06-10 16:37:50 +0000162 elif isinstance(node, list):
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300163 if not node:
164 return '[]', True
165 return '[%s%s]' % (prefix, sep.join(_format(x, level)[0] for x in node)), False
166 return repr(node), True
167
Georg Brandl0c77a822008-06-10 16:37:50 +0000168 if not isinstance(node, AST):
169 raise TypeError('expected AST, got %r' % node.__class__.__name__)
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300170 if indent is not None and not isinstance(indent, str):
171 indent = ' ' * indent
172 return _format(node)[0]
Georg Brandl0c77a822008-06-10 16:37:50 +0000173
174
175def copy_location(new_node, old_node):
176 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000177 Copy source location (`lineno`, `col_offset`, `end_lineno`, and `end_col_offset`
178 attributes) from *old_node* to *new_node* if possible, and return *new_node*.
Georg Brandl0c77a822008-06-10 16:37:50 +0000179 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000180 for attr in 'lineno', 'col_offset', 'end_lineno', 'end_col_offset':
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200181 if attr in old_node._attributes and attr in new_node._attributes:
182 value = getattr(old_node, attr, None)
Batuhan Taskaya8f4380d2020-08-05 16:32:32 +0300183 # end_lineno and end_col_offset are optional attributes, and they
184 # should be copied whether the value is None or not.
185 if value is not None or (
186 hasattr(old_node, attr) and attr.startswith("end_")
187 ):
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200188 setattr(new_node, attr, value)
Georg Brandl0c77a822008-06-10 16:37:50 +0000189 return new_node
190
191
192def fix_missing_locations(node):
193 """
194 When you compile a node tree with compile(), the compiler expects lineno and
195 col_offset attributes for every node that supports them. This is rather
196 tedious to fill in for generated nodes, so this helper adds these attributes
197 recursively where not already set, by setting them to the values of the
198 parent node. It works recursively starting at *node*.
199 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000200 def _fix(node, lineno, col_offset, end_lineno, end_col_offset):
Georg Brandl0c77a822008-06-10 16:37:50 +0000201 if 'lineno' in node._attributes:
202 if not hasattr(node, 'lineno'):
203 node.lineno = lineno
204 else:
205 lineno = node.lineno
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000206 if 'end_lineno' in node._attributes:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200207 if getattr(node, 'end_lineno', None) is None:
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000208 node.end_lineno = end_lineno
209 else:
210 end_lineno = node.end_lineno
Georg Brandl0c77a822008-06-10 16:37:50 +0000211 if 'col_offset' in node._attributes:
212 if not hasattr(node, 'col_offset'):
213 node.col_offset = col_offset
214 else:
215 col_offset = node.col_offset
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000216 if 'end_col_offset' in node._attributes:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200217 if getattr(node, 'end_col_offset', None) is None:
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000218 node.end_col_offset = end_col_offset
219 else:
220 end_col_offset = node.end_col_offset
Georg Brandl0c77a822008-06-10 16:37:50 +0000221 for child in iter_child_nodes(node):
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000222 _fix(child, lineno, col_offset, end_lineno, end_col_offset)
223 _fix(node, 1, 0, 1, 0)
Georg Brandl0c77a822008-06-10 16:37:50 +0000224 return node
225
226
227def increment_lineno(node, n=1):
228 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000229 Increment the line number and end line number of each node in the tree
230 starting at *node* by *n*. This is useful to "move code" to a different
231 location in a file.
Georg Brandl0c77a822008-06-10 16:37:50 +0000232 """
Georg Brandl0c77a822008-06-10 16:37:50 +0000233 for child in walk(node):
234 if 'lineno' in child._attributes:
235 child.lineno = getattr(child, 'lineno', 0) + n
Batuhan Taskaya8f4380d2020-08-05 16:32:32 +0300236 if (
237 "end_lineno" in child._attributes
238 and (end_lineno := getattr(child, "end_lineno", 0)) is not None
239 ):
240 child.end_lineno = end_lineno + n
Georg Brandl0c77a822008-06-10 16:37:50 +0000241 return node
242
243
244def iter_fields(node):
245 """
246 Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
247 that is present on *node*.
248 """
249 for field in node._fields:
250 try:
251 yield field, getattr(node, field)
252 except AttributeError:
253 pass
254
255
256def iter_child_nodes(node):
257 """
258 Yield all direct child nodes of *node*, that is, all fields that are nodes
259 and all items of fields that are lists of nodes.
260 """
261 for name, field in iter_fields(node):
262 if isinstance(field, AST):
263 yield field
264 elif isinstance(field, list):
265 for item in field:
266 if isinstance(item, AST):
267 yield item
268
269
270def get_docstring(node, clean=True):
271 """
272 Return the docstring for the given node or None if no docstring can
273 be found. If the node provided does not have docstrings a TypeError
274 will be raised.
Matthias Bussonnier41cea702017-02-23 22:44:19 -0800275
276 If *clean* is `True`, all tabs are expanded to spaces and any whitespace
277 that can be uniformly removed from the second line onwards is removed.
Georg Brandl0c77a822008-06-10 16:37:50 +0000278 """
Yury Selivanov2f07a662015-07-23 08:54:35 +0300279 if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
Georg Brandl0c77a822008-06-10 16:37:50 +0000280 raise TypeError("%r can't have docstrings" % node.__class__.__name__)
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300281 if not(node.body and isinstance(node.body[0], Expr)):
Serhiy Storchaka73cbe7a2018-05-29 12:04:55 +0300282 return None
283 node = node.body[0].value
284 if isinstance(node, Str):
285 text = node.s
286 elif isinstance(node, Constant) and isinstance(node.value, str):
287 text = node.value
288 else:
289 return None
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300290 if clean:
Victor Stinnerf2c1aa12016-01-26 00:40:57 +0100291 import inspect
292 text = inspect.cleandoc(text)
293 return text
Georg Brandl0c77a822008-06-10 16:37:50 +0000294
295
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000296def _splitlines_no_ff(source):
297 """Split a string into lines ignoring form feed and other chars.
298
299 This mimics how the Python parser splits source code.
300 """
301 idx = 0
302 lines = []
303 next_line = ''
304 while idx < len(source):
305 c = source[idx]
306 next_line += c
307 idx += 1
308 # Keep \r\n together
309 if c == '\r' and idx < len(source) and source[idx] == '\n':
310 next_line += '\n'
311 idx += 1
312 if c in '\r\n':
313 lines.append(next_line)
314 next_line = ''
315
316 if next_line:
317 lines.append(next_line)
318 return lines
319
320
321def _pad_whitespace(source):
mpheathfbeba8f2020-02-14 04:32:09 +1000322 r"""Replace all chars except '\f\t' in a line with spaces."""
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000323 result = ''
324 for c in source:
325 if c in '\f\t':
326 result += c
327 else:
328 result += ' '
329 return result
330
331
332def get_source_segment(source, node, *, padded=False):
333 """Get source code segment of the *source* that generated *node*.
334
335 If some location information (`lineno`, `end_lineno`, `col_offset`,
336 or `end_col_offset`) is missing, return None.
337
338 If *padded* is `True`, the first line of a multi-line statement will
339 be padded with spaces to match its original position.
340 """
341 try:
Irit Katriele6578a22020-05-18 19:14:12 +0100342 if node.end_lineno is None or node.end_col_offset is None:
343 return None
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000344 lineno = node.lineno - 1
345 end_lineno = node.end_lineno - 1
346 col_offset = node.col_offset
347 end_col_offset = node.end_col_offset
348 except AttributeError:
349 return None
350
351 lines = _splitlines_no_ff(source)
352 if end_lineno == lineno:
353 return lines[lineno].encode()[col_offset:end_col_offset].decode()
354
355 if padded:
356 padding = _pad_whitespace(lines[lineno].encode()[:col_offset].decode())
357 else:
358 padding = ''
359
360 first = padding + lines[lineno].encode()[col_offset:].decode()
361 last = lines[end_lineno].encode()[:end_col_offset].decode()
362 lines = lines[lineno+1:end_lineno]
363
364 lines.insert(0, first)
365 lines.append(last)
366 return ''.join(lines)
367
368
Georg Brandl0c77a822008-06-10 16:37:50 +0000369def walk(node):
370 """
Georg Brandl619e7ba2011-01-09 07:38:51 +0000371 Recursively yield all descendant nodes in the tree starting at *node*
372 (including *node* itself), in no specified order. This is useful if you
373 only want to modify nodes in place and don't care about the context.
Georg Brandl0c77a822008-06-10 16:37:50 +0000374 """
375 from collections import deque
376 todo = deque([node])
377 while todo:
378 node = todo.popleft()
379 todo.extend(iter_child_nodes(node))
380 yield node
381
382
383class NodeVisitor(object):
384 """
385 A node visitor base class that walks the abstract syntax tree and calls a
386 visitor function for every node found. This function may return a value
387 which is forwarded by the `visit` method.
388
389 This class is meant to be subclassed, with the subclass adding visitor
390 methods.
391
392 Per default the visitor functions for the nodes are ``'visit_'`` +
393 class name of the node. So a `TryFinally` node visit function would
394 be `visit_TryFinally`. This behavior can be changed by overriding
395 the `visit` method. If no visitor function exists for a node
396 (return value `None`) the `generic_visit` visitor is used instead.
397
398 Don't use the `NodeVisitor` if you want to apply changes to nodes during
399 traversing. For this a special visitor exists (`NodeTransformer`) that
400 allows modifications.
401 """
402
403 def visit(self, node):
404 """Visit a node."""
405 method = 'visit_' + node.__class__.__name__
406 visitor = getattr(self, method, self.generic_visit)
407 return visitor(node)
408
409 def generic_visit(self, node):
410 """Called if no explicit visitor function exists for a node."""
411 for field, value in iter_fields(node):
412 if isinstance(value, list):
413 for item in value:
414 if isinstance(item, AST):
415 self.visit(item)
416 elif isinstance(value, AST):
417 self.visit(value)
418
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300419 def visit_Constant(self, node):
420 value = node.value
421 type_name = _const_node_type_names.get(type(value))
422 if type_name is None:
423 for cls, name in _const_node_type_names.items():
424 if isinstance(value, cls):
425 type_name = name
426 break
427 if type_name is not None:
428 method = 'visit_' + type_name
429 try:
430 visitor = getattr(self, method)
431 except AttributeError:
432 pass
433 else:
434 import warnings
435 warnings.warn(f"{method} is deprecated; add visit_Constant",
436 DeprecationWarning, 2)
437 return visitor(node)
438 return self.generic_visit(node)
439
Georg Brandl0c77a822008-06-10 16:37:50 +0000440
441class NodeTransformer(NodeVisitor):
442 """
443 A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
444 allows modification of nodes.
445
446 The `NodeTransformer` will walk the AST and use the return value of the
447 visitor methods to replace or remove the old node. If the return value of
448 the visitor method is ``None``, the node will be removed from its location,
449 otherwise it is replaced with the return value. The return value may be the
450 original node in which case no replacement takes place.
451
452 Here is an example transformer that rewrites all occurrences of name lookups
453 (``foo``) to ``data['foo']``::
454
455 class RewriteName(NodeTransformer):
456
457 def visit_Name(self, node):
Pablo Galindoc00c86b2020-03-12 00:48:19 +0000458 return Subscript(
Georg Brandl0c77a822008-06-10 16:37:50 +0000459 value=Name(id='data', ctx=Load()),
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200460 slice=Constant(value=node.id),
Georg Brandl0c77a822008-06-10 16:37:50 +0000461 ctx=node.ctx
Pablo Galindoc00c86b2020-03-12 00:48:19 +0000462 )
Georg Brandl0c77a822008-06-10 16:37:50 +0000463
464 Keep in mind that if the node you're operating on has child nodes you must
465 either transform the child nodes yourself or call the :meth:`generic_visit`
466 method for the node first.
467
468 For nodes that were part of a collection of statements (that applies to all
469 statement nodes), the visitor may also return a list of nodes rather than
470 just a single node.
471
472 Usually you use the transformer like this::
473
474 node = YourTransformer().visit(node)
475 """
476
477 def generic_visit(self, node):
478 for field, old_value in iter_fields(node):
Georg Brandl0c77a822008-06-10 16:37:50 +0000479 if isinstance(old_value, list):
480 new_values = []
481 for value in old_value:
482 if isinstance(value, AST):
483 value = self.visit(value)
484 if value is None:
485 continue
486 elif not isinstance(value, AST):
487 new_values.extend(value)
488 continue
489 new_values.append(value)
490 old_value[:] = new_values
491 elif isinstance(old_value, AST):
492 new_node = self.visit(old_value)
493 if new_node is None:
494 delattr(node, field)
495 else:
496 setattr(node, field, new_node)
497 return node
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300498
499
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200500# If the ast module is loaded more than once, only add deprecated methods once
501if not hasattr(Constant, 'n'):
502 # The following code is for backward compatibility.
503 # It will be removed in future.
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300504
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200505 def _getter(self):
506 """Deprecated. Use value instead."""
507 return self.value
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300508
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200509 def _setter(self, value):
510 self.value = value
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300511
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200512 Constant.n = property(_getter, _setter)
513 Constant.s = property(_getter, _setter)
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300514
515class _ABC(type):
516
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200517 def __init__(cls, *args):
518 cls.__doc__ = """Deprecated AST node class. Use ast.Constant instead"""
519
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300520 def __instancecheck__(cls, inst):
521 if not isinstance(inst, Constant):
522 return False
523 if cls in _const_types:
524 try:
525 value = inst.value
526 except AttributeError:
527 return False
528 else:
Anthony Sottile74176222019-01-18 11:30:28 -0800529 return (
530 isinstance(value, _const_types[cls]) and
531 not isinstance(value, _const_types_not.get(cls, ()))
532 )
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300533 return type.__instancecheck__(cls, inst)
534
535def _new(cls, *args, **kwargs):
Rémi Lapeyrec73914a2020-05-24 23:12:57 +0200536 for key in kwargs:
537 if key not in cls._fields:
538 # arbitrary keyword arguments are accepted
539 continue
540 pos = cls._fields.index(key)
541 if pos < len(args):
542 raise TypeError(f"{cls.__name__} got multiple values for argument {key!r}")
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300543 if cls in _const_types:
544 return Constant(*args, **kwargs)
545 return Constant.__new__(cls, *args, **kwargs)
546
547class Num(Constant, metaclass=_ABC):
548 _fields = ('n',)
549 __new__ = _new
550
551class Str(Constant, metaclass=_ABC):
552 _fields = ('s',)
553 __new__ = _new
554
555class Bytes(Constant, metaclass=_ABC):
556 _fields = ('s',)
557 __new__ = _new
558
559class NameConstant(Constant, metaclass=_ABC):
560 __new__ = _new
561
562class Ellipsis(Constant, metaclass=_ABC):
563 _fields = ()
564
565 def __new__(cls, *args, **kwargs):
566 if cls is Ellipsis:
567 return Constant(..., *args, **kwargs)
568 return Constant.__new__(cls, *args, **kwargs)
569
570_const_types = {
571 Num: (int, float, complex),
572 Str: (str,),
573 Bytes: (bytes,),
574 NameConstant: (type(None), bool),
575 Ellipsis: (type(...),),
576}
Anthony Sottile74176222019-01-18 11:30:28 -0800577_const_types_not = {
578 Num: (bool,),
579}
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200580
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300581_const_node_type_names = {
582 bool: 'NameConstant', # should be before int
583 type(None): 'NameConstant',
584 int: 'Num',
585 float: 'Num',
586 complex: 'Num',
587 str: 'Str',
588 bytes: 'Bytes',
589 type(...): 'Ellipsis',
590}
Serhiy Storchaka832e8642019-09-09 23:36:13 +0300591
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200592class slice(AST):
593 """Deprecated AST node class."""
594
595class Index(slice):
596 """Deprecated AST node class. Use the index value directly instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200597 def __new__(cls, value, **kwargs):
598 return value
599
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200600class ExtSlice(slice):
601 """Deprecated AST node class. Use ast.Tuple instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200602 def __new__(cls, dims=(), **kwargs):
603 return Tuple(list(dims), Load(), **kwargs)
604
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200605# If the ast module is loaded more than once, only add deprecated methods once
606if not hasattr(Tuple, 'dims'):
607 # The following code is for backward compatibility.
608 # It will be removed in future.
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200609
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200610 def _dims_getter(self):
611 """Deprecated. Use elts instead."""
612 return self.elts
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200613
Victor Stinnere5fbe0c2020-09-15 18:03:34 +0200614 def _dims_setter(self, value):
615 self.elts = value
616
617 Tuple.dims = property(_dims_getter, _dims_setter)
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200618
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200619class Suite(mod):
620 """Deprecated AST node class. Unused in Python 3."""
621
622class AugLoad(expr_context):
623 """Deprecated AST node class. Unused in Python 3."""
624
625class AugStore(expr_context):
626 """Deprecated AST node class. Unused in Python 3."""
627
628class Param(expr_context):
629 """Deprecated AST node class. Unused in Python 3."""
630
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200631
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000632# Large float and imaginary literals get turned into infinities in the AST.
633# We unparse those infinities to INFSTR.
634_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
635
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300636class _Precedence(IntEnum):
637 """Precedence table that originated from python grammar."""
638
639 TUPLE = auto()
640 YIELD = auto() # 'yield', 'yield from'
641 TEST = auto() # 'if'-'else', 'lambda'
642 OR = auto() # 'or'
643 AND = auto() # 'and'
644 NOT = auto() # 'not'
645 CMP = auto() # '<', '>', '==', '>=', '<=', '!=',
646 # 'in', 'not in', 'is', 'is not'
647 EXPR = auto()
648 BOR = EXPR # '|'
649 BXOR = auto() # '^'
650 BAND = auto() # '&'
651 SHIFT = auto() # '<<', '>>'
652 ARITH = auto() # '+', '-'
653 TERM = auto() # '*', '@', '/', '%', '//'
654 FACTOR = auto() # unary '+', '-', '~'
655 POWER = auto() # '**'
656 AWAIT = auto() # 'await'
657 ATOM = auto()
658
659 def next(self):
660 try:
661 return self.__class__(self + 1)
662 except ValueError:
663 return self
664
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000665class _Unparser(NodeVisitor):
666 """Methods in this class recursively traverse an AST and
667 output source code for the abstract syntax; original formatting
668 is disregarded."""
669
670 def __init__(self):
671 self._source = []
672 self._buffer = []
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300673 self._precedences = {}
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300674 self._type_ignores = {}
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000675 self._indent = 0
676
677 def interleave(self, inter, f, seq):
678 """Call f on each item in seq, calling inter() in between."""
679 seq = iter(seq)
680 try:
681 f(next(seq))
682 except StopIteration:
683 pass
684 else:
685 for x in seq:
686 inter()
687 f(x)
688
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +0300689 def items_view(self, traverser, items):
690 """Traverse and separate the given *items* with a comma and append it to
691 the buffer. If *items* is a single item sequence, a trailing comma
692 will be added."""
693 if len(items) == 1:
694 traverser(items[0])
695 self.write(",")
696 else:
697 self.interleave(lambda: self.write(", "), traverser, items)
698
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300699 def maybe_newline(self):
700 """Adds a newline if it isn't the start of generated source"""
701 if self._source:
702 self.write("\n")
703
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000704 def fill(self, text=""):
705 """Indent a piece of text and append it, according to the current
706 indentation level"""
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300707 self.maybe_newline()
708 self.write(" " * self._indent + text)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000709
710 def write(self, text):
711 """Append a piece of text"""
712 self._source.append(text)
713
714 def buffer_writer(self, text):
715 self._buffer.append(text)
716
717 @property
718 def buffer(self):
719 value = "".join(self._buffer)
720 self._buffer.clear()
721 return value
722
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000723 @contextmanager
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300724 def block(self, *, extra = None):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000725 """A context manager for preparing the source for blocks. It adds
726 the character':', increases the indentation on enter and decreases
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300727 the indentation on exit. If *extra* is given, it will be directly
728 appended after the colon character.
729 """
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000730 self.write(":")
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300731 if extra:
732 self.write(extra)
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000733 self._indent += 1
734 yield
735 self._indent -= 1
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000736
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300737 @contextmanager
738 def delimit(self, start, end):
739 """A context manager for preparing the source for expressions. It adds
740 *start* to the buffer and enters, after exit it adds *end*."""
741
742 self.write(start)
743 yield
744 self.write(end)
745
746 def delimit_if(self, start, end, condition):
747 if condition:
748 return self.delimit(start, end)
749 else:
750 return nullcontext()
751
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300752 def require_parens(self, precedence, node):
753 """Shortcut to adding precedence related parens"""
754 return self.delimit_if("(", ")", self.get_precedence(node) > precedence)
755
756 def get_precedence(self, node):
757 return self._precedences.get(node, _Precedence.TEST)
758
759 def set_precedence(self, precedence, *nodes):
760 for node in nodes:
761 self._precedences[node] = precedence
762
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300763 def get_raw_docstring(self, node):
764 """If a docstring node is found in the body of the *node* parameter,
765 return that docstring node, None otherwise.
766
767 Logic mirrored from ``_PyAST_GetDocString``."""
768 if not isinstance(
769 node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)
770 ) or len(node.body) < 1:
771 return None
772 node = node.body[0]
773 if not isinstance(node, Expr):
774 return None
775 node = node.value
776 if isinstance(node, Constant) and isinstance(node.value, str):
777 return node
778
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300779 def get_type_comment(self, node):
780 comment = self._type_ignores.get(node.lineno) or node.type_comment
781 if comment is not None:
782 return f" # type: {comment}"
783
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000784 def traverse(self, node):
785 if isinstance(node, list):
786 for item in node:
787 self.traverse(item)
788 else:
789 super().visit(node)
790
791 def visit(self, node):
792 """Outputs a source code string that, if converted back to an ast
793 (using ast.parse) will generate an AST equivalent to *node*"""
794 self._source = []
795 self.traverse(node)
796 return "".join(self._source)
797
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300798 def _write_docstring_and_traverse_body(self, node):
799 if (docstring := self.get_raw_docstring(node)):
800 self._write_docstring(docstring)
801 self.traverse(node.body[1:])
802 else:
803 self.traverse(node.body)
804
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000805 def visit_Module(self, node):
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300806 self._type_ignores = {
807 ignore.lineno: f"ignore{ignore.tag}"
808 for ignore in node.type_ignores
809 }
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300810 self._write_docstring_and_traverse_body(node)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300811 self._type_ignores.clear()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000812
Batuhan Taşkaya5b66ec12020-03-15 22:56:57 +0300813 def visit_FunctionType(self, node):
814 with self.delimit("(", ")"):
815 self.interleave(
816 lambda: self.write(", "), self.traverse, node.argtypes
817 )
818
819 self.write(" -> ")
820 self.traverse(node.returns)
821
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000822 def visit_Expr(self, node):
823 self.fill()
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300824 self.set_precedence(_Precedence.YIELD, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000825 self.traverse(node.value)
826
827 def visit_NamedExpr(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300828 with self.require_parens(_Precedence.TUPLE, node):
829 self.set_precedence(_Precedence.ATOM, node.target, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300830 self.traverse(node.target)
831 self.write(" := ")
832 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000833
834 def visit_Import(self, node):
835 self.fill("import ")
836 self.interleave(lambda: self.write(", "), self.traverse, node.names)
837
838 def visit_ImportFrom(self, node):
839 self.fill("from ")
840 self.write("." * node.level)
841 if node.module:
842 self.write(node.module)
843 self.write(" import ")
844 self.interleave(lambda: self.write(", "), self.traverse, node.names)
845
846 def visit_Assign(self, node):
847 self.fill()
848 for target in node.targets:
849 self.traverse(target)
850 self.write(" = ")
851 self.traverse(node.value)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300852 if type_comment := self.get_type_comment(node):
853 self.write(type_comment)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000854
855 def visit_AugAssign(self, node):
856 self.fill()
857 self.traverse(node.target)
858 self.write(" " + self.binop[node.op.__class__.__name__] + "= ")
859 self.traverse(node.value)
860
861 def visit_AnnAssign(self, node):
862 self.fill()
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300863 with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)):
864 self.traverse(node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000865 self.write(": ")
866 self.traverse(node.annotation)
867 if node.value:
868 self.write(" = ")
869 self.traverse(node.value)
870
871 def visit_Return(self, node):
872 self.fill("return")
873 if node.value:
874 self.write(" ")
875 self.traverse(node.value)
876
877 def visit_Pass(self, node):
878 self.fill("pass")
879
880 def visit_Break(self, node):
881 self.fill("break")
882
883 def visit_Continue(self, node):
884 self.fill("continue")
885
886 def visit_Delete(self, node):
887 self.fill("del ")
888 self.interleave(lambda: self.write(", "), self.traverse, node.targets)
889
890 def visit_Assert(self, node):
891 self.fill("assert ")
892 self.traverse(node.test)
893 if node.msg:
894 self.write(", ")
895 self.traverse(node.msg)
896
897 def visit_Global(self, node):
898 self.fill("global ")
899 self.interleave(lambda: self.write(", "), self.write, node.names)
900
901 def visit_Nonlocal(self, node):
902 self.fill("nonlocal ")
903 self.interleave(lambda: self.write(", "), self.write, node.names)
904
905 def visit_Await(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300906 with self.require_parens(_Precedence.AWAIT, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300907 self.write("await")
908 if node.value:
909 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300910 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300911 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000912
913 def visit_Yield(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300914 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300915 self.write("yield")
916 if node.value:
917 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300918 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300919 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000920
921 def visit_YieldFrom(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300922 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300923 self.write("yield from ")
924 if not node.value:
925 raise ValueError("Node can't be used without a value attribute.")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300926 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300927 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000928
929 def visit_Raise(self, node):
930 self.fill("raise")
931 if not node.exc:
932 if node.cause:
933 raise ValueError(f"Node can't use cause without an exception.")
934 return
935 self.write(" ")
936 self.traverse(node.exc)
937 if node.cause:
938 self.write(" from ")
939 self.traverse(node.cause)
940
941 def visit_Try(self, node):
942 self.fill("try")
943 with self.block():
944 self.traverse(node.body)
945 for ex in node.handlers:
946 self.traverse(ex)
947 if node.orelse:
948 self.fill("else")
949 with self.block():
950 self.traverse(node.orelse)
951 if node.finalbody:
952 self.fill("finally")
953 with self.block():
954 self.traverse(node.finalbody)
955
956 def visit_ExceptHandler(self, node):
957 self.fill("except")
958 if node.type:
959 self.write(" ")
960 self.traverse(node.type)
961 if node.name:
962 self.write(" as ")
963 self.write(node.name)
964 with self.block():
965 self.traverse(node.body)
966
967 def visit_ClassDef(self, node):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300968 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000969 for deco in node.decorator_list:
970 self.fill("@")
971 self.traverse(deco)
972 self.fill("class " + node.name)
Batuhan Taskaya25160cd2020-05-17 00:53:25 +0300973 with self.delimit_if("(", ")", condition = node.bases or node.keywords):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300974 comma = False
975 for e in node.bases:
976 if comma:
977 self.write(", ")
978 else:
979 comma = True
980 self.traverse(e)
981 for e in node.keywords:
982 if comma:
983 self.write(", ")
984 else:
985 comma = True
986 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000987
988 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300989 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000990
991 def visit_FunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300992 self._function_helper(node, "def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000993
994 def visit_AsyncFunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300995 self._function_helper(node, "async def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000996
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300997 def _function_helper(self, node, fill_suffix):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300998 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000999 for deco in node.decorator_list:
1000 self.fill("@")
1001 self.traverse(deco)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001002 def_str = fill_suffix + " " + node.name
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001003 self.fill(def_str)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001004 with self.delimit("(", ")"):
1005 self.traverse(node.args)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001006 if node.returns:
1007 self.write(" -> ")
1008 self.traverse(node.returns)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001009 with self.block(extra=self.get_type_comment(node)):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001010 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001011
1012 def visit_For(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001013 self._for_helper("for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001014
1015 def visit_AsyncFor(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001016 self._for_helper("async for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001017
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001018 def _for_helper(self, fill, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001019 self.fill(fill)
1020 self.traverse(node.target)
1021 self.write(" in ")
1022 self.traverse(node.iter)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001023 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001024 self.traverse(node.body)
1025 if node.orelse:
1026 self.fill("else")
1027 with self.block():
1028 self.traverse(node.orelse)
1029
1030 def visit_If(self, node):
1031 self.fill("if ")
1032 self.traverse(node.test)
1033 with self.block():
1034 self.traverse(node.body)
1035 # collapse nested ifs into equivalent elifs.
1036 while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If):
1037 node = node.orelse[0]
1038 self.fill("elif ")
1039 self.traverse(node.test)
1040 with self.block():
1041 self.traverse(node.body)
1042 # final else
1043 if node.orelse:
1044 self.fill("else")
1045 with self.block():
1046 self.traverse(node.orelse)
1047
1048 def visit_While(self, node):
1049 self.fill("while ")
1050 self.traverse(node.test)
1051 with self.block():
1052 self.traverse(node.body)
1053 if node.orelse:
1054 self.fill("else")
1055 with self.block():
1056 self.traverse(node.orelse)
1057
1058 def visit_With(self, node):
1059 self.fill("with ")
1060 self.interleave(lambda: self.write(", "), self.traverse, node.items)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001061 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001062 self.traverse(node.body)
1063
1064 def visit_AsyncWith(self, node):
1065 self.fill("async with ")
1066 self.interleave(lambda: self.write(", "), self.traverse, node.items)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001067 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001068 self.traverse(node.body)
1069
1070 def visit_JoinedStr(self, node):
1071 self.write("f")
1072 self._fstring_JoinedStr(node, self.buffer_writer)
1073 self.write(repr(self.buffer))
1074
1075 def visit_FormattedValue(self, node):
1076 self.write("f")
1077 self._fstring_FormattedValue(node, self.buffer_writer)
1078 self.write(repr(self.buffer))
1079
1080 def _fstring_JoinedStr(self, node, write):
1081 for value in node.values:
1082 meth = getattr(self, "_fstring_" + type(value).__name__)
1083 meth(value, write)
1084
1085 def _fstring_Constant(self, node, write):
1086 if not isinstance(node.value, str):
1087 raise ValueError("Constants inside JoinedStr should be a string.")
1088 value = node.value.replace("{", "{{").replace("}", "}}")
1089 write(value)
1090
1091 def _fstring_FormattedValue(self, node, write):
1092 write("{")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001093 unparser = type(self)()
1094 unparser.set_precedence(_Precedence.TEST.next(), node.value)
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +03001095 expr = unparser.visit(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001096 if expr.startswith("{"):
1097 write(" ") # Separate pair of opening brackets as "{ {"
1098 write(expr)
1099 if node.conversion != -1:
1100 conversion = chr(node.conversion)
1101 if conversion not in "sra":
1102 raise ValueError("Unknown f-string conversion.")
1103 write(f"!{conversion}")
1104 if node.format_spec:
1105 write(":")
1106 meth = getattr(self, "_fstring_" + type(node.format_spec).__name__)
1107 meth(node.format_spec, write)
1108 write("}")
1109
1110 def visit_Name(self, node):
1111 self.write(node.id)
1112
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001113 def _write_docstring(self, node):
CyberSaxosTiGERd71a6492020-05-18 21:41:35 +03001114 def esc_char(c):
1115 if c in ("\n", "\t"):
1116 # In the AST form, we don't know the author's intentation
1117 # about how this should be displayed. We'll only escape
1118 # \n and \t, because they are more likely to be unescaped
1119 # in the source
1120 return c
1121 return c.encode('unicode_escape').decode('ascii')
1122
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001123 self.fill()
1124 if node.kind == "u":
1125 self.write("u")
1126
Batuhan Taskayae966af72020-05-17 01:49:07 +03001127 value = node.value
1128 if value:
1129 # Preserve quotes in the docstring by escaping them
CyberSaxosTiGERd71a6492020-05-18 21:41:35 +03001130 value = "".join(map(esc_char, value))
Batuhan Taskayae966af72020-05-17 01:49:07 +03001131 if value[-1] == '"':
1132 value = value.replace('"', '\\"', -1)
CyberSaxosTiGERd71a6492020-05-18 21:41:35 +03001133 value = value.replace('"""', '""\\"')
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001134
1135 self.write(f'"""{value}"""')
1136
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001137 def _write_constant(self, value):
1138 if isinstance(value, (float, complex)):
1139 # Substitute overflowing decimal literal for AST infinities.
1140 self.write(repr(value).replace("inf", _INFSTR))
1141 else:
1142 self.write(repr(value))
1143
1144 def visit_Constant(self, node):
1145 value = node.value
1146 if isinstance(value, tuple):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001147 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001148 self.items_view(self._write_constant, value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001149 elif value is ...:
1150 self.write("...")
1151 else:
1152 if node.kind == "u":
1153 self.write("u")
1154 self._write_constant(node.value)
1155
1156 def visit_List(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001157 with self.delimit("[", "]"):
1158 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001159
1160 def visit_ListComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001161 with self.delimit("[", "]"):
1162 self.traverse(node.elt)
1163 for gen in node.generators:
1164 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001165
1166 def visit_GeneratorExp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001167 with self.delimit("(", ")"):
1168 self.traverse(node.elt)
1169 for gen in node.generators:
1170 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001171
1172 def visit_SetComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001173 with self.delimit("{", "}"):
1174 self.traverse(node.elt)
1175 for gen in node.generators:
1176 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001177
1178 def visit_DictComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001179 with self.delimit("{", "}"):
1180 self.traverse(node.key)
1181 self.write(": ")
1182 self.traverse(node.value)
1183 for gen in node.generators:
1184 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001185
1186 def visit_comprehension(self, node):
1187 if node.is_async:
1188 self.write(" async for ")
1189 else:
1190 self.write(" for ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001191 self.set_precedence(_Precedence.TUPLE, node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001192 self.traverse(node.target)
1193 self.write(" in ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001194 self.set_precedence(_Precedence.TEST.next(), node.iter, *node.ifs)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001195 self.traverse(node.iter)
1196 for if_clause in node.ifs:
1197 self.write(" if ")
1198 self.traverse(if_clause)
1199
1200 def visit_IfExp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001201 with self.require_parens(_Precedence.TEST, node):
1202 self.set_precedence(_Precedence.TEST.next(), node.body, node.test)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001203 self.traverse(node.body)
1204 self.write(" if ")
1205 self.traverse(node.test)
1206 self.write(" else ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001207 self.set_precedence(_Precedence.TEST, node.orelse)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001208 self.traverse(node.orelse)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001209
1210 def visit_Set(self, node):
1211 if not node.elts:
Shantanu01508dc2020-04-16 03:10:12 -07001212 raise ValueError("Set node should have at least one item")
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001213 with self.delimit("{", "}"):
1214 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001215
1216 def visit_Dict(self, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001217 def write_key_value_pair(k, v):
1218 self.traverse(k)
1219 self.write(": ")
1220 self.traverse(v)
1221
1222 def write_item(item):
1223 k, v = item
1224 if k is None:
1225 # for dictionary unpacking operator in dicts {**{'y': 2}}
1226 # see PEP 448 for details
1227 self.write("**")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001228 self.set_precedence(_Precedence.EXPR, v)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001229 self.traverse(v)
1230 else:
1231 write_key_value_pair(k, v)
1232
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001233 with self.delimit("{", "}"):
1234 self.interleave(
1235 lambda: self.write(", "), write_item, zip(node.keys, node.values)
1236 )
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001237
1238 def visit_Tuple(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001239 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001240 self.items_view(self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001241
1242 unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001243 unop_precedence = {
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001244 "not": _Precedence.NOT,
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001245 "~": _Precedence.FACTOR,
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001246 "+": _Precedence.FACTOR,
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001247 "-": _Precedence.FACTOR,
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001248 }
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001249
1250 def visit_UnaryOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001251 operator = self.unop[node.op.__class__.__name__]
1252 operator_precedence = self.unop_precedence[operator]
1253 with self.require_parens(operator_precedence, node):
1254 self.write(operator)
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001255 # factor prefixes (+, -, ~) shouldn't be seperated
1256 # from the value they belong, (e.g: +1 instead of + 1)
1257 if operator_precedence is not _Precedence.FACTOR:
1258 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001259 self.set_precedence(operator_precedence, node.operand)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001260 self.traverse(node.operand)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001261
1262 binop = {
1263 "Add": "+",
1264 "Sub": "-",
1265 "Mult": "*",
1266 "MatMult": "@",
1267 "Div": "/",
1268 "Mod": "%",
1269 "LShift": "<<",
1270 "RShift": ">>",
1271 "BitOr": "|",
1272 "BitXor": "^",
1273 "BitAnd": "&",
1274 "FloorDiv": "//",
1275 "Pow": "**",
1276 }
1277
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001278 binop_precedence = {
1279 "+": _Precedence.ARITH,
1280 "-": _Precedence.ARITH,
1281 "*": _Precedence.TERM,
1282 "@": _Precedence.TERM,
1283 "/": _Precedence.TERM,
1284 "%": _Precedence.TERM,
1285 "<<": _Precedence.SHIFT,
1286 ">>": _Precedence.SHIFT,
1287 "|": _Precedence.BOR,
1288 "^": _Precedence.BXOR,
1289 "&": _Precedence.BAND,
1290 "//": _Precedence.TERM,
1291 "**": _Precedence.POWER,
1292 }
1293
1294 binop_rassoc = frozenset(("**",))
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001295 def visit_BinOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001296 operator = self.binop[node.op.__class__.__name__]
1297 operator_precedence = self.binop_precedence[operator]
1298 with self.require_parens(operator_precedence, node):
1299 if operator in self.binop_rassoc:
1300 left_precedence = operator_precedence.next()
1301 right_precedence = operator_precedence
1302 else:
1303 left_precedence = operator_precedence
1304 right_precedence = operator_precedence.next()
1305
1306 self.set_precedence(left_precedence, node.left)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001307 self.traverse(node.left)
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001308 self.write(f" {operator} ")
1309 self.set_precedence(right_precedence, node.right)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001310 self.traverse(node.right)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001311
1312 cmpops = {
1313 "Eq": "==",
1314 "NotEq": "!=",
1315 "Lt": "<",
1316 "LtE": "<=",
1317 "Gt": ">",
1318 "GtE": ">=",
1319 "Is": "is",
1320 "IsNot": "is not",
1321 "In": "in",
1322 "NotIn": "not in",
1323 }
1324
1325 def visit_Compare(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001326 with self.require_parens(_Precedence.CMP, node):
1327 self.set_precedence(_Precedence.CMP.next(), node.left, *node.comparators)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001328 self.traverse(node.left)
1329 for o, e in zip(node.ops, node.comparators):
1330 self.write(" " + self.cmpops[o.__class__.__name__] + " ")
1331 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001332
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001333 boolops = {"And": "and", "Or": "or"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001334 boolop_precedence = {"and": _Precedence.AND, "or": _Precedence.OR}
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001335
1336 def visit_BoolOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001337 operator = self.boolops[node.op.__class__.__name__]
1338 operator_precedence = self.boolop_precedence[operator]
1339
1340 def increasing_level_traverse(node):
1341 nonlocal operator_precedence
1342 operator_precedence = operator_precedence.next()
1343 self.set_precedence(operator_precedence, node)
1344 self.traverse(node)
1345
1346 with self.require_parens(operator_precedence, node):
1347 s = f" {operator} "
1348 self.interleave(lambda: self.write(s), increasing_level_traverse, node.values)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001349
1350 def visit_Attribute(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001351 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001352 self.traverse(node.value)
1353 # Special case: 3.__abs__() is a syntax error, so if node.value
1354 # is an integer literal then we need to either parenthesize
1355 # it or add an extra space to get 3 .__abs__().
1356 if isinstance(node.value, Constant) and isinstance(node.value.value, int):
1357 self.write(" ")
1358 self.write(".")
1359 self.write(node.attr)
1360
1361 def visit_Call(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001362 self.set_precedence(_Precedence.ATOM, node.func)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001363 self.traverse(node.func)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001364 with self.delimit("(", ")"):
1365 comma = False
1366 for e in node.args:
1367 if comma:
1368 self.write(", ")
1369 else:
1370 comma = True
1371 self.traverse(e)
1372 for e in node.keywords:
1373 if comma:
1374 self.write(", ")
1375 else:
1376 comma = True
1377 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001378
1379 def visit_Subscript(self, node):
Batuhan Taskayac102a142020-05-18 23:48:49 +03001380 def is_simple_tuple(slice_value):
1381 # when unparsing a non-empty tuple, the parantheses can be safely
1382 # omitted if there aren't any elements that explicitly requires
1383 # parantheses (such as starred expressions).
1384 return (
1385 isinstance(slice_value, Tuple)
1386 and slice_value.elts
1387 and not any(isinstance(elt, Starred) for elt in slice_value.elts)
1388 )
1389
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001390 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001391 self.traverse(node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001392 with self.delimit("[", "]"):
Batuhan Taskayac102a142020-05-18 23:48:49 +03001393 if is_simple_tuple(node.slice):
Serhiy Storchaka13d52c22020-03-10 18:52:34 +02001394 self.items_view(self.traverse, node.slice.elts)
Serhiy Storchakac4928fc2020-03-07 17:25:32 +02001395 else:
1396 self.traverse(node.slice)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001397
1398 def visit_Starred(self, node):
1399 self.write("*")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001400 self.set_precedence(_Precedence.EXPR, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001401 self.traverse(node.value)
1402
1403 def visit_Ellipsis(self, node):
1404 self.write("...")
1405
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001406 def visit_Slice(self, node):
1407 if node.lower:
1408 self.traverse(node.lower)
1409 self.write(":")
1410 if node.upper:
1411 self.traverse(node.upper)
1412 if node.step:
1413 self.write(":")
1414 self.traverse(node.step)
1415
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001416 def visit_arg(self, node):
1417 self.write(node.arg)
1418 if node.annotation:
1419 self.write(": ")
1420 self.traverse(node.annotation)
1421
1422 def visit_arguments(self, node):
1423 first = True
1424 # normal arguments
1425 all_args = node.posonlyargs + node.args
1426 defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults
1427 for index, elements in enumerate(zip(all_args, defaults), 1):
1428 a, d = elements
1429 if first:
1430 first = False
1431 else:
1432 self.write(", ")
1433 self.traverse(a)
1434 if d:
1435 self.write("=")
1436 self.traverse(d)
1437 if index == len(node.posonlyargs):
1438 self.write(", /")
1439
1440 # varargs, or bare '*' if no varargs but keyword-only arguments present
1441 if node.vararg or node.kwonlyargs:
1442 if first:
1443 first = False
1444 else:
1445 self.write(", ")
1446 self.write("*")
1447 if node.vararg:
1448 self.write(node.vararg.arg)
1449 if node.vararg.annotation:
1450 self.write(": ")
1451 self.traverse(node.vararg.annotation)
1452
1453 # keyword-only arguments
1454 if node.kwonlyargs:
1455 for a, d in zip(node.kwonlyargs, node.kw_defaults):
Batuhan Taşkayaa322f502019-12-16 15:26:58 +03001456 self.write(", ")
1457 self.traverse(a)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001458 if d:
1459 self.write("=")
1460 self.traverse(d)
1461
1462 # kwargs
1463 if node.kwarg:
1464 if first:
1465 first = False
1466 else:
1467 self.write(", ")
1468 self.write("**" + node.kwarg.arg)
1469 if node.kwarg.annotation:
1470 self.write(": ")
1471 self.traverse(node.kwarg.annotation)
1472
1473 def visit_keyword(self, node):
1474 if node.arg is None:
1475 self.write("**")
1476 else:
1477 self.write(node.arg)
1478 self.write("=")
1479 self.traverse(node.value)
1480
1481 def visit_Lambda(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001482 with self.require_parens(_Precedence.TEST, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001483 self.write("lambda ")
1484 self.traverse(node.args)
1485 self.write(": ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001486 self.set_precedence(_Precedence.TEST, node.body)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001487 self.traverse(node.body)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001488
1489 def visit_alias(self, node):
1490 self.write(node.name)
1491 if node.asname:
1492 self.write(" as " + node.asname)
1493
1494 def visit_withitem(self, node):
1495 self.traverse(node.context_expr)
1496 if node.optional_vars:
1497 self.write(" as ")
1498 self.traverse(node.optional_vars)
1499
1500def unparse(ast_obj):
1501 unparser = _Unparser()
1502 return unparser.visit(ast_obj)
1503
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001504
1505def main():
1506 import argparse
1507
1508 parser = argparse.ArgumentParser(prog='python -m ast')
1509 parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?',
1510 default='-',
1511 help='the file to parse; defaults to stdin')
1512 parser.add_argument('-m', '--mode', default='exec',
1513 choices=('exec', 'single', 'eval', 'func_type'),
1514 help='specify what kind of code must be parsed')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001515 parser.add_argument('--no-type-comments', default=True, action='store_false',
1516 help="don't add information about type comments")
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001517 parser.add_argument('-a', '--include-attributes', action='store_true',
1518 help='include attributes such as line numbers and '
1519 'column offsets')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001520 parser.add_argument('-i', '--indent', type=int, default=3,
1521 help='indentation of nodes (number of spaces)')
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001522 args = parser.parse_args()
1523
1524 with args.infile as infile:
1525 source = infile.read()
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001526 tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
1527 print(dump(tree, include_attributes=args.include_attributes, indent=args.indent))
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001528
1529if __name__ == '__main__':
1530 main()