From 204c7dada412b0ca4ce22315d2acae640adb128f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 5 May 2021 17:25:58 +0200 Subject: [PATCH] Add support for functools.cached_property (#10408) Add support for functools.cached_property decorator: https://docs.python.org/3/library/functools.html#functools.cached_property Closes #8913. Closes python/typeshed#3963. --- mypy/semanal.py | 5 ++++- test-data/unit/check-functools.test | 20 ++++++++++++++++++++ test-data/unit/fixtures/property.pyi | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 0927e465b07c..f4702635fb9d 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -999,12 +999,15 @@ def visit_decorator(self, dec: Decorator) -> None: dec.var.is_classmethod = True self.check_decorated_function_is_method('classmethod', dec) elif (refers_to_fullname(d, 'builtins.property') or - refers_to_fullname(d, 'abc.abstractproperty')): + refers_to_fullname(d, 'abc.abstractproperty') or + refers_to_fullname(d, 'functools.cached_property')): removed.append(i) dec.func.is_property = True dec.var.is_property = True if refers_to_fullname(d, 'abc.abstractproperty'): dec.func.is_abstract = True + elif refers_to_fullname(d, 'functools.cached_property'): + dec.var.is_settable_property = True self.check_decorated_function_is_method('property', dec) if len(dec.func.arguments) > 1: self.fail('Too many arguments', dec.func) diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 416006591425..33653c8d3fbc 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -107,3 +107,23 @@ Ord() > 1 Ord() >= 1 # E: Unsupported left operand type for >= ("Ord") [builtins fixtures/ops.pyi] [builtins fixtures/dict.pyi] + +[case testCachedProperty] +# flags: --python-version 3.8 +from functools import cached_property +class Parent: + @property + def f(self) -> str: pass +class Child(Parent): + @cached_property + def f(self) -> str: pass + @cached_property + def g(self) -> int: pass + @cached_property + def h(self, arg) -> int: pass # E: Too many arguments +reveal_type(Parent().f) # N: Revealed type is "builtins.str" +reveal_type(Child().f) # N: Revealed type is "builtins.str" +reveal_type(Child().g) # N: Revealed type is "builtins.int" +Child().f = "Hello World" +Child().g = "invalid" # E: Incompatible types in assignment (expression has type "str", variable has type "int") +[builtins fixtures/property.pyi] diff --git a/test-data/unit/fixtures/property.pyi b/test-data/unit/fixtures/property.pyi index 5dc785da2364..b3f60abaf8a0 100644 --- a/test-data/unit/fixtures/property.pyi +++ b/test-data/unit/fixtures/property.pyi @@ -12,6 +12,7 @@ class function: pass property = object() # Dummy definition +class dict: pass class int: pass class str: pass class bytes: pass