diff options
author | Guido van Rossum <guido@python.org> | 2020-05-14 19:01:14 -0700 |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 2020-05-14 19:01:14 -0700 |
commit | f93a54c48fc1644012aa0d4ee3887c1d121ac40e (patch) | |
tree | 189c841245d15318e5895638105bdbc532cbcdf5 /Lib/test | |
parent | 31641ff0e4b18c8d002d019f4506f0e8fb446983 (diff) | |
parent | 16ab07063cb564c1937714bd39d6915172f005b5 (diff) | |
download | cpython-git-fix-traceback-syntax-error.tar.gz |
Merge branch 'master' into fix-traceback-syntax-errorfix-traceback-syntax-error
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/test__xxsubinterpreters.py | 301 | ||||
-rw-r--r-- | Lib/test/test_asyncio/test_tasks.py | 27 | ||||
-rw-r--r-- | Lib/test/test_compileall.py | 224 | ||||
-rw-r--r-- | Lib/test/test_dictcomps.py | 2 | ||||
-rw-r--r-- | Lib/test/test_email/test_contentmanager.py | 15 | ||||
-rw-r--r-- | Lib/test/test_exceptions.py | 2 | ||||
-rw-r--r-- | Lib/test/test_generators.py | 21 | ||||
-rw-r--r-- | Lib/test/test_genexps.py | 2 | ||||
-rw-r--r-- | Lib/test/test_grammar.py | 5 | ||||
-rw-r--r-- | Lib/test/test_json/test_recursion.py | 2 | ||||
-rw-r--r-- | Lib/test/test_peg_parser.py | 27 | ||||
-rw-r--r-- | Lib/test/test_syntax.py | 52 |
12 files changed, 355 insertions, 325 deletions
diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 039c040ad3..e17bfde2c2 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -1,4 +1,3 @@ -import builtins from collections import namedtuple import contextlib import itertools @@ -867,11 +866,10 @@ class RunStringTests(TestBase): yield if msg is None: self.assertEqual(str(caught.exception).split(':')[0], - exctype.__name__) + str(exctype)) else: self.assertEqual(str(caught.exception), - "{}: {}".format(exctype.__name__, msg)) - self.assertIsInstance(caught.exception.__cause__, exctype) + "{}: {}".format(exctype, msg)) def test_invalid_syntax(self): with self.assert_run_failed(SyntaxError): @@ -1062,301 +1060,6 @@ class RunStringTests(TestBase): self.assertEqual(retcode, 0) -def build_exception(exctype, /, *args, **kwargs): - # XXX Use __qualname__? - name = exctype.__name__ - argreprs = [repr(a) for a in args] - if kwargs: - kwargreprs = [f'{k}={v!r}' for k, v in kwargs.items()] - script = f'{name}({", ".join(argreprs)}, {", ".join(kwargreprs)})' - else: - script = f'{name}({", ".join(argreprs)})' - expected = exctype(*args, **kwargs) - return script, expected - - -def build_exceptions(self, *exctypes, default=None, custom=None, bases=True): - if not exctypes: - raise NotImplementedError - if not default: - default = ((), {}) - elif isinstance(default, str): - default = ((default,), {}) - elif type(default) is not tuple: - raise NotImplementedError - elif len(default) != 2: - default = (default, {}) - elif type(default[0]) is not tuple: - default = (default, {}) - elif type(default[1]) is not dict: - default = (default, {}) - # else leave it alone - - for exctype in exctypes: - customtype = None - values = default - if custom: - if exctype in custom: - customtype = exctype - elif bases: - for customtype in custom: - if issubclass(exctype, customtype): - break - else: - customtype = None - if customtype is not None: - values = custom[customtype] - if values is None: - continue - args, kwargs = values - script, expected = build_exception(exctype, *args, **kwargs) - yield exctype, customtype, script, expected - - -try: - raise Exception -except Exception as exc: - assert exc.__traceback__ is not None - Traceback = type(exc.__traceback__) - - -class RunFailedTests(TestBase): - - BUILTINS = [v - for v in vars(builtins).values() - if (type(v) is type - and issubclass(v, Exception) - #and issubclass(v, BaseException) - ) - ] - BUILTINS_SPECIAL = [ - # These all have extra attributes (i.e. args/kwargs) - SyntaxError, - ImportError, - UnicodeError, - OSError, - SystemExit, - StopIteration, - ] - - @classmethod - def build_exceptions(cls, exctypes=None, default=(), custom=None): - if exctypes is None: - exctypes = cls.BUILTINS - if custom is None: - # Skip the "special" ones. - custom = {et: None for et in cls.BUILTINS_SPECIAL} - yield from build_exceptions(*exctypes, default=default, custom=custom) - - def assertExceptionsEqual(self, exc, expected, *, chained=True): - if type(expected) is type: - self.assertIs(type(exc), expected) - return - elif not isinstance(exc, Exception): - self.assertEqual(exc, expected) - elif not isinstance(expected, Exception): - self.assertEqual(exc, expected) - else: - # Plain equality doesn't work, so we have to compare manually. - self.assertIs(type(exc), type(expected)) - self.assertEqual(exc.args, expected.args) - self.assertEqual(exc.__reduce__(), expected.__reduce__()) - if chained: - self.assertExceptionsEqual(exc.__context__, - expected.__context__) - self.assertExceptionsEqual(exc.__cause__, - expected.__cause__) - self.assertEqual(exc.__suppress_context__, - expected.__suppress_context__) - - def assertTracebacksEqual(self, tb, expected): - if not isinstance(tb, Traceback): - self.assertEqual(tb, expected) - elif not isinstance(expected, Traceback): - self.assertEqual(tb, expected) - else: - self.assertEqual(tb.tb_frame.f_code.co_name, - expected.tb_frame.f_code.co_name) - self.assertEqual(tb.tb_frame.f_code.co_filename, - expected.tb_frame.f_code.co_filename) - self.assertEqual(tb.tb_lineno, expected.tb_lineno) - self.assertTracebacksEqual(tb.tb_next, expected.tb_next) - - # XXX Move this to TestBase? - @contextlib.contextmanager - def expected_run_failure(self, expected): - exctype = expected if type(expected) is type else type(expected) - - with self.assertRaises(interpreters.RunFailedError) as caught: - yield caught - exc = caught.exception - - modname = exctype.__module__ - if modname == 'builtins' or modname == '__main__': - exctypename = exctype.__name__ - else: - exctypename = f'{modname}.{exctype.__name__}' - if exctype is expected: - self.assertEqual(str(exc).split(':')[0], exctypename) - else: - self.assertEqual(str(exc), f'{exctypename}: {expected}') - self.assertExceptionsEqual(exc.__cause__, expected) - if exc.__cause__ is not None: - self.assertIsNotNone(exc.__cause__.__traceback__) - - def test_builtin_exceptions(self): - interpid = interpreters.create() - msg = '<a message>' - for i, info in enumerate(self.build_exceptions( - default=msg, - custom={ - SyntaxError: ((msg, '<stdin>', 1, 3, 'a +?'), {}), - ImportError: ((msg,), {'name': 'spam', 'path': '/x/spam.py'}), - UnicodeError: None, - #UnicodeError: ((), {}), - #OSError: ((), {}), - SystemExit: ((1,), {}), - StopIteration: (('<a value>',), {}), - }, - )): - exctype, _, script, expected = info - testname = f'{i+1} - {script}' - script = f'raise {script}' - - with self.subTest(testname): - with self.expected_run_failure(expected): - interpreters.run_string(interpid, script) - - def test_custom_exception_from___main__(self): - script = dedent(""" - class SpamError(Exception): - def __init__(self, q): - super().__init__(f'got {q}') - self.q = q - raise SpamError('eggs') - """) - expected = Exception(f'SpamError: got {"eggs"}') - - interpid = interpreters.create() - with self.assertRaises(interpreters.RunFailedError) as caught: - interpreters.run_string(interpid, script) - cause = caught.exception.__cause__ - - self.assertExceptionsEqual(cause, expected) - - class SpamError(Exception): - # The normal Exception.__reduce__() produces a funny result - # here. So we have to use a custom __new__(). - def __new__(cls, q): - if type(q) is SpamError: - return q - return super().__new__(cls, q) - def __init__(self, q): - super().__init__(f'got {q}') - self.q = q - - def test_custom_exception(self): - script = dedent(""" - import test.test__xxsubinterpreters - SpamError = test.test__xxsubinterpreters.RunFailedTests.SpamError - raise SpamError('eggs') - """) - try: - ns = {} - exec(script, ns, ns) - except Exception as exc: - expected = exc - - interpid = interpreters.create() - with self.expected_run_failure(expected): - interpreters.run_string(interpid, script) - - class SpamReducedError(Exception): - def __init__(self, q): - super().__init__(f'got {q}') - self.q = q - def __reduce__(self): - return (type(self), (self.q,), {}) - - def test_custom___reduce__(self): - script = dedent(""" - import test.test__xxsubinterpreters - SpamError = test.test__xxsubinterpreters.RunFailedTests.SpamReducedError - raise SpamError('eggs') - """) - try: - exec(script, (ns := {'__name__': '__main__'}), ns) - except Exception as exc: - expected = exc - - interpid = interpreters.create() - with self.expected_run_failure(expected): - interpreters.run_string(interpid, script) - - def test_traceback_propagated(self): - script = dedent(""" - def do_spam(): - raise Exception('uh-oh') - def do_eggs(): - return do_spam() - class Spam: - def do(self): - return do_eggs() - def get_handler(): - def handler(): - return Spam().do() - return handler - go = (lambda: get_handler()()) - def iter_all(): - yield from (go() for _ in [True]) - yield None - def main(): - for v in iter_all(): - pass - main() - """) - try: - ns = {} - exec(script, ns, ns) - except Exception as exc: - expected = exc - expectedtb = exc.__traceback__.tb_next - - interpid = interpreters.create() - with self.expected_run_failure(expected) as caught: - interpreters.run_string(interpid, script) - exc = caught.exception - - self.assertTracebacksEqual(exc.__cause__.__traceback__, - expectedtb) - - def test_chained_exceptions(self): - script = dedent(""" - try: - raise ValueError('msg 1') - except Exception as exc1: - try: - raise TypeError('msg 2') - except Exception as exc2: - try: - raise IndexError('msg 3') from exc2 - except Exception: - raise AttributeError('msg 4') - """) - try: - exec(script, {}, {}) - except Exception as exc: - expected = exc - - interpid = interpreters.create() - with self.expected_run_failure(expected) as caught: - interpreters.run_string(interpid, script) - exc = caught.exception - - # ...just to be sure. - self.assertIs(type(exc.__cause__), AttributeError) - - ################################## # channel tests diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 68f3b8cce9..6eb6b46ec8 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -466,6 +466,33 @@ class BaseTaskTests: t = outer() self.assertEqual(self.loop.run_until_complete(t), 1042) + def test_exception_chaining_after_await(self): + # Test that when awaiting on a task when an exception is already + # active, if the task raises an exception it will be chained + # with the original. + loop = asyncio.new_event_loop() + self.set_event_loop(loop) + + async def raise_error(): + raise ValueError + + async def run(): + try: + raise KeyError(3) + except Exception as exc: + task = self.new_task(loop, raise_error()) + try: + await task + except Exception as exc: + self.assertEqual(type(exc), ValueError) + chained = exc.__context__ + self.assertEqual((type(chained), chained.args), + (KeyError, (3,))) + + task = self.new_task(loop, run()) + loop.run_until_complete(task) + loop.close() + def test_cancel(self): def gen(): diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index 7267894508..b4061b7935 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -1,16 +1,19 @@ -import sys import compileall +import contextlib +import filecmp import importlib.util -import test.test_importlib.util +import io +import itertools import os import pathlib import py_compile import shutil import struct +import sys import tempfile +import test.test_importlib.util import time import unittest -import io from unittest import mock, skipUnless try: @@ -26,6 +29,24 @@ from .test_py_compile import without_source_date_epoch from .test_py_compile import SourceDateEpochTestMeta +def get_pyc(script, opt): + if not opt: + # Replace None and 0 with '' + opt = '' + return importlib.util.cache_from_source(script, optimization=opt) + + +def get_pycs(script): + return [get_pyc(script, opt) for opt in (0, 1, 2)] + + +def is_hardlink(filename1, filename2): + """Returns True if two files have the same inode (hardlink)""" + inode1 = os.stat(filename1).st_ino + inode2 = os.stat(filename2).st_ino + return inode1 == inode2 + + class CompileallTestsBase: def setUp(self): @@ -825,6 +846,32 @@ class CommandLineTestsBase: self.assertTrue(os.path.isfile(allowed_bc)) self.assertFalse(os.path.isfile(prohibited_bc)) + def test_hardlink_bad_args(self): + # Bad arguments combination, hardlink deduplication make sense + # only for more than one optimization level + self.assertRunNotOK(self.directory, "-o 1", "--hardlink-dupes") + + def test_hardlink(self): + # 'a = 0' code produces the same bytecode for the 3 optimization + # levels. All three .pyc files must have the same inode (hardlinks). + # + # If deduplication is disabled, all pyc files must have different + # inodes. + for dedup in (True, False): + with tempfile.TemporaryDirectory() as path: + with self.subTest(dedup=dedup): + script = script_helper.make_script(path, "script", "a = 0") + pycs = get_pycs(script) + + args = ["-q", "-o 0", "-o 1", "-o 2"] + if dedup: + args.append("--hardlink-dupes") + self.assertRunOK(path, *args) + + self.assertEqual(is_hardlink(pycs[0], pycs[1]), dedup) + self.assertEqual(is_hardlink(pycs[1], pycs[2]), dedup) + self.assertEqual(is_hardlink(pycs[0], pycs[2]), dedup) + class CommandLineTestsWithSourceEpoch(CommandLineTestsBase, unittest.TestCase, @@ -841,5 +888,176 @@ class CommandLineTestsNoSourceEpoch(CommandLineTestsBase, +class HardlinkDedupTestsBase: + # Test hardlink_dupes parameter of compileall.compile_dir() + + def setUp(self): + self.path = None + + @contextlib.contextmanager + def temporary_directory(self): + with tempfile.TemporaryDirectory() as path: + self.path = path + yield path + self.path = None + + def make_script(self, code, name="script"): + return script_helper.make_script(self.path, name, code) + + def compile_dir(self, *, dedup=True, optimize=(0, 1, 2), force=False): + compileall.compile_dir(self.path, quiet=True, optimize=optimize, + hardlink_dupes=dedup, force=force) + + def test_bad_args(self): + # Bad arguments combination, hardlink deduplication make sense + # only for more than one optimization level + with self.temporary_directory(): + self.make_script("pass") + with self.assertRaises(ValueError): + compileall.compile_dir(self.path, quiet=True, optimize=0, + hardlink_dupes=True) + with self.assertRaises(ValueError): + # same optimization level specified twice: + # compile_dir() removes duplicates + compileall.compile_dir(self.path, quiet=True, optimize=[0, 0], + hardlink_dupes=True) + + def create_code(self, docstring=False, assertion=False): + lines = [] + if docstring: + lines.append("'module docstring'") + lines.append('x = 1') + if assertion: + lines.append("assert x == 1") + return '\n'.join(lines) + + def iter_codes(self): + for docstring in (False, True): + for assertion in (False, True): + code = self.create_code(docstring=docstring, assertion=assertion) + yield (code, docstring, assertion) + + def test_disabled(self): + # Deduplication disabled, no hardlinks + for code, docstring, assertion in self.iter_codes(): + with self.subTest(docstring=docstring, assertion=assertion): + with self.temporary_directory(): + script = self.make_script(code) + pycs = get_pycs(script) + self.compile_dir(dedup=False) + self.assertFalse(is_hardlink(pycs[0], pycs[1])) + self.assertFalse(is_hardlink(pycs[0], pycs[2])) + self.assertFalse(is_hardlink(pycs[1], pycs[2])) + + def check_hardlinks(self, script, docstring=False, assertion=False): + pycs = get_pycs(script) + self.assertEqual(is_hardlink(pycs[0], pycs[1]), + not assertion) + self.assertEqual(is_hardlink(pycs[0], pycs[2]), + not assertion and not docstring) + self.assertEqual(is_hardlink(pycs[1], pycs[2]), + not docstring) + + def test_hardlink(self): + # Test deduplication on all combinations + for code, docstring, assertion in self.iter_codes(): + with self.subTest(docstring=docstring, assertion=assertion): + with self.temporary_directory(): + script = self.make_script(code) + self.compile_dir() + self.check_hardlinks(script, docstring, assertion) + + def test_only_two_levels(self): + # Don't build the 3 optimization levels, but only 2 + for opts in ((0, 1), (1, 2), (0, 2)): + with self.subTest(opts=opts): + with self.temporary_directory(): + # code with no dostring and no assertion: + # same bytecode for all optimization levels + script = self.make_script(self.create_code()) + self.compile_dir(optimize=opts) + pyc1 = get_pyc(script, opts[0]) + pyc2 = get_pyc(script, opts[1]) + self.assertTrue(is_hardlink(pyc1, pyc2)) + + def test_duplicated_levels(self): + # compile_dir() must not fail if optimize contains duplicated + # optimization levels and/or if optimization levels are not sorted. + with self.temporary_directory(): + # code with no dostring and no assertion: + # same bytecode for all optimization levels + script = self.make_script(self.create_code()) + self.compile_dir(optimize=[1, 0, 1, 0]) + pyc1 = get_pyc(script, 0) + pyc2 = get_pyc(script, 1) + self.assertTrue(is_hardlink(pyc1, pyc2)) + + def test_recompilation(self): + # Test compile_dir() when pyc files already exists and the script + # content changed + with self.temporary_directory(): + script = self.make_script("a = 0") + self.compile_dir() + # All three levels have the same inode + self.check_hardlinks(script) + + pycs = get_pycs(script) + inode = os.stat(pycs[0]).st_ino + + # Change of the module content + script = self.make_script("print(0)") + + # Recompilation without -o 1 + self.compile_dir(optimize=[0, 2], force=True) + + # opt-1.pyc should have the same inode as before and others should not + self.assertEqual(inode, os.stat(pycs[1]).st_ino) + self.assertTrue(is_hardlink(pycs[0], pycs[2])) + self.assertNotEqual(inode, os.stat(pycs[2]).st_ino) + # opt-1.pyc and opt-2.pyc have different content + self.assertFalse(filecmp.cmp(pycs[1], pycs[2], shallow=True)) + + def test_import(self): + # Test that import updates a single pyc file when pyc files already + # exists and the script content changed + with self.temporary_directory(): + script = self.make_script(self.create_code(), name="module") + self.compile_dir() + # All three levels have the same inode + self.check_hardlinks(script) + + pycs = get_pycs(script) + inode = os.stat(pycs[0]).st_ino + + # Change of the module content + script = self.make_script("print(0)", name="module") + + # Import the module in Python with -O (optimization level 1) + script_helper.assert_python_ok( + "-O", "-c", "import module", __isolated=False, PYTHONPATH=self.path + ) + + # Only opt-1.pyc is changed + self.assertEqual(inode, os.stat(pycs[0]).st_ino) + self.assertEqual(inode, os.stat(pycs[2]).st_ino) + self.assertFalse(is_hardlink(pycs[1], pycs[2])) + # opt-1.pyc and opt-2.pyc have different content + self.assertFalse(filecmp.cmp(pycs[1], pycs[2], shallow=True)) + + +class HardlinkDedupTestsWithSourceEpoch(HardlinkDedupTestsBase, + unittest.TestCase, + metaclass=SourceDateEpochTestMeta, + source_date_epoch=True): + pass + + +class HardlinkDedupTestsNoSourceEpoch(HardlinkDedupTestsBase, + unittest.TestCase, + metaclass=SourceDateEpochTestMeta, + source_date_epoch=False): + pass + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_dictcomps.py b/Lib/test/test_dictcomps.py index 16aa651b93..472e3dfa0d 100644 --- a/Lib/test/test_dictcomps.py +++ b/Lib/test/test_dictcomps.py @@ -77,7 +77,7 @@ class DictComprehensionTest(unittest.TestCase): compile("{x: y for y, x in ((1, 2), (3, 4))} = 5", "<test>", "exec") - with self.assertRaisesRegex(SyntaxError, "cannot assign"): + with self.assertRaisesRegex(SyntaxError, "illegal expression"): compile("{x: y for y, x in ((1, 2), (3, 4))} += 5", "<test>", "exec") diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py index 169058eac8..64dca2d017 100644 --- a/Lib/test/test_email/test_contentmanager.py +++ b/Lib/test/test_email/test_contentmanager.py @@ -329,6 +329,21 @@ class TestRawDataManager(TestEmailBase): self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content) self.assertEqual(m.get_content(), content) + def test_set_text_plain_long_line_heuristics(self): + m = self._make_message() + content = ("Simple but long message that is over 78 characters" + " long to force transfer encoding.\n") + raw_data_manager.set_content(m, content) + self.assertEqual(str(m), textwrap.dedent("""\ + Content-Type: text/plain; charset="utf-8" + Content-Transfer-Encoding: quoted-printable + + Simple but long message that is over 78 characters long to = + force transfer encoding. + """)) + self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content) + self.assertEqual(m.get_content(), content) + def test_set_text_short_line_minimal_non_ascii_heuristics(self): m = self._make_message() content = "et là il est monté sur moi et il commence à m'éto.\n" diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index b689ec7aed..efd77fdbaa 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -228,6 +228,8 @@ class ExceptionTests(unittest.TestCase): def baz(): '''quux''' """, 9, 20) + check("pass\npass\npass\n(1+)\npass\npass\npass", 4, 4) + check("(1+)", 1, 4) # Errors thrown by symtable.c check('x = [(yield i) for i in range(3)]', 1, 5) diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index e047801199..348ae15aa6 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -318,7 +318,7 @@ class ExceptionTest(unittest.TestCase): class GeneratorThrowTest(unittest.TestCase): - def test_exception_context_set(self): + def test_exception_context_with_yield(self): def f(): try: raise KeyError('a') @@ -332,6 +332,23 @@ class GeneratorThrowTest(unittest.TestCase): context = cm.exception.__context__ self.assertEqual((type(context), context.args), (KeyError, ('a',))) + def test_exception_context_with_yield_from(self): + def f(): + yield + + def g(): + try: + raise KeyError('a') + except Exception: + yield from f() + + gen = g() + gen.send(None) + with self.assertRaises(ValueError) as cm: + gen.throw(ValueError) + context = cm.exception.__context__ + self.assertEqual((type(context), context.args), (KeyError, ('a',))) + def test_throw_after_none_exc_type(self): def g(): try: @@ -1904,7 +1921,7 @@ SyntaxError: cannot assign to yield expression >>> def f(): (yield bar) += y Traceback (most recent call last): ... -SyntaxError: cannot assign to yield expression +SyntaxError: 'yield expression' is an illegal expression for augmented assignment Now check some throw() conditions: diff --git a/Lib/test/test_genexps.py b/Lib/test/test_genexps.py index 86e4e195f5..5c1a209b0e 100644 --- a/Lib/test/test_genexps.py +++ b/Lib/test/test_genexps.py @@ -158,7 +158,7 @@ Verify that syntax error's are raised for genexps used as lvalues >>> (y for y in (1,2)) += 10 Traceback (most recent call last): ... - SyntaxError: cannot assign to generator expression + SyntaxError: 'generator expression' is an illegal expression for augmented assignment ########### Tests borrowed from or inspired by test_generators.py ############ diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 02ba8a8b15..e1a402e2b4 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -1279,7 +1279,7 @@ class GrammarTests(unittest.TestCase): def test_try(self): ### try_stmt: 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite] ### | 'try' ':' suite 'finally' ':' suite - ### except_clause: 'except' [expr ['as' expr]] + ### except_clause: 'except' [expr ['as' NAME]] try: 1/0 except ZeroDivisionError: @@ -1297,6 +1297,9 @@ class GrammarTests(unittest.TestCase): except (EOFError, TypeError, ZeroDivisionError) as msg: pass try: pass finally: pass + with self.assertRaises(SyntaxError): + compile("try:\n pass\nexcept Exception as a.b:\n pass", "?", "exec") + compile("try:\n pass\nexcept Exception as a[b]:\n pass", "?", "exec") def test_suite(self): # simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT diff --git a/Lib/test/test_json/test_recursion.py b/Lib/test/test_json/test_recursion.py index 877dc448b1..543c62839b 100644 --- a/Lib/test/test_json/test_recursion.py +++ b/Lib/test/test_json/test_recursion.py @@ -52,7 +52,7 @@ class TestRecursion: return [JSONTestObject] else: return 'JSONTestObject' - return pyjson.JSONEncoder.default(o) + return self.json.JSONEncoder.default(o) enc = RecursiveJSONEncoder() self.assertEqual(enc.encode(JSONTestObject), '"JSONTestObject"') diff --git a/Lib/test/test_peg_parser.py b/Lib/test/test_peg_parser.py index df2d46d882..9614e45799 100644 --- a/Lib/test/test_peg_parser.py +++ b/Lib/test/test_peg_parser.py @@ -35,6 +35,9 @@ TEST_CASES = [ ('attribute_simple', 'a.b'), ('attributes_subscript', 'a.b[0]'), ('augmented_assignment', 'x += 42'), + ('augmented_assignment_attribute', 'a.b.c += 42'), + ('augmented_assignment_paren', '(x) += 42'), + ('augmented_assignment_paren_subscript', '(x[0]) -= 42'), ('binop_add', '1 + 1'), ('binop_add_multiple', '1 + 1 + 1 + 1'), ('binop_all', '1 + 2 * 5 + 3 ** 2 - -3'), @@ -547,6 +550,11 @@ TEST_CASES = [ with a as (x, y): pass '''), + ('with_list_target', + ''' + with a as [x, y]: + pass + '''), ('yield', 'yield'), ('yield_expr', 'yield a'), ('yield_from', 'yield from a'), @@ -560,6 +568,9 @@ FAIL_TEST_CASES = [ ("annotation_tuple", "(a,): int"), ("annotation_tuple_without_paren", "a,: int"), ("assignment_keyword", "a = if"), + ("augmented_assignment_list", "[a, b] += 1"), + ("augmented_assignment_tuple", "a, b += 1"), + ("augmented_assignment_tuple_paren", "(a, b) += (1, 2)"), ("comprehension_lambda", "(a for a in lambda: b)"), ("comprehension_else", "(a for a in b if c else d"), ("del_call", "del a()"), @@ -589,6 +600,20 @@ FAIL_TEST_CASES = [ a """), ("not_terminated_string", "a = 'example"), + ("try_except_attribute_target", + """ + try: + pass + except Exception as a.b: + pass + """), + ("try_except_subscript_target", + """ + try: + pass + except Exception as a[0]: + pass + """), ] FAIL_SPECIALIZED_MESSAGE_CASES = [ @@ -600,7 +625,7 @@ FAIL_SPECIALIZED_MESSAGE_CASES = [ ("(a, b): int", "only single target (not tuple) can be annotated"), ("[a, b]: int", "only single target (not list) can be annotated"), ("a(): int", "illegal target for annotation"), - ("1 += 1", "cannot assign to literal"), + ("1 += 1", "'literal' is an illegal expression for augmented assignment"), ("pass\n pass", "unexpected indent"), ("def f():\npass", "expected an indented block"), ("def f(*): pass", "named arguments must follow bare *"), diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 06636ae8a1..60c7d9fd38 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -100,35 +100,54 @@ expression inside that contain should still cause a syntax error. This test just checks a couple of cases rather than enumerating all of them. -# All of the following also produce different error messages with pegen -# >>> (a, "b", c) = (1, 2, 3) -# Traceback (most recent call last): -# SyntaxError: cannot assign to literal +>>> (a, "b", c) = (1, 2, 3) +Traceback (most recent call last): +SyntaxError: cannot assign to literal -# >>> (a, True, c) = (1, 2, 3) -# Traceback (most recent call last): -# SyntaxError: cannot assign to True +>>> (a, True, c) = (1, 2, 3) +Traceback (most recent call last): +SyntaxError: cannot assign to True >>> (a, __debug__, c) = (1, 2, 3) Traceback (most recent call last): SyntaxError: cannot assign to __debug__ -# >>> (a, *True, c) = (1, 2, 3) -# Traceback (most recent call last): -# SyntaxError: cannot assign to True +>>> (a, *True, c) = (1, 2, 3) +Traceback (most recent call last): +SyntaxError: cannot assign to True >>> (a, *__debug__, c) = (1, 2, 3) Traceback (most recent call last): SyntaxError: cannot assign to __debug__ -# >>> [a, b, c + 1] = [1, 2, 3] -# Traceback (most recent call last): -# SyntaxError: cannot assign to operator +>>> [a, b, c + 1] = [1, 2, 3] +Traceback (most recent call last): +SyntaxError: cannot assign to operator + +>>> [a, b[1], c + 1] = [1, 2, 3] +Traceback (most recent call last): +SyntaxError: cannot assign to operator + +>>> [a, b.c.d, c + 1] = [1, 2, 3] +Traceback (most recent call last): +SyntaxError: cannot assign to operator >>> a if 1 else b = 1 Traceback (most recent call last): SyntaxError: cannot assign to conditional expression +>>> a, b += 1, 2 +Traceback (most recent call last): +SyntaxError: 'tuple' is an illegal expression for augmented assignment + +>>> (a, b) += 1, 2 +Traceback (most recent call last): +SyntaxError: 'tuple' is an illegal expression for augmented assignment + +>>> [a, b] += 1, 2 +Traceback (most recent call last): +SyntaxError: 'list' is an illegal expression for augmented assignment + From compiler_complex_args(): >>> def f(None=1): @@ -334,16 +353,16 @@ More set_context(): >>> (x for x in x) += 1 Traceback (most recent call last): -SyntaxError: cannot assign to generator expression +SyntaxError: 'generator expression' is an illegal expression for augmented assignment >>> None += 1 Traceback (most recent call last): -SyntaxError: cannot assign to None +SyntaxError: 'None' is an illegal expression for augmented assignment >>> __debug__ += 1 Traceback (most recent call last): SyntaxError: cannot assign to __debug__ >>> f() += 1 Traceback (most recent call last): -SyntaxError: cannot assign to function call +SyntaxError: 'function call' is an illegal expression for augmented assignment Test continue in finally in weird combinations. @@ -676,6 +695,7 @@ class SyntaxTestCase(unittest.TestCase): def test_assign_call(self): self._check_error("f() = 1", "assign") + @unittest.skipIf(support.use_old_parser(), "The old parser cannot generate these error messages") def test_assign_del(self): self._check_error("del (,)", "invalid syntax") self._check_error("del 1", "delete literal") |