11"""
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.
2+ The `ast` module helps Python applications to process trees of the Python
3+ abstract syntax grammar. The abstract syntax itself might change with
4+ each Python release; this module helps to find out programmatically what
5+ the current grammar looks like and allows modifications of it.
6+
7+ An abstract syntax tree can be generated by passing `ast.PyCF_ONLY_AST` as
8+ a flag to the `compile()` builtin function or by using the `parse()`
9+ function from this module. The result will be a tree of objects whose
10+ classes all inherit from `ast.AST`.
11+
12+ A modified abstract syntax tree can be compiled into a Python code object
13+ using the built-in `compile()` function.
14+
15+ Additionally various helper functions are provided that make working with
16+ the trees simpler. The main intention of the helper functions and this
17+ module in general is to provide an easy to use interface for libraries
18+ that work tightly with the python syntax (template engines for example).
19+
20+ :copyright: Copyright 2008 by Armin Ronacher.
21+ :license: Python License.
2622"""
2723import sys
2824import re
3228
3329
3430def parse (source , filename = '<unknown>' , mode = 'exec' , * ,
35- type_comments = False , feature_version = None ):
31+ type_comments = False , feature_version = None , optimize = - 1 ):
3632 """
3733 Parse the source into an AST node.
3834 Equivalent to compile(source, filename, mode, PyCF_ONLY_AST).
3935 Pass type_comments=True to get back type comments where the syntax allows.
4036 """
4137 flags = PyCF_ONLY_AST
38+ if optimize > 0 :
39+ flags |= PyCF_OPTIMIZED_AST
4240 if type_comments :
4341 flags |= PyCF_TYPE_COMMENTS
4442 if feature_version is None :
@@ -50,7 +48,7 @@ def parse(source, filename='<unknown>', mode='exec', *,
5048 feature_version = minor
5149 # Else it should be an int giving the minor version for 3.x.
5250 return compile (source , filename , mode , flags ,
53- _feature_version = feature_version )
51+ _feature_version = feature_version , optimize = optimize )
5452
5553
5654def literal_eval (node_or_string ):
@@ -112,7 +110,11 @@ def _convert(node):
112110 return _convert (node_or_string )
113111
114112
115- def dump (node , annotate_fields = True , include_attributes = False , * , indent = None ):
113+ def dump (
114+ node , annotate_fields = True , include_attributes = False ,
115+ * ,
116+ indent = None , show_empty = False ,
117+ ):
116118 """
117119 Return a formatted dump of the tree in node. This is mainly useful for
118120 debugging purposes. If annotate_fields is true (by default),
@@ -123,6 +125,8 @@ def dump(node, annotate_fields=True, include_attributes=False, *, indent=None):
123125 include_attributes can be set to true. If indent is a non-negative
124126 integer or string, then the tree will be pretty-printed with that indent
125127 level. None (the default) selects the single line representation.
128+ If show_empty is False, then empty lists and fields that are None
129+ will be omitted from the output for better readability.
126130 """
127131 def _format (node , level = 0 ):
128132 if indent is not None :
@@ -135,6 +139,7 @@ def _format(node, level=0):
135139 if isinstance (node , AST ):
136140 cls = type (node )
137141 args = []
142+ args_buffer = []
138143 allsimple = True
139144 keywords = annotate_fields
140145 for name in node ._fields :
@@ -146,6 +151,16 @@ def _format(node, level=0):
146151 if value is None and getattr (cls , name , ...) is None :
147152 keywords = True
148153 continue
154+ if not show_empty :
155+ if value == []:
156+ field_type = cls ._field_types .get (name , object )
157+ if getattr (field_type , '__origin__' , ...) is list :
158+ if not keywords :
159+ args_buffer .append (repr (value ))
160+ continue
161+ if not keywords :
162+ args .extend (args_buffer )
163+ args_buffer = []
149164 value , simple = _format (value , level )
150165 allsimple = allsimple and simple
151166 if keywords :
@@ -726,12 +741,11 @@ class _Unparser(NodeVisitor):
726741 output source code for the abstract syntax; original formatting
727742 is disregarded."""
728743
729- def __init__ (self , * , _avoid_backslashes = False ):
744+ def __init__ (self ):
730745 self ._source = []
731746 self ._precedences = {}
732747 self ._type_ignores = {}
733748 self ._indent = 0
734- self ._avoid_backslashes = _avoid_backslashes
735749 self ._in_try_star = False
736750
737751 def interleave (self , inter , f , seq ):
@@ -1104,12 +1118,21 @@ def visit_TypeVar(self, node):
11041118 if node .bound :
11051119 self .write (": " )
11061120 self .traverse (node .bound )
1121+ if node .default_value :
1122+ self .write (" = " )
1123+ self .traverse (node .default_value )
11071124
11081125 def visit_TypeVarTuple (self , node ):
11091126 self .write ("*" + node .name )
1127+ if node .default_value :
1128+ self .write (" = " )
1129+ self .traverse (node .default_value )
11101130
11111131 def visit_ParamSpec (self , node ):
11121132 self .write ("**" + node .name )
1133+ if node .default_value :
1134+ self .write (" = " )
1135+ self .traverse (node .default_value )
11131136
11141137 def visit_TypeAlias (self , node ):
11151138 self .fill ("type " )
@@ -1246,9 +1269,14 @@ def visit_JoinedStr(self, node):
12461269 fallback_to_repr = True
12471270 break
12481271 quote_types = new_quote_types
1249- elif "\n " in value :
1250- quote_types = [q for q in quote_types if q in _MULTI_QUOTES ]
1251- assert quote_types
1272+ else :
1273+ if "\n " in value :
1274+ quote_types = [q for q in quote_types if q in _MULTI_QUOTES ]
1275+ assert quote_types
1276+
1277+ new_quote_types = [q for q in quote_types if q not in value ]
1278+ if new_quote_types :
1279+ quote_types = new_quote_types
12521280 new_fstring_parts .append (value )
12531281
12541282 if fallback_to_repr :
@@ -1268,13 +1296,19 @@ def visit_JoinedStr(self, node):
12681296 quote_type = quote_types [0 ]
12691297 self .write (f"{ quote_type } { value } { quote_type } " )
12701298
1271- def _write_fstring_inner (self , node ):
1299+ def _write_fstring_inner (self , node , is_format_spec = False ):
12721300 if isinstance (node , JoinedStr ):
12731301 # for both the f-string itself, and format_spec
12741302 for value in node .values :
1275- self ._write_fstring_inner (value )
1303+ self ._write_fstring_inner (value , is_format_spec = is_format_spec )
12761304 elif isinstance (node , Constant ) and isinstance (node .value , str ):
12771305 value = node .value .replace ("{" , "{{" ).replace ("}" , "}}" )
1306+
1307+ if is_format_spec :
1308+ value = value .replace ("\\ " , "\\ \\ " )
1309+ value = value .replace ("'" , "\\ '" )
1310+ value = value .replace ('"' , '\\ "' )
1311+ value = value .replace ("\n " , "\\ n" )
12781312 self .write (value )
12791313 elif isinstance (node , FormattedValue ):
12801314 self .visit_FormattedValue (node )
@@ -1297,7 +1331,7 @@ def unparse_inner(inner):
12971331 self .write (f"!{ chr (node .conversion )} " )
12981332 if node .format_spec :
12991333 self .write (":" )
1300- self ._write_fstring_inner (node .format_spec )
1334+ self ._write_fstring_inner (node .format_spec , is_format_spec = True )
13011335
13021336 def visit_Name (self , node ):
13031337 self .write (node .id )
@@ -1317,8 +1351,6 @@ def _write_constant(self, value):
13171351 .replace ("inf" , _INFSTR )
13181352 .replace ("nan" , f"({ _INFSTR } -{ _INFSTR } )" )
13191353 )
1320- elif self ._avoid_backslashes and isinstance (value , str ):
1321- self ._write_str_avoiding_backslashes (value )
13221354 else :
13231355 self .write (repr (value ))
13241356
@@ -1805,8 +1837,7 @@ def main():
18051837 import argparse
18061838
18071839 parser = argparse .ArgumentParser (prog = 'python -m ast' )
1808- parser .add_argument ('infile' , type = argparse .FileType (mode = 'rb' ), nargs = '?' ,
1809- default = '-' ,
1840+ parser .add_argument ('infile' , nargs = '?' , default = '-' ,
18101841 help = 'the file to parse; defaults to stdin' )
18111842 parser .add_argument ('-m' , '--mode' , default = 'exec' ,
18121843 choices = ('exec' , 'single' , 'eval' , 'func_type' ),
@@ -1820,9 +1851,14 @@ def main():
18201851 help = 'indentation of nodes (number of spaces)' )
18211852 args = parser .parse_args ()
18221853
1823- with args .infile as infile :
1824- source = infile .read ()
1825- tree = parse (source , args .infile .name , args .mode , type_comments = args .no_type_comments )
1854+ if args .infile == '-' :
1855+ name = '<stdin>'
1856+ source = sys .stdin .buffer .read ()
1857+ else :
1858+ name = args .infile
1859+ with open (args .infile , 'rb' ) as infile :
1860+ source = infile .read ()
1861+ tree = parse (source , name , args .mode , type_comments = args .no_type_comments )
18261862 print (dump (tree , include_attributes = args .include_attributes , indent = args .indent ))
18271863
18281864if __name__ == '__main__' :
0 commit comments