Skip to content

Commit 4fe7c36

Browse files
committed
Use subTests for easier test investigation.
* Moved test set up to module scope * Use subTests decorator to inject compiler parameter into tests * Introduced new compiler parameter to assert* family of methods * Used functools to maintain the same API for the assert* methods where applicable
1 parent af0b622 commit 4fe7c36

1 file changed

Lines changed: 115 additions & 115 deletions

File tree

Lib/test/test_codeop.py

Lines changed: 115 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,31 @@
44
"""
55
import unittest
66
import warnings
7-
from test.support import warnings_helper
7+
from test.support import subTests, warnings_helper
88
from textwrap import dedent
9+
import functools
910

1011
from codeop import compile_command, CommandCompiler, Compile
1112
from codeop import PyCF_DONT_IMPLY_DEDENT, PyCF_ONLY_AST
1213
import 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("\na = 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\ndef 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("\nx = 5", compiler)
319-
validate_ast("x = 5\n", compiler)
320-
validate_ast("x = 5\n\n", compiler)
321-
validate_ast("\n\nx = 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("\nx = 5", compiler)
319+
validate_ast("x = 5\n", compiler)
320+
validate_ast("x = 5\n\n", compiler)
321+
validate_ast("\n\nx = 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

389389
if __name__ == "__main__":

0 commit comments

Comments
 (0)