From 07be66c6bcfa694915542ec2eb66034f110c2fc1 Mon Sep 17 00:00:00 2001 From: TH3CHARLie Date: Mon, 4 Nov 2019 21:10:41 +0800 Subject: [PATCH 1/5] generate an error if trying to use @final with TypedDict --- mypy/semanal.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index 379f15648c89..a78cbb6cf9b8 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1067,6 +1067,11 @@ def analyze_class(self, defn: ClassDef) -> None: is_typeddict, info = self.typed_dict_analyzer.analyze_typeddict_classdef(defn) if is_typeddict: + for d in defn.decorators: + d.accept(self) + if d.fullname in ('typing.final', + 'typing_extensions.final'): + self.fail("@final cannot be used with TypedDict", d) if info is None: self.mark_incomplete(defn.name, defn) else: From 958d998419a0800fa9be9f4ff8488b6fee3a038b Mon Sep 17 00:00:00 2001 From: TH3CHARLie Date: Mon, 4 Nov 2019 21:57:32 +0800 Subject: [PATCH 2/5] adjust indentation, add RefExpr check --- mypy/semanal.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index a78cbb6cf9b8..ec8d97bc8fd6 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1067,11 +1067,12 @@ def analyze_class(self, defn: ClassDef) -> None: is_typeddict, info = self.typed_dict_analyzer.analyze_typeddict_classdef(defn) if is_typeddict: - for d in defn.decorators: - d.accept(self) - if d.fullname in ('typing.final', - 'typing_extensions.final'): - self.fail("@final cannot be used with TypedDict", d) + for decorator in defn.decorators: + decorator.accept(self) + if isinstance(decorator, RefExpr): + if decorator.fullname in ('typing.final', + 'typing_extensions.final'): + self.fail("@final cannot be used with TypedDict", decorator) if info is None: self.mark_incomplete(defn.name, defn) else: From a6bd4ae6ca379eb1c0b64e782c03df51b6d24f49 Mon Sep 17 00:00:00 2001 From: TH3CHARLie Date: Tue, 5 Nov 2019 00:31:58 +0800 Subject: [PATCH 3/5] Update spacing in mypy/semanal.py Co-Authored-By: Ivan Levkivskyi --- mypy/semanal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index ec8d97bc8fd6..1975f2704c70 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1071,7 +1071,7 @@ def analyze_class(self, defn: ClassDef) -> None: decorator.accept(self) if isinstance(decorator, RefExpr): if decorator.fullname in ('typing.final', - 'typing_extensions.final'): + 'typing_extensions.final'): self.fail("@final cannot be used with TypedDict", decorator) if info is None: self.mark_incomplete(defn.name, defn) From 466627405107e911e9e2f6e7b7104b602bee3783 Mon Sep 17 00:00:00 2001 From: TH3CHARLie Date: Tue, 5 Nov 2019 01:09:12 +0800 Subject: [PATCH 4/5] add test case: testCannotUseFinalDecoratorWithTypedDict --- test-data/unit/check-typeddict.test | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 267eca43ea7a..b1b2789a3bad 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -1924,3 +1924,16 @@ v = {bad2: 2} # E: Extra key 'bad' for TypedDict "Value" [builtins fixtures/dict.pyi] [typing fixtures/typing-full.pyi] + +[case testCannotUseFinalDecoratorWithTypedDict] +from typing import TypedDict +from typing_extensions import final + +@final # E: @final cannot be used with TypedDict +class DummyTypedDict(TypedDict): + int_val: int + float_val: float + str_val: str + +[builtins fixtures/dict.pyi] +[typing fixtures/typing-full.pyi] \ No newline at end of file From 1fcb297130463ef97e26e5c07f5fd728c7f6e7cd Mon Sep 17 00:00:00 2001 From: TH3CHARLie Date: Tue, 5 Nov 2019 01:16:24 +0800 Subject: [PATCH 5/5] Add newline at end of test file --- test-data/unit/check-typeddict.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index b1b2789a3bad..a11564080819 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -1936,4 +1936,4 @@ class DummyTypedDict(TypedDict): str_val: str [builtins fixtures/dict.pyi] -[typing fixtures/typing-full.pyi] \ No newline at end of file +[typing fixtures/typing-full.pyi]