Skip to content

Errors caused by functional Enum API with custom class #10469

Closed
@ziima

Description

@ziima

Bug Report

I have an Enum using function Enum API on external data (hence the functional API) with custom class (also according by docs) and I encountered a series of errors with mypy. I was able to mitigate several of them by a workaround, but I'm not able to solve one at the class level.

To Reproduce

from enum import Enum

_ANIMALS = {
    'ANT': (0, 'Ant'),
    'BEE': (1, 'Bee'),
    'CAT': (2, 'Cat'),
    'DOG': (3, 'Dog'),
}

class LabeledEnum(int, Enum):
    def __new__(cls, value: int, label: str):
        """Construct item with label."""
        obj = super().__new__(cls, value)
        obj._value_ = value

        obj.label = label
        return obj

    label: str

Animal = LabeledEnum('Animal', _ANIMALS)

def get_animal() -> Animal:
    return Animal.ANT

Animal(3)

Expected Behavior

Ideally no errors. At least keep the information that LabeledEnum is a type and not variable. That would require to ignore the valid-type error at every usage in annotations of function arguments or return type.

Actual Behavior

error: Incompatible types in assignment (expression has type "int", variable has type "str")  [assignment]
error: Argument 1 to "LabeledEnum" has incompatible type "str"; expected "int"  [arg-type]
error: Argument 2 to "LabeledEnum" has incompatible type "Dict[str, Tuple[int, str]]"; expected "str"  [arg-type]
error: Variable "custom_enum.Animal" is not valid as a type  [valid-type]
note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
error: "LabeledEnum" has no attribute "ANT"  [attr-defined]
error: "LabeledEnum" not callable  [operator]

Workaround
I was able to mitigate most of the errors by ignores and overrides:

class LabeledEnum(int, Enum):
    def __new__(cls, value: int, label: str):
        """Construct item with label."""
        obj = super().__new__(cls, value)
        obj._value_ = value  # type: ignore[assignment]

        obj.label = label
        return obj

    label: str

    # mypy doesn't detect enum items, if generated dynamically.
    # Define __getattribute__ to mask that.
    def __getattribute__(self, name: str) -> 'LabeledEnum':
        return super().__getattribute__(name)

    # Call is not visible to mypy :-/
    def __call__(self, *args, **kwargs) -> 'LabeledEnum':
        return super().__call__(*args, **kwargs)  # type: ignore[misc]


Animal = LabeledEnum('Animal', _ANIMALS)  # type: ignore[arg-type]

but mypy still complain about the valid-type when Animal is used in function argument/return value.

Your Environment

  • Mypy version used:0.812
  • Mypy command-line flags: --show-error-codes (doesn't affect the error)
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: 3.9.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions