44"""
55import unittest
66import warnings
7- from test .support import warnings_helper
7+ from test .support import subTests , warnings_helper
88from textwrap import dedent
9+ import functools
910
1011from codeop import compile_command , CommandCompiler , Compile
1112from codeop import PyCF_DONT_IMPLY_DEDENT , PyCF_ONLY_AST
1213import ast
1314
1415
15- class CodeopTests (unittest .TestCase ):
16+ WRAPPING_COMPILERS = [compile_command , CommandCompiler ()]
17+ RAW_COMPILERS = [Compile ()]
18+ COMPILERS = WRAPPING_COMPILERS + RAW_COMPILERS
1619
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
2120
22- def assertValid (self , str , symbol = 'single' ):
21+ class CodeopTests (unittest .TestCase ):
22+ def assertValid (self , str , symbol = 'single' , * , compiler ):
2323 '''succeed iff str is a valid piece of code'''
24- for compiler in self .compilers :
25- expected = compile (str , "<input>" , symbol , PyCF_DONT_IMPLY_DEDENT )
26- self .assertEqual (compiler (str , "<input>" , symbol ), expected )
24+ expected = compile (str , "<input>" , symbol , PyCF_DONT_IMPLY_DEDENT )
25+ self .assertEqual (compiler (str , "<input>" , symbol ), expected )
2726
28- def assertIncomplete (self , str , symbol = 'single' ):
27+ def assertIncomplete (self , str , symbol = 'single' , * , compiler ):
2928 '''succeed iff str is the start of a valid piece of code'''
30- for compiler in self . wrapping_compilers :
29+ if compiler in WRAPPING_COMPILERS :
3130 self .assertEqual (compiler (str , "<input>" , symbol = symbol ), None )
32-
33- for compiler in self .raw_compilers :
31+ else :
3432 # Compile has should raise like built-in compile
3533 with self .assertRaises (SyntaxError ) as cm_original_error :
3634 compile (str , "<input>" , symbol , compiler .flags )
@@ -42,29 +40,28 @@ def assertIncomplete(self, str, symbol='single'):
4240 cm_wrapped_error .exception .args
4341 )
4442
45- def assertInvalid (self , str , symbol = 'single' , is_syntax = 1 ):
43+ def assertInvalid (self , str , symbol = 'single' , is_syntax = 1 , * , compiler ):
4644 '''succeed iff str is the start of an invalid piece of code'''
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 )
55-
56- def test_valid (self ):
57- av = self .assertValid
58-
59- # special case
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 ))
67-
45+ try :
46+ compiler (str ,"<input>" , symbol = symbol )
47+ self .fail ("No exception raised for invalid code" )
48+ except SyntaxError :
49+ self .assertTrue (is_syntax )
50+ except OverflowError :
51+ self .assertTrue (not is_syntax )
52+
53+ @subTests ('compiler' , WRAPPING_COMPILERS )
54+ def test_empty (self , compiler ):
55+ self .assertEqual (
56+ compiler ("" , "<input>" , 'single' ),
57+ compile ("pass" , "<input>" , 'single' , PyCF_DONT_IMPLY_DEDENT ))
58+ self .assertEqual (
59+ compiler ("\n " , "<input>" , 'single' ),
60+ compile ("pass" , "<input>" , 'single' , PyCF_DONT_IMPLY_DEDENT ))
61+
62+ @subTests ('compiler' , COMPILERS )
63+ def test_valid (self , compiler ):
64+ av = functools .partial (self .assertValid , compiler = compiler )
6865 av ("a = 1" )
6966 av ("\n a = 1" )
7067 av ("a = 1\n " )
@@ -116,8 +113,9 @@ def test_valid(self):
116113 av ("def f():\n pass\n #foo\n " )
117114 av ("@a.b.c\n def f():\n pass\n " )
118115
119- def test_incomplete (self ):
120- ai = self .assertIncomplete
116+ @subTests ('compiler' , COMPILERS )
117+ def test_incomplete (self , compiler ):
118+ ai = functools .partial (self .assertIncomplete , compiler = compiler )
121119
122120 ai ("(a **" )
123121 ai ("(a,b," )
@@ -250,8 +248,9 @@ def test_incomplete(self):
250248 ai ('a = f"""' )
251249 ai ('a = \\ ' )
252250
253- def test_invalid (self ):
254- ai = self .assertInvalid
251+ @subTests ('compiler' , COMPILERS )
252+ def test_invalid (self , compiler ):
253+ ai = functools .partial (self .assertInvalid , compiler = compiler )
255254 ai ("a b" )
256255
257256 ai ("a @" )
@@ -287,103 +286,104 @@ def test_invalid(self):
287286
288287 ai ("[i for i in range(10)] = (1, 2, 3)" )
289288
290- def test_invalid_exec (self ):
291- ai = self .assertInvalid
289+ @subTests ('compiler' , COMPILERS )
290+ def test_invalid_exec (self , compiler ):
291+ ai = functools .partial (self .assertInvalid , compiler = compiler )
292292 ai ("raise = 4" , symbol = "exec" )
293293 ai ('def a-b' , symbol = 'exec' )
294294 ai ('await?' , symbol = 'exec' )
295295 ai ('=!=' , symbol = 'exec' )
296296 ai ('a await raise b' , symbol = 'exec' )
297297 ai ('a await raise b?+1' , symbol = 'exec' )
298298
299- def test_filename ( self ):
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- )
299+ @ subTests ( 'compiler' , COMPILERS )
300+ def test_filename ( self , compiler ) :
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+ )
309309
310310 def assertReturnsModule (self , code , compiler ):
311311 retval = compiler (code , "<input>" , 'exec' , PyCF_ONLY_AST )
312312 self .assertIsInstance (retval , ast .Module )
313313
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 )
322-
323- def test_warning (self ):
314+ @subTests ('compiler' , RAW_COMPILERS )
315+ def test_ast_return_value (self , compiler ):
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 )
322+
323+ @subTests ('compiler' , COMPILERS )
324+ def test_warning (self , compiler ):
324325 # Test that the warning is only returned once.
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" )
331- self .assertEqual (len (w .warnings ), 2 )
332-
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' )
337-
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' )
342-
343- def test_incomplete_warning (self ):
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 )
358-
359- def test_invalid_warning (self ):
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" )
331+ self .assertEqual (len (w .warnings ), 2 )
332+
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' )
337+
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' )
342+
343+ @subTests ('compiler' , WRAPPING_COMPILERS )
344+ def test_incomplete_warning (self , compiler ):
360345 with warnings .catch_warnings (record = True ) as w :
361346 warnings .simplefilter ('always' )
362- self .assertInvalid ("'\\ e' 1" )
363- self .assertEqual (len (w ), len (self .compilers ))
347+ compiler ("'\\ e' + (" )
348+ self .assertEqual (w , [])
349+
350+ @subTests ('compiler' , RAW_COMPILERS )
351+ def test_raw_raises_error (self , compiler ):
352+ warnings_cm = warnings_helper .check_warnings (
353+ ('"\\ \\ e" is an invalid esceape sequence' , SyntaxWarning )
354+ )
355+ with self .assertRaises (SyntaxError ), warnings_cm as w :
356+ compiler ("'\\ e' + (" , "<input>" , 'single' )
357+ self .assertEqual (len (w .warnings ), 1 )
358+
359+ @subTests ('compiler' , COMPILERS )
360+ def test_invalid_warning (self , compiler ):
361+ with warnings .catch_warnings (record = True ) as w :
362+ warnings .simplefilter ('always' )
363+ self .assertInvalid ("'\\ e' 1" , compiler = compiler )
364+ self .assertEqual (len (w ), 1 )
364365 for warning in w :
365366 self .assertEqual (warning .category , SyntaxWarning )
366367 self .assertRegex (str (warning ), 'invalid escape sequence' )
367368 self .assertEqual (warning .filename , '<input>' )
368369
369- def assertSyntaxErrorMatches (self , code , message ):
370- with self .subTest (code ):
371- for compiler in self .compilers :
372- with self .assertRaisesRegex (SyntaxError , message ):
373- compiler (code , "<input>" , 'exec' )
374-
375- def test_syntax_errors (self ):
376- self .assertSyntaxErrorMatches (
377- dedent ("""\
370+ @subTests ('compiler' , COMPILERS )
371+ def test_syntax_errors (self , compiler ):
372+ code = dedent ("""\
378373 def foo(x,x):
379374 pass
380- """ ), "duplicate parameter 'x' in function definition" )
381-
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 )
375+ """ )
376+ message = "duplicate parameter 'x' in function definition"
377+ with self .assertRaisesRegex (SyntaxError , message ):
378+ compiler (code , "<input>" , 'exec' )
379+
380+ @subTests ('compiler' , RAW_COMPILERS )
381+ def test_future_imports (self , compiler ):
382+ original_flags = compiler .flags
383+ compiler ('from __future__ import annotations' , "<input>" , 'single' )
384+ self .assertGreater (compiler .flags , original_flags )
385+ # reset flags to ensure test has no side-effects
386+ compiler .flags = original_flags
387387
388388
389389if __name__ == "__main__" :
0 commit comments