1
1
import _ast_unparse
2
2
import ast
3
3
import builtins
4
+ import contextlib
4
5
import copy
5
6
import dis
6
7
import enum
8
+ import itertools
7
9
import os
8
10
import re
9
11
import sys
12
+ import tempfile
10
13
import textwrap
11
14
import types
12
15
import unittest
13
16
import weakref
17
+ from io import StringIO
14
18
from pathlib import Path
15
19
from textwrap import dedent
16
20
try :
19
23
_testinternalcapi = None
20
24
21
25
from test import support
22
- from test .support import os_helper , script_helper
26
+ from test .support import os_helper
23
27
from test .support import skip_emscripten_stack_overflow , skip_wasi_stack_overflow
24
28
from test .support .ast_helper import ASTTestMixin
25
29
from test .test_ast .utils import to_tuple
@@ -3232,23 +3236,169 @@ def test_subinterpreter(self):
3232
3236
self .assertEqual (res , 0 )
3233
3237
3234
3238
3235
- class ASTMainTests (unittest .TestCase ):
3236
- # Tests `ast.main()` function.
3239
+ class CommandLineTests (unittest .TestCase ):
3240
+ def setUp (self ):
3241
+ self .filename = tempfile .mktemp ()
3242
+ self .addCleanup (os_helper .unlink , self .filename )
3237
3243
3238
- def test_cli_file_input (self ):
3239
- code = "print(1, 2, 3)"
3240
- expected = ast .dump (ast .parse (code ), indent = 3 )
3241
-
3242
- with os_helper .temp_dir () as tmp_dir :
3243
- filename = os .path .join (tmp_dir , "test_module.py" )
3244
- with open (filename , 'w' , encoding = 'utf-8' ) as f :
3245
- f .write (code )
3246
- res , _ = script_helper .run_python_until_end ("-m" , "ast" , filename )
3244
+ @staticmethod
3245
+ def text_normalize (string ):
3246
+ return textwrap .dedent (string ).strip ()
3247
+
3248
+ def set_source (self , content ):
3249
+ Path (self .filename ).write_text (self .text_normalize (content ))
3250
+
3251
+ def invoke_ast (self , * flags ):
3252
+ stderr = StringIO ()
3253
+ stdout = StringIO ()
3254
+ with (
3255
+ contextlib .redirect_stdout (stdout ),
3256
+ contextlib .redirect_stderr (stderr ),
3257
+ ):
3258
+ ast .main (args = [* flags , self .filename ])
3259
+ self .assertEqual (stderr .getvalue (), '' )
3260
+ return stdout .getvalue ().strip ()
3261
+
3262
+ def check_output (self , source , expect , * flags ):
3263
+ self .set_source (source )
3264
+ res = self .invoke_ast (* flags )
3265
+ expect = self .text_normalize (expect )
3266
+ self .assertEqual (res , expect )
3267
+
3268
+ def test_invocation (self ):
3269
+ # test various combinations of parameters
3270
+ base_flags = (
3271
+ ('-m=exec' , '--mode=exec' ),
3272
+ ('--no-type-comments' , '--no-type-comments' ),
3273
+ ('-a' , '--include-attributes' ),
3274
+ ('-i=4' , '--indent=4' ),
3275
+ )
3276
+ self .set_source ('''
3277
+ print(1, 2, 3)
3278
+ def f(x: int) -> int:
3279
+ x -= 1
3280
+ return x
3281
+ ''' )
3247
3282
3248
- self .assertEqual (res .err , b"" )
3249
- self .assertEqual (expected .splitlines (),
3250
- res .out .decode ("utf8" ).splitlines ())
3251
- self .assertEqual (res .rc , 0 )
3283
+ for r in range (1 , len (base_flags ) + 1 ):
3284
+ for choices in itertools .combinations (base_flags , r = r ):
3285
+ for args in itertools .product (* choices ):
3286
+ with self .subTest (flags = args ):
3287
+ self .invoke_ast (* args )
3288
+
3289
+ def test_help_message (self ):
3290
+ for flag in ('-h' , '--help' , '--unknown' ):
3291
+ with self .subTest (flag = flag ):
3292
+ output = StringIO ()
3293
+ with self .assertRaises (SystemExit ):
3294
+ with contextlib .redirect_stderr (output ):
3295
+ ast .main (args = flag )
3296
+ self .assertStartsWith (output .getvalue (), 'usage: ' )
3297
+
3298
+ def test_exec_mode_flag (self ):
3299
+ # test 'python -m ast -m/--mode exec'
3300
+ source = 'x: bool = 1 # type: ignore[assignment]'
3301
+ expect = '''
3302
+ Module(
3303
+ body=[
3304
+ AnnAssign(
3305
+ target=Name(id='x', ctx=Store()),
3306
+ annotation=Name(id='bool', ctx=Load()),
3307
+ value=Constant(value=1),
3308
+ simple=1)],
3309
+ type_ignores=[
3310
+ TypeIgnore(lineno=1, tag='[assignment]')])
3311
+ '''
3312
+ for flag in ('-m=exec' , '--mode=exec' ):
3313
+ with self .subTest (flag = flag ):
3314
+ self .check_output (source , expect , flag )
3315
+
3316
+ def test_single_mode_flag (self ):
3317
+ # test 'python -m ast -m/--mode single'
3318
+ source = 'pass'
3319
+ expect = '''
3320
+ Interactive(
3321
+ body=[
3322
+ Pass()])
3323
+ '''
3324
+ for flag in ('-m=single' , '--mode=single' ):
3325
+ with self .subTest (flag = flag ):
3326
+ self .check_output (source , expect , flag )
3327
+
3328
+ def test_eval_mode_flag (self ):
3329
+ # test 'python -m ast -m/--mode eval'
3330
+ source = 'print(1, 2, 3)'
3331
+ expect = '''
3332
+ Expression(
3333
+ body=Call(
3334
+ func=Name(id='print', ctx=Load()),
3335
+ args=[
3336
+ Constant(value=1),
3337
+ Constant(value=2),
3338
+ Constant(value=3)]))
3339
+ '''
3340
+ for flag in ('-m=eval' , '--mode=eval' ):
3341
+ with self .subTest (flag = flag ):
3342
+ self .check_output (source , expect , flag )
3343
+
3344
+ def test_func_type_mode_flag (self ):
3345
+ # test 'python -m ast -m/--mode func_type'
3346
+ source = '(int, str) -> list[int]'
3347
+ expect = '''
3348
+ FunctionType(
3349
+ argtypes=[
3350
+ Name(id='int', ctx=Load()),
3351
+ Name(id='str', ctx=Load())],
3352
+ returns=Subscript(
3353
+ value=Name(id='list', ctx=Load()),
3354
+ slice=Name(id='int', ctx=Load()),
3355
+ ctx=Load()))
3356
+ '''
3357
+ for flag in ('-m=func_type' , '--mode=func_type' ):
3358
+ with self .subTest (flag = flag ):
3359
+ self .check_output (source , expect , flag )
3360
+
3361
+ def test_no_type_comments_flag (self ):
3362
+ # test 'python -m ast --no-type-comments'
3363
+ source = 'x: bool = 1 # type: ignore[assignment]'
3364
+ expect = '''
3365
+ Module(
3366
+ body=[
3367
+ AnnAssign(
3368
+ target=Name(id='x', ctx=Store()),
3369
+ annotation=Name(id='bool', ctx=Load()),
3370
+ value=Constant(value=1),
3371
+ simple=1)])
3372
+ '''
3373
+ self .check_output (source , expect , '--no-type-comments' )
3374
+
3375
+ def test_include_attributes_flag (self ):
3376
+ # test 'python -m ast -a/--include-attributes'
3377
+ source = 'pass'
3378
+ expect = '''
3379
+ Module(
3380
+ body=[
3381
+ Pass(
3382
+ lineno=1,
3383
+ col_offset=0,
3384
+ end_lineno=1,
3385
+ end_col_offset=4)])
3386
+ '''
3387
+ for flag in ('-a' , '--include-attributes' ):
3388
+ with self .subTest (flag = flag ):
3389
+ self .check_output (source , expect , flag )
3390
+
3391
+ def test_indent_flag (self ):
3392
+ # test 'python -m ast -i/--indent'
3393
+ source = 'pass'
3394
+ expect = '''
3395
+ Module(
3396
+ body=[
3397
+ Pass()])
3398
+ '''
3399
+ for flag in ('-i=0' , '--indent=0' ):
3400
+ with self .subTest (flag = flag ):
3401
+ self .check_output (source , expect , flag )
3252
3402
3253
3403
3254
3404
class ASTOptimiziationTests (unittest .TestCase ):
0 commit comments