From 53e9c62243f4fb0d07bc2f61a3b8039beaae8452 Mon Sep 17 00:00:00 2001 From: Andrew Haigh Date: Mon, 10 May 2021 20:54:22 +1000 Subject: [PATCH] Fix inference of Enum.__members__ property for subclasses Ref PyCQA/pylint#2626. Ref PyCQA/pylint#3535. Ref PyCQA/pylint#4358. This updates the namedtuple/enum brain to add a dictionary for __members__ --- astroid/brain/brain_namedtuple_enum.py | 10 ++++++++++ tests/unittest_scoped_nodes.py | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/astroid/brain/brain_namedtuple_enum.py b/astroid/brain/brain_namedtuple_enum.py index 1af5ba2bb4..9a9cc98981 100644 --- a/astroid/brain/brain_namedtuple_enum.py +++ b/astroid/brain/brain_namedtuple_enum.py @@ -315,6 +315,7 @@ def infer_enum_class(node): if node.root().name == "enum": # Skip if the class is directly from enum module. break + dunder_members = {} for local, values in node.locals.items(): if any(not isinstance(value, nodes.AssignName) for value in values): continue @@ -372,7 +373,16 @@ def name(self): for method in node.mymethods(): fake.locals[method.name] = [method] new_targets.append(fake.instantiate_class()) + dunder_members[local] = fake node.locals[local] = new_targets + members = nodes.Dict(parent=node) + members.postinit( + [ + (nodes.Const(k, parent=members), nodes.Name(v.name, parent=members)) + for k, v in dunder_members.items() + ] + ) + node.locals["__members__"] = [members] break return node diff --git a/tests/unittest_scoped_nodes.py b/tests/unittest_scoped_nodes.py index e9a15dafbe..7fe537b2fb 100644 --- a/tests/unittest_scoped_nodes.py +++ b/tests/unittest_scoped_nodes.py @@ -2054,6 +2054,22 @@ class Derived(Parent): assert isinstance(inferred, objects.Property) +def test_issue940_enums_as_a_real_world_usecase(): + node = builder.extract_node( + """ + from enum import Enum + class Sounds(Enum): + bee = "buzz" + cat = "meow" + Sounds.__members__ + """ + ) + inferred_result = next(node.infer()) + assert isinstance(inferred_result, nodes.Dict) + actual = [k.value for k, _ in inferred_result.items] + assert sorted(actual) == ["bee", "cat"] + + def test_metaclass_cannot_infer_call_yields_an_instance(): node = builder.extract_node( """