2023-03-12 18:04:28 +01:00
|
|
|
from typing import (
|
|
|
|
Any,
|
|
|
|
Generic,
|
|
|
|
Literal,
|
|
|
|
Optional,
|
|
|
|
TypeVar,
|
|
|
|
Union,
|
|
|
|
get_args,
|
|
|
|
get_origin,
|
|
|
|
overload,
|
|
|
|
)
|
2023-03-09 19:50:11 +01:00
|
|
|
|
2023-03-12 18:04:28 +01:00
|
|
|
_T0 = TypeVar("_T0")
|
|
|
|
_T1 = TypeVar("_T1")
|
|
|
|
_T2 = TypeVar("_T2")
|
|
|
|
_T3 = TypeVar("_T3")
|
|
|
|
_T4 = TypeVar("_T4")
|
2023-03-09 19:50:11 +01:00
|
|
|
|
|
|
|
|
2023-03-12 18:04:28 +01:00
|
|
|
class GenericInsightMixin(Generic[_T0, _T1, _T2, _T3, _T4]):
|
|
|
|
_type_arg_0: Optional[type[_T0]] = None
|
|
|
|
_type_arg_1: Optional[type[_T1]] = None
|
|
|
|
_type_arg_2: Optional[type[_T2]] = None
|
|
|
|
_type_arg_3: Optional[type[_T3]] = None
|
|
|
|
_type_arg_4: Optional[type[_T4]] = None
|
2023-03-09 19:50:11 +01:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def __init_subclass__(cls, **kwargs: Any) -> None:
|
|
|
|
"""Saves the type argument in the `_type_arg` class attribute."""
|
|
|
|
super().__init_subclass__(**kwargs)
|
|
|
|
for base in cls.__orig_bases__: # type: ignore[attr-defined]
|
|
|
|
origin = get_origin(base)
|
|
|
|
if origin is None or not issubclass(origin, GenericInsightMixin):
|
|
|
|
continue
|
2023-03-12 18:04:28 +01:00
|
|
|
type_args = get_args(base)
|
|
|
|
for idx, arg in enumerate(type_args):
|
|
|
|
# Do not set the attribute for generics:
|
|
|
|
if isinstance(arg, TypeVar):
|
|
|
|
continue
|
|
|
|
# Do not set `NoneType`:
|
|
|
|
if isinstance(arg, type) and isinstance(None, arg):
|
|
|
|
continue
|
|
|
|
setattr(cls, f"_type_arg_{idx}", arg)
|
|
|
|
return
|
2023-03-09 19:50:11 +01:00
|
|
|
|
|
|
|
@classmethod
|
2023-03-12 18:04:28 +01:00
|
|
|
@overload
|
|
|
|
def _get_type_arg(cls, idx: Literal[0]) -> type[_T0]:
|
|
|
|
...
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@overload
|
|
|
|
def _get_type_arg(cls, idx: Literal[1]) -> type[_T1]:
|
|
|
|
...
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@overload
|
|
|
|
def _get_type_arg(cls, idx: Literal[2]) -> type[_T2]:
|
|
|
|
...
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@overload
|
|
|
|
def _get_type_arg(cls, idx: Literal[3]) -> type[_T3]:
|
|
|
|
...
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@overload
|
|
|
|
def _get_type_arg(cls, idx: Literal[4]) -> type[_T4]:
|
|
|
|
...
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def _get_type_arg(
|
|
|
|
cls,
|
|
|
|
idx: Literal[0, 1, 2, 3, 4],
|
|
|
|
) -> Union[type[_T0], type[_T1], type[_T2], type[_T3], type[_T4]]:
|
2023-03-09 19:50:11 +01:00
|
|
|
"""Returns the type argument of the class (if specified)."""
|
2023-03-12 18:04:28 +01:00
|
|
|
if idx == 0:
|
|
|
|
type_ = cls._type_arg_0
|
|
|
|
elif idx == 1:
|
|
|
|
type_ = cls._type_arg_1
|
|
|
|
elif idx == 2: # noqa: PLR2004
|
|
|
|
type_ = cls._type_arg_2
|
|
|
|
elif idx == 3: # noqa: PLR2004
|
|
|
|
type_ = cls._type_arg_3
|
|
|
|
elif idx == 4: # noqa: PLR2004
|
|
|
|
type_ = cls._type_arg_4
|
|
|
|
else:
|
|
|
|
raise ValueError("Only 5 type parameters available")
|
|
|
|
if type_ is None:
|
2023-03-09 19:50:11 +01:00
|
|
|
raise AttributeError(
|
2023-03-12 18:04:28 +01:00
|
|
|
f"{cls.__name__} is generic; type argument {idx} unspecified"
|
2023-03-09 19:50:11 +01:00
|
|
|
)
|
2023-03-12 18:04:28 +01:00
|
|
|
return type_
|
|
|
|
|
|
|
|
|
|
|
|
class GenericInsightMixin1(GenericInsightMixin[_T0, None, None, None, None]):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class GenericInsightMixin2(GenericInsightMixin[_T0, _T1, None, None, None]):
|
|
|
|
pass
|