blob: 511f0956a00b0bf1a2a6c6999858bf73852a10d4 [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
Georg Brandl0c77a822008-06-10 16:37:50 +000030
31
Guido van Rossum495da292019-03-07 12:38:08 -080032def parse(source, filename='<unknown>', mode='exec', *,
Guido van Rossum10b55c12019-06-11 17:23:12 -070033 type_comments=False, feature_version=None):
Georg Brandl0c77a822008-06-10 16:37:50 +000034 """
Terry Reedyfeac6242011-01-24 21:36:03 +000035 Parse the source into an AST node.
36 Equivalent to compile(source, filename, mode, PyCF_ONLY_AST).
Guido van Rossumdcfcd142019-01-31 03:40:27 -080037 Pass type_comments=True to get back type comments where the syntax allows.
Georg Brandl0c77a822008-06-10 16:37:50 +000038 """
Guido van Rossumdcfcd142019-01-31 03:40:27 -080039 flags = PyCF_ONLY_AST
40 if type_comments:
41 flags |= PyCF_TYPE_COMMENTS
Guido van Rossum10b55c12019-06-11 17:23:12 -070042 if isinstance(feature_version, tuple):
43 major, minor = feature_version # Should be a 2-tuple.
44 assert major == 3
45 feature_version = minor
46 elif feature_version is None:
47 feature_version = -1
48 # Else it should be an int giving the minor version for 3.x.
Guido van Rossum495da292019-03-07 12:38:08 -080049 return compile(source, filename, mode, flags,
Victor Stinnerefdf6ca2019-06-12 02:52:16 +020050 _feature_version=feature_version)
Georg Brandl0c77a822008-06-10 16:37:50 +000051
52
53def literal_eval(node_or_string):
54 """
55 Safely evaluate an expression node or a string containing a Python
56 expression. The string or node provided may only consist of the following
Éric Araujo2a83cc62011-04-17 19:10:27 +020057 Python literal structures: strings, bytes, numbers, tuples, lists, dicts,
58 sets, booleans, and None.
Georg Brandl0c77a822008-06-10 16:37:50 +000059 """
Georg Brandl0c77a822008-06-10 16:37:50 +000060 if isinstance(node_or_string, str):
61 node_or_string = parse(node_or_string, mode='eval')
62 if isinstance(node_or_string, Expression):
63 node_or_string = node_or_string.body
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020064 def _convert_num(node):
65 if isinstance(node, Constant):
Serhiy Storchaka3f228112018-09-27 17:42:37 +030066 if type(node.value) in (int, float, complex):
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020067 return node.value
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020068 raise ValueError('malformed node or string: ' + repr(node))
69 def _convert_signed_num(node):
70 if isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)):
71 operand = _convert_num(node.operand)
72 if isinstance(node.op, UAdd):
73 return + operand
74 else:
75 return - operand
76 return _convert_num(node)
Georg Brandl0c77a822008-06-10 16:37:50 +000077 def _convert(node):
Victor Stinnerf2c1aa12016-01-26 00:40:57 +010078 if isinstance(node, Constant):
79 return node.value
Georg Brandl0c77a822008-06-10 16:37:50 +000080 elif isinstance(node, Tuple):
81 return tuple(map(_convert, node.elts))
82 elif isinstance(node, List):
83 return list(map(_convert, node.elts))
Georg Brandl492f3fc2010-07-11 09:41:21 +000084 elif isinstance(node, Set):
85 return set(map(_convert, node.elts))
Raymond Hettinger4fcf5c12020-01-02 22:21:18 -070086 elif (isinstance(node, Call) and isinstance(node.func, Name) and
87 node.func.id == 'set' and node.args == node.keywords == []):
88 return set()
Georg Brandl0c77a822008-06-10 16:37:50 +000089 elif isinstance(node, Dict):
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020090 return dict(zip(map(_convert, node.keys),
91 map(_convert, node.values)))
Victor Stinnerf2c1aa12016-01-26 00:40:57 +010092 elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)):
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +020093 left = _convert_signed_num(node.left)
94 right = _convert_num(node.right)
95 if isinstance(left, (int, float)) and isinstance(right, complex):
Victor Stinnerf2c1aa12016-01-26 00:40:57 +010096 if isinstance(node.op, Add):
97 return left + right
98 else:
99 return left - right
Serhiy Storchakad8ac4d12018-01-04 11:15:39 +0200100 return _convert_signed_num(node)
Georg Brandl0c77a822008-06-10 16:37:50 +0000101 return _convert(node_or_string)
102
103
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300104def dump(node, annotate_fields=True, include_attributes=False, *, indent=None):
Georg Brandl0c77a822008-06-10 16:37:50 +0000105 """
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300106 Return a formatted dump of the tree in node. This is mainly useful for
107 debugging purposes. If annotate_fields is true (by default),
108 the returned string will show the names and the values for fields.
109 If annotate_fields is false, the result string will be more compact by
110 omitting unambiguous field names. Attributes such as line
Benjamin Petersondcf97b92008-07-02 17:30:14 +0000111 numbers and column offsets are not dumped by default. If this is wanted,
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300112 include_attributes can be set to true. If indent is a non-negative
113 integer or string, then the tree will be pretty-printed with that indent
114 level. None (the default) selects the single line representation.
Georg Brandl0c77a822008-06-10 16:37:50 +0000115 """
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300116 def _format(node, level=0):
117 if indent is not None:
118 level += 1
119 prefix = '\n' + indent * level
120 sep = ',\n' + indent * level
121 else:
122 prefix = ''
123 sep = ', '
Georg Brandl0c77a822008-06-10 16:37:50 +0000124 if isinstance(node, AST):
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300125 args = []
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300126 allsimple = True
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300127 keywords = annotate_fields
128 for field in node._fields:
129 try:
130 value = getattr(node, field)
131 except AttributeError:
132 keywords = True
133 else:
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300134 value, simple = _format(value, level)
135 allsimple = allsimple and simple
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300136 if keywords:
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300137 args.append('%s=%s' % (field, value))
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300138 else:
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300139 args.append(value)
Georg Brandl0c77a822008-06-10 16:37:50 +0000140 if include_attributes and node._attributes:
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300141 for attr in node._attributes:
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300142 try:
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300143 value = getattr(node, attr)
Serhiy Storchakae64f9482019-08-29 09:30:23 +0300144 except AttributeError:
145 pass
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300146 else:
147 value, simple = _format(value, level)
148 allsimple = allsimple and simple
149 args.append('%s=%s' % (attr, value))
150 if allsimple and len(args) <= 3:
151 return '%s(%s)' % (node.__class__.__name__, ', '.join(args)), not args
152 return '%s(%s%s)' % (node.__class__.__name__, prefix, sep.join(args)), False
Georg Brandl0c77a822008-06-10 16:37:50 +0000153 elif isinstance(node, list):
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300154 if not node:
155 return '[]', True
156 return '[%s%s]' % (prefix, sep.join(_format(x, level)[0] for x in node)), False
157 return repr(node), True
158
Georg Brandl0c77a822008-06-10 16:37:50 +0000159 if not isinstance(node, AST):
160 raise TypeError('expected AST, got %r' % node.__class__.__name__)
Serhiy Storchaka850573b2019-09-09 19:33:13 +0300161 if indent is not None and not isinstance(indent, str):
162 indent = ' ' * indent
163 return _format(node)[0]
Georg Brandl0c77a822008-06-10 16:37:50 +0000164
165
166def copy_location(new_node, old_node):
167 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000168 Copy source location (`lineno`, `col_offset`, `end_lineno`, and `end_col_offset`
169 attributes) from *old_node* to *new_node* if possible, and return *new_node*.
Georg Brandl0c77a822008-06-10 16:37:50 +0000170 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000171 for attr in 'lineno', 'col_offset', 'end_lineno', 'end_col_offset':
Georg Brandl0c77a822008-06-10 16:37:50 +0000172 if attr in old_node._attributes and attr in new_node._attributes \
173 and hasattr(old_node, attr):
174 setattr(new_node, attr, getattr(old_node, attr))
175 return new_node
176
177
178def fix_missing_locations(node):
179 """
180 When you compile a node tree with compile(), the compiler expects lineno and
181 col_offset attributes for every node that supports them. This is rather
182 tedious to fill in for generated nodes, so this helper adds these attributes
183 recursively where not already set, by setting them to the values of the
184 parent node. It works recursively starting at *node*.
185 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000186 def _fix(node, lineno, col_offset, end_lineno, end_col_offset):
Georg Brandl0c77a822008-06-10 16:37:50 +0000187 if 'lineno' in node._attributes:
188 if not hasattr(node, 'lineno'):
189 node.lineno = lineno
190 else:
191 lineno = node.lineno
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000192 if 'end_lineno' in node._attributes:
193 if not hasattr(node, 'end_lineno'):
194 node.end_lineno = end_lineno
195 else:
196 end_lineno = node.end_lineno
Georg Brandl0c77a822008-06-10 16:37:50 +0000197 if 'col_offset' in node._attributes:
198 if not hasattr(node, 'col_offset'):
199 node.col_offset = col_offset
200 else:
201 col_offset = node.col_offset
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000202 if 'end_col_offset' in node._attributes:
203 if not hasattr(node, 'end_col_offset'):
204 node.end_col_offset = end_col_offset
205 else:
206 end_col_offset = node.end_col_offset
Georg Brandl0c77a822008-06-10 16:37:50 +0000207 for child in iter_child_nodes(node):
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000208 _fix(child, lineno, col_offset, end_lineno, end_col_offset)
209 _fix(node, 1, 0, 1, 0)
Georg Brandl0c77a822008-06-10 16:37:50 +0000210 return node
211
212
213def increment_lineno(node, n=1):
214 """
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000215 Increment the line number and end line number of each node in the tree
216 starting at *node* by *n*. This is useful to "move code" to a different
217 location in a file.
Georg Brandl0c77a822008-06-10 16:37:50 +0000218 """
Georg Brandl0c77a822008-06-10 16:37:50 +0000219 for child in walk(node):
220 if 'lineno' in child._attributes:
221 child.lineno = getattr(child, 'lineno', 0) + n
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000222 if 'end_lineno' in child._attributes:
223 child.end_lineno = getattr(child, 'end_lineno', 0) + n
Georg Brandl0c77a822008-06-10 16:37:50 +0000224 return node
225
226
227def iter_fields(node):
228 """
229 Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
230 that is present on *node*.
231 """
232 for field in node._fields:
233 try:
234 yield field, getattr(node, field)
235 except AttributeError:
236 pass
237
238
239def iter_child_nodes(node):
240 """
241 Yield all direct child nodes of *node*, that is, all fields that are nodes
242 and all items of fields that are lists of nodes.
243 """
244 for name, field in iter_fields(node):
245 if isinstance(field, AST):
246 yield field
247 elif isinstance(field, list):
248 for item in field:
249 if isinstance(item, AST):
250 yield item
251
252
253def get_docstring(node, clean=True):
254 """
255 Return the docstring for the given node or None if no docstring can
256 be found. If the node provided does not have docstrings a TypeError
257 will be raised.
Matthias Bussonnier41cea702017-02-23 22:44:19 -0800258
259 If *clean* is `True`, all tabs are expanded to spaces and any whitespace
260 that can be uniformly removed from the second line onwards is removed.
Georg Brandl0c77a822008-06-10 16:37:50 +0000261 """
Yury Selivanov2f07a662015-07-23 08:54:35 +0300262 if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
Georg Brandl0c77a822008-06-10 16:37:50 +0000263 raise TypeError("%r can't have docstrings" % node.__class__.__name__)
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300264 if not(node.body and isinstance(node.body[0], Expr)):
Serhiy Storchaka73cbe7a2018-05-29 12:04:55 +0300265 return None
266 node = node.body[0].value
267 if isinstance(node, Str):
268 text = node.s
269 elif isinstance(node, Constant) and isinstance(node.value, str):
270 text = node.value
271 else:
272 return None
Serhiy Storchaka08f127a2018-06-15 11:05:15 +0300273 if clean:
Victor Stinnerf2c1aa12016-01-26 00:40:57 +0100274 import inspect
275 text = inspect.cleandoc(text)
276 return text
Georg Brandl0c77a822008-06-10 16:37:50 +0000277
278
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000279def _splitlines_no_ff(source):
280 """Split a string into lines ignoring form feed and other chars.
281
282 This mimics how the Python parser splits source code.
283 """
284 idx = 0
285 lines = []
286 next_line = ''
287 while idx < len(source):
288 c = source[idx]
289 next_line += c
290 idx += 1
291 # Keep \r\n together
292 if c == '\r' and idx < len(source) and source[idx] == '\n':
293 next_line += '\n'
294 idx += 1
295 if c in '\r\n':
296 lines.append(next_line)
297 next_line = ''
298
299 if next_line:
300 lines.append(next_line)
301 return lines
302
303
304def _pad_whitespace(source):
mpheathfbeba8f2020-02-14 04:32:09 +1000305 r"""Replace all chars except '\f\t' in a line with spaces."""
Ivan Levkivskyi9932a222019-01-22 11:18:22 +0000306 result = ''
307 for c in source:
308 if c in '\f\t':
309 result += c
310 else:
311 result += ' '
312 return result
313
314
315def get_source_segment(source, node, *, padded=False):
316 """Get source code segment of the *source* that generated *node*.
317
318 If some location information (`lineno`, `end_lineno`, `col_offset`,
319 or `end_col_offset`) is missing, return None.
320
321 If *padded* is `True`, the first line of a multi-line statement will
322 be padded with spaces to match its original position.
323 """
324 try:
325 lineno = node.lineno - 1
326 end_lineno = node.end_lineno - 1
327 col_offset = node.col_offset
328 end_col_offset = node.end_col_offset
329 except AttributeError:
330 return None
331
332 lines = _splitlines_no_ff(source)
333 if end_lineno == lineno:
334 return lines[lineno].encode()[col_offset:end_col_offset].decode()
335
336 if padded:
337 padding = _pad_whitespace(lines[lineno].encode()[:col_offset].decode())
338 else:
339 padding = ''
340
341 first = padding + lines[lineno].encode()[col_offset:].decode()
342 last = lines[end_lineno].encode()[:end_col_offset].decode()
343 lines = lines[lineno+1:end_lineno]
344
345 lines.insert(0, first)
346 lines.append(last)
347 return ''.join(lines)
348
349
Georg Brandl0c77a822008-06-10 16:37:50 +0000350def walk(node):
351 """
Georg Brandl619e7ba2011-01-09 07:38:51 +0000352 Recursively yield all descendant nodes in the tree starting at *node*
353 (including *node* itself), in no specified order. This is useful if you
354 only want to modify nodes in place and don't care about the context.
Georg Brandl0c77a822008-06-10 16:37:50 +0000355 """
356 from collections import deque
357 todo = deque([node])
358 while todo:
359 node = todo.popleft()
360 todo.extend(iter_child_nodes(node))
361 yield node
362
363
364class NodeVisitor(object):
365 """
366 A node visitor base class that walks the abstract syntax tree and calls a
367 visitor function for every node found. This function may return a value
368 which is forwarded by the `visit` method.
369
370 This class is meant to be subclassed, with the subclass adding visitor
371 methods.
372
373 Per default the visitor functions for the nodes are ``'visit_'`` +
374 class name of the node. So a `TryFinally` node visit function would
375 be `visit_TryFinally`. This behavior can be changed by overriding
376 the `visit` method. If no visitor function exists for a node
377 (return value `None`) the `generic_visit` visitor is used instead.
378
379 Don't use the `NodeVisitor` if you want to apply changes to nodes during
380 traversing. For this a special visitor exists (`NodeTransformer`) that
381 allows modifications.
382 """
383
384 def visit(self, node):
385 """Visit a node."""
386 method = 'visit_' + node.__class__.__name__
387 visitor = getattr(self, method, self.generic_visit)
388 return visitor(node)
389
390 def generic_visit(self, node):
391 """Called if no explicit visitor function exists for a node."""
392 for field, value in iter_fields(node):
393 if isinstance(value, list):
394 for item in value:
395 if isinstance(item, AST):
396 self.visit(item)
397 elif isinstance(value, AST):
398 self.visit(value)
399
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300400 def visit_Constant(self, node):
401 value = node.value
402 type_name = _const_node_type_names.get(type(value))
403 if type_name is None:
404 for cls, name in _const_node_type_names.items():
405 if isinstance(value, cls):
406 type_name = name
407 break
408 if type_name is not None:
409 method = 'visit_' + type_name
410 try:
411 visitor = getattr(self, method)
412 except AttributeError:
413 pass
414 else:
415 import warnings
416 warnings.warn(f"{method} is deprecated; add visit_Constant",
417 DeprecationWarning, 2)
418 return visitor(node)
419 return self.generic_visit(node)
420
Georg Brandl0c77a822008-06-10 16:37:50 +0000421
422class NodeTransformer(NodeVisitor):
423 """
424 A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
425 allows modification of nodes.
426
427 The `NodeTransformer` will walk the AST and use the return value of the
428 visitor methods to replace or remove the old node. If the return value of
429 the visitor method is ``None``, the node will be removed from its location,
430 otherwise it is replaced with the return value. The return value may be the
431 original node in which case no replacement takes place.
432
433 Here is an example transformer that rewrites all occurrences of name lookups
434 (``foo``) to ``data['foo']``::
435
436 class RewriteName(NodeTransformer):
437
438 def visit_Name(self, node):
439 return copy_location(Subscript(
440 value=Name(id='data', ctx=Load()),
441 slice=Index(value=Str(s=node.id)),
442 ctx=node.ctx
443 ), node)
444
445 Keep in mind that if the node you're operating on has child nodes you must
446 either transform the child nodes yourself or call the :meth:`generic_visit`
447 method for the node first.
448
449 For nodes that were part of a collection of statements (that applies to all
450 statement nodes), the visitor may also return a list of nodes rather than
451 just a single node.
452
453 Usually you use the transformer like this::
454
455 node = YourTransformer().visit(node)
456 """
457
458 def generic_visit(self, node):
459 for field, old_value in iter_fields(node):
Georg Brandl0c77a822008-06-10 16:37:50 +0000460 if isinstance(old_value, list):
461 new_values = []
462 for value in old_value:
463 if isinstance(value, AST):
464 value = self.visit(value)
465 if value is None:
466 continue
467 elif not isinstance(value, AST):
468 new_values.extend(value)
469 continue
470 new_values.append(value)
471 old_value[:] = new_values
472 elif isinstance(old_value, AST):
473 new_node = self.visit(old_value)
474 if new_node is None:
475 delattr(node, field)
476 else:
477 setattr(node, field, new_node)
478 return node
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300479
480
481# The following code is for backward compatibility.
482# It will be removed in future.
483
484def _getter(self):
485 return self.value
486
487def _setter(self, value):
488 self.value = value
489
490Constant.n = property(_getter, _setter)
491Constant.s = property(_getter, _setter)
492
493class _ABC(type):
494
495 def __instancecheck__(cls, inst):
496 if not isinstance(inst, Constant):
497 return False
498 if cls in _const_types:
499 try:
500 value = inst.value
501 except AttributeError:
502 return False
503 else:
Anthony Sottile74176222019-01-18 11:30:28 -0800504 return (
505 isinstance(value, _const_types[cls]) and
506 not isinstance(value, _const_types_not.get(cls, ()))
507 )
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300508 return type.__instancecheck__(cls, inst)
509
510def _new(cls, *args, **kwargs):
511 if cls in _const_types:
512 return Constant(*args, **kwargs)
513 return Constant.__new__(cls, *args, **kwargs)
514
515class Num(Constant, metaclass=_ABC):
516 _fields = ('n',)
517 __new__ = _new
518
519class Str(Constant, metaclass=_ABC):
520 _fields = ('s',)
521 __new__ = _new
522
523class Bytes(Constant, metaclass=_ABC):
524 _fields = ('s',)
525 __new__ = _new
526
527class NameConstant(Constant, metaclass=_ABC):
528 __new__ = _new
529
530class Ellipsis(Constant, metaclass=_ABC):
531 _fields = ()
532
533 def __new__(cls, *args, **kwargs):
534 if cls is Ellipsis:
535 return Constant(..., *args, **kwargs)
536 return Constant.__new__(cls, *args, **kwargs)
537
538_const_types = {
539 Num: (int, float, complex),
540 Str: (str,),
541 Bytes: (bytes,),
542 NameConstant: (type(None), bool),
543 Ellipsis: (type(...),),
544}
Anthony Sottile74176222019-01-18 11:30:28 -0800545_const_types_not = {
546 Num: (bool,),
547}
Serhiy Storchakac3ea41e2019-08-26 10:13:19 +0300548_const_node_type_names = {
549 bool: 'NameConstant', # should be before int
550 type(None): 'NameConstant',
551 int: 'Num',
552 float: 'Num',
553 complex: 'Num',
554 str: 'Str',
555 bytes: 'Bytes',
556 type(...): 'Ellipsis',
557}
Serhiy Storchaka832e8642019-09-09 23:36:13 +0300558
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000559# Large float and imaginary literals get turned into infinities in the AST.
560# We unparse those infinities to INFSTR.
561_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
562
563class _Unparser(NodeVisitor):
564 """Methods in this class recursively traverse an AST and
565 output source code for the abstract syntax; original formatting
566 is disregarded."""
567
568 def __init__(self):
569 self._source = []
570 self._buffer = []
571 self._indent = 0
572
573 def interleave(self, inter, f, seq):
574 """Call f on each item in seq, calling inter() in between."""
575 seq = iter(seq)
576 try:
577 f(next(seq))
578 except StopIteration:
579 pass
580 else:
581 for x in seq:
582 inter()
583 f(x)
584
585 def fill(self, text=""):
586 """Indent a piece of text and append it, according to the current
587 indentation level"""
588 self.write("\n" + " " * self._indent + text)
589
590 def write(self, text):
591 """Append a piece of text"""
592 self._source.append(text)
593
594 def buffer_writer(self, text):
595 self._buffer.append(text)
596
597 @property
598 def buffer(self):
599 value = "".join(self._buffer)
600 self._buffer.clear()
601 return value
602
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000603 @contextmanager
604 def block(self):
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000605 """A context manager for preparing the source for blocks. It adds
606 the character':', increases the indentation on enter and decreases
607 the indentation on exit."""
Pablo Galindod69cbeb2019-12-23 16:42:48 +0000608 self.write(":")
609 self._indent += 1
610 yield
611 self._indent -= 1
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000612
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300613 @contextmanager
614 def delimit(self, start, end):
615 """A context manager for preparing the source for expressions. It adds
616 *start* to the buffer and enters, after exit it adds *end*."""
617
618 self.write(start)
619 yield
620 self.write(end)
621
622 def delimit_if(self, start, end, condition):
623 if condition:
624 return self.delimit(start, end)
625 else:
626 return nullcontext()
627
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000628 def traverse(self, node):
629 if isinstance(node, list):
630 for item in node:
631 self.traverse(item)
632 else:
633 super().visit(node)
634
635 def visit(self, node):
636 """Outputs a source code string that, if converted back to an ast
637 (using ast.parse) will generate an AST equivalent to *node*"""
638 self._source = []
639 self.traverse(node)
640 return "".join(self._source)
641
642 def visit_Module(self, node):
643 for subnode in node.body:
644 self.traverse(subnode)
645
646 def visit_Expr(self, node):
647 self.fill()
648 self.traverse(node.value)
649
650 def visit_NamedExpr(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300651 with self.delimit("(", ")"):
652 self.traverse(node.target)
653 self.write(" := ")
654 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000655
656 def visit_Import(self, node):
657 self.fill("import ")
658 self.interleave(lambda: self.write(", "), self.traverse, node.names)
659
660 def visit_ImportFrom(self, node):
661 self.fill("from ")
662 self.write("." * node.level)
663 if node.module:
664 self.write(node.module)
665 self.write(" import ")
666 self.interleave(lambda: self.write(", "), self.traverse, node.names)
667
668 def visit_Assign(self, node):
669 self.fill()
670 for target in node.targets:
671 self.traverse(target)
672 self.write(" = ")
673 self.traverse(node.value)
674
675 def visit_AugAssign(self, node):
676 self.fill()
677 self.traverse(node.target)
678 self.write(" " + self.binop[node.op.__class__.__name__] + "= ")
679 self.traverse(node.value)
680
681 def visit_AnnAssign(self, node):
682 self.fill()
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300683 with self.delimit_if("(", ")", not node.simple and isinstance(node.target, Name)):
684 self.traverse(node.target)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000685 self.write(": ")
686 self.traverse(node.annotation)
687 if node.value:
688 self.write(" = ")
689 self.traverse(node.value)
690
691 def visit_Return(self, node):
692 self.fill("return")
693 if node.value:
694 self.write(" ")
695 self.traverse(node.value)
696
697 def visit_Pass(self, node):
698 self.fill("pass")
699
700 def visit_Break(self, node):
701 self.fill("break")
702
703 def visit_Continue(self, node):
704 self.fill("continue")
705
706 def visit_Delete(self, node):
707 self.fill("del ")
708 self.interleave(lambda: self.write(", "), self.traverse, node.targets)
709
710 def visit_Assert(self, node):
711 self.fill("assert ")
712 self.traverse(node.test)
713 if node.msg:
714 self.write(", ")
715 self.traverse(node.msg)
716
717 def visit_Global(self, node):
718 self.fill("global ")
719 self.interleave(lambda: self.write(", "), self.write, node.names)
720
721 def visit_Nonlocal(self, node):
722 self.fill("nonlocal ")
723 self.interleave(lambda: self.write(", "), self.write, node.names)
724
725 def visit_Await(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300726 with self.delimit("(", ")"):
727 self.write("await")
728 if node.value:
729 self.write(" ")
730 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000731
732 def visit_Yield(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300733 with self.delimit("(", ")"):
734 self.write("yield")
735 if node.value:
736 self.write(" ")
737 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000738
739 def visit_YieldFrom(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300740 with self.delimit("(", ")"):
Batuhan Taşkaya7b35bef2020-01-02 21:20:04 +0300741 self.write("yield from ")
742 if not node.value:
743 raise ValueError("Node can't be used without a value attribute.")
744 self.traverse(node.value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000745
746 def visit_Raise(self, node):
747 self.fill("raise")
748 if not node.exc:
749 if node.cause:
750 raise ValueError(f"Node can't use cause without an exception.")
751 return
752 self.write(" ")
753 self.traverse(node.exc)
754 if node.cause:
755 self.write(" from ")
756 self.traverse(node.cause)
757
758 def visit_Try(self, node):
759 self.fill("try")
760 with self.block():
761 self.traverse(node.body)
762 for ex in node.handlers:
763 self.traverse(ex)
764 if node.orelse:
765 self.fill("else")
766 with self.block():
767 self.traverse(node.orelse)
768 if node.finalbody:
769 self.fill("finally")
770 with self.block():
771 self.traverse(node.finalbody)
772
773 def visit_ExceptHandler(self, node):
774 self.fill("except")
775 if node.type:
776 self.write(" ")
777 self.traverse(node.type)
778 if node.name:
779 self.write(" as ")
780 self.write(node.name)
781 with self.block():
782 self.traverse(node.body)
783
784 def visit_ClassDef(self, node):
785 self.write("\n")
786 for deco in node.decorator_list:
787 self.fill("@")
788 self.traverse(deco)
789 self.fill("class " + node.name)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300790 with self.delimit("(", ")"):
791 comma = False
792 for e in node.bases:
793 if comma:
794 self.write(", ")
795 else:
796 comma = True
797 self.traverse(e)
798 for e in node.keywords:
799 if comma:
800 self.write(", ")
801 else:
802 comma = True
803 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000804
805 with self.block():
806 self.traverse(node.body)
807
808 def visit_FunctionDef(self, node):
809 self.__FunctionDef_helper(node, "def")
810
811 def visit_AsyncFunctionDef(self, node):
812 self.__FunctionDef_helper(node, "async def")
813
814 def __FunctionDef_helper(self, node, fill_suffix):
815 self.write("\n")
816 for deco in node.decorator_list:
817 self.fill("@")
818 self.traverse(deco)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300819 def_str = fill_suffix + " " + node.name
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000820 self.fill(def_str)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300821 with self.delimit("(", ")"):
822 self.traverse(node.args)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000823 if node.returns:
824 self.write(" -> ")
825 self.traverse(node.returns)
826 with self.block():
827 self.traverse(node.body)
828
829 def visit_For(self, node):
830 self.__For_helper("for ", node)
831
832 def visit_AsyncFor(self, node):
833 self.__For_helper("async for ", node)
834
835 def __For_helper(self, fill, node):
836 self.fill(fill)
837 self.traverse(node.target)
838 self.write(" in ")
839 self.traverse(node.iter)
840 with self.block():
841 self.traverse(node.body)
842 if node.orelse:
843 self.fill("else")
844 with self.block():
845 self.traverse(node.orelse)
846
847 def visit_If(self, node):
848 self.fill("if ")
849 self.traverse(node.test)
850 with self.block():
851 self.traverse(node.body)
852 # collapse nested ifs into equivalent elifs.
853 while node.orelse and len(node.orelse) == 1 and isinstance(node.orelse[0], If):
854 node = node.orelse[0]
855 self.fill("elif ")
856 self.traverse(node.test)
857 with self.block():
858 self.traverse(node.body)
859 # final else
860 if node.orelse:
861 self.fill("else")
862 with self.block():
863 self.traverse(node.orelse)
864
865 def visit_While(self, node):
866 self.fill("while ")
867 self.traverse(node.test)
868 with self.block():
869 self.traverse(node.body)
870 if node.orelse:
871 self.fill("else")
872 with self.block():
873 self.traverse(node.orelse)
874
875 def visit_With(self, node):
876 self.fill("with ")
877 self.interleave(lambda: self.write(", "), self.traverse, node.items)
878 with self.block():
879 self.traverse(node.body)
880
881 def visit_AsyncWith(self, node):
882 self.fill("async with ")
883 self.interleave(lambda: self.write(", "), self.traverse, node.items)
884 with self.block():
885 self.traverse(node.body)
886
887 def visit_JoinedStr(self, node):
888 self.write("f")
889 self._fstring_JoinedStr(node, self.buffer_writer)
890 self.write(repr(self.buffer))
891
892 def visit_FormattedValue(self, node):
893 self.write("f")
894 self._fstring_FormattedValue(node, self.buffer_writer)
895 self.write(repr(self.buffer))
896
897 def _fstring_JoinedStr(self, node, write):
898 for value in node.values:
899 meth = getattr(self, "_fstring_" + type(value).__name__)
900 meth(value, write)
901
902 def _fstring_Constant(self, node, write):
903 if not isinstance(node.value, str):
904 raise ValueError("Constants inside JoinedStr should be a string.")
905 value = node.value.replace("{", "{{").replace("}", "}}")
906 write(value)
907
908 def _fstring_FormattedValue(self, node, write):
909 write("{")
910 expr = type(self)().visit(node.value).rstrip("\n")
911 if expr.startswith("{"):
912 write(" ") # Separate pair of opening brackets as "{ {"
913 write(expr)
914 if node.conversion != -1:
915 conversion = chr(node.conversion)
916 if conversion not in "sra":
917 raise ValueError("Unknown f-string conversion.")
918 write(f"!{conversion}")
919 if node.format_spec:
920 write(":")
921 meth = getattr(self, "_fstring_" + type(node.format_spec).__name__)
922 meth(node.format_spec, write)
923 write("}")
924
925 def visit_Name(self, node):
926 self.write(node.id)
927
928 def _write_constant(self, value):
929 if isinstance(value, (float, complex)):
930 # Substitute overflowing decimal literal for AST infinities.
931 self.write(repr(value).replace("inf", _INFSTR))
932 else:
933 self.write(repr(value))
934
935 def visit_Constant(self, node):
936 value = node.value
937 if isinstance(value, tuple):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300938 with self.delimit("(", ")"):
939 if len(value) == 1:
940 self._write_constant(value[0])
941 self.write(",")
942 else:
943 self.interleave(lambda: self.write(", "), self._write_constant, value)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000944 elif value is ...:
945 self.write("...")
946 else:
947 if node.kind == "u":
948 self.write("u")
949 self._write_constant(node.value)
950
951 def visit_List(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300952 with self.delimit("[", "]"):
953 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000954
955 def visit_ListComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300956 with self.delimit("[", "]"):
957 self.traverse(node.elt)
958 for gen in node.generators:
959 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000960
961 def visit_GeneratorExp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300962 with self.delimit("(", ")"):
963 self.traverse(node.elt)
964 for gen in node.generators:
965 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000966
967 def visit_SetComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300968 with self.delimit("{", "}"):
969 self.traverse(node.elt)
970 for gen in node.generators:
971 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000972
973 def visit_DictComp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300974 with self.delimit("{", "}"):
975 self.traverse(node.key)
976 self.write(": ")
977 self.traverse(node.value)
978 for gen in node.generators:
979 self.traverse(gen)
Pablo Galindo27fc3b62019-11-24 23:02:40 +0000980
981 def visit_comprehension(self, node):
982 if node.is_async:
983 self.write(" async for ")
984 else:
985 self.write(" for ")
986 self.traverse(node.target)
987 self.write(" in ")
988 self.traverse(node.iter)
989 for if_clause in node.ifs:
990 self.write(" if ")
991 self.traverse(if_clause)
992
993 def visit_IfExp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +0300994 with self.delimit("(", ")"):
995 self.traverse(node.body)
996 self.write(" if ")
997 self.traverse(node.test)
998 self.write(" else ")
999 self.traverse(node.orelse)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001000
1001 def visit_Set(self, node):
1002 if not node.elts:
1003 raise ValueError("Set node should has at least one item")
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001004 with self.delimit("{", "}"):
1005 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001006
1007 def visit_Dict(self, node):
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001008 def write_key_value_pair(k, v):
1009 self.traverse(k)
1010 self.write(": ")
1011 self.traverse(v)
1012
1013 def write_item(item):
1014 k, v = item
1015 if k is None:
1016 # for dictionary unpacking operator in dicts {**{'y': 2}}
1017 # see PEP 448 for details
1018 self.write("**")
1019 self.traverse(v)
1020 else:
1021 write_key_value_pair(k, v)
1022
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001023 with self.delimit("{", "}"):
1024 self.interleave(
1025 lambda: self.write(", "), write_item, zip(node.keys, node.values)
1026 )
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001027
1028 def visit_Tuple(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001029 with self.delimit("(", ")"):
1030 if len(node.elts) == 1:
1031 elt = node.elts[0]
1032 self.traverse(elt)
1033 self.write(",")
1034 else:
1035 self.interleave(lambda: self.write(", "), self.traverse, node.elts)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001036
1037 unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-"}
1038
1039 def visit_UnaryOp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001040 with self.delimit("(", ")"):
1041 self.write(self.unop[node.op.__class__.__name__])
1042 self.write(" ")
1043 self.traverse(node.operand)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001044
1045 binop = {
1046 "Add": "+",
1047 "Sub": "-",
1048 "Mult": "*",
1049 "MatMult": "@",
1050 "Div": "/",
1051 "Mod": "%",
1052 "LShift": "<<",
1053 "RShift": ">>",
1054 "BitOr": "|",
1055 "BitXor": "^",
1056 "BitAnd": "&",
1057 "FloorDiv": "//",
1058 "Pow": "**",
1059 }
1060
1061 def visit_BinOp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001062 with self.delimit("(", ")"):
1063 self.traverse(node.left)
1064 self.write(" " + self.binop[node.op.__class__.__name__] + " ")
1065 self.traverse(node.right)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001066
1067 cmpops = {
1068 "Eq": "==",
1069 "NotEq": "!=",
1070 "Lt": "<",
1071 "LtE": "<=",
1072 "Gt": ">",
1073 "GtE": ">=",
1074 "Is": "is",
1075 "IsNot": "is not",
1076 "In": "in",
1077 "NotIn": "not in",
1078 }
1079
1080 def visit_Compare(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001081 with self.delimit("(", ")"):
1082 self.traverse(node.left)
1083 for o, e in zip(node.ops, node.comparators):
1084 self.write(" " + self.cmpops[o.__class__.__name__] + " ")
1085 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001086
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001087 boolops = {"And": "and", "Or": "or"}
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001088
1089 def visit_BoolOp(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001090 with self.delimit("(", ")"):
1091 s = " %s " % self.boolops[node.op.__class__.__name__]
1092 self.interleave(lambda: self.write(s), self.traverse, node.values)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001093
1094 def visit_Attribute(self, node):
1095 self.traverse(node.value)
1096 # Special case: 3.__abs__() is a syntax error, so if node.value
1097 # is an integer literal then we need to either parenthesize
1098 # it or add an extra space to get 3 .__abs__().
1099 if isinstance(node.value, Constant) and isinstance(node.value.value, int):
1100 self.write(" ")
1101 self.write(".")
1102 self.write(node.attr)
1103
1104 def visit_Call(self, node):
1105 self.traverse(node.func)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001106 with self.delimit("(", ")"):
1107 comma = False
1108 for e in node.args:
1109 if comma:
1110 self.write(", ")
1111 else:
1112 comma = True
1113 self.traverse(e)
1114 for e in node.keywords:
1115 if comma:
1116 self.write(", ")
1117 else:
1118 comma = True
1119 self.traverse(e)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001120
1121 def visit_Subscript(self, node):
1122 self.traverse(node.value)
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001123 with self.delimit("[", "]"):
1124 self.traverse(node.slice)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001125
1126 def visit_Starred(self, node):
1127 self.write("*")
1128 self.traverse(node.value)
1129
1130 def visit_Ellipsis(self, node):
1131 self.write("...")
1132
1133 def visit_Index(self, node):
1134 self.traverse(node.value)
1135
1136 def visit_Slice(self, node):
1137 if node.lower:
1138 self.traverse(node.lower)
1139 self.write(":")
1140 if node.upper:
1141 self.traverse(node.upper)
1142 if node.step:
1143 self.write(":")
1144 self.traverse(node.step)
1145
1146 def visit_ExtSlice(self, node):
1147 self.interleave(lambda: self.write(", "), self.traverse, node.dims)
1148
1149 def visit_arg(self, node):
1150 self.write(node.arg)
1151 if node.annotation:
1152 self.write(": ")
1153 self.traverse(node.annotation)
1154
1155 def visit_arguments(self, node):
1156 first = True
1157 # normal arguments
1158 all_args = node.posonlyargs + node.args
1159 defaults = [None] * (len(all_args) - len(node.defaults)) + node.defaults
1160 for index, elements in enumerate(zip(all_args, defaults), 1):
1161 a, d = elements
1162 if first:
1163 first = False
1164 else:
1165 self.write(", ")
1166 self.traverse(a)
1167 if d:
1168 self.write("=")
1169 self.traverse(d)
1170 if index == len(node.posonlyargs):
1171 self.write(", /")
1172
1173 # varargs, or bare '*' if no varargs but keyword-only arguments present
1174 if node.vararg or node.kwonlyargs:
1175 if first:
1176 first = False
1177 else:
1178 self.write(", ")
1179 self.write("*")
1180 if node.vararg:
1181 self.write(node.vararg.arg)
1182 if node.vararg.annotation:
1183 self.write(": ")
1184 self.traverse(node.vararg.annotation)
1185
1186 # keyword-only arguments
1187 if node.kwonlyargs:
1188 for a, d in zip(node.kwonlyargs, node.kw_defaults):
Batuhan Taşkayaa322f502019-12-16 15:26:58 +03001189 self.write(", ")
1190 self.traverse(a)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001191 if d:
1192 self.write("=")
1193 self.traverse(d)
1194
1195 # kwargs
1196 if node.kwarg:
1197 if first:
1198 first = False
1199 else:
1200 self.write(", ")
1201 self.write("**" + node.kwarg.arg)
1202 if node.kwarg.annotation:
1203 self.write(": ")
1204 self.traverse(node.kwarg.annotation)
1205
1206 def visit_keyword(self, node):
1207 if node.arg is None:
1208 self.write("**")
1209 else:
1210 self.write(node.arg)
1211 self.write("=")
1212 self.traverse(node.value)
1213
1214 def visit_Lambda(self, node):
Batuhan Taşkaya4b3b1222019-12-23 19:11:00 +03001215 with self.delimit("(", ")"):
1216 self.write("lambda ")
1217 self.traverse(node.args)
1218 self.write(": ")
1219 self.traverse(node.body)
Pablo Galindo27fc3b62019-11-24 23:02:40 +00001220
1221 def visit_alias(self, node):
1222 self.write(node.name)
1223 if node.asname:
1224 self.write(" as " + node.asname)
1225
1226 def visit_withitem(self, node):
1227 self.traverse(node.context_expr)
1228 if node.optional_vars:
1229 self.write(" as ")
1230 self.traverse(node.optional_vars)
1231
1232def unparse(ast_obj):
1233 unparser = _Unparser()
1234 return unparser.visit(ast_obj)
1235
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001236
1237def main():
1238 import argparse
1239
1240 parser = argparse.ArgumentParser(prog='python -m ast')
1241 parser.add_argument('infile', type=argparse.FileType(mode='rb'), nargs='?',
1242 default='-',
1243 help='the file to parse; defaults to stdin')
1244 parser.add_argument('-m', '--mode', default='exec',
1245 choices=('exec', 'single', 'eval', 'func_type'),
1246 help='specify what kind of code must be parsed')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001247 parser.add_argument('--no-type-comments', default=True, action='store_false',
1248 help="don't add information about type comments")
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001249 parser.add_argument('-a', '--include-attributes', action='store_true',
1250 help='include attributes such as line numbers and '
1251 'column offsets')
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001252 parser.add_argument('-i', '--indent', type=int, default=3,
1253 help='indentation of nodes (number of spaces)')
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001254 args = parser.parse_args()
1255
1256 with args.infile as infile:
1257 source = infile.read()
Batuhan Taşkaya814d6872019-12-16 21:23:27 +03001258 tree = parse(source, args.infile.name, args.mode, type_comments=args.no_type_comments)
1259 print(dump(tree, include_attributes=args.include_attributes, indent=args.indent))
Serhiy Storchaka832e8642019-09-09 23:36:13 +03001260
1261if __name__ == '__main__':
1262 main()