mirror of
https://github.com/EyeTrackVR/EyeTrackVR.git
synced 2025-09-26 23:09:28 +08:00
202 lines
7.1 KiB
Python
202 lines
7.1 KiB
Python
"""
|
|
------------------------------------------------------------------------------------------------------
|
|
|
|
,@@@@@@
|
|
@@@@@@@@@@@ @@@
|
|
@@@@@@@@@@@@ @@@@@@@@@@@
|
|
@@@@@@@@@@@@@ @@@@@@@@@@@@@@
|
|
@@@@@@@/ ,@@@@@@@@@@@@@
|
|
/@@@@@@@@@@@@@@@ @@@@@@@@
|
|
@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@
|
|
@@@@@@@@ @@@@@
|
|
,@@@ @@@@&
|
|
@@@@@@. @@@@
|
|
@@@ @@@@@@@@@/ @@@@@
|
|
,@@@. @@@@@@((@ @@@@(
|
|
//@@@ ,, @@@@ @@@@@
|
|
@@@( @@@@@@@
|
|
@@@ @ @@@@@@@@#
|
|
@@@@@@@@@@@@@@@@@
|
|
@@@@@@@@@@@@@(
|
|
|
|
Copyright (c) 2023 EyeTrackVR <3
|
|
LICENSE: GNU GPLv3
|
|
------------------------------------------------------------------------------------------------------
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import types
|
|
from collections import namedtuple
|
|
from typing import Any, ClassVar, Dict, List, Optional, TYPE_CHECKING, Tuple, Type, TypeVar, Iterator, Mapping
|
|
|
|
__all__ = (
|
|
'Enum',
|
|
# 'EyeId',
|
|
'EyeLR',
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from typing_extensions import Self
|
|
|
|
|
|
def _create_value_cls(name: str, comparable: bool):
|
|
# All the type ignores here are due to the type checker being unable to recognise
|
|
# Runtime type creation without exploding.
|
|
cls = namedtuple('_EnumValue_' + name, 'name value')
|
|
cls.__repr__ = lambda self: f'<{name}.{self.name}: {self.value!r}>' # type: ignore
|
|
cls.__str__ = lambda self: f'{name}.{self.name}' # type: ignore
|
|
if comparable:
|
|
cls.__le__ = lambda self, other: isinstance(other, self.__class__) and self.value <= other.value # type: ignore
|
|
cls.__ge__ = lambda self, other: isinstance(other, self.__class__) and self.value >= other.value # type: ignore
|
|
cls.__lt__ = lambda self, other: isinstance(other, self.__class__) and self.value < other.value # type: ignore
|
|
cls.__gt__ = lambda self, other: isinstance(other, self.__class__) and self.value > other.value # type: ignore
|
|
return cls
|
|
|
|
|
|
def _is_descriptor(obj):
|
|
return hasattr(obj, '__get__') or hasattr(obj, '__set__') or hasattr(obj, '__delete__')
|
|
|
|
|
|
class EnumMeta(type):
|
|
if TYPE_CHECKING:
|
|
__name__: ClassVar[str]
|
|
_enum_member_names_: ClassVar[List[str]]
|
|
_enum_member_map_: ClassVar[Dict[str, Any]]
|
|
_enum_value_map_: ClassVar[Dict[Any, Any]]
|
|
|
|
def __new__(cls, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any], *, comparable: bool = False) -> Self:
|
|
value_mapping = {}
|
|
member_mapping = {}
|
|
member_names = []
|
|
|
|
value_cls = _create_value_cls(name, comparable)
|
|
for key, value in list(attrs.items()):
|
|
is_descriptor = _is_descriptor(value)
|
|
if key[0] == '_' and not is_descriptor:
|
|
continue
|
|
|
|
# Special case classmethod to just pass through
|
|
if isinstance(value, classmethod):
|
|
continue
|
|
|
|
if is_descriptor:
|
|
setattr(value_cls, key, value)
|
|
del attrs[key]
|
|
continue
|
|
|
|
try:
|
|
new_value = value_mapping[value]
|
|
except KeyError:
|
|
new_value = value_cls(name=key, value=value)
|
|
value_mapping[value] = new_value
|
|
member_names.append(key)
|
|
|
|
member_mapping[key] = new_value
|
|
attrs[key] = new_value
|
|
|
|
attrs['_enum_value_map_'] = value_mapping
|
|
attrs['_enum_member_map_'] = member_mapping
|
|
attrs['_enum_member_names_'] = member_names
|
|
attrs['_enum_value_cls_'] = value_cls
|
|
actual_cls = super().__new__(cls, name, bases, attrs)
|
|
value_cls._actual_enum_cls_ = actual_cls # type: ignore # Runtime attribute isn't understood
|
|
return actual_cls
|
|
|
|
def __iter__(cls) -> Iterator[Any]:
|
|
return (cls._enum_member_map_[name] for name in cls._enum_member_names_)
|
|
|
|
def __reversed__(cls) -> Iterator[Any]:
|
|
return (cls._enum_member_map_[name] for name in reversed(cls._enum_member_names_))
|
|
|
|
def __len__(cls) -> int:
|
|
return len(cls._enum_member_names_)
|
|
|
|
def __repr__(cls) -> str:
|
|
return f'<enum {cls.__name__}>'
|
|
|
|
@property
|
|
def __members__(cls) -> Mapping[str, Any]:
|
|
return types.MappingProxyType(cls._enum_member_map_)
|
|
|
|
def __call__(cls, value: str) -> Any:
|
|
try:
|
|
return cls._enum_value_map_[value]
|
|
except (KeyError, TypeError):
|
|
raise ValueError(f"{value!r} is not a valid {cls.__name__}")
|
|
|
|
def __getitem__(cls, key: str) -> Any:
|
|
return cls._enum_member_map_[key]
|
|
|
|
def __setattr__(cls, name: str, value: Any) -> None:
|
|
raise TypeError('Enums are immutable.')
|
|
|
|
def __delattr__(cls, attr: str) -> None:
|
|
raise TypeError('Enums are immutable')
|
|
|
|
def __instancecheck__(self, instance: Any) -> bool:
|
|
# isinstance(x, Y)
|
|
# -> __instancecheck__(Y, x)
|
|
try:
|
|
return instance._actual_enum_cls_ is self
|
|
except AttributeError:
|
|
return False
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
from enum import Enum
|
|
else:
|
|
class Enum(metaclass=EnumMeta):
|
|
@classmethod
|
|
def try_value(cls, value):
|
|
try:
|
|
return cls._enum_value_map_[value]
|
|
except (KeyError, TypeError):
|
|
return value
|
|
|
|
E = TypeVar('E', bound='Enum')
|
|
|
|
def create_unknown_value(cls: Type[E], val: Any) -> E:
|
|
value_cls = cls._enum_value_cls_ # type: ignore # This is narrowed below
|
|
name = f'unknown_{val}'
|
|
return value_cls(name=name, value=val)
|
|
|
|
|
|
def try_enum(cls: Type[E], val: Any) -> E:
|
|
"""A function that tries to turn the value into enum ``cls``.
|
|
If it fails it returns a proxy invalid value instead.
|
|
"""
|
|
|
|
try:
|
|
return cls._enum_value_map_[val] # type: ignore # All errors are caught below
|
|
except (KeyError, TypeError, AttributeError):
|
|
return create_unknown_value(cls, val)
|
|
|
|
|
|
# The line above is based on the code in the following url
|
|
# https://github.com/Rapptz/discord.py/blob/f7e97954950ffb0e34238d70813454caa6f1a3ae/discord/enums.py
|
|
|
|
|
|
# class EyeId(Enum):
|
|
# # https://docs.python.org/3.9/library/enum.html#functional-api
|
|
# # > The reason for defaulting to 1 as the starting number and not 0 is that 0 is False in a boolean sense, but enum members all evaluate to True.
|
|
# RIGHT = 1
|
|
# LEFT = 2
|
|
# BOTH = 3
|
|
# SETTINGS = 4
|
|
#
|
|
# def __str__(self) -> str:
|
|
# return self.name
|
|
#
|
|
# def __int__(self) -> int:
|
|
# return self.value
|
|
|
|
class EyeLR(Enum):
|
|
LEFT = 1
|
|
RIGHT = 2
|
|
|
|
def __str__(self) -> str:
|
|
return self.name
|
|
|
|
def __int__(self) -> int:
|
|
return self.value |