Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type hint enum @classmethod that returns a member [duplicate]

I've written an enum.Enum type with a couple of members. Now I want to write a "selector" @classmethod that will return one of the members of the Enum based on arguments, and I don't know how to type hint it properly.

Here's what I have to far:

from enum import Enum, auto
from typing import TypeVar, Type

_StringSizeT = TypeVar("_StringSizeT", bound="StringSize")
class StringSize(Enum):

    SMALL = auto()
    BIG = auto()

    @classmethod
    def from_string(cls: Type[_StringSizeT], source_string: str) -> _StringSizeT:
        n_chars = len(source_string)
        if n_chars <= 10:
            return cls.SMALL
        return cls.BIG

The above use of TypeVar(), etc is me trying to follow approach of Martijn Pieters' post in "Can you annotate return type when value is instance of cls?" for @classmethod factories, but applying them to Enum @classmethods that return one of the enum members.

Unfortunately, mypy does not like the types of my return statements:

error: Incompatible return value type (got "StringSize", expected "_StringSizeT")

This is because the properties of this @classmethod are different than in that post: I'm not returning an instance of the class, but a class attribute, or in enum-speak, a member.

How can I fix my method annotation here?

UPDATE: Ok, while writing this, I riffed on the advice of mypy and found that

-> "StringSize"

is acceptable. This is a little confusing because, again, I'm not returning an instance, but I'll chalk that up to the metaprogramming machinery underneath Enum. Followup: Is there a non-string-literal type annotation I could use here instead? Through my experience, I've learned (maybe incorrectly) to dislike the string literal annotations. (Am I sacrificing anything with them?)

like image 730
t-mart Avatar asked May 02 '26 20:05

t-mart


1 Answers

As discussed in the comment section, the correct annotation is the enum class itself:

from __future__ import annotations

class StringSize(Enum):
    # ...

    @classmethod
    def from_string(cls, source_string: str) -> StringSize:
        ...
like image 88
InSync Avatar answered May 05 '26 09:05

InSync