Skip to content

Commit e7a4bb5

Browse files
bpo-35798: Add test.support.check_syntax_warning(). (#11895)
It checks that a SyntaxWarning is raised when compile specified statement, that it is raised only once, that it is converted to a SyntaxError when raised as exception, and that both warning and exception objects have corresponding attributes.
1 parent ee0f927 commit e7a4bb5

File tree

5 files changed

+62
-55
lines changed

5 files changed

+62
-55
lines changed

Doc/library/test.rst

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -936,9 +936,24 @@ The :mod:`test.support` module defines the following functions:
936936

937937
Test for syntax errors in *statement* by attempting to compile *statement*.
938938
*testcase* is the :mod:`unittest` instance for the test. *errtext* is the
939-
text of the error raised by :exc:`SyntaxError`. If *lineno* is not None,
940-
compares to the line of the :exc:`SyntaxError`. If *offset* is not None,
941-
compares to the offset of the :exc:`SyntaxError`.
939+
regular expression which should match the string representation of the
940+
raised :exc:`SyntaxError`. If *lineno* is not ``None``, compares to
941+
the line of the exception. If *offset* is not ``None``, compares to
942+
the offset of the exception.
943+
944+
945+
.. function:: check_syntax_warning(testcase, statement, errtext='', *, lineno=1, offset=None)
946+
947+
Test for syntax warning in *statement* by attempting to compile *statement*.
948+
Test also that the :exc:`SyntaxWarning` is emitted only once, and that it
949+
will be converted to a :exc:`SyntaxError` when turned into error.
950+
*testcase* is the :mod:`unittest` instance for the test. *errtext* is the
951+
regular expression which should match the string representation of the
952+
emitted :exc:`SyntaxWarning` and raised :exc:`SyntaxError`. If *lineno*
953+
is not ``None``, compares to the line of the warning and exception.
954+
If *offset* is not ``None``, compares to the offset of the exception.
955+
956+
.. versionadded:: 3.8
942957

943958

944959
.. function:: open_urlresource(url, *args, **kw)

Lib/test/support/__init__.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
# unittest
8787
"is_resource_enabled", "requires", "requires_freebsd_version",
8888
"requires_linux_version", "requires_mac_ver", "check_syntax_error",
89+
"check_syntax_warning",
8990
"TransientResource", "time_out", "socket_peer_reset", "ioerror_peer_reset",
9091
"transient_internet", "BasicTestRunner", "run_unittest", "run_doctest",
9192
"skip_unless_symlink", "requires_gzip", "requires_bz2", "requires_lzma",
@@ -1113,6 +1114,7 @@ def make_bad_fd():
11131114
file.close()
11141115
unlink(TESTFN)
11151116

1117+
11161118
def check_syntax_error(testcase, statement, errtext='', *, lineno=None, offset=None):
11171119
with testcase.assertRaisesRegex(SyntaxError, errtext) as cm:
11181120
compile(statement, '<test string>', 'exec')
@@ -1124,6 +1126,33 @@ def check_syntax_error(testcase, statement, errtext='', *, lineno=None, offset=N
11241126
if offset is not None:
11251127
testcase.assertEqual(err.offset, offset)
11261128

1129+
def check_syntax_warning(testcase, statement, errtext='', *, lineno=1, offset=None):
1130+
# Test also that a warning is emitted only once.
1131+
with warnings.catch_warnings(record=True) as warns:
1132+
warnings.simplefilter('always', SyntaxWarning)
1133+
compile(statement, '<testcase>', 'exec')
1134+
testcase.assertEqual(len(warns), 1, warns)
1135+
1136+
warn, = warns
1137+
testcase.assertTrue(issubclass(warn.category, SyntaxWarning), warn.category)
1138+
if errtext:
1139+
testcase.assertRegex(str(warn.message), errtext)
1140+
testcase.assertEqual(warn.filename, '<testcase>')
1141+
testcase.assertIsNotNone(warn.lineno)
1142+
if lineno is not None:
1143+
testcase.assertEqual(warn.lineno, lineno)
1144+
1145+
# SyntaxWarning should be converted to SyntaxError when raised,
1146+
# since the latter contains more information and provides better
1147+
# error report.
1148+
with warnings.catch_warnings(record=True) as warns:
1149+
warnings.simplefilter('error', SyntaxWarning)
1150+
check_syntax_error(testcase, statement, errtext,
1151+
lineno=lineno, offset=offset)
1152+
# No warnings are leaked when a SyntaxError is raised.
1153+
testcase.assertEqual(warns, [])
1154+
1155+
11271156
def open_urlresource(url, *args, **kw):
11281157
import urllib.request, urllib.parse
11291158

Lib/test/test_grammar.py

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Python test set -- part 1, grammar.
22
# This just tests whether the parser accepts them all.
33

4-
from test.support import check_syntax_error
4+
from test.support import check_syntax_error, check_syntax_warning
55
import inspect
66
import unittest
77
import sys
@@ -101,7 +101,7 @@
101101

102102
class TokenTests(unittest.TestCase):
103103

104-
check_syntax_error = check_syntax_error
104+
from test.support import check_syntax_error
105105

106106
def test_backslash(self):
107107
# Backslash means line continuation:
@@ -276,7 +276,7 @@ def __getitem__(self, item):
276276

277277
class GrammarTests(unittest.TestCase):
278278

279-
check_syntax_error = check_syntax_error
279+
from test.support import check_syntax_error, check_syntax_warning
280280

281281
# single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
282282
# XXX can't test in a script -- this rule is only used when interactive
@@ -1109,12 +1109,10 @@ def testAssert2(self):
11091109
else:
11101110
self.fail("AssertionError not raised by 'assert False'")
11111111

1112-
with self.assertWarnsRegex(SyntaxWarning, 'assertion is always true'):
1113-
compile('assert(x, "msg")', '<testcase>', 'exec')
1112+
self.check_syntax_warning('assert(x, "msg")',
1113+
'assertion is always true')
11141114
with warnings.catch_warnings():
1115-
warnings.filterwarnings('error', category=SyntaxWarning)
1116-
with self.assertRaisesRegex(SyntaxError, 'assertion is always true'):
1117-
compile('assert(x, "msg")', '<testcase>', 'exec')
1115+
warnings.simplefilter('error', SyntaxWarning)
11181116
compile('assert x, "msg"', '<testcase>', 'exec')
11191117

11201118

@@ -1243,12 +1241,7 @@ def test_comparison(self):
12431241

12441242
def test_comparison_is_literal(self):
12451243
def check(test, msg='"is" with a literal'):
1246-
with self.assertWarnsRegex(SyntaxWarning, msg):
1247-
compile(test, '<testcase>', 'exec')
1248-
with warnings.catch_warnings():
1249-
warnings.filterwarnings('error', category=SyntaxWarning)
1250-
with self.assertRaisesRegex(SyntaxError, msg):
1251-
compile(test, '<testcase>', 'exec')
1244+
self.check_syntax_warning(test, msg)
12521245

12531246
check('x is 1')
12541247
check('x is "thing"')
@@ -1257,20 +1250,15 @@ def check(test, msg='"is" with a literal'):
12571250
check('x is not 1', '"is not" with a literal')
12581251

12591252
with warnings.catch_warnings():
1260-
warnings.filterwarnings('error', category=SyntaxWarning)
1253+
warnings.simplefilter('error', SyntaxWarning)
12611254
compile('x is None', '<testcase>', 'exec')
12621255
compile('x is False', '<testcase>', 'exec')
12631256
compile('x is True', '<testcase>', 'exec')
12641257
compile('x is ...', '<testcase>', 'exec')
12651258

12661259
def test_warn_missed_comma(self):
12671260
def check(test):
1268-
with self.assertWarnsRegex(SyntaxWarning, msg):
1269-
compile(test, '<testcase>', 'exec')
1270-
with warnings.catch_warnings():
1271-
warnings.filterwarnings('error', category=SyntaxWarning)
1272-
with self.assertRaisesRegex(SyntaxError, msg):
1273-
compile(test, '<testcase>', 'exec')
1261+
self.check_syntax_warning(test, msg)
12741262

12751263
msg=r'is not callable; perhaps you missed a comma\?'
12761264
check('[(1, 2) (3, 4)]')
@@ -1342,7 +1330,7 @@ def check(test):
13421330
check('[[1, 2] [...]]')
13431331

13441332
with warnings.catch_warnings():
1345-
warnings.filterwarnings('error', category=SyntaxWarning)
1333+
warnings.simplefilter('error', SyntaxWarning)
13461334
compile('[(lambda x, y: x) (3, 4)]', '<testcase>', 'exec')
13471335
compile('[[1, 2] [i]]', '<testcase>', 'exec')
13481336
compile('[[1, 2] [0]]', '<testcase>', 'exec')

Lib/test/test_string_literals.py

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ def byte(i):
6363

6464
class TestLiterals(unittest.TestCase):
6565

66+
from test.support import check_syntax_warning
67+
6668
def setUp(self):
6769
self.save_path = sys.path[:]
6870
self.tmpdir = tempfile.mkdtemp()
@@ -112,21 +114,7 @@ def test_eval_str_invalid_escape(self):
112114
with self.assertWarns(SyntaxWarning):
113115
self.assertEqual(eval(r"'\%c'" % b), '\\' + chr(b))
114116

115-
with warnings.catch_warnings(record=True) as w:
116-
warnings.simplefilter('always', category=SyntaxWarning)
117-
eval("'''\n\\z'''")
118-
self.assertEqual(len(w), 1)
119-
self.assertEqual(w[0].filename, '<string>')
120-
self.assertEqual(w[0].lineno, 1)
121-
122-
with warnings.catch_warnings(record=True) as w:
123-
warnings.simplefilter('error', category=SyntaxWarning)
124-
with self.assertRaises(SyntaxError) as cm:
125-
eval("'''\n\\z'''")
126-
exc = cm.exception
127-
self.assertEqual(w, [])
128-
self.assertEqual(exc.filename, '<string>')
129-
self.assertEqual(exc.lineno, 1)
117+
self.check_syntax_warning("'''\n\\z'''")
130118

131119
def test_eval_str_raw(self):
132120
self.assertEqual(eval(""" r'x' """), 'x')
@@ -161,21 +149,7 @@ def test_eval_bytes_invalid_escape(self):
161149
with self.assertWarns(SyntaxWarning):
162150
self.assertEqual(eval(r"b'\%c'" % b), b'\\' + bytes([b]))
163151

164-
with warnings.catch_warnings(record=True) as w:
165-
warnings.simplefilter('always', category=SyntaxWarning)
166-
eval("b'''\n\\z'''")
167-
self.assertEqual(len(w), 1)
168-
self.assertEqual(w[0].filename, '<string>')
169-
self.assertEqual(w[0].lineno, 1)
170-
171-
with warnings.catch_warnings(record=True) as w:
172-
warnings.simplefilter('error', category=SyntaxWarning)
173-
with self.assertRaises(SyntaxError) as cm:
174-
eval("b'''\n\\z'''")
175-
exc = cm.exception
176-
self.assertEqual(w, [])
177-
self.assertEqual(exc.filename, '<string>')
178-
self.assertEqual(exc.lineno, 1)
152+
self.check_syntax_warning("b'''\n\\z'''")
179153

180154
def test_eval_bytes_raw(self):
181155
self.assertEqual(eval(""" br'x' """), b'x')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added :func:`test.support.check_syntax_warning`.

0 commit comments

Comments
 (0)