From f7ed5ce0244eae4e0b1887e4cbaff4960edb0fca Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 31 May 2023 19:35:29 -0700 Subject: [PATCH 1/2] gh-105164: Detect annotations inside match blocks --- Lib/test/test_type_annotations.py | 15 +++++++++++++++ ...2023-05-31-19-35-22.gh-issue-105164.6Wajph.rst | 2 ++ Python/compile.c | 10 ++++++++++ 3 files changed, 27 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-05-31-19-35-22.gh-issue-105164.6Wajph.rst diff --git a/Lib/test/test_type_annotations.py b/Lib/test/test_type_annotations.py index 87f46c2ce8ce61..527acec6acc209 100644 --- a/Lib/test/test_type_annotations.py +++ b/Lib/test/test_type_annotations.py @@ -1,4 +1,5 @@ import unittest +from test.support import run_code class TypeAnnotationTests(unittest.TestCase): @@ -101,3 +102,17 @@ class D(metaclass=C): with self.assertRaises(AttributeError): del D.__annotations__ self.assertEqual(D.__annotations__, {}) + + def test_module_level(self): + ns = run_code("x: int = 1") + self.assertEqual(ns["__annotations__"], {"x": int}) + + ns = run_code("if True:\n x: int = 1") + self.assertEqual(ns["__annotations__"], {"x": int}) + + ns = run_code(""" + match 0: + case 0: + x: int = 1 + """) + self.assertEqual(ns["__annotations__"], {"x": int}) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-31-19-35-22.gh-issue-105164.6Wajph.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-31-19-35-22.gh-issue-105164.6Wajph.rst new file mode 100644 index 00000000000000..7d3486c3b6e98a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-05-31-19-35-22.gh-issue-105164.6Wajph.rst @@ -0,0 +1,2 @@ +Ensure annotations are set up correctly if the only annotation in a block is +within a :keyword:`match` block. Patch by Jelle Zijlstra. diff --git a/Python/compile.c b/Python/compile.c index f2314ae11c417b..32eda4d407ea12 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1422,8 +1422,18 @@ find_ann(asdl_stmt_seq *stmts) find_ann(st->v.TryStar.finalbody) || find_ann(st->v.TryStar.orelse); break; + case Match_kind: + for (j = 0; j < asdl_seq_LEN(st->v.Match.cases); j++) { + match_case_ty match_case = (match_case_ty)asdl_seq_GET( + st->v.Match.cases, j); + if (find_ann(match_case->body)) { + return true; + } + } + break; default: res = false; + break; } if (res) { break; From 4d09ac45aefc72bdda44432ac8ed90a4148700df Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 31 May 2023 19:44:04 -0700 Subject: [PATCH 2/2] more tests --- Lib/test/test_type_annotations.py | 110 ++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_type_annotations.py b/Lib/test/test_type_annotations.py index 527acec6acc209..3dbb35afcb620f 100644 --- a/Lib/test/test_type_annotations.py +++ b/Lib/test/test_type_annotations.py @@ -1,3 +1,4 @@ +import textwrap import unittest from test.support import run_code @@ -103,16 +104,111 @@ class D(metaclass=C): del D.__annotations__ self.assertEqual(D.__annotations__, {}) - def test_module_level(self): - ns = run_code("x: int = 1") - self.assertEqual(ns["__annotations__"], {"x": int}) - ns = run_code("if True:\n x: int = 1") - self.assertEqual(ns["__annotations__"], {"x": int}) +class TestSetupAnnotations(unittest.TestCase): + def check(self, code: str): + code = textwrap.dedent(code) + for scope in ("module", "class"): + with self.subTest(scope=scope): + if scope == "class": + code = f"class C:\n{textwrap.indent(code, ' ')}" + ns = run_code(code) + if scope == "class": + annotations = ns["C"].__annotations__ + else: + annotations = ns["__annotations__"] + self.assertEqual(annotations, {"x": int}) + + def test_top_level(self): + self.check("x: int = 1") + + def test_blocks(self): + self.check("if True:\n x: int = 1") + self.check(""" + while True: + x: int = 1 + break + """) + self.check(""" + while False: + pass + else: + x: int = 1 + """) + self.check(""" + for i in range(1): + x: int = 1 + """) + self.check(""" + for i in range(1): + pass + else: + x: int = 1 + """) + + def test_try(self): + self.check(""" + try: + x: int = 1 + except: + pass + """) + self.check(""" + try: + pass + except: + pass + else: + x: int = 1 + """) + self.check(""" + try: + pass + except: + pass + finally: + x: int = 1 + """) + self.check(""" + try: + 1/0 + except: + x: int = 1 + """) + + def test_try_star(self): + self.check(""" + try: + x: int = 1 + except* Exception: + pass + """) + self.check(""" + try: + pass + except* Exception: + pass + else: + x: int = 1 + """) + self.check(""" + try: + pass + except* Exception: + pass + finally: + x: int = 1 + """) + self.check(""" + try: + 1/0 + except* Exception: + x: int = 1 + """) - ns = run_code(""" + def test_match(self): + self.check(""" match 0: case 0: x: int = 1 """) - self.assertEqual(ns["__annotations__"], {"x": int})