From bebd6558234a7edf29040338ce999ae0f233a0fb Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Fri, 30 Jan 2026 01:42:13 -0500 Subject: [PATCH 1/7] added fuzzy matching for missing enum member --- mypy/checkmember.py | 2 ++ mypy/messages.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index b22a3d9e0849..8939bc04a1f8 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1159,6 +1159,8 @@ def analyze_class_attribute_access( return itype.extra_attrs.attrs[name] if info.fallback_to_any or info.meta_fallback_to_any: return apply_class_attr_hook(mx, hook, AnyType(TypeOfAny.special_form)) + if info.is_enum: + return report_missing_attribute(mx.original_type, itype, name, mx) return None if ( diff --git a/mypy/messages.py b/mypy/messages.py index c5756a463894..d9bd7a43480f 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -510,6 +510,23 @@ def has_no_attr( code=codes.ATTR_DEFINED, ) failed = True + elif isinstance(typ, Instance) and typ.type.names: + alternatives = set(typ.type.names.keys()) + alternatives.discard(member) + matches = [m for m in COMMON_MISTAKES.get(member, []) if m in alternatives] + matches.extend(best_matches(member, alternatives, n=3)) + if matches: + self.fail( + '{} has no attribute "{}"; maybe {}?{}'.format( + format_type(original_type, self.options), + member, + pretty_seq(matches, "or"), + extra, + ), + context, + code=codes.ATTR_DEFINED, + ) + failed = True if not failed: self.fail( '{} has no attribute "{}"{}'.format( From 02d1264faba21a8f867d370825d0fd62248d7e8e Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Fri, 30 Jan 2026 01:42:36 -0500 Subject: [PATCH 2/7] account for invalid/empty enums --- mypy/checkmember.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 8939bc04a1f8..9b959d56fc34 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1159,7 +1159,8 @@ def analyze_class_attribute_access( return itype.extra_attrs.attrs[name] if info.fallback_to_any or info.meta_fallback_to_any: return apply_class_attr_hook(mx, hook, AnyType(TypeOfAny.special_form)) - if info.is_enum: + # For enum types, report missing member with fuzzy matching suggestions + if info.is_enum and info.enum_members: return report_missing_attribute(mx.original_type, itype, name, mx) return None From 0aa486739ce3187a6476be02dc9c5dc2d7711607 Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Fri, 30 Jan 2026 01:42:51 -0500 Subject: [PATCH 3/7] added test case --- test-data/unit/check-enum.test | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index c05dfdef2bf7..673eda587294 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -2550,6 +2550,20 @@ class Pet(Enum): # E: Incompatible types in assignment (expression has type "ellipsis", variable has type "str") [builtins fixtures/enum.pyi] +[case testEnumMemberFuzzyMatch] +from enum import Enum + +class Color(Enum): + GREEN = 1 + RED = 2 + YELLOW = 3 + +Color.GREN # E: "type[Color]" has no attribute "GREN"; maybe "GREEN"? +Color.YELOW # E: "type[Color]" has no attribute "YELOW"; maybe "YELLOW"? +Color.RDE # E: "type[Color]" has no attribute "RDE" +Color.BLUE # E: "type[Color]" has no attribute "BLUE" +[builtins fixtures/enum.pyi] + [case testEnumValueWithPlaceholderNodeType] # https://github.com/python/mypy/issues/11971 from enum import Enum From 8cdac522bf38e838ebfb6a99bbbf98160310012b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 30 Jan 2026 06:57:59 +0000 Subject: [PATCH 4/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/messages.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index d9bd7a43480f..eb82b70d13e9 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -510,12 +510,12 @@ def has_no_attr( code=codes.ATTR_DEFINED, ) failed = True - elif isinstance(typ, Instance) and typ.type.names: + elif isinstance(typ, Instance) and typ.type.names: alternatives = set(typ.type.names.keys()) alternatives.discard(member) matches = [m for m in COMMON_MISTAKES.get(member, []) if m in alternatives] matches.extend(best_matches(member, alternatives, n=3)) - if matches: + if matches: self.fail( '{} has no attribute "{}"; maybe {}?{}'.format( format_type(original_type, self.options), From e250b272cb51d2be21964a2892eaf29705f44dab Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Fri, 30 Jan 2026 03:14:57 -0500 Subject: [PATCH 5/7] skip dunder methods --- mypy/checkmember.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 9b959d56fc34..d7270b58f00d 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1160,7 +1160,7 @@ def analyze_class_attribute_access( if info.fallback_to_any or info.meta_fallback_to_any: return apply_class_attr_hook(mx, hook, AnyType(TypeOfAny.special_form)) # For enum types, report missing member with fuzzy matching suggestions - if info.is_enum and info.enum_members: + if info.is_enum and info.enum_members and not (name.startswith("__") and name.endswith("__"): return report_missing_attribute(mx.original_type, itype, name, mx) return None From 1eeeebaa22df3492a6751effe9b0d842b0e919bc Mon Sep 17 00:00:00 2001 From: KevinRK29 Date: Fri, 30 Jan 2026 03:28:00 -0500 Subject: [PATCH 6/7] minor fix --- mypy/checkmember.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index d7270b58f00d..ed90edd94742 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1160,7 +1160,7 @@ def analyze_class_attribute_access( if info.fallback_to_any or info.meta_fallback_to_any: return apply_class_attr_hook(mx, hook, AnyType(TypeOfAny.special_form)) # For enum types, report missing member with fuzzy matching suggestions - if info.is_enum and info.enum_members and not (name.startswith("__") and name.endswith("__"): + if info.is_enum and info.enum_members and not (name.startswith("__") and name.endswith("__")): return report_missing_attribute(mx.original_type, itype, name, mx) return None From 0343f4b488d0d193024ef440c3f1f414e4e28434 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 30 Jan 2026 08:29:47 +0000 Subject: [PATCH 7/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/checkmember.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index ed90edd94742..727196877750 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1160,7 +1160,11 @@ def analyze_class_attribute_access( if info.fallback_to_any or info.meta_fallback_to_any: return apply_class_attr_hook(mx, hook, AnyType(TypeOfAny.special_form)) # For enum types, report missing member with fuzzy matching suggestions - if info.is_enum and info.enum_members and not (name.startswith("__") and name.endswith("__")): + if ( + info.is_enum + and info.enum_members + and not (name.startswith("__") and name.endswith("__")) + ): return report_missing_attribute(mx.original_type, itype, name, mx) return None