77from test .support import warnings_helper
88from textwrap import dedent
99
10- from codeop import compile_command , PyCF_DONT_IMPLY_DEDENT
10+ from codeop import compile_command , CommandCompiler , Compile
11+ from codeop import PyCF_DONT_IMPLY_DEDENT , PyCF_ONLY_AST
12+ import ast
13+
1114
1215class CodeopTests (unittest .TestCase ):
1316
17+ def setUp (self ):
18+ self .wrapping_compilers = [compile_command , CommandCompiler ()]
19+ self .raw_compilers = [Compile ()]
20+ self .compilers = self .wrapping_compilers + self .raw_compilers
21+
1422 def assertValid (self , str , symbol = 'single' ):
1523 '''succeed iff str is a valid piece of code'''
16- expected = compile (str , "<input>" , symbol , PyCF_DONT_IMPLY_DEDENT )
17- self .assertEqual (compile_command (str , "<input>" , symbol ), expected )
24+ for compiler in self .compilers :
25+ expected = compile (str , "<input>" , symbol , PyCF_DONT_IMPLY_DEDENT )
26+ self .assertEqual (compiler (str , "<input>" , symbol ), expected )
1827
1928 def assertIncomplete (self , str , symbol = 'single' ):
2029 '''succeed iff str is the start of a valid piece of code'''
21- self .assertEqual (compile_command (str , symbol = symbol ), None )
30+ for compiler in self .wrapping_compilers :
31+ self .assertEqual (compiler (str , "<input>" , symbol = symbol ), None )
32+
33+ for compiler in self .raw_compilers :
34+ # Compile has should raise like built-in compile
35+ with self .assertRaises (SyntaxError ) as cm_original_error :
36+ compile (str , "<input>" , symbol , compiler .flags )
37+ expected_error = cm_original_error .exception
38+ with self .assertRaises (type (expected_error )) as cm_wrapped_error :
39+ compiler (str , "<input>" , symbol = symbol )
40+ self .assertEqual (
41+ expected_error .args ,
42+ cm_wrapped_error .exception .args
43+ )
2244
2345 def assertInvalid (self , str , symbol = 'single' , is_syntax = 1 ):
2446 '''succeed iff str is the start of an invalid piece of code'''
25- try :
26- compile_command (str ,symbol = symbol )
27- self .fail ("No exception raised for invalid code" )
28- except SyntaxError :
29- self .assertTrue (is_syntax )
30- except OverflowError :
31- self .assertTrue (not is_syntax )
47+ for compiler in self .compilers :
48+ try :
49+ compiler (str ,"<input>" , symbol = symbol )
50+ self .fail ("No exception raised for invalid code" )
51+ except SyntaxError :
52+ self .assertTrue (is_syntax )
53+ except OverflowError :
54+ self .assertTrue (not is_syntax )
3255
3356 def test_valid (self ):
3457 av = self .assertValid
3558
3659 # special case
37- self .assertEqual (compile_command ("" ),
38- compile ("pass" , "<input>" , 'single' ,
39- PyCF_DONT_IMPLY_DEDENT ))
40- self .assertEqual (compile_command ("\n " ),
41- compile ("pass" , "<input>" , 'single' ,
42- PyCF_DONT_IMPLY_DEDENT ))
60+ for compiler in self .wrapping_compilers :
61+ self .assertEqual (
62+ compiler ("" , "<input>" , 'single' ),
63+ compile ("pass" , "<input>" , 'single' , PyCF_DONT_IMPLY_DEDENT ))
64+ self .assertEqual (
65+ compiler ("\n " , "<input>" , 'single' ),
66+ compile ("pass" , "<input>" , 'single' , PyCF_DONT_IMPLY_DEDENT ))
4367
4468 av ("a = 1" )
4569 av ("\n a = 1" )
@@ -273,49 +297,80 @@ def test_invalid_exec(self):
273297 ai ('a await raise b?+1' , symbol = 'exec' )
274298
275299 def test_filename (self ):
276- self .assertEqual (compile_command ("a = 1\n " , "abc" ).co_filename ,
277- compile ("a = 1\n " , "abc" , 'single' ).co_filename )
278- self .assertNotEqual (compile_command ("a = 1\n " , "abc" ).co_filename ,
279- compile ("a = 1\n " , "def" , 'single' ).co_filename )
300+ for compiler in self .compilers :
301+ self .assertEqual (
302+ compiler ("a = 1\n " , "abc" , "single" ).co_filename ,
303+ compile ("a = 1\n " , "abc" , 'single' ).co_filename
304+ )
305+ self .assertNotEqual (
306+ compiler ("a = 1\n " , "abc" , "single" ).co_filename ,
307+ compile ("a = 1\n " , "def" , 'single' ).co_filename
308+ )
309+
310+ def assertReturnsModule (self , code , compiler ):
311+ retval = compiler (code , "<input>" , 'exec' , PyCF_ONLY_AST )
312+ self .assertIsInstance (retval , ast .Module )
313+
314+ def test_ast_return_value (self ):
315+ for compiler in self .raw_compilers :
316+ validate_ast = self .assertReturnsModule
317+ validate_ast ("x = 5" , compiler )
318+ validate_ast ("\n x = 5" , compiler )
319+ validate_ast ("x = 5\n " , compiler )
320+ validate_ast ("x = 5\n \n " , compiler )
321+ validate_ast ("\n \n x = 5\n \n " , compiler )
280322
281323 def test_warning (self ):
282324 # Test that the warning is only returned once.
283- with warnings_helper .check_warnings (
284- ('"is" with \' str\' literal' , SyntaxWarning ),
285- ('"\\ \\ e" is an invalid escape sequence' , SyntaxWarning ),
286- ) as w :
287- compile_command (r"'\e' is 0" )
325+ for compiler in self .compilers :
326+ with warnings_helper .check_warnings (
327+ ('"is" with \' str\' literal' , SyntaxWarning ),
328+ ('"\\ \\ e" is an invalid escape sequence' , SyntaxWarning ),
329+ ) as w :
330+ compiler (r"'\e' is 0" , "<input>" , "single" )
288331 self .assertEqual (len (w .warnings ), 2 )
289332
290- # bpo-41520: check SyntaxWarning treated as an SyntaxError
291- with warnings .catch_warnings (), self .assertRaises (SyntaxError ):
292- warnings .simplefilter ('error' , SyntaxWarning )
293- compile_command ('1 is 1' , symbol = 'exec' )
333+ # bpo-41520: check SyntaxWarning treated as an SyntaxError
334+ with warnings .catch_warnings (), self .assertRaises (SyntaxError ):
335+ warnings .simplefilter ('error' , SyntaxWarning )
336+ compiler ('1 is 1' , "<input>" , 'exec' )
294337
295- # Check SyntaxWarning treated as an SyntaxError
296- with warnings .catch_warnings (), self .assertRaises (SyntaxError ):
297- warnings .simplefilter ('error' , SyntaxWarning )
298- compile_command (r"'\e'" , symbol = 'exec' )
338+ # Check SyntaxWarning treated as an SyntaxError
339+ with warnings .catch_warnings (), self .assertRaises (SyntaxError ):
340+ warnings .simplefilter ('error' , SyntaxWarning )
341+ compiler (r"'\e'" , "<input>" , 'exec' )
299342
300343 def test_incomplete_warning (self ):
301- with warnings .catch_warnings (record = True ) as w :
302- warnings .simplefilter ('always' )
303- self .assertIncomplete ("'\\ e' + (" )
304- self .assertEqual (w , [])
344+ code = "'\\ e' + ("
345+ for compiler in self .wrapping_compilers :
346+ with warnings .catch_warnings (record = True ) as w :
347+ warnings .simplefilter ('always' )
348+ compiler (code )
349+ self .assertEqual (w , [])
350+ for compiler in self .raw_compilers :
351+ warnings_cm = warnings_helper .check_warnings (
352+ ('"\\ \\ e" is an invalid esceape sequence' , SyntaxWarning )
353+ )
354+ with self .assertRaises (SyntaxError ), warnings_cm as w :
355+ compiler = self .compilers [- 1 ]
356+ compiler (code , "<input>" , 'single' )
357+ self .assertEqual (len (w .warnings ), 1 )
305358
306359 def test_invalid_warning (self ):
307360 with warnings .catch_warnings (record = True ) as w :
308361 warnings .simplefilter ('always' )
309362 self .assertInvalid ("'\\ e' 1" )
310- self .assertEqual (len (w ), 1 )
311- self .assertEqual (w [0 ].category , SyntaxWarning )
312- self .assertRegex (str (w [0 ].message ), 'invalid escape sequence' )
313- self .assertEqual (w [0 ].filename , '<input>' )
363+ self .assertEqual (len (w ), len (self .compilers ))
364+ for warning in w :
365+ self .assertEqual (warning .category , SyntaxWarning )
366+ self .assertRegex (str (warning ), 'invalid escape sequence' )
367+ self .assertEqual (warning .filename , '<input>' )
314368
315369 def assertSyntaxErrorMatches (self , code , message ):
316370 with self .subTest (code ):
317- with self .assertRaisesRegex (SyntaxError , message ):
318- compile_command (code , symbol = 'exec' )
371+ for compiler in self .compilers :
372+ with self .assertRaisesRegex (SyntaxError , message ):
373+ compiler (code , "<input>" , 'exec' )
319374
320375 def test_syntax_errors (self ):
321376 self .assertSyntaxErrorMatches (
@@ -324,6 +379,11 @@ def foo(x,x):
324379 pass
325380 """ ), "duplicate parameter 'x' in function definition" )
326381
382+ def test_future_imports (self ):
383+ for compiler in self .raw_compilers :
384+ original_flags = compiler .flags
385+ compiler ('from __future__ import annotations' , "<input>" , 'single' )
386+ self .assertGreater (compiler .flags , original_flags )
327387
328388
329389if __name__ == "__main__" :
0 commit comments