From 94a65c257d54e22b0b8394db86bcdccb162da8be Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 30 Mar 2023 20:50:20 +0300 Subject: [PATCH] [stubgen] Add `@functools.cached_property` support, refs #14980 --- mypy/stubgen.py | 11 ++++++++ test-data/unit/stubgen.test | 56 +++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 212d934a11b7..4c02b931fc47 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -849,6 +849,9 @@ def process_name_expr_decorator(self, expr: NameExpr, context: Decorator) -> tup self.add_decorator("property") self.add_decorator("abc.abstractmethod") is_abstract = True + elif self.refers_to_fullname(name, "functools.cached_property"): + self.import_tracker.require_name(name) + self.add_decorator(name) elif self.refers_to_fullname(name, OVERLOAD_NAMES): self.add_decorator(name) self.add_typing_import("overload") @@ -894,6 +897,14 @@ def process_member_expr_decorator( self.import_tracker.require_name(expr.expr.name) self.add_decorator(f"{expr.expr.name}.{expr.name}") is_abstract = True + elif expr.name == "cached_property" and isinstance(expr.expr, NameExpr): + explicit_name = expr.expr.name + reverse = self.import_tracker.reverse_alias.get(explicit_name) + if reverse == "functools" or (reverse is None and explicit_name == "functools"): + if reverse is not None: + self.import_tracker.add_import(reverse, alias=explicit_name) + self.import_tracker.require_name(explicit_name) + self.add_decorator(f"{explicit_name}.{expr.name}") elif expr.name == "coroutine": if ( isinstance(expr.expr, MemberExpr) diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 8e4285b7de2e..16584a134147 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -319,6 +319,62 @@ class A: def f(self, x) -> None: ... def h(self) -> None: ... +[case testFunctoolsCachedProperty] +import functools + +class A: + @functools.cached_property + def x(self): + return 'x' +[out] +import functools + +class A: + @functools.cached_property + def x(self): ... + +[case testFunctoolsCachedPropertyAlias] +import functools as ft + +class A: + @ft.cached_property + def x(self): + return 'x' +[out] +import functools as ft + +class A: + @ft.cached_property + def x(self): ... + +[case testCachedProperty] +from functools import cached_property + +class A: + @cached_property + def x(self): + return 'x' +[out] +from functools import cached_property + +class A: + @cached_property + def x(self): ... + +[case testCachedPropertyAlias] +from functools import cached_property as cp + +class A: + @cp + def x(self): + return 'x' +[out] +from functools import cached_property as cp + +class A: + @cp + def x(self): ... + [case testStaticMethod] class A: @staticmethod