From dd079fb630401fa22f7aaebe3cca133784e8963c Mon Sep 17 00:00:00 2001 From: Kevin Murphy Date: Thu, 3 Jul 2025 20:48:25 -0700 Subject: [PATCH] Fix `Enum.value` inference for `Enum`s with `@cache`d methods Before this, adding an annotation like `@functools.cache` to any method on an `Enum` caused the inference for the class's `.value` to fail (i.e., become `Any`). Fixes #19368 Co-authored-by: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> --- mypy/plugins/enums.py | 9 ++------- test-data/unit/check-enum.test | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py index 8b7c5df6f51f..35338d091559 100644 --- a/mypy/plugins/enums.py +++ b/mypy/plugins/enums.py @@ -184,7 +184,7 @@ class SomeEnum: if _implements_new(info): return ctx.default_attr_type - stnodes = (info.get(name) for name in info.names) + stnodes = (info.get(name) for name in info.enum_members) # Enums _can_ have methods, instance attributes, and `nonmember`s. # Omit methods and attributes created by assigning to self.* @@ -194,12 +194,7 @@ class SomeEnum: for n in stnodes if n is None or not n.implicit ) - proper_types = [ - _infer_value_type_with_auto_fallback(ctx, t) - for t in node_types - if t is None - or (not isinstance(t, CallableType) and not is_named_instance(t, "enum.nonmember")) - ] + proper_types = [_infer_value_type_with_auto_fallback(ctx, t) for t in node_types] underlying_type = _first(proper_types) if underlying_type is None: return ctx.default_attr_type diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 1ab8109eda75..08d260fade2e 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -2539,3 +2539,28 @@ def check(thing: Things) -> None: return None return None # E: Statement is unreachable [builtins fixtures/enum.pyi] + +[case testValueFallbackWithCachedMethod] +from enum import Enum, auto +from collections.abc import Hashable +from typing import Callable, Generic, TypeVar + +_T = TypeVar("_T") + +class _lru_cache_wrapper(Generic[_T]): + def __call__(self, *args: Hashable, **kwargs: Hashable) -> _T: ... + +def cache(user_function: Callable[..., _T], /) -> _lru_cache_wrapper[_T]: ... + +class Color(Enum): + RED = auto() + + @cache + def lowercase_name(self) -> str: + return self.name + +reveal_type(Color.RED.value) # N: Revealed type is "builtins.int" + +def frobnicate(color: Color) -> None: + reveal_type(color.value) # N: Revealed type is "builtins.int" +[builtins fixtures/primitives.pyi]