blob: 65ebd0100de0074df17d0a08b6789ecbc022783f [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
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)
Miss Islington (bot)a1320982020-08-05 06:52:29 -0700183 # 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
Miss Islington (bot)a1320982020-08-05 06:52:29 -0700236 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
500# The following code is for backward compatibility.
501# It will be removed in future.
502
503def _getter(self):
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200504 """Deprecated. Use value instead."""
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300505 return self.value
506
507def _setter(self, value):
508 self.value = value
509
510Constant.n = property(_getter, _setter)
511Constant.s = property(_getter, _setter)
512
513class _ABC(type):
514
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200515 def __init__(cls, *args):
516 cls.__doc__ = """Deprecated AST node class. Use ast.Constant instead"""
517
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300518 def __instancecheck__(cls, inst):
519 if not isinstance(inst, Constant):
520 return False
521 if cls in _const_types:
522 try:
523 value = inst.value
524 except AttributeError:
525 return False
526 else:
Anthony Sottile74176222019-01-18 11:30:28 -0800527 return (
528 isinstance(value, _const_types[cls]) and
529 not isinstance(value, _const_types_not.get(cls, ()))
530 )
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300531 return type.__instancecheck__(cls, inst)
532
533def _new(cls, *args, **kwargs):
Miss Islington (bot)1a4e9e62020-05-24 14:32:32 -0700534 for key in kwargs:
535 if key not in cls._fields:
536 # arbitrary keyword arguments are accepted
537 continue
538 pos = cls._fields.index(key)
539 if pos < len(args):
540 raise TypeError(f"{cls.__name__} got multiple values for argument {key!r}")
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300541 if cls in _const_types:
542 return Constant(*args, **kwargs)
543 return Constant.__new__(cls, *args, **kwargs)
544
545class Num(Constant, metaclass=_ABC):
546 _fields = ('n',)
547 __new__ = _new
548
549class Str(Constant, metaclass=_ABC):
550 _fields = ('s',)
551 __new__ = _new
552
553class Bytes(Constant, metaclass=_ABC):
554 _fields = ('s',)
555 __new__ = _new
556
557class NameConstant(Constant, metaclass=_ABC):
558 __new__ = _new
559
560class Ellipsis(Constant, metaclass=_ABC):
561 _fields = ()
562
563 def __new__(cls, *args, **kwargs):
564 if cls is Ellipsis:
565 return Constant(..., *args, **kwargs)
566 return Constant.__new__(cls, *args, **kwargs)
567
568_const_types = {
569 Num: (int, float, complex),
570 Str: (str,),
571 Bytes: (bytes,),
572 NameConstant: (type(None), bool),
573 Ellipsis: (type(...),),
574}
Anthony Sottile74176222019-01-18 11:30:28 -0800575_const_types_not = {
576 Num: (bool,),
577}
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200578
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300579_const_node_type_names = {
580 bool: 'NameConstant', # should be before int
581 type(None): 'NameConstant',
582 int: 'Num',
583 float: 'Num',
584 complex: 'Num',
585 str: 'Str',
586 bytes: 'Bytes',
587 type(...): 'Ellipsis',
588}
Serhiy Storchaka832e8642019-09-09 23:36:13 +0300589
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200590class slice(AST):
591 """Deprecated AST node class."""
592
593class Index(slice):
594 """Deprecated AST node class. Use the index value directly instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200595 def __new__(cls, value, **kwargs):
596 return value
597
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200598class ExtSlice(slice):
599 """Deprecated AST node class. Use ast.Tuple instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200600 def __new__(cls, dims=(), **kwargs):
601 return Tuple(list(dims), Load(), **kwargs)
602
603def _dims_getter(self):
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200604 """Deprecated. Use elts instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200605 return self.elts
606
607def _dims_setter(self, value):
608 self.elts = value
609
610Tuple.dims = property(_dims_getter, _dims_setter)
611
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200612class Suite(mod):
613 """Deprecated AST node class. Unused in Python 3."""
614
615class AugLoad(expr_context):
616 """Deprecated AST node class. Unused in Python 3."""
617
618class AugStore(expr_context):
619 """Deprecated AST node class. Unused in Python 3."""
620
621class Param(expr_context):
622 """Deprecated AST node class. Unused in Python 3."""
623
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200624
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000625# Large float and imaginary literals get turned into infinities in the AST.
626# We unparse those infinities to INFSTR.
627_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
628
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300629class _Precedence(IntEnum):
630 """Precedence table that originated from python grammar."""
631
632 TUPLE = auto()
633 YIELD = auto() # 'yield', 'yield from'
634 TEST = auto() # 'if'-'else', 'lambda'
635 OR = auto() # 'or'
636 AND = auto() # 'and'
637 NOT = auto() # 'not'
638 CMP = auto() # '<', '>', '==', '>=', '<=', '!=',
639 # 'in', 'not in', 'is', 'is not'
640 EXPR = auto()
641 BOR = EXPR # '|'
642 BXOR = auto() # '^'
643 BAND = auto() # '&'
644 SHIFT = auto() # '<<', '>>'
645 ARITH = auto() # '+', '-'
646 TERM = auto() # '*', '@', '/', '%', '//'
647 FACTOR = auto() # unary '+', '-', '~'
648 POWER = auto() # '**'
649 AWAIT = auto() # 'await'
650 ATOM = auto()
651
652 def next(self):
653 try:
654 return self.__class__(self + 1)
655 except ValueError:
656 return self
657
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000658class _Unparser(NodeVisitor):
659 """Methods in this class recursively traverse an AST and
660 output source code for the abstract syntax; original formatting
661 is disregarded."""
662
663 def __init__(self):
664 self._source = []
665 self._buffer = []
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300666 self._precedences = {}
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300667 self._type_ignores = {}
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000668 self._indent = 0
669
670 def interleave(self, inter, f, seq):
671 """Call f on each item in seq, calling inter() in between."""
672 seq = iter(seq)
673 try:
674 f(next(seq))
675 except StopIteration:
676 pass
677 else:
678 for x in seq:
679 inter()
680 f(x)
681
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +0300682 def items_view(self, traverser, items):
683 """Traverse and separate the given *items* with a comma and append it to
684 the buffer. If *items* is a single item sequence, a trailing comma
685 will be added."""
686 if len(items) == 1:
687 traverser(items[0])
688 self.write(",")
689 else:
690 self.interleave(lambda: self.write(", "), traverser, items)
691
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300692 def maybe_newline(self):
693 """Adds a newline if it isn't the start of generated source"""
694 if self._source:
695 self.write("\n")
696
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000697 def fill(self, text=""):
698 """Indent a piece of text and append it, according to the current
699 indentation level"""
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300700 self.maybe_newline()
701 self.write(" " * self._indent + text)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000702
703 def write(self, text):
704 """Append a piece of text"""
705 self._source.append(text)
706
707 def buffer_writer(self, text):
708 self._buffer.append(text)
709
710 @property
711 def buffer(self):
712 value = "".join(self._buffer)
713 self._buffer.clear()
714 return value
715
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000716 @contextmanager
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300717 def block(self, *, extra = None):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000718 """A context manager for preparing the source for blocks. It adds
719 the character':', increases the indentation on enter and decreases
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300720 the indentation on exit. If *extra* is given, it will be directly
721 appended after the colon character.
722 """
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000723 self.write(":")
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300724 if extra:
725 self.write(extra)
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000726 self._indent += 1
727 yield
728 self._indent -= 1
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000729
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300730 @contextmanager
731 def delimit(self, start, end):
732 """A context manager for preparing the source for expressions. It adds
733 *start* to the buffer and enters, after exit it adds *end*."""
734
735 self.write(start)
736 yield
737 self.write(end)
738
739 def delimit_if(self, start, end, condition):
740 if condition:
741 return self.delimit(start, end)
742 else:
743 return nullcontext()
744
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300745 def require_parens(self, precedence, node):
746 """Shortcut to adding precedence related parens"""
747 return self.delimit_if("(", ")", self.get_precedence(node) > precedence)
748
749 def get_precedence(self, node):
750 return self._precedences.get(node, _Precedence.TEST)
751
752 def set_precedence(self, precedence, *nodes):
753 for node in nodes:
754 self._precedences[node] = precedence
755
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300756 def get_raw_docstring(self, node):
757 """If a docstring node is found in the body of the *node* parameter,
758 return that docstring node, None otherwise.
759
760 Logic mirrored from ``_PyAST_GetDocString``."""
761 if not isinstance(
762 node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)
763 ) or len(node.body) < 1:
764 return None
765 node = node.body[0]
766 if not isinstance(node, Expr):
767 return None
768 node = node.value
769 if isinstance(node, Constant) and isinstance(node.value, str):
770 return node
771
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300772 def get_type_comment(self, node):
773 comment = self._type_ignores.get(node.lineno) or node.type_comment
774 if comment is not None:
775 return f" # type: {comment}"
776
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000777 def traverse(self, node):
778 if isinstance(node, list):
779 for item in node:
780 self.traverse(item)
781 else:
782 super().visit(node)
783
784 def visit(self, node):
785 """Outputs a source code string that, if converted back to an ast
786 (using ast.parse) will generate an AST equivalent to *node*"""
787 self._source = []
788 self.traverse(node)
789 return "".join(self._source)
790
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300791 def _write_docstring_and_traverse_body(self, node):
792 if (docstring := self.get_raw_docstring(node)):
793 self._write_docstring(docstring)
794 self.traverse(node.body[1:])
795 else:
796 self.traverse(node.body)
797
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000798 def visit_Module(self, node):
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300799 self._type_ignores = {
800 ignore.lineno: f"ignore{ignore.tag}"
801 for ignore in node.type_ignores
802 }
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300803 self._write_docstring_and_traverse_body(node)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300804 self._type_ignores.clear()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000805
Batuhan Taşkaya5b66ec12020-03-15 22:56:57 +0300806 def visit_FunctionType(self, node):
807 with self.delimit("(", ")"):
808 self.interleave(
809 lambda: self.write(", "), self.traverse, node.argtypes
810 )
811
812 self.write(" -> ")
813 self.traverse(node.returns)
814
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000815 def visit_Expr(self, node):
816 self.fill()
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300817 self.set_precedence(_Precedence.YIELD, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000818 self.traverse(node.value)
819
820 def visit_NamedExpr(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300821 with self.require_parens(_Precedence.TUPLE, node):
822 self.set_precedence(_Precedence.ATOM, node.target, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300823 self.traverse(node.target)
824 self.write(" := ")
825 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000826
827 def visit_Import(self, node):
828 self.fill("import ")
829 self.interleave(lambda: self.write(", "), self.traverse, node.names)
830
831 def visit_ImportFrom(self, node):
832 self.fill("from ")
833 self.write("." * node.level)
834 if node.module:
835 self.write(node.module)
836 self.write(" import ")
837 self.interleave(lambda: self.write(", "), self.traverse, node.names)
838
839 def visit_Assign(self, node):
840 self.fill()
841 for target in node.targets:
842 self.traverse(target)
843 self.write(" = ")
844 self.traverse(node.value)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300845 if type_comment := self.get_type_comment(node):
846 self.write(type_comment)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000847
848 def visit_AugAssign(self, node):
849 self.fill()
850 self.traverse(node.target)
851 self.write(" " + self.binop[node.op.__class__.__name__] + "= ")
852 self.traverse(node.value)
853
854 def visit_AnnAssign(self, node):
855 self.fill()
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300856 with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)):
857 self.traverse(node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000858 self.write(": ")
859 self.traverse(node.annotation)
860 if node.value:
861 self.write(" = ")
862 self.traverse(node.value)
863
864 def visit_Return(self, node):
865 self.fill("return")
866 if node.value:
867 self.write(" ")
868 self.traverse(node.value)
869
870 def visit_Pass(self, node):
871 self.fill("pass")
872
873 def visit_Break(self, node):
874 self.fill("break")
875
876 def visit_Continue(self, node):
877 self.fill("continue")
878
879 def visit_Delete(self, node):
880 self.fill("del ")
881 self.interleave(lambda: self.write(", "), self.traverse, node.targets)
882
883 def visit_Assert(self, node):
884 self.fill("assert ")
885 self.traverse(node.test)
886 if node.msg:
887 self.write(", ")
888 self.traverse(node.msg)
889
890 def visit_Global(self, node):
891 self.fill("global ")
892 self.interleave(lambda: self.write(", "), self.write, node.names)
893
894 def visit_Nonlocal(self, node):
895 self.fill("nonlocal ")
896 self.interleave(lambda: self.write(", "), self.write, node.names)
897
898 def visit_Await(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300899 with self.require_parens(_Precedence.AWAIT, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300900 self.write("await")
901 if node.value:
902 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300903 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300904 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000905
906 def visit_Yield(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300907 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300908 self.write("yield")
909 if node.value:
910 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300911 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300912 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000913
914 def visit_YieldFrom(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300915 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300916 self.write("yield from ")
917 if not node.value:
918 raise ValueError("Node can't be used without a value attribute.")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300919 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300920 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000921
922 def visit_Raise(self, node):
923 self.fill("raise")
924 if not node.exc:
925 if node.cause:
926 raise ValueError(f"Node can't use cause without an exception.")
927 return
928 self.write(" ")
929 self.traverse(node.exc)
930 if node.cause:
931 self.write(" from ")
932 self.traverse(node.cause)
933
934 def visit_Try(self, node):
935 self.fill("try")
936 with self.block():
937 self.traverse(node.body)
938 for ex in node.handlers:
939 self.traverse(ex)
940 if node.orelse:
941 self.fill("else")
942 with self.block():
943 self.traverse(node.orelse)
944 if node.finalbody:
945 self.fill("finally")
946 with self.block():
947 self.traverse(node.finalbody)
948
949 def visit_ExceptHandler(self, node):
950 self.fill("except")
951 if node.type:
952 self.write(" ")
953 self.traverse(node.type)
954 if node.name:
955 self.write(" as ")
956 self.write(node.name)
957 with self.block():
958 self.traverse(node.body)
959
960 def visit_ClassDef(self, node):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300961 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000962 for deco in node.decorator_list:
963 self.fill("@")
964 self.traverse(deco)
965 self.fill("class " + node.name)
Batuhan Taskaya25160cd2020-05-17 00:53:25 +0300966 with self.delimit_if("(", ")", condition = node.bases or node.keywords):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300967 comma = False
968 for e in node.bases:
969 if comma:
970 self.write(", ")
971 else:
972 comma = True
973 self.traverse(e)
974 for e in node.keywords:
975 if comma:
976 self.write(", ")
977 else:
978 comma = True
979 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000980
981 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300982 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000983
984 def visit_FunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300985 self._function_helper(node, "def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000986
987 def visit_AsyncFunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300988 self._function_helper(node, "async def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000989
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300990 def _function_helper(self, node, fill_suffix):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300991 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000992 for deco in node.decorator_list:
993 self.fill("@")
994 self.traverse(deco)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300995 def_str = fill_suffix + " " + node.name
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000996 self.fill(def_str)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300997 with self.delimit("(", ")"):
998 self.traverse(node.args)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000999 if node.returns:
1000 self.write(" -> ")
1001 self.traverse(node.returns)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001002 with self.block(extra=self.get_type_comment(node)):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001003 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001004
1005 def visit_For(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001006 self._for_helper("for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001007
1008 def visit_AsyncFor(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001009 self._for_helper("async for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001010
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001011 def _for_helper(self, fill, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001012 self.fill(fill)
1013 self.traverse(node.target)
1014 self.write(" in ")
1015 self.traverse(node.iter)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001016 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001017 self.traverse(node.body)
1018 if node.orelse:
1019 self.fill("else")
1020 with self.block():
1021 self.traverse(node.orelse)
1022
1023 def visit_If(self, node):
1024 self.fill("if ")
1025 self.traverse(node.test)
1026 with self.block():
1027 self.traverse(node.body)
1028 # collapse nested ifs into equivalent elifs.
1029 while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If):
1030 node = node.orelse[0]
1031 self.fill("elif ")
1032 self.traverse(node.test)
1033 with self.block():
1034 self.traverse(node.body)
1035 # final else
1036 if node.orelse:
1037 self.fill("else")
1038 with self.block():
1039 self.traverse(node.orelse)
1040
1041 def visit_While(self, node):
1042 self.fill("while ")
1043 self.traverse(node.test)
1044 with self.block():
1045 self.traverse(node.body)
1046 if node.orelse:
1047 self.fill("else")
1048 with self.block():
1049 self.traverse(node.orelse)
1050
1051 def visit_With(self, node):
1052 self.fill("with ")
1053 self.interleave(lambda: self.write(", "), self.traverse, node.items)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001054 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001055 self.traverse(node.body)
1056
1057 def visit_AsyncWith(self, node):
1058 self.fill("async with ")
1059 self.interleave(lambda: self.write(", "), self.traverse, node.items)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001060 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001061 self.traverse(node.body)
1062
1063 def visit_JoinedStr(self, node):
1064 self.write("f")
1065 self._fstring_JoinedStr(node, self.buffer_writer)
1066 self.write(repr(self.buffer))
1067
1068 def visit_FormattedValue(self, node):
1069 self.write("f")
1070 self._fstring_FormattedValue(node, self.buffer_writer)
1071 self.write(repr(self.buffer))
1072
1073 def _fstring_JoinedStr(self, node, write):
1074 for value in node.values:
1075 meth = getattr(self, "_fstring_" + type(value).__name__)
1076 meth(value, write)
1077
1078 def _fstring_Constant(self, node, write):
1079 if not isinstance(node.value, str):
1080 raise ValueError("Constants inside JoinedStr should be a string.")
1081 value = node.value.replace("{", "{{").replace("}", "}}")
1082 write(value)
1083
1084 def _fstring_FormattedValue(self, node, write):
1085 write("{")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001086 unparser = type(self)()
1087 unparser.set_precedence(_Precedence.TEST.next(), node.value)
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +03001088 expr = unparser.visit(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001089 if expr.startswith("{"):
1090 write(" ") # Separate pair of opening brackets as "{ {"
1091 write(expr)
1092 if node.conversion != -1:
1093 conversion = chr(node.conversion)
1094 if conversion not in "sra":
1095 raise ValueError("Unknown f-string conversion.")
1096 write(f"!{conversion}")
1097 if node.format_spec:
1098 write(":")
1099 meth = getattr(self, "_fstring_" + type(node.format_spec).__name__)
1100 meth(node.format_spec, write)
1101 write("}")
1102
1103 def visit_Name(self, node):
1104 self.write(node.id)
1105
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001106 def _write_docstring(self, node):
CyberSaxosTiGERd71a6492020-05-18 21:41:35 +03001107 def esc_char(c):
1108 if c in ("\n", "\t"):
1109 # In the AST form, we don't know the author's intentation
1110 # about how this should be displayed. We'll only escape
1111 # \n and \t, because they are more likely to be unescaped
1112 # in the source
1113 return c
1114 return c.encode('unicode_escape').decode('ascii')
1115
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001116 self.fill()
1117 if node.kind == "u":
1118 self.write("u")
1119
Batuhan Taskayae966af72020-05-17 01:49:07 +03001120 value = node.value
1121 if value:
1122 # Preserve quotes in the docstring by escaping them
CyberSaxosTiGERd71a6492020-05-18 21:41:35 +03001123 value = "".join(map(esc_char, value))
Batuhan Taskayae966af72020-05-17 01:49:07 +03001124 if value[-1] == '"':
1125 value = value.replace('"', '\\"', -1)
CyberSaxosTiGERd71a6492020-05-18 21:41:35 +03001126 value = value.replace('"""', '""\\"')
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001127
1128 self.write(f'"""{value}"""')
1129
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001130 def _write_constant(self, value):
1131 if isinstance(value, (float, complex)):
1132 # Substitute overflowing decimal literal for AST infinities.
1133 self.write(repr(value).replace("inf", _INFSTR))
1134 else:
1135 self.write(repr(value))
1136
1137 def visit_Constant(self, node):
1138 value = node.value
1139 if isinstance(value, tuple):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001140 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001141 self.items_view(self._write_constant, value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001142 elif value is ...:
1143 self.write("...")
1144 else:
1145 if node.kind == "u":
1146 self.write("u")
1147 self._write_constant(node.value)
1148
1149 def visit_List(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001150 with self.delimit("[", "]"):
1151 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001152
1153 def visit_ListComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001154 with self.delimit("[", "]"):
1155 self.traverse(node.elt)
1156 for gen in node.generators:
1157 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001158
1159 def visit_GeneratorExp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001160 with self.delimit("(", ")"):
1161 self.traverse(node.elt)
1162 for gen in node.generators:
1163 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001164
1165 def visit_SetComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001166 with self.delimit("{", "}"):
1167 self.traverse(node.elt)
1168 for gen in node.generators:
1169 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001170
1171 def visit_DictComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001172 with self.delimit("{", "}"):
1173 self.traverse(node.key)
1174 self.write(": ")
1175 self.traverse(node.value)
1176 for gen in node.generators:
1177 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001178
1179 def visit_comprehension(self, node):
1180 if node.is_async:
1181 self.write(" async for ")
1182 else:
1183 self.write(" for ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001184 self.set_precedence(_Precedence.TUPLE, node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001185 self.traverse(node.target)
1186 self.write(" in ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001187 self.set_precedence(_Precedence.TEST.next(), node.iter, *node.ifs)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001188 self.traverse(node.iter)
1189 for if_clause in node.ifs:
1190 self.write(" if ")
1191 self.traverse(if_clause)
1192
1193 def visit_IfExp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001194 with self.require_parens(_Precedence.TEST, node):
1195 self.set_precedence(_Precedence.TEST.next(), node.body, node.test)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001196 self.traverse(node.body)
1197 self.write(" if ")
1198 self.traverse(node.test)
1199 self.write(" else ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001200 self.set_precedence(_Precedence.TEST, node.orelse)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001201 self.traverse(node.orelse)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001202
1203 def visit_Set(self, node):
1204 if not node.elts:
Shantanu01508dc2020-04-16 03:10:12 -07001205 raise ValueError("Set node should have at least one item")
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001206 with self.delimit("{", "}"):
1207 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001208
1209 def visit_Dict(self, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001210 def write_key_value_pair(k, v):
1211 self.traverse(k)
1212 self.write(": ")
1213 self.traverse(v)
1214
1215 def write_item(item):
1216 k, v = item
1217 if k is None:
1218 # for dictionary unpacking operator in dicts {**{'y': 2}}
1219 # see PEP 448 for details
1220 self.write("**")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001221 self.set_precedence(_Precedence.EXPR, v)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001222 self.traverse(v)
1223 else:
1224 write_key_value_pair(k, v)
1225
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001226 with self.delimit("{", "}"):
1227 self.interleave(
1228 lambda: self.write(", "), write_item, zip(node.keys, node.values)
1229 )
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001230
1231 def visit_Tuple(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001232 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001233 self.items_view(self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001234
1235 unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001236 unop_precedence = {
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001237 "not": _Precedence.NOT,
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001238 "~": _Precedence.FACTOR,
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001239 "+": _Precedence.FACTOR,
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001240 "-": _Precedence.FACTOR,
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001241 }
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001242
1243 def visit_UnaryOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001244 operator = self.unop[node.op.__class__.__name__]
1245 operator_precedence = self.unop_precedence[operator]
1246 with self.require_parens(operator_precedence, node):
1247 self.write(operator)
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001248 # factor prefixes (+, -, ~) shouldn't be seperated
1249 # from the value they belong, (e.g: +1 instead of + 1)
1250 if operator_precedence is not _Precedence.FACTOR:
1251 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001252 self.set_precedence(operator_precedence, node.operand)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001253 self.traverse(node.operand)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001254
1255 binop = {
1256 "Add": "+",
1257 "Sub": "-",
1258 "Mult": "*",
1259 "MatMult": "@",
1260 "Div": "/",
1261 "Mod": "%",
1262 "LShift": "<<",
1263 "RShift": ">>",
1264 "BitOr": "|",
1265 "BitXor": "^",
1266 "BitAnd": "&",
1267 "FloorDiv": "//",
1268 "Pow": "**",
1269 }
1270
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001271 binop_precedence = {
1272 "+": _Precedence.ARITH,
1273 "-": _Precedence.ARITH,
1274 "*": _Precedence.TERM,
1275 "@": _Precedence.TERM,
1276 "/": _Precedence.TERM,
1277 "%": _Precedence.TERM,
1278 "<<": _Precedence.SHIFT,
1279 ">>": _Precedence.SHIFT,
1280 "|": _Precedence.BOR,
1281 "^": _Precedence.BXOR,
1282 "&": _Precedence.BAND,
1283 "//": _Precedence.TERM,
1284 "**": _Precedence.POWER,
1285 }
1286
1287 binop_rassoc = frozenset(("**",))
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001288 def visit_BinOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001289 operator = self.binop[node.op.__class__.__name__]
1290 operator_precedence = self.binop_precedence[operator]
1291 with self.require_parens(operator_precedence, node):
1292 if operator in self.binop_rassoc:
1293 left_precedence = operator_precedence.next()
1294 right_precedence = operator_precedence
1295 else:
1296 left_precedence = operator_precedence
1297 right_precedence = operator_precedence.next()
1298
1299 self.set_precedence(left_precedence, node.left)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001300 self.traverse(node.left)
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001301 self.write(f" {operator} ")
1302 self.set_precedence(right_precedence, node.right)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001303 self.traverse(node.right)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001304
1305 cmpops = {
1306 "Eq": "==",
1307 "NotEq": "!=",
1308 "Lt": "<",
1309 "LtE": "<=",
1310 "Gt": ">",
1311 "GtE": ">=",
1312 "Is": "is",
1313 "IsNot": "is not",
1314 "In": "in",
1315 "NotIn": "not in",
1316 }
1317
1318 def visit_Compare(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001319 with self.require_parens(_Precedence.CMP, node):
1320 self.set_precedence(_Precedence.CMP.next(), node.left, *node.comparators)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001321 self.traverse(node.left)
1322 for o, e in zip(node.ops, node.comparators):
1323 self.write(" " + self.cmpops[o.__class__.__name__] + " ")
1324 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001325
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001326 boolops = {"And": "and", "Or": "or"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001327 boolop_precedence = {"and": _Precedence.AND, "or": _Precedence.OR}
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001328
1329 def visit_BoolOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001330 operator = self.boolops[node.op.__class__.__name__]
1331 operator_precedence = self.boolop_precedence[operator]
1332
1333 def increasing_level_traverse(node):
1334 nonlocal operator_precedence
1335 operator_precedence = operator_precedence.next()
1336 self.set_precedence(operator_precedence, node)
1337 self.traverse(node)
1338
1339 with self.require_parens(operator_precedence, node):
1340 s = f" {operator} "
1341 self.interleave(lambda: self.write(s), increasing_level_traverse, node.values)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001342
1343 def visit_Attribute(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001344 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001345 self.traverse(node.value)
1346 # Special case: 3.__abs__() is a syntax error, so if node.value
1347 # is an integer literal then we need to either parenthesize
1348 # it or add an extra space to get 3 .__abs__().
1349 if isinstance(node.value, Constant) and isinstance(node.value.value, int):
1350 self.write(" ")
1351 self.write(".")
1352 self.write(node.attr)
1353
1354 def visit_Call(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001355 self.set_precedence(_Precedence.ATOM, node.func)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001356 self.traverse(node.func)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001357 with self.delimit("(", ")"):
1358 comma = False
1359 for e in node.args:
1360 if comma:
1361 self.write(", ")
1362 else:
1363 comma = True
1364 self.traverse(e)
1365 for e in node.keywords:
1366 if comma:
1367 self.write(", ")
1368 else:
1369 comma = True
1370 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001371
1372 def visit_Subscript(self, node):
Batuhan Taskayac102a142020-05-18 23:48:49 +03001373 def is_simple_tuple(slice_value):
1374 # when unparsing a non-empty tuple, the parantheses can be safely
1375 # omitted if there aren't any elements that explicitly requires
1376 # parantheses (such as starred expressions).
1377 return (
1378 isinstance(slice_value, Tuple)
1379 and slice_value.elts
1380 and not any(isinstance(elt, Starred) for elt in slice_value.elts)
1381 )
1382
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001383 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001384 self.traverse(node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001385 with self.delimit("[", "]"):
Batuhan Taskayac102a142020-05-18 23:48:49 +03001386 if is_simple_tuple(node.slice):
Serhiy Storchaka13d52c22020-03-10 18:52:34 +02001387 self.items_view(self.traverse, node.slice.elts)
Serhiy Storchakac4928fc2020-03-07 17:25:32 +02001388 else:
1389 self.traverse(node.slice)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001390
1391 def visit_Starred(self, node):
1392 self.write("*")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001393 self.set_precedence(_Precedence.EXPR, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001394 self.traverse(node.value)
1395
1396 def visit_Ellipsis(self, node):
1397 self.write("...")
1398
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001399 def visit_Slice(self, node):
1400 if node.lower:
1401 self.traverse(node.lower)
1402 self.write(":")
1403 if node.upper:
1404 self.traverse(node.upper)
1405 if node.step:
1406 self.write(":")
1407 self.traverse(node.step)
1408
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001409 def visit_arg(self, node):
1410 self.write(node.arg)
1411 if node.annotation:
1412 self.write(": ")
1413 self.traverse(node.annotation)
1414
1415 def visit_arguments(self, node):
1416 first = True
1417 # normal arguments
1418 all_args = node.posonlyargs + node.args
1419 defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults
1420 for index, elements in enumerate(zip(all_args, defaults), 1):
1421 a, d = elements
1422 if first:
1423 first = False
1424 else:
1425 self.write(", ")
1426 self.traverse(a)
1427 if d:
1428 self.write("=")
1429 self.traverse(d)
1430 if index == len(node.posonlyargs):
1431 self.write(", /")
1432
1433 # varargs, or bare '*' if no varargs but keyword-only arguments present
1434 if node.vararg or node.kwonlyargs:
1435 if first:
1436 first = False
1437 else:
1438 self.write(", ")
1439 self.write("*")
1440 if node.vararg:
1441 self.write(node.vararg.arg)
1442 if node.vararg.annotation:
1443 self.write(": ")
1444 self.traverse(node.vararg.annotation)
1445
1446 # keyword-only arguments
1447 if node.kwonlyargs:
1448 for a, d in zip(node.kwonlyargs, node.kw_defaults):
Batuhan Taşkayaa322f502019-12-16 15:26:58 +03001449 self.write(", ")
1450 self.traverse(a)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001451 if d:
1452 self.write("=")
1453 self.traverse(d)
1454
1455 # kwargs
1456 if node.kwarg:
1457 if first:
1458 first = False
1459 else:
1460 self.write(", ")
1461 self.write("**" + node.kwarg.arg)
1462 if node.kwarg.annotation:
1463 self.write(": ")
1464 self.traverse(node.kwarg.annotation)
1465
1466 def visit_keyword(self, node):
1467 if node.arg is None:
1468 self.write("**")
1469 else:
1470 self.write(node.arg)
1471 self.write("=")
1472 self.traverse(node.value)
1473
1474 def visit_Lambda(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001475 with self.require_parens(_Precedence.TEST, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001476 self.write("lambda ")
1477 self.traverse(node.args)
1478 self.write(": ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001479 self.set_precedence(_Precedence.TEST, node.body)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001480 self.traverse(node.body)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001481
1482 def visit_alias(self, node):
1483 self.write(node.name)
1484 if node.asname:
1485 self.write(" as " + node.asname)
1486
1487 def visit_withitem(self, node):
1488 self.traverse(node.context_expr)
1489 if node.optional_vars:
1490 self.write(" as ")
1491 self.traverse(node.optional_vars)
1492
1493def unparse(ast_obj):
1494 unparser = _Unparser()
1495 return unparser.visit(ast_obj)
1496
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001497
1498def main():
1499 import argparse
1500
1501 parser = argparse.ArgumentParser(prog='python -m ast')
1502 parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?',
1503 default='-',
1504 help='the file to parse; defaults to stdin')
1505 parser.add_argument('-m', '--mode', default='exec',
1506 choices=('exec', 'single', 'eval', 'func_type'),
1507 help='specify what kind of code must be parsed')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001508 parser.add_argument('--no-type-comments', default=True, action='store_false',
1509 help="don't add information about type comments")
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001510 parser.add_argument('-a', '--include-attributes', action='store_true',
1511 help='include attributes such as line numbers and '
1512 'column offsets')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001513 parser.add_argument('-i', '--indent', type=int, default=3,
1514 help='indentation of nodes (number of spaces)')
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001515 args = parser.parse_args()
1516
1517 with args.infile as infile:
1518 source = infile.read()
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001519 tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
1520 print(dump(tree, include_attributes=args.include_attributes, indent=args.indent))
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001521
1522if __name__ == '__main__':
1523 main()