blob: 52e51b485877478972e23e76ed3b74145d2ff65e [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)
183 if value is not None:
184 setattr(new_node, attr, value)
Georg Brandl0c77a822008-06-10 16:37:50 +0000185 return new_node
186
187
188def fix_missing_locations(node):
189 """
190 When you compile a node tree with compile(), the compiler expects lineno and
191 col_offset attributes for every node that supports them. This is rather
192 tedious to fill in for generated nodes, so this helper adds these attributes
193 recursively where not already set, by setting them to the values of the
194 parent node. It works recursively starting at *node*.
195 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000196 def _fix(node, lineno, col_offset, end_lineno, end_col_offset):
Georg Brandl0c77a822008-06-10 16:37:50 +0000197 if 'lineno' in node._attributes:
198 if not hasattr(node, 'lineno'):
199 node.lineno = lineno
200 else:
201 lineno = node.lineno
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000202 if 'end_lineno' in node._attributes:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200203 if getattr(node, 'end_lineno', None) is None:
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000204 node.end_lineno = end_lineno
205 else:
206 end_lineno = node.end_lineno
Georg Brandl0c77a822008-06-10 16:37:50 +0000207 if 'col_offset' in node._attributes:
208 if not hasattr(node, 'col_offset'):
209 node.col_offset = col_offset
210 else:
211 col_offset = node.col_offset
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000212 if 'end_col_offset' in node._attributes:
Serhiy Storchakab7e95252020-03-10 00:07:47 +0200213 if getattr(node, 'end_col_offset', None) is None:
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000214 node.end_col_offset = end_col_offset
215 else:
216 end_col_offset = node.end_col_offset
Georg Brandl0c77a822008-06-10 16:37:50 +0000217 for child in iter_child_nodes(node):
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000218 _fix(child, lineno, col_offset, end_lineno, end_col_offset)
219 _fix(node, 1, 0, 1, 0)
Georg Brandl0c77a822008-06-10 16:37:50 +0000220 return node
221
222
223def increment_lineno(node, n=1):
224 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000225 Increment the line number and end line number of each node in the tree
226 starting at *node* by *n*. This is useful to "move code" to a different
227 location in a file.
Georg Brandl0c77a822008-06-10 16:37:50 +0000228 """
Georg Brandl0c77a822008-06-10 16:37:50 +0000229 for child in walk(node):
230 if 'lineno' in child._attributes:
231 child.lineno = getattr(child, 'lineno', 0) + n
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000232 if 'end_lineno' in child._attributes:
233 child.end_lineno = getattr(child, 'end_lineno', 0) + n
Georg Brandl0c77a822008-06-10 16:37:50 +0000234 return node
235
236
237def iter_fields(node):
238 """
239 Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
240 that is present on *node*.
241 """
242 for field in node._fields:
243 try:
244 yield field, getattr(node, field)
245 except AttributeError:
246 pass
247
248
249def iter_child_nodes(node):
250 """
251 Yield all direct child nodes of *node*, that is, all fields that are nodes
252 and all items of fields that are lists of nodes.
253 """
254 for name, field in iter_fields(node):
255 if isinstance(field, AST):
256 yield field
257 elif isinstance(field, list):
258 for item in field:
259 if isinstance(item, AST):
260 yield item
261
262
263def get_docstring(node, clean=True):
264 """
265 Return the docstring for the given node or None if no docstring can
266 be found. If the node provided does not have docstrings a TypeError
267 will be raised.
Matthias Bussonnier41cea702017-02-23 22:44:19 -0800268
269 If *clean* is `True`, all tabs are expanded to spaces and any whitespace
270 that can be uniformly removed from the second line onwards is removed.
Georg Brandl0c77a822008-06-10 16:37:50 +0000271 """
Yury Selivanov2f07a662015-07-23 08:54:35 +0300272 if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
Georg Brandl0c77a822008-06-10 16:37:50 +0000273 raise TypeError("%r can't have docstrings" % node.__class__.__name__)
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300274 if not(node.body and isinstance(node.body[0], Expr)):
Serhiy Storchaka73cbe7a2018-05-29 12:04:55 +0300275 return None
276 node = node.body[0].value
277 if isinstance(node, Str):
278 text = node.s
279 elif isinstance(node, Constant) and isinstance(node.value, str):
280 text = node.value
281 else:
282 return None
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300283 if clean:
Victor Stinnerf2c1aa12016-01-26 00:40:57 +0100284 import inspect
285 text = inspect.cleandoc(text)
286 return text
Georg Brandl0c77a822008-06-10 16:37:50 +0000287
288
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000289def _splitlines_no_ff(source):
290 """Split a string into lines ignoring form feed and other chars.
291
292 This mimics how the Python parser splits source code.
293 """
294 idx = 0
295 lines = []
296 next_line = ''
297 while idx < len(source):
298 c = source[idx]
299 next_line += c
300 idx += 1
301 # Keep \r\n together
302 if c == '\r' and idx < len(source) and source[idx] == '\n':
303 next_line += '\n'
304 idx += 1
305 if c in '\r\n':
306 lines.append(next_line)
307 next_line = ''
308
309 if next_line:
310 lines.append(next_line)
311 return lines
312
313
314def _pad_whitespace(source):
mpheathfbeba8f2020-02-14 04:32:09 +1000315 r"""Replace all chars except '\f\t' in a line with spaces."""
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000316 result = ''
317 for c in source:
318 if c in '\f\t':
319 result += c
320 else:
321 result += ' '
322 return result
323
324
325def get_source_segment(source, node, *, padded=False):
326 """Get source code segment of the *source* that generated *node*.
327
328 If some location information (`lineno`, `end_lineno`, `col_offset`,
329 or `end_col_offset`) is missing, return None.
330
331 If *padded* is `True`, the first line of a multi-line statement will
332 be padded with spaces to match its original position.
333 """
334 try:
Irit Katriele6578a22020-05-18 19:14:12 +0100335 if node.end_lineno is None or node.end_col_offset is None:
336 return None
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000337 lineno = node.lineno - 1
338 end_lineno = node.end_lineno - 1
339 col_offset = node.col_offset
340 end_col_offset = node.end_col_offset
341 except AttributeError:
342 return None
343
344 lines = _splitlines_no_ff(source)
345 if end_lineno == lineno:
346 return lines[lineno].encode()[col_offset:end_col_offset].decode()
347
348 if padded:
349 padding = _pad_whitespace(lines[lineno].encode()[:col_offset].decode())
350 else:
351 padding = ''
352
353 first = padding + lines[lineno].encode()[col_offset:].decode()
354 last = lines[end_lineno].encode()[:end_col_offset].decode()
355 lines = lines[lineno+1:end_lineno]
356
357 lines.insert(0, first)
358 lines.append(last)
359 return ''.join(lines)
360
361
Georg Brandl0c77a822008-06-10 16:37:50 +0000362def walk(node):
363 """
Georg Brandl619e7ba2011-01-09 07:38:51 +0000364 Recursively yield all descendant nodes in the tree starting at *node*
365 (including *node* itself), in no specified order. This is useful if you
366 only want to modify nodes in place and don't care about the context.
Georg Brandl0c77a822008-06-10 16:37:50 +0000367 """
368 from collections import deque
369 todo = deque([node])
370 while todo:
371 node = todo.popleft()
372 todo.extend(iter_child_nodes(node))
373 yield node
374
375
376class NodeVisitor(object):
377 """
378 A node visitor base class that walks the abstract syntax tree and calls a
379 visitor function for every node found. This function may return a value
380 which is forwarded by the `visit` method.
381
382 This class is meant to be subclassed, with the subclass adding visitor
383 methods.
384
385 Per default the visitor functions for the nodes are ``'visit_'`` +
386 class name of the node. So a `TryFinally` node visit function would
387 be `visit_TryFinally`. This behavior can be changed by overriding
388 the `visit` method. If no visitor function exists for a node
389 (return value `None`) the `generic_visit` visitor is used instead.
390
391 Don't use the `NodeVisitor` if you want to apply changes to nodes during
392 traversing. For this a special visitor exists (`NodeTransformer`) that
393 allows modifications.
394 """
395
396 def visit(self, node):
397 """Visit a node."""
398 method = 'visit_' + node.__class__.__name__
399 visitor = getattr(self, method, self.generic_visit)
400 return visitor(node)
401
402 def generic_visit(self, node):
403 """Called if no explicit visitor function exists for a node."""
404 for field, value in iter_fields(node):
405 if isinstance(value, list):
406 for item in value:
407 if isinstance(item, AST):
408 self.visit(item)
409 elif isinstance(value, AST):
410 self.visit(value)
411
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300412 def visit_Constant(self, node):
413 value = node.value
414 type_name = _const_node_type_names.get(type(value))
415 if type_name is None:
416 for cls, name in _const_node_type_names.items():
417 if isinstance(value, cls):
418 type_name = name
419 break
420 if type_name is not None:
421 method = 'visit_' + type_name
422 try:
423 visitor = getattr(self, method)
424 except AttributeError:
425 pass
426 else:
427 import warnings
428 warnings.warn(f"{method} is deprecated; add visit_Constant",
429 DeprecationWarning, 2)
430 return visitor(node)
431 return self.generic_visit(node)
432
Georg Brandl0c77a822008-06-10 16:37:50 +0000433
434class NodeTransformer(NodeVisitor):
435 """
436 A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
437 allows modification of nodes.
438
439 The `NodeTransformer` will walk the AST and use the return value of the
440 visitor methods to replace or remove the old node. If the return value of
441 the visitor method is ``None``, the node will be removed from its location,
442 otherwise it is replaced with the return value. The return value may be the
443 original node in which case no replacement takes place.
444
445 Here is an example transformer that rewrites all occurrences of name lookups
446 (``foo``) to ``data['foo']``::
447
448 class RewriteName(NodeTransformer):
449
450 def visit_Name(self, node):
Pablo Galindoc00c86b2020-03-12 00:48:19 +0000451 return Subscript(
Georg Brandl0c77a822008-06-10 16:37:50 +0000452 value=Name(id='data', ctx=Load()),
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200453 slice=Constant(value=node.id),
Georg Brandl0c77a822008-06-10 16:37:50 +0000454 ctx=node.ctx
Pablo Galindoc00c86b2020-03-12 00:48:19 +0000455 )
Georg Brandl0c77a822008-06-10 16:37:50 +0000456
457 Keep in mind that if the node you're operating on has child nodes you must
458 either transform the child nodes yourself or call the :meth:`generic_visit`
459 method for the node first.
460
461 For nodes that were part of a collection of statements (that applies to all
462 statement nodes), the visitor may also return a list of nodes rather than
463 just a single node.
464
465 Usually you use the transformer like this::
466
467 node = YourTransformer().visit(node)
468 """
469
470 def generic_visit(self, node):
471 for field, old_value in iter_fields(node):
Georg Brandl0c77a822008-06-10 16:37:50 +0000472 if isinstance(old_value, list):
473 new_values = []
474 for value in old_value:
475 if isinstance(value, AST):
476 value = self.visit(value)
477 if value is None:
478 continue
479 elif not isinstance(value, AST):
480 new_values.extend(value)
481 continue
482 new_values.append(value)
483 old_value[:] = new_values
484 elif isinstance(old_value, AST):
485 new_node = self.visit(old_value)
486 if new_node is None:
487 delattr(node, field)
488 else:
489 setattr(node, field, new_node)
490 return node
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300491
492
493# The following code is for backward compatibility.
494# It will be removed in future.
495
496def _getter(self):
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200497 """Deprecated. Use value instead."""
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300498 return self.value
499
500def _setter(self, value):
501 self.value = value
502
503Constant.n = property(_getter, _setter)
504Constant.s = property(_getter, _setter)
505
506class _ABC(type):
507
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200508 def __init__(cls, *args):
509 cls.__doc__ = """Deprecated AST node class. Use ast.Constant instead"""
510
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300511 def __instancecheck__(cls, inst):
512 if not isinstance(inst, Constant):
513 return False
514 if cls in _const_types:
515 try:
516 value = inst.value
517 except AttributeError:
518 return False
519 else:
Anthony Sottile74176222019-01-18 11:30:28 -0800520 return (
521 isinstance(value, _const_types[cls]) and
522 not isinstance(value, _const_types_not.get(cls, ()))
523 )
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300524 return type.__instancecheck__(cls, inst)
525
526def _new(cls, *args, **kwargs):
527 if cls in _const_types:
528 return Constant(*args, **kwargs)
529 return Constant.__new__(cls, *args, **kwargs)
530
531class Num(Constant, metaclass=_ABC):
532 _fields = ('n',)
533 __new__ = _new
534
535class Str(Constant, metaclass=_ABC):
536 _fields = ('s',)
537 __new__ = _new
538
539class Bytes(Constant, metaclass=_ABC):
540 _fields = ('s',)
541 __new__ = _new
542
543class NameConstant(Constant, metaclass=_ABC):
544 __new__ = _new
545
546class Ellipsis(Constant, metaclass=_ABC):
547 _fields = ()
548
549 def __new__(cls, *args, **kwargs):
550 if cls is Ellipsis:
551 return Constant(..., *args, **kwargs)
552 return Constant.__new__(cls, *args, **kwargs)
553
554_const_types = {
555 Num: (int, float, complex),
556 Str: (str,),
557 Bytes: (bytes,),
558 NameConstant: (type(None), bool),
559 Ellipsis: (type(...),),
560}
Anthony Sottile74176222019-01-18 11:30:28 -0800561_const_types_not = {
562 Num: (bool,),
563}
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200564
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300565_const_node_type_names = {
566 bool: 'NameConstant', # should be before int
567 type(None): 'NameConstant',
568 int: 'Num',
569 float: 'Num',
570 complex: 'Num',
571 str: 'Str',
572 bytes: 'Bytes',
573 type(...): 'Ellipsis',
574}
Serhiy Storchaka832e8642019-09-09 23:36:13 +0300575
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200576class slice(AST):
577 """Deprecated AST node class."""
578
579class Index(slice):
580 """Deprecated AST node class. Use the index value directly instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200581 def __new__(cls, value, **kwargs):
582 return value
583
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200584class ExtSlice(slice):
585 """Deprecated AST node class. Use ast.Tuple instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200586 def __new__(cls, dims=(), **kwargs):
587 return Tuple(list(dims), Load(), **kwargs)
588
589def _dims_getter(self):
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200590 """Deprecated. Use elts instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200591 return self.elts
592
593def _dims_setter(self, value):
594 self.elts = value
595
596Tuple.dims = property(_dims_getter, _dims_setter)
597
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200598class Suite(mod):
599 """Deprecated AST node class. Unused in Python 3."""
600
601class AugLoad(expr_context):
602 """Deprecated AST node class. Unused in Python 3."""
603
604class AugStore(expr_context):
605 """Deprecated AST node class. Unused in Python 3."""
606
607class Param(expr_context):
608 """Deprecated AST node class. Unused in Python 3."""
609
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200610
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000611# Large float and imaginary literals get turned into infinities in the AST.
612# We unparse those infinities to INFSTR.
613_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
614
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300615class _Precedence(IntEnum):
616 """Precedence table that originated from python grammar."""
617
618 TUPLE = auto()
619 YIELD = auto() # 'yield', 'yield from'
620 TEST = auto() # 'if'-'else', 'lambda'
621 OR = auto() # 'or'
622 AND = auto() # 'and'
623 NOT = auto() # 'not'
624 CMP = auto() # '<', '>', '==', '>=', '<=', '!=',
625 # 'in', 'not in', 'is', 'is not'
626 EXPR = auto()
627 BOR = EXPR # '|'
628 BXOR = auto() # '^'
629 BAND = auto() # '&'
630 SHIFT = auto() # '<<', '>>'
631 ARITH = auto() # '+', '-'
632 TERM = auto() # '*', '@', '/', '%', '//'
633 FACTOR = auto() # unary '+', '-', '~'
634 POWER = auto() # '**'
635 AWAIT = auto() # 'await'
636 ATOM = auto()
637
638 def next(self):
639 try:
640 return self.__class__(self + 1)
641 except ValueError:
642 return self
643
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000644class _Unparser(NodeVisitor):
645 """Methods in this class recursively traverse an AST and
646 output source code for the abstract syntax; original formatting
647 is disregarded."""
648
649 def __init__(self):
650 self._source = []
651 self._buffer = []
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300652 self._precedences = {}
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300653 self._type_ignores = {}
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000654 self._indent = 0
655
656 def interleave(self, inter, f, seq):
657 """Call f on each item in seq, calling inter() in between."""
658 seq = iter(seq)
659 try:
660 f(next(seq))
661 except StopIteration:
662 pass
663 else:
664 for x in seq:
665 inter()
666 f(x)
667
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +0300668 def items_view(self, traverser, items):
669 """Traverse and separate the given *items* with a comma and append it to
670 the buffer. If *items* is a single item sequence, a trailing comma
671 will be added."""
672 if len(items) == 1:
673 traverser(items[0])
674 self.write(",")
675 else:
676 self.interleave(lambda: self.write(", "), traverser, items)
677
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300678 def maybe_newline(self):
679 """Adds a newline if it isn't the start of generated source"""
680 if self._source:
681 self.write("\n")
682
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000683 def fill(self, text=""):
684 """Indent a piece of text and append it, according to the current
685 indentation level"""
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300686 self.maybe_newline()
687 self.write(" " * self._indent + text)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000688
689 def write(self, text):
690 """Append a piece of text"""
691 self._source.append(text)
692
693 def buffer_writer(self, text):
694 self._buffer.append(text)
695
696 @property
697 def buffer(self):
698 value = "".join(self._buffer)
699 self._buffer.clear()
700 return value
701
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000702 @contextmanager
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300703 def block(self, *, extra = None):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000704 """A context manager for preparing the source for blocks. It adds
705 the character':', increases the indentation on enter and decreases
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300706 the indentation on exit. If *extra* is given, it will be directly
707 appended after the colon character.
708 """
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000709 self.write(":")
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300710 if extra:
711 self.write(extra)
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000712 self._indent += 1
713 yield
714 self._indent -= 1
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000715
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300716 @contextmanager
717 def delimit(self, start, end):
718 """A context manager for preparing the source for expressions. It adds
719 *start* to the buffer and enters, after exit it adds *end*."""
720
721 self.write(start)
722 yield
723 self.write(end)
724
725 def delimit_if(self, start, end, condition):
726 if condition:
727 return self.delimit(start, end)
728 else:
729 return nullcontext()
730
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300731 def require_parens(self, precedence, node):
732 """Shortcut to adding precedence related parens"""
733 return self.delimit_if("(", ")", self.get_precedence(node) > precedence)
734
735 def get_precedence(self, node):
736 return self._precedences.get(node, _Precedence.TEST)
737
738 def set_precedence(self, precedence, *nodes):
739 for node in nodes:
740 self._precedences[node] = precedence
741
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300742 def get_raw_docstring(self, node):
743 """If a docstring node is found in the body of the *node* parameter,
744 return that docstring node, None otherwise.
745
746 Logic mirrored from ``_PyAST_GetDocString``."""
747 if not isinstance(
748 node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)
749 ) or len(node.body) < 1:
750 return None
751 node = node.body[0]
752 if not isinstance(node, Expr):
753 return None
754 node = node.value
755 if isinstance(node, Constant) and isinstance(node.value, str):
756 return node
757
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300758 def get_type_comment(self, node):
759 comment = self._type_ignores.get(node.lineno) or node.type_comment
760 if comment is not None:
761 return f" # type: {comment}"
762
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000763 def traverse(self, node):
764 if isinstance(node, list):
765 for item in node:
766 self.traverse(item)
767 else:
768 super().visit(node)
769
770 def visit(self, node):
771 """Outputs a source code string that, if converted back to an ast
772 (using ast.parse) will generate an AST equivalent to *node*"""
773 self._source = []
774 self.traverse(node)
775 return "".join(self._source)
776
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300777 def _write_docstring_and_traverse_body(self, node):
778 if (docstring := self.get_raw_docstring(node)):
779 self._write_docstring(docstring)
780 self.traverse(node.body[1:])
781 else:
782 self.traverse(node.body)
783
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000784 def visit_Module(self, node):
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300785 self._type_ignores = {
786 ignore.lineno: f"ignore{ignore.tag}"
787 for ignore in node.type_ignores
788 }
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300789 self._write_docstring_and_traverse_body(node)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300790 self._type_ignores.clear()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000791
Batuhan Taşkaya5b66ec12020-03-15 22:56:57 +0300792 def visit_FunctionType(self, node):
793 with self.delimit("(", ")"):
794 self.interleave(
795 lambda: self.write(", "), self.traverse, node.argtypes
796 )
797
798 self.write(" -> ")
799 self.traverse(node.returns)
800
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000801 def visit_Expr(self, node):
802 self.fill()
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300803 self.set_precedence(_Precedence.YIELD, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000804 self.traverse(node.value)
805
806 def visit_NamedExpr(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300807 with self.require_parens(_Precedence.TUPLE, node):
808 self.set_precedence(_Precedence.ATOM, node.target, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300809 self.traverse(node.target)
810 self.write(" := ")
811 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000812
813 def visit_Import(self, node):
814 self.fill("import ")
815 self.interleave(lambda: self.write(", "), self.traverse, node.names)
816
817 def visit_ImportFrom(self, node):
818 self.fill("from ")
819 self.write("." * node.level)
820 if node.module:
821 self.write(node.module)
822 self.write(" import ")
823 self.interleave(lambda: self.write(", "), self.traverse, node.names)
824
825 def visit_Assign(self, node):
826 self.fill()
827 for target in node.targets:
828 self.traverse(target)
829 self.write(" = ")
830 self.traverse(node.value)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300831 if type_comment := self.get_type_comment(node):
832 self.write(type_comment)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000833
834 def visit_AugAssign(self, node):
835 self.fill()
836 self.traverse(node.target)
837 self.write(" " + self.binop[node.op.__class__.__name__] + "= ")
838 self.traverse(node.value)
839
840 def visit_AnnAssign(self, node):
841 self.fill()
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300842 with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)):
843 self.traverse(node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000844 self.write(": ")
845 self.traverse(node.annotation)
846 if node.value:
847 self.write(" = ")
848 self.traverse(node.value)
849
850 def visit_Return(self, node):
851 self.fill("return")
852 if node.value:
853 self.write(" ")
854 self.traverse(node.value)
855
856 def visit_Pass(self, node):
857 self.fill("pass")
858
859 def visit_Break(self, node):
860 self.fill("break")
861
862 def visit_Continue(self, node):
863 self.fill("continue")
864
865 def visit_Delete(self, node):
866 self.fill("del ")
867 self.interleave(lambda: self.write(", "), self.traverse, node.targets)
868
869 def visit_Assert(self, node):
870 self.fill("assert ")
871 self.traverse(node.test)
872 if node.msg:
873 self.write(", ")
874 self.traverse(node.msg)
875
876 def visit_Global(self, node):
877 self.fill("global ")
878 self.interleave(lambda: self.write(", "), self.write, node.names)
879
880 def visit_Nonlocal(self, node):
881 self.fill("nonlocal ")
882 self.interleave(lambda: self.write(", "), self.write, node.names)
883
884 def visit_Await(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300885 with self.require_parens(_Precedence.AWAIT, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300886 self.write("await")
887 if node.value:
888 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300889 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300890 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000891
892 def visit_Yield(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300893 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300894 self.write("yield")
895 if node.value:
896 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300897 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300898 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000899
900 def visit_YieldFrom(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300901 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300902 self.write("yield from ")
903 if not node.value:
904 raise ValueError("Node can't be used without a value attribute.")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300905 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300906 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000907
908 def visit_Raise(self, node):
909 self.fill("raise")
910 if not node.exc:
911 if node.cause:
912 raise ValueError(f"Node can't use cause without an exception.")
913 return
914 self.write(" ")
915 self.traverse(node.exc)
916 if node.cause:
917 self.write(" from ")
918 self.traverse(node.cause)
919
920 def visit_Try(self, node):
921 self.fill("try")
922 with self.block():
923 self.traverse(node.body)
924 for ex in node.handlers:
925 self.traverse(ex)
926 if node.orelse:
927 self.fill("else")
928 with self.block():
929 self.traverse(node.orelse)
930 if node.finalbody:
931 self.fill("finally")
932 with self.block():
933 self.traverse(node.finalbody)
934
935 def visit_ExceptHandler(self, node):
936 self.fill("except")
937 if node.type:
938 self.write(" ")
939 self.traverse(node.type)
940 if node.name:
941 self.write(" as ")
942 self.write(node.name)
943 with self.block():
944 self.traverse(node.body)
945
946 def visit_ClassDef(self, node):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300947 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000948 for deco in node.decorator_list:
949 self.fill("@")
950 self.traverse(deco)
951 self.fill("class " + node.name)
Batuhan Taskaya25160cd2020-05-17 00:53:25 +0300952 with self.delimit_if("(", ")", condition = node.bases or node.keywords):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300953 comma = False
954 for e in node.bases:
955 if comma:
956 self.write(", ")
957 else:
958 comma = True
959 self.traverse(e)
960 for e in node.keywords:
961 if comma:
962 self.write(", ")
963 else:
964 comma = True
965 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000966
967 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300968 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000969
970 def visit_FunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300971 self._function_helper(node, "def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000972
973 def visit_AsyncFunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300974 self._function_helper(node, "async def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000975
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300976 def _function_helper(self, node, fill_suffix):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300977 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000978 for deco in node.decorator_list:
979 self.fill("@")
980 self.traverse(deco)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300981 def_str = fill_suffix + " " + node.name
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000982 self.fill(def_str)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300983 with self.delimit("(", ")"):
984 self.traverse(node.args)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000985 if node.returns:
986 self.write(" -> ")
987 self.traverse(node.returns)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300988 with self.block(extra=self.get_type_comment(node)):
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_For(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300992 self._for_helper("for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000993
994 def visit_AsyncFor(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300995 self._for_helper("async for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000996
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300997 def _for_helper(self, fill, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000998 self.fill(fill)
999 self.traverse(node.target)
1000 self.write(" in ")
1001 self.traverse(node.iter)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001002 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001003 self.traverse(node.body)
1004 if node.orelse:
1005 self.fill("else")
1006 with self.block():
1007 self.traverse(node.orelse)
1008
1009 def visit_If(self, node):
1010 self.fill("if ")
1011 self.traverse(node.test)
1012 with self.block():
1013 self.traverse(node.body)
1014 # collapse nested ifs into equivalent elifs.
1015 while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If):
1016 node = node.orelse[0]
1017 self.fill("elif ")
1018 self.traverse(node.test)
1019 with self.block():
1020 self.traverse(node.body)
1021 # final else
1022 if node.orelse:
1023 self.fill("else")
1024 with self.block():
1025 self.traverse(node.orelse)
1026
1027 def visit_While(self, node):
1028 self.fill("while ")
1029 self.traverse(node.test)
1030 with self.block():
1031 self.traverse(node.body)
1032 if node.orelse:
1033 self.fill("else")
1034 with self.block():
1035 self.traverse(node.orelse)
1036
1037 def visit_With(self, node):
1038 self.fill("with ")
1039 self.interleave(lambda: self.write(", "), self.traverse, node.items)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001040 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001041 self.traverse(node.body)
1042
1043 def visit_AsyncWith(self, node):
1044 self.fill("async with ")
1045 self.interleave(lambda: self.write(", "), self.traverse, node.items)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001046 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001047 self.traverse(node.body)
1048
1049 def visit_JoinedStr(self, node):
1050 self.write("f")
1051 self._fstring_JoinedStr(node, self.buffer_writer)
1052 self.write(repr(self.buffer))
1053
1054 def visit_FormattedValue(self, node):
1055 self.write("f")
1056 self._fstring_FormattedValue(node, self.buffer_writer)
1057 self.write(repr(self.buffer))
1058
1059 def _fstring_JoinedStr(self, node, write):
1060 for value in node.values:
1061 meth = getattr(self, "_fstring_" + type(value).__name__)
1062 meth(value, write)
1063
1064 def _fstring_Constant(self, node, write):
1065 if not isinstance(node.value, str):
1066 raise ValueError("Constants inside JoinedStr should be a string.")
1067 value = node.value.replace("{", "{{").replace("}", "}}")
1068 write(value)
1069
1070 def _fstring_FormattedValue(self, node, write):
1071 write("{")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001072 unparser = type(self)()
1073 unparser.set_precedence(_Precedence.TEST.next(), node.value)
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +03001074 expr = unparser.visit(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001075 if expr.startswith("{"):
1076 write(" ") # Separate pair of opening brackets as "{ {"
1077 write(expr)
1078 if node.conversion != -1:
1079 conversion = chr(node.conversion)
1080 if conversion not in "sra":
1081 raise ValueError("Unknown f-string conversion.")
1082 write(f"!{conversion}")
1083 if node.format_spec:
1084 write(":")
1085 meth = getattr(self, "_fstring_" + type(node.format_spec).__name__)
1086 meth(node.format_spec, write)
1087 write("}")
1088
1089 def visit_Name(self, node):
1090 self.write(node.id)
1091
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001092 def _write_docstring(self, node):
CyberSaxosTiGERd71a6492020-05-18 21:41:35 +03001093 def esc_char(c):
1094 if c in ("\n", "\t"):
1095 # In the AST form, we don't know the author's intentation
1096 # about how this should be displayed. We'll only escape
1097 # \n and \t, because they are more likely to be unescaped
1098 # in the source
1099 return c
1100 return c.encode('unicode_escape').decode('ascii')
1101
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001102 self.fill()
1103 if node.kind == "u":
1104 self.write("u")
1105
Batuhan Taskayae966af72020-05-17 01:49:07 +03001106 value = node.value
1107 if value:
1108 # Preserve quotes in the docstring by escaping them
CyberSaxosTiGERd71a6492020-05-18 21:41:35 +03001109 value = "".join(map(esc_char, value))
Batuhan Taskayae966af72020-05-17 01:49:07 +03001110 if value[-1] == '"':
1111 value = value.replace('"', '\\"', -1)
CyberSaxosTiGERd71a6492020-05-18 21:41:35 +03001112 value = value.replace('"""', '""\\"')
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001113
1114 self.write(f'"""{value}"""')
1115
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001116 def _write_constant(self, value):
1117 if isinstance(value, (float, complex)):
1118 # Substitute overflowing decimal literal for AST infinities.
1119 self.write(repr(value).replace("inf", _INFSTR))
1120 else:
1121 self.write(repr(value))
1122
1123 def visit_Constant(self, node):
1124 value = node.value
1125 if isinstance(value, tuple):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001126 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001127 self.items_view(self._write_constant, value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001128 elif value is ...:
1129 self.write("...")
1130 else:
1131 if node.kind == "u":
1132 self.write("u")
1133 self._write_constant(node.value)
1134
1135 def visit_List(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001136 with self.delimit("[", "]"):
1137 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001138
1139 def visit_ListComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001140 with self.delimit("[", "]"):
1141 self.traverse(node.elt)
1142 for gen in node.generators:
1143 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001144
1145 def visit_GeneratorExp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001146 with self.delimit("(", ")"):
1147 self.traverse(node.elt)
1148 for gen in node.generators:
1149 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001150
1151 def visit_SetComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001152 with self.delimit("{", "}"):
1153 self.traverse(node.elt)
1154 for gen in node.generators:
1155 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001156
1157 def visit_DictComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001158 with self.delimit("{", "}"):
1159 self.traverse(node.key)
1160 self.write(": ")
1161 self.traverse(node.value)
1162 for gen in node.generators:
1163 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001164
1165 def visit_comprehension(self, node):
1166 if node.is_async:
1167 self.write(" async for ")
1168 else:
1169 self.write(" for ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001170 self.set_precedence(_Precedence.TUPLE, node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001171 self.traverse(node.target)
1172 self.write(" in ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001173 self.set_precedence(_Precedence.TEST.next(), node.iter, *node.ifs)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001174 self.traverse(node.iter)
1175 for if_clause in node.ifs:
1176 self.write(" if ")
1177 self.traverse(if_clause)
1178
1179 def visit_IfExp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001180 with self.require_parens(_Precedence.TEST, node):
1181 self.set_precedence(_Precedence.TEST.next(), node.body, node.test)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001182 self.traverse(node.body)
1183 self.write(" if ")
1184 self.traverse(node.test)
1185 self.write(" else ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001186 self.set_precedence(_Precedence.TEST, node.orelse)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001187 self.traverse(node.orelse)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001188
1189 def visit_Set(self, node):
1190 if not node.elts:
Shantanu01508dc2020-04-16 03:10:12 -07001191 raise ValueError("Set node should have at least one item")
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001192 with self.delimit("{", "}"):
1193 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001194
1195 def visit_Dict(self, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001196 def write_key_value_pair(k, v):
1197 self.traverse(k)
1198 self.write(": ")
1199 self.traverse(v)
1200
1201 def write_item(item):
1202 k, v = item
1203 if k is None:
1204 # for dictionary unpacking operator in dicts {**{'y': 2}}
1205 # see PEP 448 for details
1206 self.write("**")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001207 self.set_precedence(_Precedence.EXPR, v)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001208 self.traverse(v)
1209 else:
1210 write_key_value_pair(k, v)
1211
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001212 with self.delimit("{", "}"):
1213 self.interleave(
1214 lambda: self.write(", "), write_item, zip(node.keys, node.values)
1215 )
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001216
1217 def visit_Tuple(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001218 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001219 self.items_view(self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001220
1221 unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001222 unop_precedence = {
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001223 "not": _Precedence.NOT,
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001224 "~": _Precedence.FACTOR,
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001225 "+": _Precedence.FACTOR,
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001226 "-": _Precedence.FACTOR,
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001227 }
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001228
1229 def visit_UnaryOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001230 operator = self.unop[node.op.__class__.__name__]
1231 operator_precedence = self.unop_precedence[operator]
1232 with self.require_parens(operator_precedence, node):
1233 self.write(operator)
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001234 # factor prefixes (+, -, ~) shouldn't be seperated
1235 # from the value they belong, (e.g: +1 instead of + 1)
1236 if operator_precedence is not _Precedence.FACTOR:
1237 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001238 self.set_precedence(operator_precedence, node.operand)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001239 self.traverse(node.operand)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001240
1241 binop = {
1242 "Add": "+",
1243 "Sub": "-",
1244 "Mult": "*",
1245 "MatMult": "@",
1246 "Div": "/",
1247 "Mod": "%",
1248 "LShift": "<<",
1249 "RShift": ">>",
1250 "BitOr": "|",
1251 "BitXor": "^",
1252 "BitAnd": "&",
1253 "FloorDiv": "//",
1254 "Pow": "**",
1255 }
1256
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001257 binop_precedence = {
1258 "+": _Precedence.ARITH,
1259 "-": _Precedence.ARITH,
1260 "*": _Precedence.TERM,
1261 "@": _Precedence.TERM,
1262 "/": _Precedence.TERM,
1263 "%": _Precedence.TERM,
1264 "<<": _Precedence.SHIFT,
1265 ">>": _Precedence.SHIFT,
1266 "|": _Precedence.BOR,
1267 "^": _Precedence.BXOR,
1268 "&": _Precedence.BAND,
1269 "//": _Precedence.TERM,
1270 "**": _Precedence.POWER,
1271 }
1272
1273 binop_rassoc = frozenset(("**",))
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001274 def visit_BinOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001275 operator = self.binop[node.op.__class__.__name__]
1276 operator_precedence = self.binop_precedence[operator]
1277 with self.require_parens(operator_precedence, node):
1278 if operator in self.binop_rassoc:
1279 left_precedence = operator_precedence.next()
1280 right_precedence = operator_precedence
1281 else:
1282 left_precedence = operator_precedence
1283 right_precedence = operator_precedence.next()
1284
1285 self.set_precedence(left_precedence, node.left)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001286 self.traverse(node.left)
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001287 self.write(f" {operator} ")
1288 self.set_precedence(right_precedence, node.right)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001289 self.traverse(node.right)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001290
1291 cmpops = {
1292 "Eq": "==",
1293 "NotEq": "!=",
1294 "Lt": "<",
1295 "LtE": "<=",
1296 "Gt": ">",
1297 "GtE": ">=",
1298 "Is": "is",
1299 "IsNot": "is not",
1300 "In": "in",
1301 "NotIn": "not in",
1302 }
1303
1304 def visit_Compare(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001305 with self.require_parens(_Precedence.CMP, node):
1306 self.set_precedence(_Precedence.CMP.next(), node.left, *node.comparators)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001307 self.traverse(node.left)
1308 for o, e in zip(node.ops, node.comparators):
1309 self.write(" " + self.cmpops[o.__class__.__name__] + " ")
1310 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001311
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001312 boolops = {"And": "and", "Or": "or"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001313 boolop_precedence = {"and": _Precedence.AND, "or": _Precedence.OR}
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001314
1315 def visit_BoolOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001316 operator = self.boolops[node.op.__class__.__name__]
1317 operator_precedence = self.boolop_precedence[operator]
1318
1319 def increasing_level_traverse(node):
1320 nonlocal operator_precedence
1321 operator_precedence = operator_precedence.next()
1322 self.set_precedence(operator_precedence, node)
1323 self.traverse(node)
1324
1325 with self.require_parens(operator_precedence, node):
1326 s = f" {operator} "
1327 self.interleave(lambda: self.write(s), increasing_level_traverse, node.values)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001328
1329 def visit_Attribute(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001330 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001331 self.traverse(node.value)
1332 # Special case: 3.__abs__() is a syntax error, so if node.value
1333 # is an integer literal then we need to either parenthesize
1334 # it or add an extra space to get 3 .__abs__().
1335 if isinstance(node.value, Constant) and isinstance(node.value.value, int):
1336 self.write(" ")
1337 self.write(".")
1338 self.write(node.attr)
1339
1340 def visit_Call(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001341 self.set_precedence(_Precedence.ATOM, node.func)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001342 self.traverse(node.func)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001343 with self.delimit("(", ")"):
1344 comma = False
1345 for e in node.args:
1346 if comma:
1347 self.write(", ")
1348 else:
1349 comma = True
1350 self.traverse(e)
1351 for e in node.keywords:
1352 if comma:
1353 self.write(", ")
1354 else:
1355 comma = True
1356 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001357
1358 def visit_Subscript(self, node):
Batuhan Taskayac102a142020-05-18 23:48:49 +03001359 def is_simple_tuple(slice_value):
1360 # when unparsing a non-empty tuple, the parantheses can be safely
1361 # omitted if there aren't any elements that explicitly requires
1362 # parantheses (such as starred expressions).
1363 return (
1364 isinstance(slice_value, Tuple)
1365 and slice_value.elts
1366 and not any(isinstance(elt, Starred) for elt in slice_value.elts)
1367 )
1368
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001369 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001370 self.traverse(node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001371 with self.delimit("[", "]"):
Batuhan Taskayac102a142020-05-18 23:48:49 +03001372 if is_simple_tuple(node.slice):
Serhiy Storchaka13d52c22020-03-10 18:52:34 +02001373 self.items_view(self.traverse, node.slice.elts)
Serhiy Storchakac4928fc2020-03-07 17:25:32 +02001374 else:
1375 self.traverse(node.slice)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001376
1377 def visit_Starred(self, node):
1378 self.write("*")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001379 self.set_precedence(_Precedence.EXPR, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001380 self.traverse(node.value)
1381
1382 def visit_Ellipsis(self, node):
1383 self.write("...")
1384
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001385 def visit_Slice(self, node):
1386 if node.lower:
1387 self.traverse(node.lower)
1388 self.write(":")
1389 if node.upper:
1390 self.traverse(node.upper)
1391 if node.step:
1392 self.write(":")
1393 self.traverse(node.step)
1394
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001395 def visit_arg(self, node):
1396 self.write(node.arg)
1397 if node.annotation:
1398 self.write(": ")
1399 self.traverse(node.annotation)
1400
1401 def visit_arguments(self, node):
1402 first = True
1403 # normal arguments
1404 all_args = node.posonlyargs + node.args
1405 defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults
1406 for index, elements in enumerate(zip(all_args, defaults), 1):
1407 a, d = elements
1408 if first:
1409 first = False
1410 else:
1411 self.write(", ")
1412 self.traverse(a)
1413 if d:
1414 self.write("=")
1415 self.traverse(d)
1416 if index == len(node.posonlyargs):
1417 self.write(", /")
1418
1419 # varargs, or bare '*' if no varargs but keyword-only arguments present
1420 if node.vararg or node.kwonlyargs:
1421 if first:
1422 first = False
1423 else:
1424 self.write(", ")
1425 self.write("*")
1426 if node.vararg:
1427 self.write(node.vararg.arg)
1428 if node.vararg.annotation:
1429 self.write(": ")
1430 self.traverse(node.vararg.annotation)
1431
1432 # keyword-only arguments
1433 if node.kwonlyargs:
1434 for a, d in zip(node.kwonlyargs, node.kw_defaults):
Batuhan Taşkayaa322f502019-12-16 15:26:58 +03001435 self.write(", ")
1436 self.traverse(a)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001437 if d:
1438 self.write("=")
1439 self.traverse(d)
1440
1441 # kwargs
1442 if node.kwarg:
1443 if first:
1444 first = False
1445 else:
1446 self.write(", ")
1447 self.write("**" + node.kwarg.arg)
1448 if node.kwarg.annotation:
1449 self.write(": ")
1450 self.traverse(node.kwarg.annotation)
1451
1452 def visit_keyword(self, node):
1453 if node.arg is None:
1454 self.write("**")
1455 else:
1456 self.write(node.arg)
1457 self.write("=")
1458 self.traverse(node.value)
1459
1460 def visit_Lambda(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001461 with self.require_parens(_Precedence.TEST, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001462 self.write("lambda ")
1463 self.traverse(node.args)
1464 self.write(": ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001465 self.set_precedence(_Precedence.TEST, node.body)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001466 self.traverse(node.body)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001467
1468 def visit_alias(self, node):
1469 self.write(node.name)
1470 if node.asname:
1471 self.write(" as " + node.asname)
1472
1473 def visit_withitem(self, node):
1474 self.traverse(node.context_expr)
1475 if node.optional_vars:
1476 self.write(" as ")
1477 self.traverse(node.optional_vars)
1478
1479def unparse(ast_obj):
1480 unparser = _Unparser()
1481 return unparser.visit(ast_obj)
1482
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001483
1484def main():
1485 import argparse
1486
1487 parser = argparse.ArgumentParser(prog='python -m ast')
1488 parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?',
1489 default='-',
1490 help='the file to parse; defaults to stdin')
1491 parser.add_argument('-m', '--mode', default='exec',
1492 choices=('exec', 'single', 'eval', 'func_type'),
1493 help='specify what kind of code must be parsed')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001494 parser.add_argument('--no-type-comments', default=True, action='store_false',
1495 help="don't add information about type comments")
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001496 parser.add_argument('-a', '--include-attributes', action='store_true',
1497 help='include attributes such as line numbers and '
1498 'column offsets')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001499 parser.add_argument('-i', '--indent', type=int, default=3,
1500 help='indentation of nodes (number of spaces)')
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001501 args = parser.parse_args()
1502
1503 with args.infile as infile:
1504 source = infile.read()
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001505 tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
1506 print(dump(tree, include_attributes=args.include_attributes, indent=args.indent))
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001507
1508if __name__ == '__main__':
1509 main()