Skip to content

Commit

Permalink
Changed type inference logic that infers the type of the value attr…
Browse files Browse the repository at this point in the history
…ibute in an enum. If the enum class has a custom metaclass (one that is not part of the stdlib), pyright now assumes that the custom metaclass may perform some runtime magic that modifies the `value`. This occurs, for example, in django's `TextChoices` class. This addresses #5092.
  • Loading branch information
msfterictraut committed May 10, 2023
1 parent afce8cc commit 7888d1c
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 1 deletion.
9 changes: 9 additions & 0 deletions packages/pyright-internal/src/analyzer/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,15 @@ export function getTypeOfEnumMember(
}

if (memberName === 'value' || memberName === '_value_') {
// If the enum class has a custom metaclass, it may implement some
// "magic" that computes different values for the "value" attribute.
// This occurs, for example, in the django TextChoices class. If we
// detect a custom metaclass, we'll assume the value is Any.
const metaclass = classType.details.effectiveMetaclass;
if (metaclass && isClass(metaclass) && !ClassType.isBuiltIn(metaclass)) {
return { type: AnyType.create(), isIncomplete };
}

if (literalValue) {
assert(literalValue instanceof EnumLiteral);
return { type: literalValue.itemType, isIncomplete };
Expand Down
19 changes: 18 additions & 1 deletion packages/pyright-internal/src/tests/samples/enums1.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This sample tests the type checker's handling of Enum.

from enum import Enum, IntEnum
from enum import Enum, EnumMeta, IntEnum


TestEnum1 = Enum("TestEnum1", " A B, , ,C , \t D\t")
Expand Down Expand Up @@ -141,3 +141,20 @@ def __new__(cls, value: str, other1: int, other2: int):
reveal_type(te9_A._value_, expected_text="Any")
reveal_type(te9_A.name, expected_text="Literal['A']")
reveal_type(te9_A._name_, expected_text="Literal['A']")


class CustomEnumMeta1(EnumMeta):
pass


class TestEnum10(Enum, metaclass=CustomEnumMeta1):
A = 1
B = 2


te10_A = TestEnum10.A
reveal_type(te10_A, expected_text="Literal[TestEnum10.A]")
reveal_type(te10_A.value, expected_text="Any")
reveal_type(te9_A._value_, expected_text="Any")
reveal_type(te9_A.name, expected_text="Literal['A']")
reveal_type(te9_A._name_, expected_text="Literal['A']")

0 comments on commit 7888d1c

Please sign in to comment.