gh-133367: Add missing options to ast CLI (#133369)

Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
Co-authored-by: sobolevn <mail@sobolevn.me>
This commit is contained in:
Semyon Moroz 2025-05-05 21:17:43 +04:00 committed by GitHub
parent 5c245ffce7
commit 2b4e2b7830
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 154 additions and 9 deletions

View file

@ -1,4 +1,4 @@
:mod:`!ast` --- Abstract Syntax Trees :mod:`!ast` --- Abstract syntax trees
===================================== =====================================
.. module:: ast .. module:: ast
@ -29,7 +29,7 @@ compiled into a Python code object using the built-in :func:`compile` function.
.. _abstract-grammar: .. _abstract-grammar:
Abstract Grammar Abstract grammar
---------------- ----------------
The abstract grammar is currently defined as follows: The abstract grammar is currently defined as follows:
@ -2156,10 +2156,10 @@ Async and await
of :class:`ast.operator`, :class:`ast.unaryop`, :class:`ast.cmpop`, of :class:`ast.operator`, :class:`ast.unaryop`, :class:`ast.cmpop`,
:class:`ast.boolop` and :class:`ast.expr_context`) on the returned tree :class:`ast.boolop` and :class:`ast.expr_context`) on the returned tree
will be singletons. Changes to one will be reflected in all other will be singletons. Changes to one will be reflected in all other
occurrences of the same value (e.g. :class:`ast.Add`). occurrences of the same value (for example, :class:`ast.Add`).
:mod:`ast` Helpers :mod:`ast` helpers
------------------ ------------------
Apart from the node classes, the :mod:`ast` module defines these utility functions Apart from the node classes, the :mod:`ast` module defines these utility functions
@ -2484,7 +2484,7 @@ and classes for traversing abstract syntax trees:
.. _ast-compiler-flags: .. _ast-compiler-flags:
Compiler Flags Compiler flags
-------------- --------------
The following flags may be passed to :func:`compile` in order to change The following flags may be passed to :func:`compile` in order to change
@ -2533,7 +2533,7 @@ effects on the compilation of a program:
.. _ast-cli: .. _ast-cli:
Command-Line Usage Command-line usage
------------------ ------------------
.. versionadded:: 3.9 .. versionadded:: 3.9
@ -2572,6 +2572,28 @@ The following options are accepted:
Indentation of nodes in AST (number of spaces). Indentation of nodes in AST (number of spaces).
.. option:: --feature-version <version>
Python version in the format 3.x (for example, 3.10). Defaults to the
current version of the interpreter.
.. versionadded:: next
.. option:: -O <level>
--optimize <level>
Optimization level for parser. Defaults to no optimization.
.. versionadded:: next
.. option:: --show-empty
Show empty lists and fields that are ``None``. Defaults to not showing empty
objects.
.. versionadded:: next
If :file:`infile` is specified its contents are parsed to AST and dumped If :file:`infile` is specified its contents are parsed to AST and dumped
to stdout. Otherwise, the content is read from stdin. to stdout. Otherwise, the content is read from stdin.

View file

@ -875,6 +875,11 @@ ast
that the root node type is appropriate. that the root node type is appropriate.
(Contributed by Irit Katriel in :gh:`130139`.) (Contributed by Irit Katriel in :gh:`130139`.)
* Add new ``--feature-version``, ``--optimize``, ``--show-empty`` options to
command-line interface.
(Contributed by Semyon Moroz in :gh:`133367`.)
bdb bdb
--- ---

View file

@ -643,6 +643,15 @@ def main(args=None):
'column offsets') 'column offsets')
parser.add_argument('-i', '--indent', type=int, default=3, parser.add_argument('-i', '--indent', type=int, default=3,
help='indentation of nodes (number of spaces)') help='indentation of nodes (number of spaces)')
parser.add_argument('--feature-version',
type=str, default=None, metavar='VERSION',
help='Python version in the format 3.x '
'(for example, 3.10)')
parser.add_argument('-O', '--optimize',
type=int, default=-1, metavar='LEVEL',
help='optimization level for parser (default -1)')
parser.add_argument('--show-empty', default=False, action='store_true',
help='show empty lists and fields in dump output')
args = parser.parse_args(args) args = parser.parse_args(args)
if args.infile == '-': if args.infile == '-':
@ -652,8 +661,22 @@ def main(args=None):
name = args.infile name = args.infile
with open(args.infile, 'rb') as infile: with open(args.infile, 'rb') as infile:
source = infile.read() source = infile.read()
tree = parse(source, name, args.mode, type_comments=args.no_type_comments)
print(dump(tree, include_attributes=args.include_attributes, indent=args.indent)) # Process feature_version
feature_version = None
if args.feature_version:
try:
major, minor = map(int, args.feature_version.split('.', 1))
except ValueError:
parser.error('Invalid format for --feature-version; '
'expected format 3.x (for example, 3.10)')
feature_version = (major, minor)
tree = parse(source, name, args.mode, type_comments=args.no_type_comments,
feature_version=feature_version, optimize=args.optimize)
print(dump(tree, include_attributes=args.include_attributes,
indent=args.indent, show_empty=args.show_empty))
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View file

@ -3272,6 +3272,9 @@ class CommandLineTests(unittest.TestCase):
('--no-type-comments', '--no-type-comments'), ('--no-type-comments', '--no-type-comments'),
('-a', '--include-attributes'), ('-a', '--include-attributes'),
('-i=4', '--indent=4'), ('-i=4', '--indent=4'),
('--feature-version=3.13', '--feature-version=3.13'),
('-O=-1', '--optimize=-1'),
('--show-empty', '--show-empty'),
) )
self.set_source(''' self.set_source('''
print(1, 2, 3) print(1, 2, 3)
@ -3389,7 +3392,7 @@ class CommandLineTests(unittest.TestCase):
self.check_output(source, expect, flag) self.check_output(source, expect, flag)
def test_indent_flag(self): def test_indent_flag(self):
# test 'python -m ast -i/--indent' # test 'python -m ast -i/--indent 0'
source = 'pass' source = 'pass'
expect = ''' expect = '''
Module( Module(
@ -3400,6 +3403,96 @@ class CommandLineTests(unittest.TestCase):
with self.subTest(flag=flag): with self.subTest(flag=flag):
self.check_output(source, expect, flag) self.check_output(source, expect, flag)
def test_feature_version_flag(self):
# test 'python -m ast --feature-version 3.9/3.10'
source = '''
match x:
case 1:
pass
'''
expect = '''
Module(
body=[
Match(
subject=Name(id='x', ctx=Load()),
cases=[
match_case(
pattern=MatchValue(
value=Constant(value=1)),
body=[
Pass()])])])
'''
self.check_output(source, expect, '--feature-version=3.10')
with self.assertRaises(SyntaxError):
self.invoke_ast('--feature-version=3.9')
def test_no_optimize_flag(self):
# test 'python -m ast -O/--optimize -1/0'
source = '''
match a:
case 1+2j:
pass
'''
expect = '''
Module(
body=[
Match(
subject=Name(id='a', ctx=Load()),
cases=[
match_case(
pattern=MatchValue(
value=BinOp(
left=Constant(value=1),
op=Add(),
right=Constant(value=2j))),
body=[
Pass()])])])
'''
for flag in ('-O=-1', '--optimize=-1', '-O=0', '--optimize=0'):
with self.subTest(flag=flag):
self.check_output(source, expect, flag)
def test_optimize_flag(self):
# test 'python -m ast -O/--optimize 1/2'
source = '''
match a:
case 1+2j:
pass
'''
expect = '''
Module(
body=[
Match(
subject=Name(id='a', ctx=Load()),
cases=[
match_case(
pattern=MatchValue(
value=Constant(value=(1+2j))),
body=[
Pass()])])])
'''
for flag in ('-O=1', '--optimize=1', '-O=2', '--optimize=2'):
with self.subTest(flag=flag):
self.check_output(source, expect, flag)
def test_show_empty_flag(self):
# test 'python -m ast --show-empty'
source = 'print(1, 2, 3)'
expect = '''
Module(
body=[
Expr(
value=Call(
func=Name(id='print', ctx=Load()),
args=[
Constant(value=1),
Constant(value=2),
Constant(value=3)],
keywords=[]))],
type_ignores=[])
'''
self.check_output(source, expect, '--show-empty')
class ASTOptimiziationTests(unittest.TestCase): class ASTOptimiziationTests(unittest.TestCase):
def wrap_expr(self, expr): def wrap_expr(self, expr):

View file

@ -0,0 +1,2 @@
Add the ``--feature-version``, ``--optimize``, and ``--show-empty`` options
to the :mod:`ast` command-line interface. Patch by Semyon Moroz.