From 82849bd8a91b8ae1dad3da1ee15e91a12d85d293 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 22 Jun 2022 10:51:07 +0100 Subject: [PATCH 1/3] support propertied decorators --- mypy/semanal.py | 5 ++++- test-data/unit/semanal-errors.test | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index e00913a8cde4..a358301c5c6e 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1047,6 +1047,7 @@ def visit_decorator(self, dec: Decorator) -> None: d.accept(self) removed: List[int] = [] no_type_check = False + could_be_decorated_property = False for i, d in enumerate(dec.decorators): # A bunch of decorators are special cased here. if refers_to_fullname(d, 'abc.abstractmethod'): @@ -1094,6 +1095,8 @@ def visit_decorator(self, dec: Decorator) -> None: removed.append(i) else: self.fail("@final cannot be used with non-method functions", d) + elif not dec.var.is_property: + could_be_decorated_property = True for i in reversed(removed): del dec.decorators[i] if (not dec.is_overload or dec.var.is_property) and self.type: @@ -1101,7 +1104,7 @@ def visit_decorator(self, dec: Decorator) -> None: dec.var.is_initialized_in_class = True if not no_type_check and self.recurse_into_functions: dec.func.accept(self) - if dec.decorators and dec.var.is_property: + if could_be_decorated_property and dec.decorators and dec.var.is_property: self.fail('Decorated property not supported', dec) if dec.func.is_abstract and dec.func.is_final: self.fail(f"Method {dec.func.name} is both abstract and final", dec) diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index a1ff4ec1c3e7..c2ac613a5b37 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1231,7 +1231,7 @@ from typing import overload class A: @overload # E: An overloaded function outside a stub file must have an implementation def f(self) -> int: pass - @property # E: Decorated property not supported + @property @overload def f(self) -> int: pass [builtins fixtures/property.pyi] @@ -1244,7 +1244,7 @@ class A: @dec # E: Decorated property not supported @property def f(self) -> int: pass - @property # E: Decorated property not supported + @property @dec def g(self) -> int: pass [builtins fixtures/property.pyi] From 9311edd1f4b772f0a0753aecda4e17d74458063f Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 22 Jun 2022 12:19:48 +0100 Subject: [PATCH 2/3] support settered decorators --- mypy/semanal.py | 6 +++--- test-data/unit/semanal-errors.test | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index a358301c5c6e..27076f9cb467 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -957,7 +957,7 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) - deleted_items = [] for i, item in enumerate(items[1:]): if isinstance(item, Decorator): - if len(item.decorators) == 1: + if len(item.decorators) >= 1: node = item.decorators[0] if isinstance(node, MemberExpr): if node.name == 'setter': @@ -965,8 +965,8 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) - first_item.var.is_settable_property = True # Get abstractness from the original definition. item.func.is_abstract = first_item.func.is_abstract - else: - self.fail("Decorated property not supported", item) + else: + self.fail("Decorated property not supported", item) item.func.accept(self) else: self.fail(f'Unexpected definition for property "{first_item.func.name}"', diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index c2ac613a5b37..67877c40e4b4 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1250,6 +1250,22 @@ class A: [builtins fixtures/property.pyi] [out] +[case testDecoratedPropertySetter] +import typing +def dec(f): pass +class A: + @property + @dec + def f(self) -> int: pass + @f.setter + @dec + def f(self, v: int) -> None: pass + @dec # E: Decorated property not supported + @f.setter + def f(self, v: int) -> None: pass +[builtins fixtures/property.pyi] +[out] + [case testImportTwoModulesWithSameNameInFunction] import typing def f() -> None: From 0e071aad78494dc5fa110c6ffb8d4fed1fb0ddca Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 22 Jun 2022 12:51:12 +0100 Subject: [PATCH 3/3] put decorated property warning back for overloads --- mypy/semanal.py | 2 ++ test-data/unit/semanal-errors.test | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 27076f9cb467..782a2dc0e757 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -846,6 +846,8 @@ def analyze_overload_sigs_and_impl( # that. non_overload_indexes.append(i) else: + if item.var.is_property: + self.fail("Decorated property not supported", item) item.func.is_overload = True types.append(callable) elif isinstance(item, FuncDef): diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 67877c40e4b4..264071f2ec60 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1231,7 +1231,7 @@ from typing import overload class A: @overload # E: An overloaded function outside a stub file must have an implementation def f(self) -> int: pass - @property + @property # E: Decorated property not supported @overload def f(self) -> int: pass [builtins fixtures/property.pyi]