Source code for brainsets.core
_functions = [
"string_int_enum_serialize_fn",
"datetime_serialize_fn",
]
_classes = [
"NestedEnumType",
"StringIntEnum",
"Dictable",
]
_constants = [
"serialize_fn_map",
]
__all__ = _functions + _classes + _constants
from enum import Enum
import datetime
[docs]
class NestedEnumType(type(Enum)):
def __new__(cls, clsname, bases, clsdict, parent=None):
new_cls = super().__new__(cls, clsname, bases, clsdict)
new_cls._parent = parent
if parent is not None:
parent._parent_cls = new_cls
for name, member in new_cls.__members__.items():
parent.__setattr__(name, member)
return new_cls
def __contains__(cls, member):
return (isinstance(member, cls) and (member._name_ in cls._member_map_)) or (
member._parent is not None and member._parent in cls
)
[docs]
class StringIntEnum(Enum, metaclass=NestedEnumType):
r"""Base class for string-integer enums.
This class extends Python's built-in Enum class to provide:
- String representation via __str__
- Integer representation via __int__
- Case-insensitive string parsing via from_string()
- Maximum value lookup via max_value()
.. code-block:: python
>>> class Color(StringIntEnum):
... RED = 1
... BLUE = 2
>>> str(Color.RED)
'RED'
>>> int(Color.RED)
1
>>> Color.from_string("red")
<Color.RED: 1>
>>> Color.max_value()
2
"""
def __str__(self):
if self._parent is not None:
return f"{str(self._parent)}.{self.name}"
else:
return self.name
def __int__(self):
return self.value
[docs]
@classmethod
def from_string(cls, string: str) -> "StringIntEnum":
r"""Convert a string to an enum member. This method is case insensitive and
will replace spaces with underscores.
Args:
string: The string to convert to an enum member.
Examples:
>>> from brainsets.taxonomy import Sex
>>> Sex.from_string("Male")
<Sex.MALE: 1>
>>> Sex.from_string("M")
<Sex.MALE: 1>
"""
nested_string = string.split(".", maxsplit=1)
if len(nested_string) > 1:
parent = cls.from_string(nested_string[0])
return parent._parent_cls.from_string(nested_string[1])
else:
# normalize string by replacing spaces with underscores and converting
# to upper case
normalized_string = string.strip().upper().replace(" ", "_")
# create a mapping of enum names to enum members
mapping = {name.upper(): member for name, member in cls.__members__.items()}
# try to match the string to an enum name
if normalized_string in mapping:
return mapping[normalized_string]
# if there is no match raise an error
raise ValueError(
f"{normalized_string} does not exist in {cls.__name__}, "
"consider adding it to the enum."
)
[docs]
@classmethod
def max_value(cls):
r"""Return the maximum value in the enum class."""
return max(cls.__members__.values(), key=lambda x: x.value).value
[docs]
class Dictable:
r"""A dataclass that can be converted to a dict."""
[docs]
def to_dict(self):
r"""Convert the dataclass instance to a dictionary.
Returns:
dict: A dictionary containing all fields of the dataclass as key-value pairs.
.. code-block:: python
>>> from dataclasses import dataclass
>>> @dataclass
... class Person(Dictable):
... name: str
... age: int
>>> p = Person("Alice", 30)
>>> p.to_dict()
{'name': 'Alice', 'age': 30}
"""
from dataclasses import asdict
return {k: v for k, v in asdict(self).items()} # type: ignore
[docs]
def string_int_enum_serialize_fn(obj, serialize_fn_map=None):
r"""Convert a StringIntEnum object to a string."""
return str(obj)
[docs]
def datetime_serialize_fn(obj, serialize_fn_map=None):
r"""Convert a datetime object to a string."""
return str(obj)
serialize_fn_map = {
StringIntEnum: string_int_enum_serialize_fn,
datetime.datetime: datetime_serialize_fn,
}
r"""A dict that maps classes to their serialization functions"""