blob: 6a5b39e270b9b526888d9c2671b56cd93ac43a11 [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):
Rémi Lapeyrec73914a2020-05-24 23:12:57 +0200527 for key in kwargs:
528 if key not in cls._fields:
529 # arbitrary keyword arguments are accepted
530 continue
531 pos = cls._fields.index(key)
532 if pos < len(args):
533 raise TypeError(f"{cls.__name__} got multiple values for argument {key!r}")
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300534 if cls in _const_types:
535 return Constant(*args, **kwargs)
536 return Constant.__new__(cls, *args, **kwargs)
537
538class Num(Constant, metaclass=_ABC):
539 _fields = ('n',)
540 __new__ = _new
541
542class Str(Constant, metaclass=_ABC):
543 _fields = ('s',)
544 __new__ = _new
545
546class Bytes(Constant, metaclass=_ABC):
547 _fields = ('s',)
548 __new__ = _new
549
550class NameConstant(Constant, metaclass=_ABC):
551 __new__ = _new
552
553class Ellipsis(Constant, metaclass=_ABC):
554 _fields = ()
555
556 def __new__(cls, *args, **kwargs):
557 if cls is Ellipsis:
558 return Constant(..., *args, **kwargs)
559 return Constant.__new__(cls, *args, **kwargs)
560
561_const_types = {
562 Num: (int, float, complex),
563 Str: (str,),
564 Bytes: (bytes,),
565 NameConstant: (type(None), bool),
566 Ellipsis: (type(...),),
567}
Anthony Sottile74176222019-01-18 11:30:28 -0800568_const_types_not = {
569 Num: (bool,),
570}
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200571
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300572_const_node_type_names = {
573 bool: 'NameConstant', # should be before int
574 type(None): 'NameConstant',
575 int: 'Num',
576 float: 'Num',
577 complex: 'Num',
578 str: 'Str',
579 bytes: 'Bytes',
580 type(...): 'Ellipsis',
581}
Serhiy Storchaka832e8642019-09-09 23:36:13 +0300582
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200583class slice(AST):
584 """Deprecated AST node class."""
585
586class Index(slice):
587 """Deprecated AST node class. Use the index value directly instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200588 def __new__(cls, value, **kwargs):
589 return value
590
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200591class ExtSlice(slice):
592 """Deprecated AST node class. Use ast.Tuple instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200593 def __new__(cls, dims=(), **kwargs):
594 return Tuple(list(dims), Load(), **kwargs)
595
596def _dims_getter(self):
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200597 """Deprecated. Use elts instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200598 return self.elts
599
600def _dims_setter(self, value):
601 self.elts = value
602
603Tuple.dims = property(_dims_getter, _dims_setter)
604
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200605class Suite(mod):
606 """Deprecated AST node class. Unused in Python 3."""
607
608class AugLoad(expr_context):
609 """Deprecated AST node class. Unused in Python 3."""
610
611class AugStore(expr_context):
612 """Deprecated AST node class. Unused in Python 3."""
613
614class Param(expr_context):
615 """Deprecated AST node class. Unused in Python 3."""
616
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200617
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000618# Large float and imaginary literals get turned into infinities in the AST.
619# We unparse those infinities to INFSTR.
620_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
621
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300622class _Precedence(IntEnum):
623 """Precedence table that originated from python grammar."""
624
625 TUPLE = auto()
626 YIELD = auto() # 'yield', 'yield from'
627 TEST = auto() # 'if'-'else', 'lambda'
628 OR = auto() # 'or'
629 AND = auto() # 'and'
630 NOT = auto() # 'not'
631 CMP = auto() # '<', '>', '==', '>=', '<=', '!=',
632 # 'in', 'not in', 'is', 'is not'
633 EXPR = auto()
634 BOR = EXPR # '|'
635 BXOR = auto() # '^'
636 BAND = auto() # '&'
637 SHIFT = auto() # '<<', '>>'
638 ARITH = auto() # '+', '-'
639 TERM = auto() # '*', '@', '/', '%', '//'
640 FACTOR = auto() # unary '+', '-', '~'
641 POWER = auto() # '**'
642 AWAIT = auto() # 'await'
643 ATOM = auto()
644
645 def next(self):
646 try:
647 return self.__class__(self + 1)
648 except ValueError:
649 return self
650
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000651class _Unparser(NodeVisitor):
652 """Methods in this class recursively traverse an AST and
653 output source code for the abstract syntax; original formatting
654 is disregarded."""
655
656 def __init__(self):
657 self._source = []
658 self._buffer = []
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300659 self._precedences = {}
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300660 self._type_ignores = {}
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000661 self._indent = 0
662
663 def interleave(self, inter, f, seq):
664 """Call f on each item in seq, calling inter() in between."""
665 seq = iter(seq)
666 try:
667 f(next(seq))
668 except StopIteration:
669 pass
670 else:
671 for x in seq:
672 inter()
673 f(x)
674
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +0300675 def items_view(self, traverser, items):
676 """Traverse and separate the given *items* with a comma and append it to
677 the buffer. If *items* is a single item sequence, a trailing comma
678 will be added."""
679 if len(items) == 1:
680 traverser(items[0])
681 self.write(",")
682 else:
683 self.interleave(lambda: self.write(", "), traverser, items)
684
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300685 def maybe_newline(self):
686 """Adds a newline if it isn't the start of generated source"""
687 if self._source:
688 self.write("\n")
689
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000690 def fill(self, text=""):
691 """Indent a piece of text and append it, according to the current
692 indentation level"""
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300693 self.maybe_newline()
694 self.write(" " * self._indent + text)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000695
696 def write(self, text):
697 """Append a piece of text"""
698 self._source.append(text)
699
700 def buffer_writer(self, text):
701 self._buffer.append(text)
702
703 @property
704 def buffer(self):
705 value = "".join(self._buffer)
706 self._buffer.clear()
707 return value
708
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000709 @contextmanager
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300710 def block(self, *, extra = None):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000711 """A context manager for preparing the source for blocks. It adds
712 the character':', increases the indentation on enter and decreases
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300713 the indentation on exit. If *extra* is given, it will be directly
714 appended after the colon character.
715 """
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000716 self.write(":")
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300717 if extra:
718 self.write(extra)
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000719 self._indent += 1
720 yield
721 self._indent -= 1
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000722
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300723 @contextmanager
724 def delimit(self, start, end):
725 """A context manager for preparing the source for expressions. It adds
726 *start* to the buffer and enters, after exit it adds *end*."""
727
728 self.write(start)
729 yield
730 self.write(end)
731
732 def delimit_if(self, start, end, condition):
733 if condition:
734 return self.delimit(start, end)
735 else:
736 return nullcontext()
737
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300738 def require_parens(self, precedence, node):
739 """Shortcut to adding precedence related parens"""
740 return self.delimit_if("(", ")", self.get_precedence(node) > precedence)
741
742 def get_precedence(self, node):
743 return self._precedences.get(node, _Precedence.TEST)
744
745 def set_precedence(self, precedence, *nodes):
746 for node in nodes:
747 self._precedences[node] = precedence
748
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300749 def get_raw_docstring(self, node):
750 """If a docstring node is found in the body of the *node* parameter,
751 return that docstring node, None otherwise.
752
753 Logic mirrored from ``_PyAST_GetDocString``."""
754 if not isinstance(
755 node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)
756 ) or len(node.body) < 1:
757 return None
758 node = node.body[0]
759 if not isinstance(node, Expr):
760 return None
761 node = node.value
762 if isinstance(node, Constant) and isinstance(node.value, str):
763 return node
764
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300765 def get_type_comment(self, node):
766 comment = self._type_ignores.get(node.lineno) or node.type_comment
767 if comment is not None:
768 return f" # type: {comment}"
769
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000770 def traverse(self, node):
771 if isinstance(node, list):
772 for item in node:
773 self.traverse(item)
774 else:
775 super().visit(node)
776
777 def visit(self, node):
778 """Outputs a source code string that, if converted back to an ast
779 (using ast.parse) will generate an AST equivalent to *node*"""
780 self._source = []
781 self.traverse(node)
782 return "".join(self._source)
783
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300784 def _write_docstring_and_traverse_body(self, node):
785 if (docstring := self.get_raw_docstring(node)):
786 self._write_docstring(docstring)
787 self.traverse(node.body[1:])
788 else:
789 self.traverse(node.body)
790
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000791 def visit_Module(self, node):
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300792 self._type_ignores = {
793 ignore.lineno: f"ignore{ignore.tag}"
794 for ignore in node.type_ignores
795 }
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300796 self._write_docstring_and_traverse_body(node)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300797 self._type_ignores.clear()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000798
Batuhan Taşkaya5b66ec12020-03-15 22:56:57 +0300799 def visit_FunctionType(self, node):
800 with self.delimit("(", ")"):
801 self.interleave(
802 lambda: self.write(", "), self.traverse, node.argtypes
803 )
804
805 self.write(" -> ")
806 self.traverse(node.returns)
807
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000808 def visit_Expr(self, node):
809 self.fill()
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300810 self.set_precedence(_Precedence.YIELD, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000811 self.traverse(node.value)
812
813 def visit_NamedExpr(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300814 with self.require_parens(_Precedence.TUPLE, node):
815 self.set_precedence(_Precedence.ATOM, node.target, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300816 self.traverse(node.target)
817 self.write(" := ")
818 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000819
820 def visit_Import(self, node):
821 self.fill("import ")
822 self.interleave(lambda: self.write(", "), self.traverse, node.names)
823
824 def visit_ImportFrom(self, node):
825 self.fill("from ")
826 self.write("." * node.level)
827 if node.module:
828 self.write(node.module)
829 self.write(" import ")
830 self.interleave(lambda: self.write(", "), self.traverse, node.names)
831
832 def visit_Assign(self, node):
833 self.fill()
834 for target in node.targets:
835 self.traverse(target)
836 self.write(" = ")
837 self.traverse(node.value)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300838 if type_comment := self.get_type_comment(node):
839 self.write(type_comment)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000840
841 def visit_AugAssign(self, node):
842 self.fill()
843 self.traverse(node.target)
844 self.write(" " + self.binop[node.op.__class__.__name__] + "= ")
845 self.traverse(node.value)
846
847 def visit_AnnAssign(self, node):
848 self.fill()
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300849 with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)):
850 self.traverse(node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000851 self.write(": ")
852 self.traverse(node.annotation)
853 if node.value:
854 self.write(" = ")
855 self.traverse(node.value)
856
857 def visit_Return(self, node):
858 self.fill("return")
859 if node.value:
860 self.write(" ")
861 self.traverse(node.value)
862
863 def visit_Pass(self, node):
864 self.fill("pass")
865
866 def visit_Break(self, node):
867 self.fill("break")
868
869 def visit_Continue(self, node):
870 self.fill("continue")
871
872 def visit_Delete(self, node):
873 self.fill("del ")
874 self.interleave(lambda: self.write(", "), self.traverse, node.targets)
875
876 def visit_Assert(self, node):
877 self.fill("assert ")
878 self.traverse(node.test)
879 if node.msg:
880 self.write(", ")
881 self.traverse(node.msg)
882
883 def visit_Global(self, node):
884 self.fill("global ")
885 self.interleave(lambda: self.write(", "), self.write, node.names)
886
887 def visit_Nonlocal(self, node):
888 self.fill("nonlocal ")
889 self.interleave(lambda: self.write(", "), self.write, node.names)
890
891 def visit_Await(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300892 with self.require_parens(_Precedence.AWAIT, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300893 self.write("await")
894 if node.value:
895 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300896 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300897 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000898
899 def visit_Yield(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300900 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300901 self.write("yield")
902 if node.value:
903 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300904 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300905 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000906
907 def visit_YieldFrom(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300908 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300909 self.write("yield from ")
910 if not node.value:
911 raise ValueError("Node can't be used without a value attribute.")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300912 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300913 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000914
915 def visit_Raise(self, node):
916 self.fill("raise")
917 if not node.exc:
918 if node.cause:
919 raise ValueError(f"Node can't use cause without an exception.")
920 return
921 self.write(" ")
922 self.traverse(node.exc)
923 if node.cause:
924 self.write(" from ")
925 self.traverse(node.cause)
926
927 def visit_Try(self, node):
928 self.fill("try")
929 with self.block():
930 self.traverse(node.body)
931 for ex in node.handlers:
932 self.traverse(ex)
933 if node.orelse:
934 self.fill("else")
935 with self.block():
936 self.traverse(node.orelse)
937 if node.finalbody:
938 self.fill("finally")
939 with self.block():
940 self.traverse(node.finalbody)
941
942 def visit_ExceptHandler(self, node):
943 self.fill("except")
944 if node.type:
945 self.write(" ")
946 self.traverse(node.type)
947 if node.name:
948 self.write(" as ")
949 self.write(node.name)
950 with self.block():
951 self.traverse(node.body)
952
953 def visit_ClassDef(self, node):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300954 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000955 for deco in node.decorator_list:
956 self.fill("@")
957 self.traverse(deco)
958 self.fill("class " + node.name)
Batuhan Taskaya25160cd2020-05-17 00:53:25 +0300959 with self.delimit_if("(", ")", condition = node.bases or node.keywords):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300960 comma = False
961 for e in node.bases:
962 if comma:
963 self.write(", ")
964 else:
965 comma = True
966 self.traverse(e)
967 for e in node.keywords:
968 if comma:
969 self.write(", ")
970 else:
971 comma = True
972 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000973
974 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300975 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000976
977 def visit_FunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300978 self._function_helper(node, "def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000979
980 def visit_AsyncFunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300981 self._function_helper(node, "async def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000982
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300983 def _function_helper(self, node, fill_suffix):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300984 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000985 for deco in node.decorator_list:
986 self.fill("@")
987 self.traverse(deco)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300988 def_str = fill_suffix + " " + node.name
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000989 self.fill(def_str)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300990 with self.delimit("(", ")"):
991 self.traverse(node.args)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000992 if node.returns:
993 self.write(" -> ")
994 self.traverse(node.returns)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300995 with self.block(extra=self.get_type_comment(node)):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300996 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000997
998 def visit_For(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300999 self._for_helper("for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001000
1001 def visit_AsyncFor(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001002 self._for_helper("async for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001003
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001004 def _for_helper(self, fill, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001005 self.fill(fill)
1006 self.traverse(node.target)
1007 self.write(" in ")
1008 self.traverse(node.iter)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001009 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001010 self.traverse(node.body)
1011 if node.orelse:
1012 self.fill("else")
1013 with self.block():
1014 self.traverse(node.orelse)
1015
1016 def visit_If(self, node):
1017 self.fill("if ")
1018 self.traverse(node.test)
1019 with self.block():
1020 self.traverse(node.body)
1021 # collapse nested ifs into equivalent elifs.
1022 while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If):
1023 node = node.orelse[0]
1024 self.fill("elif ")
1025 self.traverse(node.test)
1026 with self.block():
1027 self.traverse(node.body)
1028 # final else
1029 if node.orelse:
1030 self.fill("else")
1031 with self.block():
1032 self.traverse(node.orelse)
1033
1034 def visit_While(self, node):
1035 self.fill("while ")
1036 self.traverse(node.test)
1037 with self.block():
1038 self.traverse(node.body)
1039 if node.orelse:
1040 self.fill("else")
1041 with self.block():
1042 self.traverse(node.orelse)
1043
1044 def visit_With(self, node):
1045 self.fill("with ")
1046 self.interleave(lambda: self.write(", "), self.traverse, node.items)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001047 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001048 self.traverse(node.body)
1049
1050 def visit_AsyncWith(self, node):
1051 self.fill("async with ")
1052 self.interleave(lambda: self.write(", "), self.traverse, node.items)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001053 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001054 self.traverse(node.body)
1055
1056 def visit_JoinedStr(self, node):
1057 self.write("f")
1058 self._fstring_JoinedStr(node, self.buffer_writer)
1059 self.write(repr(self.buffer))
1060
1061 def visit_FormattedValue(self, node):
1062 self.write("f")
1063 self._fstring_FormattedValue(node, self.buffer_writer)
1064 self.write(repr(self.buffer))
1065
1066 def _fstring_JoinedStr(self, node, write):
1067 for value in node.values:
1068 meth = getattr(self, "_fstring_" + type(value).__name__)
1069 meth(value, write)
1070
1071 def _fstring_Constant(self, node, write):
1072 if not isinstance(node.value, str):
1073 raise ValueError("Constants inside JoinedStr should be a string.")
1074 value = node.value.replace("{", "{{").replace("}", "}}")
1075 write(value)
1076
1077 def _fstring_FormattedValue(self, node, write):
1078 write("{")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001079 unparser = type(self)()
1080 unparser.set_precedence(_Precedence.TEST.next(), node.value)
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +03001081 expr = unparser.visit(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001082 if expr.startswith("{"):
1083 write(" ") # Separate pair of opening brackets as "{ {"
1084 write(expr)
1085 if node.conversion != -1:
1086 conversion = chr(node.conversion)
1087 if conversion not in "sra":
1088 raise ValueError("Unknown f-string conversion.")
1089 write(f"!{conversion}")
1090 if node.format_spec:
1091 write(":")
1092 meth = getattr(self, "_fstring_" + type(node.format_spec).__name__)
1093 meth(node.format_spec, write)
1094 write("}")
1095
1096 def visit_Name(self, node):
1097 self.write(node.id)
1098
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001099 def _write_docstring(self, node):
CyberSaxosTiGERd71a6492020-05-18 21:41:35 +03001100 def esc_char(c):
1101 if c in ("\n", "\t"):
1102 # In the AST form, we don't know the author's intentation
1103 # about how this should be displayed. We'll only escape
1104 # \n and \t, because they are more likely to be unescaped
1105 # in the source
1106 return c
1107 return c.encode('unicode_escape').decode('ascii')
1108
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001109 self.fill()
1110 if node.kind == "u":
1111 self.write("u")
1112
Batuhan Taskayae966af72020-05-17 01:49:07 +03001113 value = node.value
1114 if value:
1115 # Preserve quotes in the docstring by escaping them
CyberSaxosTiGERd71a6492020-05-18 21:41:35 +03001116 value = "".join(map(esc_char, value))
Batuhan Taskayae966af72020-05-17 01:49:07 +03001117 if value[-1] == '"':
1118 value = value.replace('"', '\\"', -1)
CyberSaxosTiGERd71a6492020-05-18 21:41:35 +03001119 value = value.replace('"""', '""\\"')
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001120
1121 self.write(f'"""{value}"""')
1122
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001123 def _write_constant(self, value):
1124 if isinstance(value, (float, complex)):
1125 # Substitute overflowing decimal literal for AST infinities.
1126 self.write(repr(value).replace("inf", _INFSTR))
1127 else:
1128 self.write(repr(value))
1129
1130 def visit_Constant(self, node):
1131 value = node.value
1132 if isinstance(value, tuple):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001133 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001134 self.items_view(self._write_constant, value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001135 elif value is ...:
1136 self.write("...")
1137 else:
1138 if node.kind == "u":
1139 self.write("u")
1140 self._write_constant(node.value)
1141
1142 def visit_List(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001143 with self.delimit("[", "]"):
1144 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001145
1146 def visit_ListComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001147 with self.delimit("[", "]"):
1148 self.traverse(node.elt)
1149 for gen in node.generators:
1150 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001151
1152 def visit_GeneratorExp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001153 with self.delimit("(", ")"):
1154 self.traverse(node.elt)
1155 for gen in node.generators:
1156 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001157
1158 def visit_SetComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001159 with self.delimit("{", "}"):
1160 self.traverse(node.elt)
1161 for gen in node.generators:
1162 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001163
1164 def visit_DictComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001165 with self.delimit("{", "}"):
1166 self.traverse(node.key)
1167 self.write(": ")
1168 self.traverse(node.value)
1169 for gen in node.generators:
1170 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001171
1172 def visit_comprehension(self, node):
1173 if node.is_async:
1174 self.write(" async for ")
1175 else:
1176 self.write(" for ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001177 self.set_precedence(_Precedence.TUPLE, node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001178 self.traverse(node.target)
1179 self.write(" in ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001180 self.set_precedence(_Precedence.TEST.next(), node.iter, *node.ifs)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001181 self.traverse(node.iter)
1182 for if_clause in node.ifs:
1183 self.write(" if ")
1184 self.traverse(if_clause)
1185
1186 def visit_IfExp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001187 with self.require_parens(_Precedence.TEST, node):
1188 self.set_precedence(_Precedence.TEST.next(), node.body, node.test)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001189 self.traverse(node.body)
1190 self.write(" if ")
1191 self.traverse(node.test)
1192 self.write(" else ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001193 self.set_precedence(_Precedence.TEST, node.orelse)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001194 self.traverse(node.orelse)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001195
1196 def visit_Set(self, node):
1197 if not node.elts:
Shantanu01508dc2020-04-16 03:10:12 -07001198 raise ValueError("Set node should have at least one item")
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001199 with self.delimit("{", "}"):
1200 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001201
1202 def visit_Dict(self, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001203 def write_key_value_pair(k, v):
1204 self.traverse(k)
1205 self.write(": ")
1206 self.traverse(v)
1207
1208 def write_item(item):
1209 k, v = item
1210 if k is None:
1211 # for dictionary unpacking operator in dicts {**{'y': 2}}
1212 # see PEP 448 for details
1213 self.write("**")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001214 self.set_precedence(_Precedence.EXPR, v)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001215 self.traverse(v)
1216 else:
1217 write_key_value_pair(k, v)
1218
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001219 with self.delimit("{", "}"):
1220 self.interleave(
1221 lambda: self.write(", "), write_item, zip(node.keys, node.values)
1222 )
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001223
1224 def visit_Tuple(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001225 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001226 self.items_view(self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001227
1228 unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001229 unop_precedence = {
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001230 "not": _Precedence.NOT,
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001231 "~": _Precedence.FACTOR,
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001232 "+": _Precedence.FACTOR,
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001233 "-": _Precedence.FACTOR,
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001234 }
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001235
1236 def visit_UnaryOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001237 operator = self.unop[node.op.__class__.__name__]
1238 operator_precedence = self.unop_precedence[operator]
1239 with self.require_parens(operator_precedence, node):
1240 self.write(operator)
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001241 # factor prefixes (+, -, ~) shouldn't be seperated
1242 # from the value they belong, (e.g: +1 instead of + 1)
1243 if operator_precedence is not _Precedence.FACTOR:
1244 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001245 self.set_precedence(operator_precedence, node.operand)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001246 self.traverse(node.operand)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001247
1248 binop = {
1249 "Add": "+",
1250 "Sub": "-",
1251 "Mult": "*",
1252 "MatMult": "@",
1253 "Div": "/",
1254 "Mod": "%",
1255 "LShift": "<<",
1256 "RShift": ">>",
1257 "BitOr": "|",
1258 "BitXor": "^",
1259 "BitAnd": "&",
1260 "FloorDiv": "//",
1261 "Pow": "**",
1262 }
1263
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001264 binop_precedence = {
1265 "+": _Precedence.ARITH,
1266 "-": _Precedence.ARITH,
1267 "*": _Precedence.TERM,
1268 "@": _Precedence.TERM,
1269 "/": _Precedence.TERM,
1270 "%": _Precedence.TERM,
1271 "<<": _Precedence.SHIFT,
1272 ">>": _Precedence.SHIFT,
1273 "|": _Precedence.BOR,
1274 "^": _Precedence.BXOR,
1275 "&": _Precedence.BAND,
1276 "//": _Precedence.TERM,
1277 "**": _Precedence.POWER,
1278 }
1279
1280 binop_rassoc = frozenset(("**",))
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001281 def visit_BinOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001282 operator = self.binop[node.op.__class__.__name__]
1283 operator_precedence = self.binop_precedence[operator]
1284 with self.require_parens(operator_precedence, node):
1285 if operator in self.binop_rassoc:
1286 left_precedence = operator_precedence.next()
1287 right_precedence = operator_precedence
1288 else:
1289 left_precedence = operator_precedence
1290 right_precedence = operator_precedence.next()
1291
1292 self.set_precedence(left_precedence, node.left)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001293 self.traverse(node.left)
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001294 self.write(f" {operator} ")
1295 self.set_precedence(right_precedence, node.right)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001296 self.traverse(node.right)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001297
1298 cmpops = {
1299 "Eq": "==",
1300 "NotEq": "!=",
1301 "Lt": "<",
1302 "LtE": "<=",
1303 "Gt": ">",
1304 "GtE": ">=",
1305 "Is": "is",
1306 "IsNot": "is not",
1307 "In": "in",
1308 "NotIn": "not in",
1309 }
1310
1311 def visit_Compare(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001312 with self.require_parens(_Precedence.CMP, node):
1313 self.set_precedence(_Precedence.CMP.next(), node.left, *node.comparators)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001314 self.traverse(node.left)
1315 for o, e in zip(node.ops, node.comparators):
1316 self.write(" " + self.cmpops[o.__class__.__name__] + " ")
1317 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001318
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001319 boolops = {"And": "and", "Or": "or"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001320 boolop_precedence = {"and": _Precedence.AND, "or": _Precedence.OR}
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001321
1322 def visit_BoolOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001323 operator = self.boolops[node.op.__class__.__name__]
1324 operator_precedence = self.boolop_precedence[operator]
1325
1326 def increasing_level_traverse(node):
1327 nonlocal operator_precedence
1328 operator_precedence = operator_precedence.next()
1329 self.set_precedence(operator_precedence, node)
1330 self.traverse(node)
1331
1332 with self.require_parens(operator_precedence, node):
1333 s = f" {operator} "
1334 self.interleave(lambda: self.write(s), increasing_level_traverse, node.values)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001335
1336 def visit_Attribute(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001337 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001338 self.traverse(node.value)
1339 # Special case: 3.__abs__() is a syntax error, so if node.value
1340 # is an integer literal then we need to either parenthesize
1341 # it or add an extra space to get 3 .__abs__().
1342 if isinstance(node.value, Constant) and isinstance(node.value.value, int):
1343 self.write(" ")
1344 self.write(".")
1345 self.write(node.attr)
1346
1347 def visit_Call(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001348 self.set_precedence(_Precedence.ATOM, node.func)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001349 self.traverse(node.func)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001350 with self.delimit("(", ")"):
1351 comma = False
1352 for e in node.args:
1353 if comma:
1354 self.write(", ")
1355 else:
1356 comma = True
1357 self.traverse(e)
1358 for e in node.keywords:
1359 if comma:
1360 self.write(", ")
1361 else:
1362 comma = True
1363 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001364
1365 def visit_Subscript(self, node):
Batuhan Taskayac102a142020-05-18 23:48:49 +03001366 def is_simple_tuple(slice_value):
1367 # when unparsing a non-empty tuple, the parantheses can be safely
1368 # omitted if there aren't any elements that explicitly requires
1369 # parantheses (such as starred expressions).
1370 return (
1371 isinstance(slice_value, Tuple)
1372 and slice_value.elts
1373 and not any(isinstance(elt, Starred) for elt in slice_value.elts)
1374 )
1375
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001376 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001377 self.traverse(node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001378 with self.delimit("[", "]"):
Batuhan Taskayac102a142020-05-18 23:48:49 +03001379 if is_simple_tuple(node.slice):
Serhiy Storchaka13d52c22020-03-10 18:52:34 +02001380 self.items_view(self.traverse, node.slice.elts)
Serhiy Storchakac4928fc2020-03-07 17:25:32 +02001381 else:
1382 self.traverse(node.slice)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001383
1384 def visit_Starred(self, node):
1385 self.write("*")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001386 self.set_precedence(_Precedence.EXPR, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001387 self.traverse(node.value)
1388
1389 def visit_Ellipsis(self, node):
1390 self.write("...")
1391
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001392 def visit_Slice(self, node):
1393 if node.lower:
1394 self.traverse(node.lower)
1395 self.write(":")
1396 if node.upper:
1397 self.traverse(node.upper)
1398 if node.step:
1399 self.write(":")
1400 self.traverse(node.step)
1401
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001402 def visit_arg(self, node):
1403 self.write(node.arg)
1404 if node.annotation:
1405 self.write(": ")
1406 self.traverse(node.annotation)
1407
1408 def visit_arguments(self, node):
1409 first = True
1410 # normal arguments
1411 all_args = node.posonlyargs + node.args
1412 defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults
1413 for index, elements in enumerate(zip(all_args, defaults), 1):
1414 a, d = elements
1415 if first:
1416 first = False
1417 else:
1418 self.write(", ")
1419 self.traverse(a)
1420 if d:
1421 self.write("=")
1422 self.traverse(d)
1423 if index == len(node.posonlyargs):
1424 self.write(", /")
1425
1426 # varargs, or bare '*' if no varargs but keyword-only arguments present
1427 if node.vararg or node.kwonlyargs:
1428 if first:
1429 first = False
1430 else:
1431 self.write(", ")
1432 self.write("*")
1433 if node.vararg:
1434 self.write(node.vararg.arg)
1435 if node.vararg.annotation:
1436 self.write(": ")
1437 self.traverse(node.vararg.annotation)
1438
1439 # keyword-only arguments
1440 if node.kwonlyargs:
1441 for a, d in zip(node.kwonlyargs, node.kw_defaults):
Batuhan Taşkayaa322f502019-12-16 15:26:58 +03001442 self.write(", ")
1443 self.traverse(a)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001444 if d:
1445 self.write("=")
1446 self.traverse(d)
1447
1448 # kwargs
1449 if node.kwarg:
1450 if first:
1451 first = False
1452 else:
1453 self.write(", ")
1454 self.write("**" + node.kwarg.arg)
1455 if node.kwarg.annotation:
1456 self.write(": ")
1457 self.traverse(node.kwarg.annotation)
1458
1459 def visit_keyword(self, node):
1460 if node.arg is None:
1461 self.write("**")
1462 else:
1463 self.write(node.arg)
1464 self.write("=")
1465 self.traverse(node.value)
1466
1467 def visit_Lambda(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001468 with self.require_parens(_Precedence.TEST, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001469 self.write("lambda ")
1470 self.traverse(node.args)
1471 self.write(": ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001472 self.set_precedence(_Precedence.TEST, node.body)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001473 self.traverse(node.body)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001474
1475 def visit_alias(self, node):
1476 self.write(node.name)
1477 if node.asname:
1478 self.write(" as " + node.asname)
1479
1480 def visit_withitem(self, node):
1481 self.traverse(node.context_expr)
1482 if node.optional_vars:
1483 self.write(" as ")
1484 self.traverse(node.optional_vars)
1485
1486def unparse(ast_obj):
1487 unparser = _Unparser()
1488 return unparser.visit(ast_obj)
1489
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001490
1491def main():
1492 import argparse
1493
1494 parser = argparse.ArgumentParser(prog='python -m ast')
1495 parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?',
1496 default='-',
1497 help='the file to parse; defaults to stdin')
1498 parser.add_argument('-m', '--mode', default='exec',
1499 choices=('exec', 'single', 'eval', 'func_type'),
1500 help='specify what kind of code must be parsed')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001501 parser.add_argument('--no-type-comments', default=True, action='store_false',
1502 help="don't add information about type comments")
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001503 parser.add_argument('-a', '--include-attributes', action='store_true',
1504 help='include attributes such as line numbers and '
1505 'column offsets')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001506 parser.add_argument('-i', '--indent', type=int, default=3,
1507 help='indentation of nodes (number of spaces)')
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001508 args = parser.parse_args()
1509
1510 with args.infile as infile:
1511 source = infile.read()
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001512 tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
1513 print(dump(tree, include_attributes=args.include_attributes, indent=args.indent))
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001514
1515if __name__ == '__main__':
1516 main()