blob: 61fbe030a7825236e508d81ce37df6021d634368 [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:
335 lineno = node.lineno - 1
336 end_lineno = node.end_lineno - 1
337 col_offset = node.col_offset
338 end_col_offset = node.end_col_offset
339 except AttributeError:
340 return None
341
342 lines = _splitlines_no_ff(source)
343 if end_lineno == lineno:
344 return lines[lineno].encode()[col_offset:end_col_offset].decode()
345
346 if padded:
347 padding = _pad_whitespace(lines[lineno].encode()[:col_offset].decode())
348 else:
349 padding = ''
350
351 first = padding + lines[lineno].encode()[col_offset:].decode()
352 last = lines[end_lineno].encode()[:end_col_offset].decode()
353 lines = lines[lineno+1:end_lineno]
354
355 lines.insert(0, first)
356 lines.append(last)
357 return ''.join(lines)
358
359
Georg Brandl0c77a822008-06-10 16:37:50 +0000360def walk(node):
361 """
Georg Brandl619e7ba2011-01-09 07:38:51 +0000362 Recursively yield all descendant nodes in the tree starting at *node*
363 (including *node* itself), in no specified order. This is useful if you
364 only want to modify nodes in place and don't care about the context.
Georg Brandl0c77a822008-06-10 16:37:50 +0000365 """
366 from collections import deque
367 todo = deque([node])
368 while todo:
369 node = todo.popleft()
370 todo.extend(iter_child_nodes(node))
371 yield node
372
373
374class NodeVisitor(object):
375 """
376 A node visitor base class that walks the abstract syntax tree and calls a
377 visitor function for every node found. This function may return a value
378 which is forwarded by the `visit` method.
379
380 This class is meant to be subclassed, with the subclass adding visitor
381 methods.
382
383 Per default the visitor functions for the nodes are ``'visit_'`` +
384 class name of the node. So a `TryFinally` node visit function would
385 be `visit_TryFinally`. This behavior can be changed by overriding
386 the `visit` method. If no visitor function exists for a node
387 (return value `None`) the `generic_visit` visitor is used instead.
388
389 Don't use the `NodeVisitor` if you want to apply changes to nodes during
390 traversing. For this a special visitor exists (`NodeTransformer`) that
391 allows modifications.
392 """
393
394 def visit(self, node):
395 """Visit a node."""
396 method = 'visit_' + node.__class__.__name__
397 visitor = getattr(self, method, self.generic_visit)
398 return visitor(node)
399
400 def generic_visit(self, node):
401 """Called if no explicit visitor function exists for a node."""
402 for field, value in iter_fields(node):
403 if isinstance(value, list):
404 for item in value:
405 if isinstance(item, AST):
406 self.visit(item)
407 elif isinstance(value, AST):
408 self.visit(value)
409
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300410 def visit_Constant(self, node):
411 value = node.value
412 type_name = _const_node_type_names.get(type(value))
413 if type_name is None:
414 for cls, name in _const_node_type_names.items():
415 if isinstance(value, cls):
416 type_name = name
417 break
418 if type_name is not None:
419 method = 'visit_' + type_name
420 try:
421 visitor = getattr(self, method)
422 except AttributeError:
423 pass
424 else:
425 import warnings
426 warnings.warn(f"{method} is deprecated; add visit_Constant",
427 DeprecationWarning, 2)
428 return visitor(node)
429 return self.generic_visit(node)
430
Georg Brandl0c77a822008-06-10 16:37:50 +0000431
432class NodeTransformer(NodeVisitor):
433 """
434 A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
435 allows modification of nodes.
436
437 The `NodeTransformer` will walk the AST and use the return value of the
438 visitor methods to replace or remove the old node. If the return value of
439 the visitor method is ``None``, the node will be removed from its location,
440 otherwise it is replaced with the return value. The return value may be the
441 original node in which case no replacement takes place.
442
443 Here is an example transformer that rewrites all occurrences of name lookups
444 (``foo``) to ``data['foo']``::
445
446 class RewriteName(NodeTransformer):
447
448 def visit_Name(self, node):
Pablo Galindoc00c86b2020-03-12 00:48:19 +0000449 return Subscript(
Georg Brandl0c77a822008-06-10 16:37:50 +0000450 value=Name(id='data', ctx=Load()),
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200451 slice=Constant(value=node.id),
Georg Brandl0c77a822008-06-10 16:37:50 +0000452 ctx=node.ctx
Pablo Galindoc00c86b2020-03-12 00:48:19 +0000453 )
Georg Brandl0c77a822008-06-10 16:37:50 +0000454
455 Keep in mind that if the node you're operating on has child nodes you must
456 either transform the child nodes yourself or call the :meth:`generic_visit`
457 method for the node first.
458
459 For nodes that were part of a collection of statements (that applies to all
460 statement nodes), the visitor may also return a list of nodes rather than
461 just a single node.
462
463 Usually you use the transformer like this::
464
465 node = YourTransformer().visit(node)
466 """
467
468 def generic_visit(self, node):
469 for field, old_value in iter_fields(node):
Georg Brandl0c77a822008-06-10 16:37:50 +0000470 if isinstance(old_value, list):
471 new_values = []
472 for value in old_value:
473 if isinstance(value, AST):
474 value = self.visit(value)
475 if value is None:
476 continue
477 elif not isinstance(value, AST):
478 new_values.extend(value)
479 continue
480 new_values.append(value)
481 old_value[:] = new_values
482 elif isinstance(old_value, AST):
483 new_node = self.visit(old_value)
484 if new_node is None:
485 delattr(node, field)
486 else:
487 setattr(node, field, new_node)
488 return node
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300489
490
491# The following code is for backward compatibility.
492# It will be removed in future.
493
494def _getter(self):
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200495 """Deprecated. Use value instead."""
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300496 return self.value
497
498def _setter(self, value):
499 self.value = value
500
501Constant.n = property(_getter, _setter)
502Constant.s = property(_getter, _setter)
503
504class _ABC(type):
505
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200506 def __init__(cls, *args):
507 cls.__doc__ = """Deprecated AST node class. Use ast.Constant instead"""
508
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300509 def __instancecheck__(cls, inst):
510 if not isinstance(inst, Constant):
511 return False
512 if cls in _const_types:
513 try:
514 value = inst.value
515 except AttributeError:
516 return False
517 else:
Anthony Sottile74176222019-01-18 11:30:28 -0800518 return (
519 isinstance(value, _const_types[cls]) and
520 not isinstance(value, _const_types_not.get(cls, ()))
521 )
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300522 return type.__instancecheck__(cls, inst)
523
524def _new(cls, *args, **kwargs):
525 if cls in _const_types:
526 return Constant(*args, **kwargs)
527 return Constant.__new__(cls, *args, **kwargs)
528
529class Num(Constant, metaclass=_ABC):
530 _fields = ('n',)
531 __new__ = _new
532
533class Str(Constant, metaclass=_ABC):
534 _fields = ('s',)
535 __new__ = _new
536
537class Bytes(Constant, metaclass=_ABC):
538 _fields = ('s',)
539 __new__ = _new
540
541class NameConstant(Constant, metaclass=_ABC):
542 __new__ = _new
543
544class Ellipsis(Constant, metaclass=_ABC):
545 _fields = ()
546
547 def __new__(cls, *args, **kwargs):
548 if cls is Ellipsis:
549 return Constant(..., *args, **kwargs)
550 return Constant.__new__(cls, *args, **kwargs)
551
552_const_types = {
553 Num: (int, float, complex),
554 Str: (str,),
555 Bytes: (bytes,),
556 NameConstant: (type(None), bool),
557 Ellipsis: (type(...),),
558}
Anthony Sottile74176222019-01-18 11:30:28 -0800559_const_types_not = {
560 Num: (bool,),
561}
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200562
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300563_const_node_type_names = {
564 bool: 'NameConstant', # should be before int
565 type(None): 'NameConstant',
566 int: 'Num',
567 float: 'Num',
568 complex: 'Num',
569 str: 'Str',
570 bytes: 'Bytes',
571 type(...): 'Ellipsis',
572}
Serhiy Storchaka832e8642019-09-09 23:36:13 +0300573
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200574class slice(AST):
575 """Deprecated AST node class."""
576
577class Index(slice):
578 """Deprecated AST node class. Use the index value directly instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200579 def __new__(cls, value, **kwargs):
580 return value
581
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200582class ExtSlice(slice):
583 """Deprecated AST node class. Use ast.Tuple instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200584 def __new__(cls, dims=(), **kwargs):
585 return Tuple(list(dims), Load(), **kwargs)
586
587def _dims_getter(self):
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200588 """Deprecated. Use elts instead."""
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200589 return self.elts
590
591def _dims_setter(self, value):
592 self.elts = value
593
594Tuple.dims = property(_dims_getter, _dims_setter)
595
Serhiy Storchakabace59d2020-03-22 20:33:34 +0200596class Suite(mod):
597 """Deprecated AST node class. Unused in Python 3."""
598
599class AugLoad(expr_context):
600 """Deprecated AST node class. Unused in Python 3."""
601
602class AugStore(expr_context):
603 """Deprecated AST node class. Unused in Python 3."""
604
605class Param(expr_context):
606 """Deprecated AST node class. Unused in Python 3."""
607
Serhiy Storchaka13d52c22020-03-10 18:52:34 +0200608
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000609# Large float and imaginary literals get turned into infinities in the AST.
610# We unparse those infinities to INFSTR.
611_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
612
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300613class _Precedence(IntEnum):
614 """Precedence table that originated from python grammar."""
615
616 TUPLE = auto()
617 YIELD = auto() # 'yield', 'yield from'
618 TEST = auto() # 'if'-'else', 'lambda'
619 OR = auto() # 'or'
620 AND = auto() # 'and'
621 NOT = auto() # 'not'
622 CMP = auto() # '<', '>', '==', '>=', '<=', '!=',
623 # 'in', 'not in', 'is', 'is not'
624 EXPR = auto()
625 BOR = EXPR # '|'
626 BXOR = auto() # '^'
627 BAND = auto() # '&'
628 SHIFT = auto() # '<<', '>>'
629 ARITH = auto() # '+', '-'
630 TERM = auto() # '*', '@', '/', '%', '//'
631 FACTOR = auto() # unary '+', '-', '~'
632 POWER = auto() # '**'
633 AWAIT = auto() # 'await'
634 ATOM = auto()
635
636 def next(self):
637 try:
638 return self.__class__(self + 1)
639 except ValueError:
640 return self
641
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000642class _Unparser(NodeVisitor):
643 """Methods in this class recursively traverse an AST and
644 output source code for the abstract syntax; original formatting
645 is disregarded."""
646
647 def __init__(self):
648 self._source = []
649 self._buffer = []
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300650 self._precedences = {}
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300651 self._type_ignores = {}
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000652 self._indent = 0
653
654 def interleave(self, inter, f, seq):
655 """Call f on each item in seq, calling inter() in between."""
656 seq = iter(seq)
657 try:
658 f(next(seq))
659 except StopIteration:
660 pass
661 else:
662 for x in seq:
663 inter()
664 f(x)
665
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +0300666 def items_view(self, traverser, items):
667 """Traverse and separate the given *items* with a comma and append it to
668 the buffer. If *items* is a single item sequence, a trailing comma
669 will be added."""
670 if len(items) == 1:
671 traverser(items[0])
672 self.write(",")
673 else:
674 self.interleave(lambda: self.write(", "), traverser, items)
675
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300676 def maybe_newline(self):
677 """Adds a newline if it isn't the start of generated source"""
678 if self._source:
679 self.write("\n")
680
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000681 def fill(self, text=""):
682 """Indent a piece of text and append it, according to the current
683 indentation level"""
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300684 self.maybe_newline()
685 self.write(" " * self._indent + text)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000686
687 def write(self, text):
688 """Append a piece of text"""
689 self._source.append(text)
690
691 def buffer_writer(self, text):
692 self._buffer.append(text)
693
694 @property
695 def buffer(self):
696 value = "".join(self._buffer)
697 self._buffer.clear()
698 return value
699
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000700 @contextmanager
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300701 def block(self, *, extra = None):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000702 """A context manager for preparing the source for blocks. It adds
703 the character':', increases the indentation on enter and decreases
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300704 the indentation on exit. If *extra* is given, it will be directly
705 appended after the colon character.
706 """
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000707 self.write(":")
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300708 if extra:
709 self.write(extra)
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000710 self._indent += 1
711 yield
712 self._indent -= 1
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000713
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300714 @contextmanager
715 def delimit(self, start, end):
716 """A context manager for preparing the source for expressions. It adds
717 *start* to the buffer and enters, after exit it adds *end*."""
718
719 self.write(start)
720 yield
721 self.write(end)
722
723 def delimit_if(self, start, end, condition):
724 if condition:
725 return self.delimit(start, end)
726 else:
727 return nullcontext()
728
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300729 def require_parens(self, precedence, node):
730 """Shortcut to adding precedence related parens"""
731 return self.delimit_if("(", ")", self.get_precedence(node) > precedence)
732
733 def get_precedence(self, node):
734 return self._precedences.get(node, _Precedence.TEST)
735
736 def set_precedence(self, precedence, *nodes):
737 for node in nodes:
738 self._precedences[node] = precedence
739
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300740 def get_raw_docstring(self, node):
741 """If a docstring node is found in the body of the *node* parameter,
742 return that docstring node, None otherwise.
743
744 Logic mirrored from ``_PyAST_GetDocString``."""
745 if not isinstance(
746 node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)
747 ) or len(node.body) < 1:
748 return None
749 node = node.body[0]
750 if not isinstance(node, Expr):
751 return None
752 node = node.value
753 if isinstance(node, Constant) and isinstance(node.value, str):
754 return node
755
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300756 def get_type_comment(self, node):
757 comment = self._type_ignores.get(node.lineno) or node.type_comment
758 if comment is not None:
759 return f" # type: {comment}"
760
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000761 def traverse(self, node):
762 if isinstance(node, list):
763 for item in node:
764 self.traverse(item)
765 else:
766 super().visit(node)
767
768 def visit(self, node):
769 """Outputs a source code string that, if converted back to an ast
770 (using ast.parse) will generate an AST equivalent to *node*"""
771 self._source = []
772 self.traverse(node)
773 return "".join(self._source)
774
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300775 def _write_docstring_and_traverse_body(self, node):
776 if (docstring := self.get_raw_docstring(node)):
777 self._write_docstring(docstring)
778 self.traverse(node.body[1:])
779 else:
780 self.traverse(node.body)
781
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000782 def visit_Module(self, node):
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300783 self._type_ignores = {
784 ignore.lineno: f"ignore{ignore.tag}"
785 for ignore in node.type_ignores
786 }
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300787 self._write_docstring_and_traverse_body(node)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300788 self._type_ignores.clear()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000789
Batuhan Taşkaya5b66ec12020-03-15 22:56:57 +0300790 def visit_FunctionType(self, node):
791 with self.delimit("(", ")"):
792 self.interleave(
793 lambda: self.write(", "), self.traverse, node.argtypes
794 )
795
796 self.write(" -> ")
797 self.traverse(node.returns)
798
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000799 def visit_Expr(self, node):
800 self.fill()
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300801 self.set_precedence(_Precedence.YIELD, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000802 self.traverse(node.value)
803
804 def visit_NamedExpr(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300805 with self.require_parens(_Precedence.TUPLE, node):
806 self.set_precedence(_Precedence.ATOM, node.target, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300807 self.traverse(node.target)
808 self.write(" := ")
809 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000810
811 def visit_Import(self, node):
812 self.fill("import ")
813 self.interleave(lambda: self.write(", "), self.traverse, node.names)
814
815 def visit_ImportFrom(self, node):
816 self.fill("from ")
817 self.write("." * node.level)
818 if node.module:
819 self.write(node.module)
820 self.write(" import ")
821 self.interleave(lambda: self.write(", "), self.traverse, node.names)
822
823 def visit_Assign(self, node):
824 self.fill()
825 for target in node.targets:
826 self.traverse(target)
827 self.write(" = ")
828 self.traverse(node.value)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300829 if type_comment := self.get_type_comment(node):
830 self.write(type_comment)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000831
832 def visit_AugAssign(self, node):
833 self.fill()
834 self.traverse(node.target)
835 self.write(" " + self.binop[node.op.__class__.__name__] + "= ")
836 self.traverse(node.value)
837
838 def visit_AnnAssign(self, node):
839 self.fill()
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300840 with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)):
841 self.traverse(node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000842 self.write(": ")
843 self.traverse(node.annotation)
844 if node.value:
845 self.write(" = ")
846 self.traverse(node.value)
847
848 def visit_Return(self, node):
849 self.fill("return")
850 if node.value:
851 self.write(" ")
852 self.traverse(node.value)
853
854 def visit_Pass(self, node):
855 self.fill("pass")
856
857 def visit_Break(self, node):
858 self.fill("break")
859
860 def visit_Continue(self, node):
861 self.fill("continue")
862
863 def visit_Delete(self, node):
864 self.fill("del ")
865 self.interleave(lambda: self.write(", "), self.traverse, node.targets)
866
867 def visit_Assert(self, node):
868 self.fill("assert ")
869 self.traverse(node.test)
870 if node.msg:
871 self.write(", ")
872 self.traverse(node.msg)
873
874 def visit_Global(self, node):
875 self.fill("global ")
876 self.interleave(lambda: self.write(", "), self.write, node.names)
877
878 def visit_Nonlocal(self, node):
879 self.fill("nonlocal ")
880 self.interleave(lambda: self.write(", "), self.write, node.names)
881
882 def visit_Await(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300883 with self.require_parens(_Precedence.AWAIT, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300884 self.write("await")
885 if node.value:
886 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300887 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300888 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000889
890 def visit_Yield(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300891 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300892 self.write("yield")
893 if node.value:
894 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300895 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300896 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000897
898 def visit_YieldFrom(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300899 with self.require_parens(_Precedence.YIELD, node):
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300900 self.write("yield from ")
901 if not node.value:
902 raise ValueError("Node can't be used without a value attribute.")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +0300903 self.set_precedence(_Precedence.ATOM, node.value)
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300904 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000905
906 def visit_Raise(self, node):
907 self.fill("raise")
908 if not node.exc:
909 if node.cause:
910 raise ValueError(f"Node can't use cause without an exception.")
911 return
912 self.write(" ")
913 self.traverse(node.exc)
914 if node.cause:
915 self.write(" from ")
916 self.traverse(node.cause)
917
918 def visit_Try(self, node):
919 self.fill("try")
920 with self.block():
921 self.traverse(node.body)
922 for ex in node.handlers:
923 self.traverse(ex)
924 if node.orelse:
925 self.fill("else")
926 with self.block():
927 self.traverse(node.orelse)
928 if node.finalbody:
929 self.fill("finally")
930 with self.block():
931 self.traverse(node.finalbody)
932
933 def visit_ExceptHandler(self, node):
934 self.fill("except")
935 if node.type:
936 self.write(" ")
937 self.traverse(node.type)
938 if node.name:
939 self.write(" as ")
940 self.write(node.name)
941 with self.block():
942 self.traverse(node.body)
943
944 def visit_ClassDef(self, node):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300945 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000946 for deco in node.decorator_list:
947 self.fill("@")
948 self.traverse(deco)
949 self.fill("class " + node.name)
Batuhan Taskaya25160cd2020-05-17 00:53:25 +0300950 with self.delimit_if("(", ")", condition = node.bases or node.keywords):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300951 comma = False
952 for e in node.bases:
953 if comma:
954 self.write(", ")
955 else:
956 comma = True
957 self.traverse(e)
958 for e in node.keywords:
959 if comma:
960 self.write(", ")
961 else:
962 comma = True
963 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000964
965 with self.block():
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300966 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000967
968 def visit_FunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300969 self._function_helper(node, "def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000970
971 def visit_AsyncFunctionDef(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300972 self._function_helper(node, "async def")
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000973
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300974 def _function_helper(self, node, fill_suffix):
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +0300975 self.maybe_newline()
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000976 for deco in node.decorator_list:
977 self.fill("@")
978 self.traverse(deco)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300979 def_str = fill_suffix + " " + node.name
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000980 self.fill(def_str)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300981 with self.delimit("(", ")"):
982 self.traverse(node.args)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000983 if node.returns:
984 self.write(" -> ")
985 self.traverse(node.returns)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +0300986 with self.block(extra=self.get_type_comment(node)):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300987 self._write_docstring_and_traverse_body(node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000988
989 def visit_For(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300990 self._for_helper("for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000991
992 def visit_AsyncFor(self, node):
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300993 self._for_helper("async for ", node)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000994
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +0300995 def _for_helper(self, fill, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000996 self.fill(fill)
997 self.traverse(node.target)
998 self.write(" in ")
999 self.traverse(node.iter)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001000 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001001 self.traverse(node.body)
1002 if node.orelse:
1003 self.fill("else")
1004 with self.block():
1005 self.traverse(node.orelse)
1006
1007 def visit_If(self, node):
1008 self.fill("if ")
1009 self.traverse(node.test)
1010 with self.block():
1011 self.traverse(node.body)
1012 # collapse nested ifs into equivalent elifs.
1013 while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If):
1014 node = node.orelse[0]
1015 self.fill("elif ")
1016 self.traverse(node.test)
1017 with self.block():
1018 self.traverse(node.body)
1019 # final else
1020 if node.orelse:
1021 self.fill("else")
1022 with self.block():
1023 self.traverse(node.orelse)
1024
1025 def visit_While(self, node):
1026 self.fill("while ")
1027 self.traverse(node.test)
1028 with self.block():
1029 self.traverse(node.body)
1030 if node.orelse:
1031 self.fill("else")
1032 with self.block():
1033 self.traverse(node.orelse)
1034
1035 def visit_With(self, node):
1036 self.fill("with ")
1037 self.interleave(lambda: self.write(", "), self.traverse, node.items)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001038 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001039 self.traverse(node.body)
1040
1041 def visit_AsyncWith(self, node):
1042 self.fill("async with ")
1043 self.interleave(lambda: self.write(", "), self.traverse, node.items)
Batuhan Taskayadff92bb2020-05-17 02:04:12 +03001044 with self.block(extra=self.get_type_comment(node)):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001045 self.traverse(node.body)
1046
1047 def visit_JoinedStr(self, node):
1048 self.write("f")
1049 self._fstring_JoinedStr(node, self.buffer_writer)
1050 self.write(repr(self.buffer))
1051
1052 def visit_FormattedValue(self, node):
1053 self.write("f")
1054 self._fstring_FormattedValue(node, self.buffer_writer)
1055 self.write(repr(self.buffer))
1056
1057 def _fstring_JoinedStr(self, node, write):
1058 for value in node.values:
1059 meth = getattr(self, "_fstring_" + type(value).__name__)
1060 meth(value, write)
1061
1062 def _fstring_Constant(self, node, write):
1063 if not isinstance(node.value, str):
1064 raise ValueError("Constants inside JoinedStr should be a string.")
1065 value = node.value.replace("{", "{{").replace("}", "}}")
1066 write(value)
1067
1068 def _fstring_FormattedValue(self, node, write):
1069 write("{")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001070 unparser = type(self)()
1071 unparser.set_precedence(_Precedence.TEST.next(), node.value)
Batuhan Taskaya493bf1c2020-05-03 20:11:51 +03001072 expr = unparser.visit(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001073 if expr.startswith("{"):
1074 write(" ") # Separate pair of opening brackets as "{ {"
1075 write(expr)
1076 if node.conversion != -1:
1077 conversion = chr(node.conversion)
1078 if conversion not in "sra":
1079 raise ValueError("Unknown f-string conversion.")
1080 write(f"!{conversion}")
1081 if node.format_spec:
1082 write(":")
1083 meth = getattr(self, "_fstring_" + type(node.format_spec).__name__)
1084 meth(node.format_spec, write)
1085 write("}")
1086
1087 def visit_Name(self, node):
1088 self.write(node.id)
1089
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001090 def _write_docstring(self, node):
1091 self.fill()
1092 if node.kind == "u":
1093 self.write("u")
1094
Batuhan Taskayae966af72020-05-17 01:49:07 +03001095 value = node.value
1096 if value:
1097 # Preserve quotes in the docstring by escaping them
1098 value = value.replace("\\", "\\\\")
1099 value = value.replace('"""', '""\"')
1100 value = value.replace("\r", "\\r")
1101 if value[-1] == '"':
1102 value = value.replace('"', '\\"', -1)
Batuhan Taşkaya89aa4692020-03-02 21:59:01 +03001103
1104 self.write(f'"""{value}"""')
1105
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001106 def _write_constant(self, value):
1107 if isinstance(value, (float, complex)):
1108 # Substitute overflowing decimal literal for AST infinities.
1109 self.write(repr(value).replace("inf", _INFSTR))
1110 else:
1111 self.write(repr(value))
1112
1113 def visit_Constant(self, node):
1114 value = node.value
1115 if isinstance(value, tuple):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001116 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001117 self.items_view(self._write_constant, value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001118 elif value is ...:
1119 self.write("...")
1120 else:
1121 if node.kind == "u":
1122 self.write("u")
1123 self._write_constant(node.value)
1124
1125 def visit_List(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001126 with self.delimit("[", "]"):
1127 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001128
1129 def visit_ListComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001130 with self.delimit("[", "]"):
1131 self.traverse(node.elt)
1132 for gen in node.generators:
1133 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001134
1135 def visit_GeneratorExp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001136 with self.delimit("(", ")"):
1137 self.traverse(node.elt)
1138 for gen in node.generators:
1139 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001140
1141 def visit_SetComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001142 with self.delimit("{", "}"):
1143 self.traverse(node.elt)
1144 for gen in node.generators:
1145 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001146
1147 def visit_DictComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001148 with self.delimit("{", "}"):
1149 self.traverse(node.key)
1150 self.write(": ")
1151 self.traverse(node.value)
1152 for gen in node.generators:
1153 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001154
1155 def visit_comprehension(self, node):
1156 if node.is_async:
1157 self.write(" async for ")
1158 else:
1159 self.write(" for ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001160 self.set_precedence(_Precedence.TUPLE, node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001161 self.traverse(node.target)
1162 self.write(" in ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001163 self.set_precedence(_Precedence.TEST.next(), node.iter, *node.ifs)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001164 self.traverse(node.iter)
1165 for if_clause in node.ifs:
1166 self.write(" if ")
1167 self.traverse(if_clause)
1168
1169 def visit_IfExp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001170 with self.require_parens(_Precedence.TEST, node):
1171 self.set_precedence(_Precedence.TEST.next(), node.body, node.test)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001172 self.traverse(node.body)
1173 self.write(" if ")
1174 self.traverse(node.test)
1175 self.write(" else ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001176 self.set_precedence(_Precedence.TEST, node.orelse)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001177 self.traverse(node.orelse)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001178
1179 def visit_Set(self, node):
1180 if not node.elts:
Shantanu01508dc2020-04-16 03:10:12 -07001181 raise ValueError("Set node should have at least one item")
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001182 with self.delimit("{", "}"):
1183 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001184
1185 def visit_Dict(self, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001186 def write_key_value_pair(k, v):
1187 self.traverse(k)
1188 self.write(": ")
1189 self.traverse(v)
1190
1191 def write_item(item):
1192 k, v = item
1193 if k is None:
1194 # for dictionary unpacking operator in dicts {**{'y': 2}}
1195 # see PEP 448 for details
1196 self.write("**")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001197 self.set_precedence(_Precedence.EXPR, v)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001198 self.traverse(v)
1199 else:
1200 write_key_value_pair(k, v)
1201
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001202 with self.delimit("{", "}"):
1203 self.interleave(
1204 lambda: self.write(", "), write_item, zip(node.keys, node.values)
1205 )
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001206
1207 def visit_Tuple(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001208 with self.delimit("(", ")"):
Batuhan Taşkayae7cab7f2020-03-09 23:27:03 +03001209 self.items_view(self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001210
1211 unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001212 unop_precedence = {
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001213 "not": _Precedence.NOT,
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001214 "~": _Precedence.FACTOR,
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001215 "+": _Precedence.FACTOR,
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001216 "-": _Precedence.FACTOR,
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001217 }
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001218
1219 def visit_UnaryOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001220 operator = self.unop[node.op.__class__.__name__]
1221 operator_precedence = self.unop_precedence[operator]
1222 with self.require_parens(operator_precedence, node):
1223 self.write(operator)
Batuhan Taskayace4a7532020-05-17 00:46:11 +03001224 # factor prefixes (+, -, ~) shouldn't be seperated
1225 # from the value they belong, (e.g: +1 instead of + 1)
1226 if operator_precedence is not _Precedence.FACTOR:
1227 self.write(" ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001228 self.set_precedence(operator_precedence, node.operand)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001229 self.traverse(node.operand)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001230
1231 binop = {
1232 "Add": "+",
1233 "Sub": "-",
1234 "Mult": "*",
1235 "MatMult": "@",
1236 "Div": "/",
1237 "Mod": "%",
1238 "LShift": "<<",
1239 "RShift": ">>",
1240 "BitOr": "|",
1241 "BitXor": "^",
1242 "BitAnd": "&",
1243 "FloorDiv": "//",
1244 "Pow": "**",
1245 }
1246
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001247 binop_precedence = {
1248 "+": _Precedence.ARITH,
1249 "-": _Precedence.ARITH,
1250 "*": _Precedence.TERM,
1251 "@": _Precedence.TERM,
1252 "/": _Precedence.TERM,
1253 "%": _Precedence.TERM,
1254 "<<": _Precedence.SHIFT,
1255 ">>": _Precedence.SHIFT,
1256 "|": _Precedence.BOR,
1257 "^": _Precedence.BXOR,
1258 "&": _Precedence.BAND,
1259 "//": _Precedence.TERM,
1260 "**": _Precedence.POWER,
1261 }
1262
1263 binop_rassoc = frozenset(("**",))
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001264 def visit_BinOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001265 operator = self.binop[node.op.__class__.__name__]
1266 operator_precedence = self.binop_precedence[operator]
1267 with self.require_parens(operator_precedence, node):
1268 if operator in self.binop_rassoc:
1269 left_precedence = operator_precedence.next()
1270 right_precedence = operator_precedence
1271 else:
1272 left_precedence = operator_precedence
1273 right_precedence = operator_precedence.next()
1274
1275 self.set_precedence(left_precedence, node.left)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001276 self.traverse(node.left)
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001277 self.write(f" {operator} ")
1278 self.set_precedence(right_precedence, node.right)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001279 self.traverse(node.right)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001280
1281 cmpops = {
1282 "Eq": "==",
1283 "NotEq": "!=",
1284 "Lt": "<",
1285 "LtE": "<=",
1286 "Gt": ">",
1287 "GtE": ">=",
1288 "Is": "is",
1289 "IsNot": "is not",
1290 "In": "in",
1291 "NotIn": "not in",
1292 }
1293
1294 def visit_Compare(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001295 with self.require_parens(_Precedence.CMP, node):
1296 self.set_precedence(_Precedence.CMP.next(), node.left, *node.comparators)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001297 self.traverse(node.left)
1298 for o, e in zip(node.ops, node.comparators):
1299 self.write(" " + self.cmpops[o.__class__.__name__] + " ")
1300 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001301
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001302 boolops = {"And": "and", "Or": "or"}
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001303 boolop_precedence = {"and": _Precedence.AND, "or": _Precedence.OR}
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001304
1305 def visit_BoolOp(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001306 operator = self.boolops[node.op.__class__.__name__]
1307 operator_precedence = self.boolop_precedence[operator]
1308
1309 def increasing_level_traverse(node):
1310 nonlocal operator_precedence
1311 operator_precedence = operator_precedence.next()
1312 self.set_precedence(operator_precedence, node)
1313 self.traverse(node)
1314
1315 with self.require_parens(operator_precedence, node):
1316 s = f" {operator} "
1317 self.interleave(lambda: self.write(s), increasing_level_traverse, node.values)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001318
1319 def visit_Attribute(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001320 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001321 self.traverse(node.value)
1322 # Special case: 3.__abs__() is a syntax error, so if node.value
1323 # is an integer literal then we need to either parenthesize
1324 # it or add an extra space to get 3 .__abs__().
1325 if isinstance(node.value, Constant) and isinstance(node.value.value, int):
1326 self.write(" ")
1327 self.write(".")
1328 self.write(node.attr)
1329
1330 def visit_Call(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001331 self.set_precedence(_Precedence.ATOM, node.func)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001332 self.traverse(node.func)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001333 with self.delimit("(", ")"):
1334 comma = False
1335 for e in node.args:
1336 if comma:
1337 self.write(", ")
1338 else:
1339 comma = True
1340 self.traverse(e)
1341 for e in node.keywords:
1342 if comma:
1343 self.write(", ")
1344 else:
1345 comma = True
1346 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001347
1348 def visit_Subscript(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001349 self.set_precedence(_Precedence.ATOM, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001350 self.traverse(node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001351 with self.delimit("[", "]"):
Serhiy Storchaka13d52c22020-03-10 18:52:34 +02001352 if isinstance(node.slice, Tuple) and node.slice.elts:
1353 self.items_view(self.traverse, node.slice.elts)
Serhiy Storchakac4928fc2020-03-07 17:25:32 +02001354 else:
1355 self.traverse(node.slice)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001356
1357 def visit_Starred(self, node):
1358 self.write("*")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001359 self.set_precedence(_Precedence.EXPR, node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001360 self.traverse(node.value)
1361
1362 def visit_Ellipsis(self, node):
1363 self.write("...")
1364
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001365 def visit_Slice(self, node):
1366 if node.lower:
1367 self.traverse(node.lower)
1368 self.write(":")
1369 if node.upper:
1370 self.traverse(node.upper)
1371 if node.step:
1372 self.write(":")
1373 self.traverse(node.step)
1374
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001375 def visit_arg(self, node):
1376 self.write(node.arg)
1377 if node.annotation:
1378 self.write(": ")
1379 self.traverse(node.annotation)
1380
1381 def visit_arguments(self, node):
1382 first = True
1383 # normal arguments
1384 all_args = node.posonlyargs + node.args
1385 defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults
1386 for index, elements in enumerate(zip(all_args, defaults), 1):
1387 a, d = elements
1388 if first:
1389 first = False
1390 else:
1391 self.write(", ")
1392 self.traverse(a)
1393 if d:
1394 self.write("=")
1395 self.traverse(d)
1396 if index == len(node.posonlyargs):
1397 self.write(", /")
1398
1399 # varargs, or bare '*' if no varargs but keyword-only arguments present
1400 if node.vararg or node.kwonlyargs:
1401 if first:
1402 first = False
1403 else:
1404 self.write(", ")
1405 self.write("*")
1406 if node.vararg:
1407 self.write(node.vararg.arg)
1408 if node.vararg.annotation:
1409 self.write(": ")
1410 self.traverse(node.vararg.annotation)
1411
1412 # keyword-only arguments
1413 if node.kwonlyargs:
1414 for a, d in zip(node.kwonlyargs, node.kw_defaults):
Batuhan Taşkayaa322f502019-12-16 15:26:58 +03001415 self.write(", ")
1416 self.traverse(a)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001417 if d:
1418 self.write("=")
1419 self.traverse(d)
1420
1421 # kwargs
1422 if node.kwarg:
1423 if first:
1424 first = False
1425 else:
1426 self.write(", ")
1427 self.write("**" + node.kwarg.arg)
1428 if node.kwarg.annotation:
1429 self.write(": ")
1430 self.traverse(node.kwarg.annotation)
1431
1432 def visit_keyword(self, node):
1433 if node.arg is None:
1434 self.write("**")
1435 else:
1436 self.write(node.arg)
1437 self.write("=")
1438 self.traverse(node.value)
1439
1440 def visit_Lambda(self, node):
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001441 with self.require_parens(_Precedence.TEST, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001442 self.write("lambda ")
1443 self.traverse(node.args)
1444 self.write(": ")
Batuhan Taşkaya397b96f2020-03-01 23:12:17 +03001445 self.set_precedence(_Precedence.TEST, node.body)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001446 self.traverse(node.body)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001447
1448 def visit_alias(self, node):
1449 self.write(node.name)
1450 if node.asname:
1451 self.write(" as " + node.asname)
1452
1453 def visit_withitem(self, node):
1454 self.traverse(node.context_expr)
1455 if node.optional_vars:
1456 self.write(" as ")
1457 self.traverse(node.optional_vars)
1458
1459def unparse(ast_obj):
1460 unparser = _Unparser()
1461 return unparser.visit(ast_obj)
1462
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001463
1464def main():
1465 import argparse
1466
1467 parser = argparse.ArgumentParser(prog='python -m ast')
1468 parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?',
1469 default='-',
1470 help='the file to parse; defaults to stdin')
1471 parser.add_argument('-m', '--mode', default='exec',
1472 choices=('exec', 'single', 'eval', 'func_type'),
1473 help='specify what kind of code must be parsed')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001474 parser.add_argument('--no-type-comments', default=True, action='store_false',
1475 help="don't add information about type comments")
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001476 parser.add_argument('-a', '--include-attributes', action='store_true',
1477 help='include attributes such as line numbers and '
1478 'column offsets')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001479 parser.add_argument('-i', '--indent', type=int, default=3,
1480 help='indentation of nodes (number of spaces)')
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001481 args = parser.parse_args()
1482
1483 with args.infile as infile:
1484 source = infile.read()
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001485 tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
1486 print(dump(tree, include_attributes=args.include_attributes, indent=args.indent))
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001487
1488if __name__ == '__main__':
1489 main()