I am using Python 3.11 and I would need to detect if an optional class attribute is type of Enum (i.e. type of a subclass of Enum).
With typing.get_type_hints()
I can get the type hints as a dict, but how to check if a field's type is optional Enum (subclass)? Even better if I could get the type of any optional field regardless is it Optional[str]
, Optional[int]
, Optional[Class_X]
, etc.
Example code
from typing import Optional, get_type_hints
from enum import IntEnum, Enum
class TestEnum(IntEnum):
foo = 1
bar = 2
class Foo():
opt_enum : TestEnum | None = None
types = get_type_hints(Foo)['opt_enum']
This works
(ipython)
In [4]: Optional[TestEnum] == types
Out[4]: True
These ones fail
(yes, these are desperate attempts)
In [6]: Optional[IntEnum] == types
Out[6]: False
and
In [11]: issubclass(Enum, types)
Out[11]: False
and
In [12]: issubclass(types, Enum)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In [12], line 1
----> 1 issubclass(types, Enum)
TypeError: issubclass() arg 1 must be a class
and
In [13]: issubclass(types, Optional[Enum])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In [13], line 1
----> 1 issubclass(types, Optional[Enum])
File /usr/lib/python3.10/typing.py:1264, in _UnionGenericAlias.__subclasscheck__(self, cls)
1262 def __subclasscheck__(self, cls):
1263 for arg in self.__args__:
-> 1264 if issubclass(cls, arg):
1265 return True
TypeError: issubclass() arg 1 must be a class
and
In [7]: IntEnum in types
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In [7], line 1
----> 1 IntEnum in types
TypeError: argument of type 'types.UnionType' is not iterable
Why I needed this
I have several cases where I am importing data from csv files and creating objects of a class from each row. csv.DictReader()
returns a dict[str, str]
and I need to fix the types for the fields before attempting to create the object. However, some of the object fields are Optional[int]
, Optional[bool]
, Optional[EnumX]
or Optional[ClassX]
. I have several of those classes multi-inheriting my CSVImportable()
class/interface. I want to implement the logic once into CSVImportable()
class instead of writing roughly same code in field-aware way in every subclass. This CSVImportable._field_type_updater()
should:
- correctly change the types at least for basic types and enums
- Gracefully skip
Optional[ClassX]
fields
Naturally I am thankful for better designs too :-)