hikari.internal.enums
Implementation of parts of Python's enum
protocol to be more performant.
View Source
# -*- coding: utf-8 -*- # cython: language_level=3 # Copyright (c) 2020 Nekokatt # Copyright (c) 2021-present davfsa # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. """Implementation of parts of Python's `enum` protocol to be more performant.""" from __future__ import annotations __all__: typing.Sequence[str] = ("Enum", "Flag") import functools import operator import sys import types import typing _T = typing.TypeVar("_T") _MAX_CACHED_MEMBERS: typing.Final[int] = 1 << 12 class _EnumNamespace(typing.Dict[str, typing.Any]): __slots__: typing.Sequence[str] = ("base", "names_to_values", "values_to_names") def __init__(self, base: typing.Type[typing.Any]) -> None: super().__init__() self.base = base self.names_to_values: typing.Dict[str, typing.Any] = {} self.values_to_names: typing.Dict[str, typing.Any] = {} self["__doc__"] = "An enumeration." def __getitem__(self, name: str) -> typing.Any: try: return super().__getitem__(name) except KeyError: try: return self.names_to_values[name] except KeyError: raise KeyError(name) from None def __setitem__(self, name: str, value: typing.Any) -> None: if name == "" or name == "mro": raise TypeError(f"Invalid enum member name: {name!r}") if name.startswith("_"): # Dunder/sunder, so skip. super().__setitem__(name, value) return if hasattr(value, "__get__") or hasattr(value, "__set__") or hasattr(value, "__del__"): super().__setitem__(name, value) return if not isinstance(value, self.base): raise TypeError(f"Expected member {name} to be of type {self.base.__name__} but was {type(value).__name__}") name = sys.intern(name) if issubclass(self.base, str): value = sys.intern(value) else: try: # This will fail if unhashable. hash(value) except TypeError: raise TypeError(f"Cannot have unhashable values in this enum type ({name}: {value!r})") from None if name in self.names_to_values: raise TypeError(f"Cannot define {name!r} name multiple times") if value in self.values_to_names: # We must have defined some alias, so just register the name self.names_to_values[name] = value return self.names_to_values[name] = value self.values_to_names[value] = name # We refer to these from the metaclasses, but obviously this won't work # until these classes are created, and since they use the metaclasses as # a base metaclass, we have to give these values for _EnumMeta to not # flake out when initializing them. _Enum = NotImplemented class _EnumMeta(type): def __call__(cls, value: typing.Any) -> typing.Any: """Cast a value to the enum, returning the raw value that was passed if value not found.""" try: return cls._value_to_member_map_[value] except KeyError: # If we can't find the value, just return what got casted in return value def __getitem__(cls, name: str) -> typing.Any: return cls._name_to_member_map_[name] def __contains__(cls, item: typing.Any) -> bool: return item in cls._value_to_member_map_ def __iter__(cls) -> typing.Iterator[typing.Any]: yield from cls._name_to_member_map_.values() def __new__( mcs: typing.Type[_T], name: str, bases: typing.Tuple[typing.Type[typing.Any], ...], namespace: typing.Union[typing.Dict[str, typing.Any], _EnumNamespace], ) -> _T: global _Enum if _Enum is NotImplemented: # noinspection PyRedundantParentheses return (_Enum := super().__new__(mcs, name, bases, namespace)) assert isinstance(namespace, _EnumNamespace) base, enum_type = bases new_namespace = { "__objtype__": base, "__enumtype__": enum_type, "_name_to_member_map_": (name_to_member := {}), "_value_to_member_map_": (value_to_member := {}), "_member_names_": (member_names := []), # Required to be immutable by enum API itself. "__members__": types.MappingProxyType(namespace.names_to_values), **{ name: value for name, value in Enum.__dict__.items() if name not in ("__class__", "__module__", "__doc__") }, } # We don't want to override the __str__ behaviour inherited from str for string based enums. if issubclass(base, str): new_namespace.pop("__str__", None) # We update the name space to ensure new fields override inherited attributes and methods. new_namespace.update(namespace) cls = super().__new__(mcs, name, bases, new_namespace) for name, value in namespace.names_to_values.items(): # Patching the member init call is around 100ns faster per call than # using the default type.__call__ which would make us do the lookup # in cls.__new__. Reason for this is that python will also always # invoke cls.__init__ if we do this, so we end up with two function # calls. member = cls.__new__(cls, value) member._name_ = name member._value_ = value name_to_member[name] = member value_to_member[value] = member member_names.append(name) setattr(cls, name, member) return cls @classmethod def __prepare__( mcs, name: str, bases: typing.Tuple[typing.Type[typing.Any], ...] = () ) -> typing.Union[typing.Dict[str, typing.Any], _EnumNamespace]: if _Enum is NotImplemented: if name != "Enum": raise TypeError("First instance of _EnumMeta must be Enum") return _EnumNamespace(object) try: # Fails if Enum is not defined. We check this in `__new__` properly. base, enum_type = bases if isinstance(base, _EnumMeta): raise TypeError("First base to an enum must be the type to combine with, not _EnumMeta") return _EnumNamespace(base) except ValueError: raise TypeError("Expected exactly two base classes for an enum") from None def __repr__(cls) -> str: return f"<enum {cls.__name__}>" __str__ = __repr__ class Enum(metaclass=_EnumMeta): """Clone of Python's `enum.Enum` implementation. This is designed to be faster and more efficient than Python's implementation, while retaining the majority of the external interface that Python's `enum.Enum` provides. An `Enum` is a simple class containing a discrete set of constant values that can be used in place of this type. This acts as a type-safe way of representing a set number of "things". .. warning:: Some semantics such as subtype checking and instance checking may differ. It is recommended to compare these values using the `==` operator rather than the `is` operator for safety reasons. Special Members on the class ---------------------------- * `__enumtype__` : Always `Enum`. * `__members__` : An immutable `typing.Mapping` that maps each member name to the member value. * ` __objtype__` : Always the first type that the enum is derived from. For example: ```py >>> class UserType(str, Enum): ... USER = "user" ... PARTIAL = "partial" ... MEMBER = "member" >>> print(UserType.__objtype__) <class 'str'> ``` Operators on the class ---------------------- * `EnumType["FOO"]` : Return the member that has the name `FOO`, raising a `KeyError` if it is not present. * `EnumType.FOO` : Return the member that has the name `FOO`, raising a `AttributeError` if it is not present. * `EnumType(x)` : Attempt to cast `x` to the enum type by finding an existing member that has the same __value__. If this fails, you should expect a `ValueError` to be raised. Operators on each enum member ----------------------------- * `e1 == e2` : `bool` Compare equality. * `e1 != e2` : `bool` Compare inequality. * `repr(e)` : `str` Get the machine readable representation of the enum member `e`. * `str(e)` : `str` Get the `str` name of the enum member `e`. Special properties on each enum member -------------------------------------- * `name` : `str` The name of the member. * `value` : The value of the member. The type depends on the implementation type of the enum you are using. All other methods and operators on enum members are inherited from the member's __value__. For example, an enum extending `int` would be able to be used as an `int` type outside these overridden definitions. """ _name_to_member_map_: typing.ClassVar[typing.Mapping[str, Enum]] _value_to_member_map_: typing.ClassVar[typing.Mapping[int, Enum]] _member_names_: typing.ClassVar[typing.Sequence[str]] __members__: typing.ClassVar[typing.Mapping[str, Enum]] __objtype__: typing.ClassVar[typing.Type[typing.Any]] __enumtype__: typing.ClassVar[typing.Type[Enum]] _name_: str _value_: typing.Any @property def name(self) -> str: """Return the name of the enum member as a `str`.""" return self._name_ @property @typing.no_type_check def value(self): """Return the value of the enum member.""" return self._value_ def __repr__(self) -> str: return f"<{type(self).__name__}.{self._name_}: {self._value_!r}>" def __str__(self) -> str: return self._name_ _Flag = NotImplemented def _name_resolver(members: typing.Dict[int, _Flag], value: int) -> typing.Generator[str, typing.Any, None]: bit = 1 has_yielded = False remaining = value while bit <= value: if member := members.get(bit): # Use ._value_ to prevent overhead of making new members each time. # Also lets my testing logic for the cache size be more accurate. if member._value_ & remaining == member._value_: remaining ^= member._value_ yield member.name has_yielded = True bit <<= 1 if not has_yielded: yield f"UNKNOWN 0x{value:x}" elif remaining: yield hex(remaining) class _FlagMeta(type): def __call__(cls, value: int = 0) -> typing.Any: """Cast a value to the flag enum, returning the raw value that was passed if values not found.""" # We want to handle value invariantly to avoid issues brought in by different behaviours from sub-classed ints # and floats. This also ensures that .__int__ only returns an invariant int. value = int(value) try: return cls._value_to_member_map_[value] except KeyError: # We only need this ability here usually, so overloading operators # is an overkill and would add more overhead. if value < 0: # Convert to a positive value instead. return cls.__everything__ - ~value temp_members = cls._temp_members_ # For huge enums, don't ever cache anything. We could consume masses of memory otherwise # (e.g. Permissions) try: # Try to get a cached value. return temp_members[value] except KeyError: # If we can't find the value, just return what got casted in by generating a pseudomember # and caching it. We can't use weakref because int is not weak referenceable, annoyingly. pseudomember = cls.__new__(cls, value) pseudomember._name_ = None pseudomember._value_ = value temp_members[value] = pseudomember if len(temp_members) > _MAX_CACHED_MEMBERS: temp_members.popitem() return pseudomember def __getitem__(cls, name: str) -> typing.Any: return cls._name_to_member_map_[name] def __iter__(cls) -> typing.Iterator[typing.Any]: yield from cls._name_to_member_map_.values() @classmethod def __prepare__( mcs, name: str, bases: typing.Tuple[typing.Type[typing.Any], ...] = () ) -> typing.Union[typing.Dict[str, typing.Any], _EnumNamespace]: if _Flag is NotImplemented: if name != "Flag": raise TypeError("First instance of _FlagMeta must be Flag") return _EnumNamespace(object) # Fails if Flag is not defined. if len(bases) == 1 and bases[0] == Flag: return _EnumNamespace(int) raise TypeError("Cannot define another Flag base type") @staticmethod def __new__( mcs: typing.Type[_T], name: str, bases: typing.Tuple[typing.Type[typing.Any], ...], namespace: typing.Union[typing.Dict[str, typing.Any], _EnumNamespace], ) -> _T: global _Flag if _Flag is NotImplemented: # noinspection PyRedundantParentheses return (_Flag := super().__new__(mcs, name, bases, namespace)) assert isinstance(namespace, _EnumNamespace) new_namespace = { "__objtype__": int, "__enumtype__": _Flag, "_name_to_member_map_": (name_to_member := {}), "_value_to_member_map_": (value_to_member := {}), "_powers_of_2_to_member_map_": (powers_of_2_map := {}), # We can't weakref, as we inherit from int. Turns out that is significantly # slower anyway, so it isn't important for now. We just manually limit # the cache size. # This also randomly ends up with a 0 value in it at the start # during the next for loop. I cannot work out for the life of me # why this happens. "_temp_members_": {}, "_member_names_": (member_names := []), # Required to be immutable by enum API itself. "__members__": types.MappingProxyType(namespace.names_to_values), # This copies over all methods, including operator overloads. This # has the effect of making pdoc aware of any methods or properties # we defined on Flag. **{ name: value for name, value in Flag.__dict__.items() if name not in ("__class__", "__module__", "__doc__") }, } # We update the namespace to ensure new fields override inherited attributes and methods. new_namespace.update(namespace) cls = super().__new__(mcs, name, (int, *bases), new_namespace) for name, value in namespace.names_to_values.items(): # Patching the member init call is around 100ns faster per call than # using the default type.__call__ which would make us do the lookup # in cls.__new__. Reason for this is that python will also always # invoke cls.__init__ if we do this, so we end up with two function # calls. member = cls.__new__(cls, value) member._name_ = name member._value_ = value name_to_member[name] = member value_to_member[value] = member member_names.append(name) setattr(cls, name, member) if not (value & value - 1): powers_of_2_map[value] = member all_bits = functools.reduce(operator.or_, value_to_member.keys()) all_bits_member = cls.__new__(cls, all_bits) all_bits_member._name_ = None all_bits_member._value_ = all_bits setattr(cls, "__everything__", all_bits_member) return cls def __repr__(cls) -> str: return f"<enum {cls.__name__}>" __str__ = __repr__ class Flag(metaclass=_FlagMeta): """Clone of Python's `enum.Flag` implementation. This is designed to be faster and more efficient than Python's implementation, while retaining the majority of the external interface that Python's `enum.Flag` provides. In simple terms, an `Flag` is a set of wrapped constant `int` values that can be combined in any combination to make a special value. This is a more efficient way of combining things like permissions together into a single integral value, and works by setting individual `1`s and `0`s on the binary representation of the integer. This implementation has extra features, in that it will actively behave like a `set` as well. .. warning:: It is important to keep in mind that some semantics such as subtype checking and instance checking may differ. It is recommended to compare these values using the `==` operator rather than the `is` operator for safety reasons. Especially where pseudo-members created from combinations are cached, results of using of `is` may not be deterministic. This is a side effect of some internal performance improvements. Failing to observe this __will__ result in unexpected behaviour occurring in your application! Also important to note is that despite wrapping `int` values, conceptually this does not behave as if it were a subclass of `int`. Special Members on the class ---------------------------- * `__enumtype__` : Always `Flag`. * `__everything__` : A special member with all documented bits set. * `__members__` : An immutable `typing.Mapping` that maps each member name to the member value. * ` __objtype__` : Always `int`. Operators on the class ---------------------- * `FlagType["FOO"]` : Return the member that has the name `FOO`, raising a `KeyError` if it is not present. * `FlagType.FOO` : Return the member that has the name `FOO`, raising a `AttributeError` if it is not present. * `FlagType(x)` : Attempt to cast `x` to the enum type by finding an existing member that has the same __value__. If this fails, then a special __composite__ instance of the type is made. The name of this type is a combination of all members that combine to make the bitwise value. Operators on each flag member ----------------------------- * `e1 & e2` : Bitwise `AND` operation. Will return a member that contains all flags that are common between both oprands on the values. This also works with one of the oprands being an `int`eger. You may instead use the `intersection` method. * `e1 | e2` : Bitwise `OR` operation. Will return a member that contains all flags that appear on at least one of the oprands. This also works with one of the oprands being an `int`eger. You may instead use the `union` method. * `e1 ^ e2` : Bitwise `XOR` operation. Will return a member that contains all flags that only appear on at least one and at most one of the oprands. This also works with one of the oprands being an `int`eger. You may instead use the `symmetric_difference` method. * `~e` : Return the inverse of this value. This is equivalent to disabling all flags that are set on this value and enabling all flags that are not set on this value. Note that this will behave slightly differently to inverting a pure int value. You may instead use the `invert` method. * `e1 - e2` : Bitwise set difference operation. Returns all flags set on `e1` that are not set on `e2` as well. You may instead use the `difference` method. * `bool(e)` : `bool` Return `True` if `e` has a non-zero value, otherwise `False`. * `E.A in e`: `bool` `True` if `E.A` is in `e`. This is functionally equivalent to `E.A & e == E.A`. * `iter(e)` : Explode the value into a iterator of each __documented__ flag that can be combined to make up the value `e`. Returns an iterator across all well-defined flags that make up this value. This will only include the flags explicitly defined on this `Flag` type and that are individual powers of two (this means if converted to twos-compliment binary, exactly one bit must be a `1`). In simple terms, this means that you should not expect combination flags to be returned. * `e1 == e2` : `bool` Compare equality. * `e1 != e2` : `bool` Compare inequality. * `e1 < e2` : `bool` Compare by ordering. * `int(e)` : `int` Get the integer value of this flag * `repr(e)` : `str` Get the machine readable representation of the flag member `e`. * `str(e)` : `str` Get the `str` name of the flag member `e`. Special properties on each flag member -------------------------------------- * `e.name` : `str` The name of the member. For composite members, this will be generated. * `e.value` : `int` The value of the member. Special members on each flag member ----------------------------------- * `e.all(E.A, E.B, E.C, ...)` : `bool` Returns `True` if __all__ of `E.A`, `E.B`, `E.C`, et cetera make up the value of `e`. * `e.any(E.A, E.B, E.C, ...)` : `bool` Returns `True` if __any__ of `E.A`, `E.B`, `E.C`, et cetera make up the value of `e`. * `e.none(E.A, E.B, E.C, ...)` : `bool` Returns `True` if __none__ of `E.A`, `E.B`, `E.C`, et cetera make up the value of `e`. * `e.split()` : `typing.Sequence` Explode the value into a sequence of each __documented__ flag that can be combined to make up the value `e`. Returns a sorted sequence of each power-of-two flag that makes up the value `e`. This is equivalent to `list(iter(e))`. All other methods and operators on `Flag` members are inherited from the member's __value__. .. note:: Due to limitations around how this is re-implemented, this class is not considered a subclass of `Enum` at runtime, even if MyPy believes this is possible """ _name_to_member_map_: typing.ClassVar[typing.Mapping[str, Flag]] _value_to_member_map_: typing.ClassVar[typing.Mapping[int, Flag]] _powers_of_2_to_member_map_: typing.ClassVar[typing.Mapping[int, Flag]] _temp_members_: typing.ClassVar[typing.Mapping[int, Flag]] _member_names_: typing.ClassVar[typing.Sequence[str]] __members__: typing.ClassVar[typing.Mapping[str, Flag]] __objtype__: typing.ClassVar[typing.Type[int]] __enumtype__: typing.ClassVar[typing.Type[Flag]] _name_: typing.Optional[str] _value_: int @property def name(self) -> str: """Return the name of the flag combination as a `str`.""" if self._name_ is None: self._name_ = "|".join(_name_resolver(self._value_to_member_map_, self._value_)) return self._name_ @property def value(self) -> int: """Return the `int` value of the flag.""" return self._value_ def all(self: _T, *flags: _T) -> bool: """Check if all of the given flags are part of this value. Returns ------- bool `True` if any of the given flags are part of this value. Otherwise, return `False`. """ return all((flag & self) == flag for flag in flags) def any(self: _T, *flags: _T) -> bool: """Check if any of the given flags are part of this value. Returns ------- bool `True` if any of the given flags are part of this value. Otherwise, return `False`. """ return any((flag & self) == flag for flag in flags) def difference(self: _T, other: typing.Union[_T, int]) -> _T: """Perform a set difference with the other set. This will return all flags in this set that are not in the other value. Equivalent to using the subtraction `-` operator. """ return self.__class__(self & ~int(other)) def intersection(self: _T, other: typing.Union[_T, int]) -> _T: """Return a combination of flags that are set for both given values. Equivalent to using the "AND" `&` operator. """ return self.__class__(self._value_ & int(other)) def invert(self: _T) -> _T: """Return a set of all flags not in the current set.""" return self.__class__(self.__class__.__everything__._value_ & ~self._value_) def is_disjoint(self: _T, other: typing.Union[_T, int]) -> bool: """Return whether two sets have a intersection or not. If the two sets have an intersection, then this returns `False`. If no common flag values exist between them, then this returns `True`. """ return not (self & other) def is_subset(self: _T, other: typing.Union[_T, int]) -> bool: """Return whether another set contains this set or not. Equivalent to using the "in" operator. """ return (self & other) == other def is_superset(self: _T, other: typing.Union[_T, int]) -> bool: """Return whether this set contains another set or not.""" return (self & other) == self def none(self: _T, *flags: _T) -> bool: """Check if none of the given flags are part of this value. .. note:: This is essentially the opposite of `Flag.any`. Returns ------- bool `True` if none of the given flags are part of this value. Otherwise, return `False`. """ return not self.any(*flags) def split(self: _T) -> typing.Sequence[_T]: """Return a list of all defined atomic values for this flag. Any unrecognised bits will be omitted for brevity. The result will be a name-sorted `typing.Sequence` of each member """ return sorted( (member for member in self.__class__._powers_of_2_to_member_map_.values() if member.value & self), # Assumption: powers of 2 already have a cached value. key=lambda m: m._name_, ) def symmetric_difference(self: _T, other: typing.Union[_T, int]) -> _T: """Return a set with the symmetric differences of two flag sets. Equivalent to using the "XOR" `^` operator. For `a ^ b`, this can be considered the same as `(a - b) | (b - a)`. """ return self.__class__(self._value_ ^ int(other)) def union(self: _T, other: typing.Union[_T, int]) -> _T: """Return a combination of all flags in this set and the other set. Equivalent to using the "OR" `~` operator. """ return self.__class__(self._value_ | int(other)) isdisjoint = is_disjoint issubset = is_subset issuperset = is_superset # Exists since Python's `set` type is inconsistent with naming, so this # will prevent tripping people up unnecessarily because we do not # name inconsistently. # This one isn't in Python's set, but the inconsistency is triggering my OCD # so this is being defined anyway. symmetricdifference = symmetric_difference def __bool__(self) -> bool: return bool(self._value_) def __int__(self) -> int: return self._value_ def __iter__(self: _T) -> typing.Iterator[_T]: return iter(self.split()) def __len__(self) -> int: return len(self.split()) def __repr__(self) -> str: return f"<{self.__class__.__name__}.{self.name}: {self.value!r}>" def __rsub__(self: _T, other: typing.Union[int, _T]) -> _T: # This logic has to be reversed to be correct, since order matters for # a subtraction operator. This also ensures `int - _T -> _T` is a valid # case for us. return self.__class__(other) - self def __str__(self) -> str: return self.name __contains__ = is_subset __rand__ = __and__ = intersection __ror__ = __or__ = union __sub__ = difference __rxor__ = __xor__ = symmetric_difference __invert__ = invert
View Source
class Enum(metaclass=_EnumMeta): """Clone of Python's `enum.Enum` implementation. This is designed to be faster and more efficient than Python's implementation, while retaining the majority of the external interface that Python's `enum.Enum` provides. An `Enum` is a simple class containing a discrete set of constant values that can be used in place of this type. This acts as a type-safe way of representing a set number of "things". .. warning:: Some semantics such as subtype checking and instance checking may differ. It is recommended to compare these values using the `==` operator rather than the `is` operator for safety reasons. Special Members on the class ---------------------------- * `__enumtype__` : Always `Enum`. * `__members__` : An immutable `typing.Mapping` that maps each member name to the member value. * ` __objtype__` : Always the first type that the enum is derived from. For example: ```py >>> class UserType(str, Enum): ... USER = "user" ... PARTIAL = "partial" ... MEMBER = "member" >>> print(UserType.__objtype__) <class 'str'> ``` Operators on the class ---------------------- * `EnumType["FOO"]` : Return the member that has the name `FOO`, raising a `KeyError` if it is not present. * `EnumType.FOO` : Return the member that has the name `FOO`, raising a `AttributeError` if it is not present. * `EnumType(x)` : Attempt to cast `x` to the enum type by finding an existing member that has the same __value__. If this fails, you should expect a `ValueError` to be raised. Operators on each enum member ----------------------------- * `e1 == e2` : `bool` Compare equality. * `e1 != e2` : `bool` Compare inequality. * `repr(e)` : `str` Get the machine readable representation of the enum member `e`. * `str(e)` : `str` Get the `str` name of the enum member `e`. Special properties on each enum member -------------------------------------- * `name` : `str` The name of the member. * `value` : The value of the member. The type depends on the implementation type of the enum you are using. All other methods and operators on enum members are inherited from the member's __value__. For example, an enum extending `int` would be able to be used as an `int` type outside these overridden definitions. """ _name_to_member_map_: typing.ClassVar[typing.Mapping[str, Enum]] _value_to_member_map_: typing.ClassVar[typing.Mapping[int, Enum]] _member_names_: typing.ClassVar[typing.Sequence[str]] __members__: typing.ClassVar[typing.Mapping[str, Enum]] __objtype__: typing.ClassVar[typing.Type[typing.Any]] __enumtype__: typing.ClassVar[typing.Type[Enum]] _name_: str _value_: typing.Any @property def name(self) -> str: """Return the name of the enum member as a `str`.""" return self._name_ @property @typing.no_type_check def value(self): """Return the value of the enum member.""" return self._value_ def __repr__(self) -> str: return f"<{type(self).__name__}.{self._name_}: {self._value_!r}>" def __str__(self) -> str: return self._name_
Clone of Python's enum.Enum
implementation.
This is designed to be faster and more efficient than Python's implementation, while retaining the majority of the external interface that Python's enum.Enum
provides.
An Enum
is a simple class containing a discrete set of constant values that can be used in place of this type. This acts as a type-safe way of representing a set number of "things".
Warning: Some semantics such as subtype checking and instance checking may differ. It is recommended to compare these values using the ==
operator rather than the is
operator for safety reasons.
Special Members on the class
__enumtype__
: AlwaysEnum
.__members__
: An immutabletyping.Mapping
that maps each member name to the member value.__objtype__
: Always the first type that the enum is derived from. For example:
>>> class UserType(str, Enum):
... USER = "user"
... PARTIAL = "partial"
... MEMBER = "member"
>>> print(UserType.__objtype__)
<class 'str'>
Operators on the class
EnumType["FOO"]
: Return the member that has the nameFOO
, raising aKeyError
if it is not present.EnumType.FOO
: Return the member that has the nameFOO
, raising aAttributeError
if it is not present.EnumType(x)
: Attempt to castx
to the enum type by finding an existing member that has the same __value__. If this fails, you should expect aValueError
to be raised.
Operators on each enum member
e1 == e2
:bool
Compare equality.e1 != e2
:bool
Compare inequality.repr(e)
:str
Get the machine readable representation of the enum membere
.str(e)
:str
Get thestr
name of the enum membere
.
Special properties on each enum member
name
:str
The name of the member.value
: The value of the member. The type depends on the implementation type of the enum you are using.
All other methods and operators on enum members are inherited from the member's __value__. For example, an enum extending int
would be able to be used as an int
type outside these overridden definitions.
Variables and properties
Return the name of the enum member as a str
.
Return the value of the enum member.
Methods
View Source
def __call__(cls, value: typing.Any) -> typing.Any: """Cast a value to the enum, returning the raw value that was passed if value not found.""" try: return cls._value_to_member_map_[value] except KeyError: # If we can't find the value, just return what got casted in return value
Cast a value to the enum, returning the raw value that was passed if value not found.
View Source
class Flag(metaclass=_FlagMeta): """Clone of Python's `enum.Flag` implementation. This is designed to be faster and more efficient than Python's implementation, while retaining the majority of the external interface that Python's `enum.Flag` provides. In simple terms, an `Flag` is a set of wrapped constant `int` values that can be combined in any combination to make a special value. This is a more efficient way of combining things like permissions together into a single integral value, and works by setting individual `1`s and `0`s on the binary representation of the integer. This implementation has extra features, in that it will actively behave like a `set` as well. .. warning:: It is important to keep in mind that some semantics such as subtype checking and instance checking may differ. It is recommended to compare these values using the `==` operator rather than the `is` operator for safety reasons. Especially where pseudo-members created from combinations are cached, results of using of `is` may not be deterministic. This is a side effect of some internal performance improvements. Failing to observe this __will__ result in unexpected behaviour occurring in your application! Also important to note is that despite wrapping `int` values, conceptually this does not behave as if it were a subclass of `int`. Special Members on the class ---------------------------- * `__enumtype__` : Always `Flag`. * `__everything__` : A special member with all documented bits set. * `__members__` : An immutable `typing.Mapping` that maps each member name to the member value. * ` __objtype__` : Always `int`. Operators on the class ---------------------- * `FlagType["FOO"]` : Return the member that has the name `FOO`, raising a `KeyError` if it is not present. * `FlagType.FOO` : Return the member that has the name `FOO`, raising a `AttributeError` if it is not present. * `FlagType(x)` : Attempt to cast `x` to the enum type by finding an existing member that has the same __value__. If this fails, then a special __composite__ instance of the type is made. The name of this type is a combination of all members that combine to make the bitwise value. Operators on each flag member ----------------------------- * `e1 & e2` : Bitwise `AND` operation. Will return a member that contains all flags that are common between both oprands on the values. This also works with one of the oprands being an `int`eger. You may instead use the `intersection` method. * `e1 | e2` : Bitwise `OR` operation. Will return a member that contains all flags that appear on at least one of the oprands. This also works with one of the oprands being an `int`eger. You may instead use the `union` method. * `e1 ^ e2` : Bitwise `XOR` operation. Will return a member that contains all flags that only appear on at least one and at most one of the oprands. This also works with one of the oprands being an `int`eger. You may instead use the `symmetric_difference` method. * `~e` : Return the inverse of this value. This is equivalent to disabling all flags that are set on this value and enabling all flags that are not set on this value. Note that this will behave slightly differently to inverting a pure int value. You may instead use the `invert` method. * `e1 - e2` : Bitwise set difference operation. Returns all flags set on `e1` that are not set on `e2` as well. You may instead use the `difference` method. * `bool(e)` : `bool` Return `True` if `e` has a non-zero value, otherwise `False`. * `E.A in e`: `bool` `True` if `E.A` is in `e`. This is functionally equivalent to `E.A & e == E.A`. * `iter(e)` : Explode the value into a iterator of each __documented__ flag that can be combined to make up the value `e`. Returns an iterator across all well-defined flags that make up this value. This will only include the flags explicitly defined on this `Flag` type and that are individual powers of two (this means if converted to twos-compliment binary, exactly one bit must be a `1`). In simple terms, this means that you should not expect combination flags to be returned. * `e1 == e2` : `bool` Compare equality. * `e1 != e2` : `bool` Compare inequality. * `e1 < e2` : `bool` Compare by ordering. * `int(e)` : `int` Get the integer value of this flag * `repr(e)` : `str` Get the machine readable representation of the flag member `e`. * `str(e)` : `str` Get the `str` name of the flag member `e`. Special properties on each flag member -------------------------------------- * `e.name` : `str` The name of the member. For composite members, this will be generated. * `e.value` : `int` The value of the member. Special members on each flag member ----------------------------------- * `e.all(E.A, E.B, E.C, ...)` : `bool` Returns `True` if __all__ of `E.A`, `E.B`, `E.C`, et cetera make up the value of `e`. * `e.any(E.A, E.B, E.C, ...)` : `bool` Returns `True` if __any__ of `E.A`, `E.B`, `E.C`, et cetera make up the value of `e`. * `e.none(E.A, E.B, E.C, ...)` : `bool` Returns `True` if __none__ of `E.A`, `E.B`, `E.C`, et cetera make up the value of `e`. * `e.split()` : `typing.Sequence` Explode the value into a sequence of each __documented__ flag that can be combined to make up the value `e`. Returns a sorted sequence of each power-of-two flag that makes up the value `e`. This is equivalent to `list(iter(e))`. All other methods and operators on `Flag` members are inherited from the member's __value__. .. note:: Due to limitations around how this is re-implemented, this class is not considered a subclass of `Enum` at runtime, even if MyPy believes this is possible """ _name_to_member_map_: typing.ClassVar[typing.Mapping[str, Flag]] _value_to_member_map_: typing.ClassVar[typing.Mapping[int, Flag]] _powers_of_2_to_member_map_: typing.ClassVar[typing.Mapping[int, Flag]] _temp_members_: typing.ClassVar[typing.Mapping[int, Flag]] _member_names_: typing.ClassVar[typing.Sequence[str]] __members__: typing.ClassVar[typing.Mapping[str, Flag]] __objtype__: typing.ClassVar[typing.Type[int]] __enumtype__: typing.ClassVar[typing.Type[Flag]] _name_: typing.Optional[str] _value_: int @property def name(self) -> str: """Return the name of the flag combination as a `str`.""" if self._name_ is None: self._name_ = "|".join(_name_resolver(self._value_to_member_map_, self._value_)) return self._name_ @property def value(self) -> int: """Return the `int` value of the flag.""" return self._value_ def all(self: _T, *flags: _T) -> bool: """Check if all of the given flags are part of this value. Returns ------- bool `True` if any of the given flags are part of this value. Otherwise, return `False`. """ return all((flag & self) == flag for flag in flags) def any(self: _T, *flags: _T) -> bool: """Check if any of the given flags are part of this value. Returns ------- bool `True` if any of the given flags are part of this value. Otherwise, return `False`. """ return any((flag & self) == flag for flag in flags) def difference(self: _T, other: typing.Union[_T, int]) -> _T: """Perform a set difference with the other set. This will return all flags in this set that are not in the other value. Equivalent to using the subtraction `-` operator. """ return self.__class__(self & ~int(other)) def intersection(self: _T, other: typing.Union[_T, int]) -> _T: """Return a combination of flags that are set for both given values. Equivalent to using the "AND" `&` operator. """ return self.__class__(self._value_ & int(other)) def invert(self: _T) -> _T: """Return a set of all flags not in the current set.""" return self.__class__(self.__class__.__everything__._value_ & ~self._value_) def is_disjoint(self: _T, other: typing.Union[_T, int]) -> bool: """Return whether two sets have a intersection or not. If the two sets have an intersection, then this returns `False`. If no common flag values exist between them, then this returns `True`. """ return not (self & other) def is_subset(self: _T, other: typing.Union[_T, int]) -> bool: """Return whether another set contains this set or not. Equivalent to using the "in" operator. """ return (self & other) == other def is_superset(self: _T, other: typing.Union[_T, int]) -> bool: """Return whether this set contains another set or not.""" return (self & other) == self def none(self: _T, *flags: _T) -> bool: """Check if none of the given flags are part of this value. .. note:: This is essentially the opposite of `Flag.any`. Returns ------- bool `True` if none of the given flags are part of this value. Otherwise, return `False`. """ return not self.any(*flags) def split(self: _T) -> typing.Sequence[_T]: """Return a list of all defined atomic values for this flag. Any unrecognised bits will be omitted for brevity. The result will be a name-sorted `typing.Sequence` of each member """ return sorted( (member for member in self.__class__._powers_of_2_to_member_map_.values() if member.value & self), # Assumption: powers of 2 already have a cached value. key=lambda m: m._name_, ) def symmetric_difference(self: _T, other: typing.Union[_T, int]) -> _T: """Return a set with the symmetric differences of two flag sets. Equivalent to using the "XOR" `^` operator. For `a ^ b`, this can be considered the same as `(a - b) | (b - a)`. """ return self.__class__(self._value_ ^ int(other)) def union(self: _T, other: typing.Union[_T, int]) -> _T: """Return a combination of all flags in this set and the other set. Equivalent to using the "OR" `~` operator. """ return self.__class__(self._value_ | int(other)) isdisjoint = is_disjoint issubset = is_subset issuperset = is_superset # Exists since Python's `set` type is inconsistent with naming, so this # will prevent tripping people up unnecessarily because we do not # name inconsistently. # This one isn't in Python's set, but the inconsistency is triggering my OCD # so this is being defined anyway. symmetricdifference = symmetric_difference def __bool__(self) -> bool: return bool(self._value_) def __int__(self) -> int: return self._value_ def __iter__(self: _T) -> typing.Iterator[_T]: return iter(self.split()) def __len__(self) -> int: return len(self.split()) def __repr__(self) -> str: return f"<{self.__class__.__name__}.{self.name}: {self.value!r}>" def __rsub__(self: _T, other: typing.Union[int, _T]) -> _T: # This logic has to be reversed to be correct, since order matters for # a subtraction operator. This also ensures `int - _T -> _T` is a valid # case for us. return self.__class__(other) - self def __str__(self) -> str: return self.name __contains__ = is_subset __rand__ = __and__ = intersection __ror__ = __or__ = union __sub__ = difference __rxor__ = __xor__ = symmetric_difference __invert__ = invert
Clone of Python's enum.Flag
implementation.
This is designed to be faster and more efficient than Python's implementation, while retaining the majority of the external interface that Python's enum.Flag
provides.
In simple terms, an Flag
is a set of wrapped constant int
values that can be combined in any combination to make a special value. This is a more efficient way of combining things like permissions together into a single integral value, and works by setting individual 1
s and 0
s on the binary representation of the integer.
This implementation has extra features, in that it will actively behave like a set
as well.
Warning: It is important to keep in mind that some semantics such as subtype checking and instance checking may differ. It is recommended to compare these values using the ==
operator rather than the is
operator for safety reasons.
Especially where pseudo-members created from combinations are cached, results of using of is
may not be deterministic. This is a side effect of some internal performance improvements.
Failing to observe this __will__ result in unexpected behaviour occurring in your application!
Also important to note is that despite wrapping int
values, conceptually this does not behave as if it were a subclass of int
.
Special Members on the class
__enumtype__
: AlwaysFlag
.__everything__
: A special member with all documented bits set.__members__
: An immutabletyping.Mapping
that maps each member name to the member value.__objtype__
: Alwaysint
.
Operators on the class
FlagType["FOO"]
: Return the member that has the nameFOO
, raising aKeyError
if it is not present.FlagType.FOO
: Return the member that has the nameFOO
, raising aAttributeError
if it is not present.FlagType(x)
: Attempt to castx
to the enum type by finding an existing member that has the same __value__. If this fails, then a special __composite__ instance of the type is made. The name of this type is a combination of all members that combine to make the bitwise value.
Operators on each flag member
e1 & e2
: BitwiseAND
operation. Will return a member that contains all flags that are common between both oprands on the values. This also works with one of the oprands being anint
eger. You may instead use theintersection
method.e1 | e2
: BitwiseOR
operation. Will return a member that contains all flags that appear on at least one of the oprands. This also works with one of the oprands being anint
eger. You may instead use theunion
method.e1 ^ e2
: BitwiseXOR
operation. Will return a member that contains all flags that only appear on at least one and at most one of the oprands. This also works with one of the oprands being anint
eger. You may instead use thesymmetric_difference
method.~e
: Return the inverse of this value. This is equivalent to disabling all flags that are set on this value and enabling all flags that are not set on this value. Note that this will behave slightly differently to inverting a pure int value. You may instead use theinvert
method.e1 - e2
: Bitwise set difference operation. Returns all flags set one1
that are not set one2
as well. You may instead use thedifference
method.bool(e)
:bool
ReturnTrue
ife
has a non-zero value, otherwiseFalse
.E.A in e
:bool
True
ifE.A
is ine
. This is functionally equivalent toE.A & e == E.A
.iter(e)
: Explode the value into a iterator of each __documented__ flag that can be combined to make up the valuee
. Returns an iterator across all well-defined flags that make up this value. This will only include the flags explicitly defined on thisFlag
type and that are individual powers of two (this means if converted to twos-compliment binary, exactly one bit must be a1
). In simple terms, this means that you should not expect combination flags to be returned.e1 == e2
:bool
Compare equality.e1 != e2
:bool
Compare inequality.e1 < e2
:bool
Compare by ordering.int(e)
:int
Get the integer value of this flagrepr(e)
:str
Get the machine readable representation of the flag membere
.str(e)
:str
Get thestr
name of the flag membere
.
Special properties on each flag member
e.name
:str
The name of the member. For composite members, this will be generated.e.value
:int
The value of the member.
Special members on each flag member
e.all(E.A, E.B, E.C, ...)
:bool
ReturnsTrue
if __all__ ofE.A
,E.B
,E.C
, et cetera make up the value ofe
.e.any(E.A, E.B, E.C, ...)
:bool
ReturnsTrue
if __any__ ofE.A
,E.B
,E.C
, et cetera make up the value ofe
.e.none(E.A, E.B, E.C, ...)
:bool
ReturnsTrue
if __none__ ofE.A
,E.B
,E.C
, et cetera make up the value ofe
.e.split()
:typing.Sequence
Explode the value into a sequence of each __documented__ flag that can be combined to make up the valuee
. Returns a sorted sequence of each power-of-two flag that makes up the valuee
. This is equivalent tolist(iter(e))
.
All other methods and operators on Flag
members are inherited from the member's __value__.
Note: Due to limitations around how this is re-implemented, this class is not considered a subclass of Enum
at runtime, even if MyPy believes this is possible
Variables and properties
Return the name of the flag combination as a str
.
Return the int
value of the flag.
Methods
View Source
def __call__(cls, value: int = 0) -> typing.Any: """Cast a value to the flag enum, returning the raw value that was passed if values not found.""" # We want to handle value invariantly to avoid issues brought in by different behaviours from sub-classed ints # and floats. This also ensures that .__int__ only returns an invariant int. value = int(value) try: return cls._value_to_member_map_[value] except KeyError: # We only need this ability here usually, so overloading operators # is an overkill and would add more overhead. if value < 0: # Convert to a positive value instead. return cls.__everything__ - ~value temp_members = cls._temp_members_ # For huge enums, don't ever cache anything. We could consume masses of memory otherwise # (e.g. Permissions) try: # Try to get a cached value. return temp_members[value] except KeyError: # If we can't find the value, just return what got casted in by generating a pseudomember # and caching it. We can't use weakref because int is not weak referenceable, annoyingly. pseudomember = cls.__new__(cls, value) pseudomember._name_ = None pseudomember._value_ = value temp_members[value] = pseudomember if len(temp_members) > _MAX_CACHED_MEMBERS: temp_members.popitem() return pseudomember
Cast a value to the flag enum, returning the raw value that was passed if values not found.
View Source
def all(self: _T, *flags: _T) -> bool: """Check if all of the given flags are part of this value. Returns ------- bool `True` if any of the given flags are part of this value. Otherwise, return `False`. """ return all((flag & self) == flag for flag in flags)
Check if all of the given flags are part of this value.
Returns
- bool:
True
if any of the given flags are part of this value. Otherwise, returnFalse
.
View Source
def any(self: _T, *flags: _T) -> bool: """Check if any of the given flags are part of this value. Returns ------- bool `True` if any of the given flags are part of this value. Otherwise, return `False`. """ return any((flag & self) == flag for flag in flags)
Check if any of the given flags are part of this value.
Returns
- bool:
True
if any of the given flags are part of this value. Otherwise, returnFalse
.
View Source
def difference(self: _T, other: typing.Union[_T, int]) -> _T: """Perform a set difference with the other set. This will return all flags in this set that are not in the other value. Equivalent to using the subtraction `-` operator. """ return self.__class__(self & ~int(other))
Perform a set difference with the other set.
This will return all flags in this set that are not in the other value.
Equivalent to using the subtraction -
operator.
View Source
def intersection(self: _T, other: typing.Union[_T, int]) -> _T: """Return a combination of flags that are set for both given values. Equivalent to using the "AND" `&` operator. """ return self.__class__(self._value_ & int(other))
Return a combination of flags that are set for both given values.
Equivalent to using the "AND" &
operator.
View Source
def invert(self: _T) -> _T: """Return a set of all flags not in the current set.""" return self.__class__(self.__class__.__everything__._value_ & ~self._value_)
Return a set of all flags not in the current set.
View Source
def is_disjoint(self: _T, other: typing.Union[_T, int]) -> bool: """Return whether two sets have a intersection or not. If the two sets have an intersection, then this returns `False`. If no common flag values exist between them, then this returns `True`. """ return not (self & other)
Return whether two sets have a intersection or not.
If the two sets have an intersection, then this returns False
. If no common flag values exist between them, then this returns True
.
View Source
def is_subset(self: _T, other: typing.Union[_T, int]) -> bool: """Return whether another set contains this set or not. Equivalent to using the "in" operator. """ return (self & other) == other
Return whether another set contains this set or not.
Equivalent to using the "in" operator.
View Source
def is_superset(self: _T, other: typing.Union[_T, int]) -> bool: """Return whether this set contains another set or not.""" return (self & other) == self
Return whether this set contains another set or not.
View Source
def is_disjoint(self: _T, other: typing.Union[_T, int]) -> bool: """Return whether two sets have a intersection or not. If the two sets have an intersection, then this returns `False`. If no common flag values exist between them, then this returns `True`. """ return not (self & other)
Return whether two sets have a intersection or not.
If the two sets have an intersection, then this returns False
. If no common flag values exist between them, then this returns True
.
View Source
def is_subset(self: _T, other: typing.Union[_T, int]) -> bool: """Return whether another set contains this set or not. Equivalent to using the "in" operator. """ return (self & other) == other
Return whether another set contains this set or not.
Equivalent to using the "in" operator.
View Source
def is_superset(self: _T, other: typing.Union[_T, int]) -> bool: """Return whether this set contains another set or not.""" return (self & other) == self
Return whether this set contains another set or not.
View Source
def none(self: _T, *flags: _T) -> bool: """Check if none of the given flags are part of this value. .. note:: This is essentially the opposite of `Flag.any`. Returns ------- bool `True` if none of the given flags are part of this value. Otherwise, return `False`. """ return not self.any(*flags)
Check if none of the given flags are part of this value.
Note: This is essentially the opposite of Flag.any
.
Returns
- bool:
True
if none of the given flags are part of this value. Otherwise, returnFalse
.
View Source
def split(self: _T) -> typing.Sequence[_T]: """Return a list of all defined atomic values for this flag. Any unrecognised bits will be omitted for brevity. The result will be a name-sorted `typing.Sequence` of each member """ return sorted( (member for member in self.__class__._powers_of_2_to_member_map_.values() if member.value & self), # Assumption: powers of 2 already have a cached value. key=lambda m: m._name_, )
Return a list of all defined atomic values for this flag.
Any unrecognised bits will be omitted for brevity.
The result will be a name-sorted typing.Sequence
of each member
View Source
def symmetric_difference(self: _T, other: typing.Union[_T, int]) -> _T: """Return a set with the symmetric differences of two flag sets. Equivalent to using the "XOR" `^` operator. For `a ^ b`, this can be considered the same as `(a - b) | (b - a)`. """ return self.__class__(self._value_ ^ int(other))
Return a set with the symmetric differences of two flag sets.
Equivalent to using the "XOR" ^
operator.
For a ^ b
, this can be considered the same as (a - b) | (b - a)
.
View Source
def symmetric_difference(self: _T, other: typing.Union[_T, int]) -> _T: """Return a set with the symmetric differences of two flag sets. Equivalent to using the "XOR" `^` operator. For `a ^ b`, this can be considered the same as `(a - b) | (b - a)`. """ return self.__class__(self._value_ ^ int(other))
Return a set with the symmetric differences of two flag sets.
Equivalent to using the "XOR" ^
operator.
For a ^ b
, this can be considered the same as (a - b) | (b - a)
.
View Source
def union(self: _T, other: typing.Union[_T, int]) -> _T: """Return a combination of all flags in this set and the other set. Equivalent to using the "OR" `~` operator. """ return self.__class__(self._value_ | int(other))
Return a combination of all flags in this set and the other set.
Equivalent to using the "OR" ~
operator.