diff --git a/docs/documentation.mako b/docs/documentation.mako
index 24a4ef8048..6b043eeb48 100644
--- a/docs/documentation.mako
+++ b/docs/documentation.mako
@@ -99,6 +99,9 @@
from pdoc.html_helpers import extract_toc, glimpse, to_html as _to_html, format_git_link
+ # Hikari Enum hack
+ from hikari.internal import enums
+
# Allow imports to resolve properly.
typing.TYPE_CHECKING = True
@@ -325,17 +328,17 @@
if hasattr(dobj.cls, "obj"):
for cls in dobj.cls.obj.mro():
if (descriptor := cls.__dict__.get(dobj.name)) is not None:
- is_descriptor = True
+ is_descriptor = hasattr(descriptor, "__get__")
break
- if is_descriptor:
+ if all(not c.isalpha() or c.isupper() for c in dobj.name):
+ prefix = f"{prefix}{QUAL_CONST} "
+ elif is_descriptor:
qual = QUAL_CACHED_PROPERTY if isinstance(descriptor, functools.cached_property) else QUAL_PROPERTY
prefix = f"{prefix}{qual} "
elif dobj.module.name == "typing" or dobj.docstring and dobj.docstring.casefold().startswith(("type hint", "typehint", "type alias")):
show_object = not simple_names
prefix = f"{prefix}{QUAL_TYPEHINT} "
- elif all(not c.isalpha() or c.isupper() for c in dobj.name):
- prefix = f"{prefix}{QUAL_CONST} "
else:
prefix = f"{prefix}{QUAL_VAR} "
@@ -347,9 +350,9 @@
elif issubclass(dobj.obj, type):
qual += QUAL_METACLASS
else:
- if enum.Flag in dobj.obj.mro():
+ if enums.Flag in dobj.obj.mro() or enum.Flag in dobj.obj.mro():
qual += QUAL_ENUM_FLAG
- elif enum.Enum in dobj.obj.mro():
+ elif enums.Enum in dobj.obj.mro() or enum.Enum in dobj.obj.mro():
qual += QUAL_ENUM
elif hasattr(dobj.obj, "__attrs_attrs__"):
qual += QUAL_DATACLASS
@@ -401,10 +404,6 @@
extra = f" = {dobj.obj}"
classes = []
- if dotted:
- classes.append("dotted")
- if css_classes:
- classes.append(css_classes)
class_str = " ".join(classes)
if class_str.strip():
@@ -520,7 +519,17 @@
print(v.name, type(ex).__name__, ex)
if value:
- return_type += f" = {value}"
+ for enum_mapping in ("_value2member_map_", "_value_to_member_map_"):
+ if mapping := getattr(v.cls.obj, enum_mapping, None):
+ try:
+ real_value = getattr(v.cls.obj, v.name)
+ if real_value in mapping.values():
+ return_type += f" = {real_value.value!r}"
+ break
+ except AttributeError:
+ pass
+ else:
+ return_type += f" = {value}"
if hasattr(parent, "mro"):
name = f"{parent.__module__}.{parent.__qualname__}.{v.name}"
@@ -549,6 +558,7 @@
params = f.params(annotate=show_type_annotations, link=link)
return_type = get_annotation(f.return_annotation, '->')
qual = QUAL_ASYNC_DEF if f._is_async else QUAL_DEF
+ anchored_name = f'{f.name}'
example_str = qual + f.name + "(" + ", ".join(params) + ")" + return_type
@@ -557,15 +567,15 @@
if len(params) > 4 or len(params) > 0 and len(example_str) > 70:
representation = "\n".join((
- qual + " " + f.name + "(",
+ qual + " " + anchored_name + "(",
*(f" {p}," for p in params),
")" + return_type + ": ..."
))
elif params:
- representation = f"{qual} {f.name}({', '.join(params)}){return_type}: ..."
+ representation = f"{qual} {anchored_name}({', '.join(params)}){return_type}: ..."
else:
- representation = f"{qual} {f.name}(){return_type}: ..."
+ representation = f"{qual} {anchored_name}(){return_type}: ..."
if f.module.name != f.obj.__module__:
try:
@@ -708,21 +718,21 @@
% endif
- % if methods:
- Methods
+ % if variables:
+ Variables and properties
- % for m in methods:
- ${show_func(m)}
+ % for i in variables:
+ ${show_var(i)}
% endfor
% endif
- % if variables:
- Variables and properties
+ % if methods:
+ Methods
- % for i in variables:
- ${show_var(i)}
+ % for m in methods:
+ ${show_func(m)}
% endfor
diff --git a/hikari/channels.py b/hikari/channels.py
index 8b0a05bf8f..6b59959deb 100644
--- a/hikari/channels.py
+++ b/hikari/channels.py
@@ -255,37 +255,24 @@ class PermissionOverwrite(snowflakes.Unique):
)
"""The type of entity this overwrite targets."""
- # Flags are lazily loaded, due to the IntFlag mechanism being overly slow
- # to execute.
- _allow: int = attr.ib(default=0, eq=False, hash=False, repr=False)
- _deny: int = attr.ib(default=0, eq=False, hash=False, repr=False)
-
- @property
- def allow(self) -> permissions.Permissions:
- """Return the permissions this overwrite allows.
-
- Returns
- -------
- hikari.permissions.Permissions
- Permissions explicitly allowed by this overwrite.
- """
- return permissions.Permissions(self._allow)
-
- @property
- def deny(self) -> permissions.Permissions:
- """Return the permissions this overwrite denies.
+ allow: permissions.Permissions = attr.ib(
+ converter=permissions.Permissions,
+ default=permissions.Permissions.NONE,
+ eq=False,
+ hash=False,
+ repr=False,
+ )
+ """The permissions this overwrite allows."""
- Returns
- -------
- hikari.permissions.Permissions
- Permissions explicitly denied by this overwrite.
- """
- return permissions.Permissions(self._deny)
+ deny: permissions.Permissions = attr.ib(
+ converter=permissions.Permissions, default=permissions.Permissions.NONE, eq=False, hash=False, repr=False
+ )
+ """The permissions this overwrite denies."""
@property
def unset(self) -> permissions.Permissions:
"""Bitfield of all permissions not explicitly allowed or denied by this overwrite."""
- return permissions.Permissions(~(self.allow | self.deny))
+ return ~(self.allow | self.deny)
@attr_extensions.with_copy
diff --git a/hikari/errors.py b/hikari/errors.py
index 98c38e983f..6239cdbe0b 100644
--- a/hikari/errors.py
+++ b/hikari/errors.py
@@ -527,4 +527,4 @@ class MissingIntentError(HikariError, ValueError):
"""The combination of intents that are missing."""
def __str__(self) -> str:
- return f"You are missing the following intent(s): {str(self.intents)}"
+ return "You are missing the following intent(s): " + ", ".join(map(str, self.intents.split()))
diff --git a/hikari/guilds.py b/hikari/guilds.py
index 9fc8ac4a80..ee457110e8 100644
--- a/hikari/guilds.py
+++ b/hikari/guilds.py
@@ -49,7 +49,6 @@
]
import abc
-import enum
import typing
import attr
@@ -60,7 +59,6 @@
from hikari import users
from hikari.internal import attr_extensions
from hikari.internal import enums
-from hikari.internal import flag
from hikari.internal import routes
if typing.TYPE_CHECKING:
@@ -220,9 +218,8 @@ def __str__(self) -> str:
return self.name
-@enum.unique
@typing.final
-class GuildSystemChannelFlag(flag.Flag):
+class GuildSystemChannelFlag(enums.Flag):
"""Defines which features are suppressed in the system channel."""
NONE = 0
@@ -921,6 +918,17 @@ class Guild(PartialGuild, abc.ABC):
splash_hash: typing.Optional[str] = attr.ib(eq=False, hash=False, repr=False)
"""The hash of the splash for the guild, if there is one."""
+ system_channel_flags: GuildSystemChannelFlag = attr.ib(eq=False, hash=False, repr=False)
+ """Return flags for the guild system channel.
+
+ These are used to describe which notifications are suppressed.
+
+ Returns
+ -------
+ GuildSystemChannelFlag
+ The system channel flags for this channel.
+ """
+
system_channel_id: typing.Optional[snowflakes.Snowflake] = attr.ib(eq=False, hash=False, repr=False)
"""The ID of the system channel or `builtins.None` if it is not enabled.
@@ -944,10 +952,6 @@ class Guild(PartialGuild, abc.ABC):
this will be `builtins.None`.
"""
- # Flags are lazily loaded, due to the IntFlag mechanism being overly slow
- # to execute.
- _system_channel_flags: int = attr.ib(eq=False, hash=False, repr=False)
-
@property
def banner_url(self) -> typing.Optional[files.URL]:
"""Banner for the guild, if set."""
@@ -985,19 +989,6 @@ def splash_url(self) -> typing.Optional[files.URL]:
"""Splash for the guild, if set."""
return self.format_splash()
- @property
- def system_channel_flags(self) -> GuildSystemChannelFlag:
- """Return flags for the guild system channel.
-
- These are used to describe which notifications are suppressed.
-
- Returns
- -------
- GuildSystemChannelFlag
- The system channel flags for this channel.
- """
- return GuildSystemChannelFlag(self._system_channel_flags)
-
def format_banner(self, *, ext: str = "png", size: int = 4096) -> typing.Optional[files.URL]:
"""Generate the guild's banner image, if set.
diff --git a/hikari/impl/bot.py b/hikari/impl/bot.py
index a48ab4399b..3fc185803f 100644
--- a/hikari/impl/bot.py
+++ b/hikari/impl/bot.py
@@ -869,7 +869,7 @@ async def start(
await self.dispatch(lifetime_events.StartedEvent(app=self))
- _LOGGER.info("application started successfully in approx %.0f seconds", time.monotonic() - start_time)
+ _LOGGER.info("application started successfully in approx %.2f seconds", time.monotonic() - start_time)
def stream(
self,
diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py
index 066d54dd9d..2af988bd81 100644
--- a/hikari/impl/entity_factory.py
+++ b/hikari/impl/entity_factory.py
@@ -121,7 +121,7 @@ class _GuildFields(_PartialGuildFields):
widget_channel_id: typing.Optional[snowflakes.Snowflake] = attr.ib()
system_channel_id: typing.Optional[snowflakes.Snowflake] = attr.ib()
is_widget_enabled: typing.Optional[bool] = attr.ib()
- system_channel_flags: int = attr.ib()
+ system_channel_flags: guild_models.GuildSystemChannelFlag = attr.ib()
rules_channel_id: typing.Optional[snowflakes.Snowflake] = attr.ib()
max_video_channel_users: typing.Optional[int] = attr.ib()
vanity_url_code: typing.Optional[str] = attr.ib()
@@ -191,8 +191,8 @@ def __init__(self, app: traits.RESTAware) -> None:
audit_log_models.AuditLogChangeKey.APPLICATION_ID: snowflakes.Snowflake,
audit_log_models.AuditLogChangeKey.PERMISSIONS: permission_models.Permissions,
audit_log_models.AuditLogChangeKey.COLOR: color_models.Color,
- audit_log_models.AuditLogChangeKey.ALLOW: int,
- audit_log_models.AuditLogChangeKey.DENY: int,
+ audit_log_models.AuditLogChangeKey.ALLOW: permission_models.Permissions,
+ audit_log_models.AuditLogChangeKey.DENY: permission_models.Permissions,
audit_log_models.AuditLogChangeKey.CHANNEL_ID: snowflakes.Snowflake,
audit_log_models.AuditLogChangeKey.INVITER_ID: snowflakes.Snowflake,
audit_log_models.AuditLogChangeKey.MAX_USES: _deserialize_max_uses,
@@ -1754,6 +1754,9 @@ def _set_user_attributes(payload: data_binding.JSONObject) -> _UserFields:
def deserialize_user(self, payload: data_binding.JSONObject) -> user_models.User:
user_fields = self._set_user_attributes(payload)
+ flags = (
+ user_models.UserFlag(payload["public_flags"]) if "public_flags" in payload else user_models.UserFlag.NONE
+ )
return user_models.UserImpl(
app=self._app,
id=user_fields.id,
@@ -1762,7 +1765,7 @@ def deserialize_user(self, payload: data_binding.JSONObject) -> user_models.User
avatar_hash=user_fields.avatar_hash,
is_bot=user_fields.is_bot,
is_system=user_fields.is_system,
- flags=int(payload.get("public_flags", 0)),
+ flags=flags,
)
def deserialize_my_user(self, payload: data_binding.JSONObject) -> user_models.OwnUser:
diff --git a/hikari/impl/event_factory.py b/hikari/impl/event_factory.py
index e4d7f1c092..cad471fc6e 100644
--- a/hikari/impl/event_factory.py
+++ b/hikari/impl/event_factory.py
@@ -319,9 +319,9 @@ def deserialize_presence_update_event(
if len(user_payload) > 1:
# PartialUser
discriminator = user_payload["discriminator"] if "discriminator" in user_payload else undefined.UNDEFINED
- flags: undefined.UndefinedOr[int] = undefined.UNDEFINED
+ flags: undefined.UndefinedOr[user_models.UserFlag] = undefined.UNDEFINED
if "public_flags" in user_payload:
- flags = int(user_payload["public_flags"])
+ flags = user_models.UserFlag(user_payload["public_flags"])
user = user_models.PartialUserImpl(
app=self._app,
diff --git a/hikari/intents.py b/hikari/intents.py
index 9b6f849064..7150baa91f 100644
--- a/hikari/intents.py
+++ b/hikari/intents.py
@@ -27,11 +27,11 @@
import typing
-from hikari.internal import flag
+from hikari.internal import enums
@typing.final
-class Intents(flag.Flag):
+class Intents(enums.Flag):
"""Represents an intent on the gateway.
This is a bitfield representation of all the categories of event
diff --git a/hikari/internal/cache.py b/hikari/internal/cache.py
index fee8c62065..171e108c52 100644
--- a/hikari/internal/cache.py
+++ b/hikari/internal/cache.py
@@ -493,7 +493,7 @@ class RichActivityData(BaseData[presences.RichActivity]):
assets: typing.Optional[presences.ActivityAssets] = attr.ib()
secrets: typing.Optional[presences.ActivitySecret] = attr.ib()
is_instance: typing.Optional[bool] = attr.ib()
- flags: typing.Optional[int] = attr.ib()
+ flags: typing.Optional[presences.ActivityFlag] = attr.ib()
@classmethod
def build_from_entity(cls: typing.Type[RichActivityData], entity: presences.RichActivity) -> RichActivityData:
diff --git a/hikari/internal/enums.py b/hikari/internal/enums.py
index 314f52d42b..bc2f3f8395 100644
--- a/hikari/internal/enums.py
+++ b/hikari/internal/enums.py
@@ -19,17 +19,19 @@
# 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 faster."""
+"""Implementation of parts of Python's `enum` protocol to be more performant."""
from __future__ import annotations
-__all__: typing.List[str] = ["Enum"]
+__all__: typing.List[str] = ["Enum", "Flag"]
-import os
+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]):
@@ -109,73 +111,51 @@ def __setitem__(self, name: str, value: typing.Any) -> None:
_Enum = NotImplemented
-def _attr_mutator(self, *_: typing.Any) -> typing.NoReturn:
- raise TypeError("Cannot mutate enum members")
-
-
class _EnumMeta(type):
def __call__(cls, value: typing.Any) -> typing.Any:
try:
- return cls._value2member_map_[value]
+ return cls._value_to_member_map_[value]
except KeyError:
# If we cant find the value, just return what got casted in
return value
- def __dir__(cls) -> typing.List[str]:
- members = ["__class__", "__doc__", "__members__", "__module__"]
- try:
- members += list(cls._name2member_map_)
- finally:
- return members
-
- def __getattr__(cls, name: str) -> typing.Any:
- if name.startswith("_") and name.endswith("_"):
- # Stop recursion errors by trying to look up _name2member_map_
- # recursively.
- raise AttributeError(name)
- try:
- return cls._name2member_map_[name]
- except KeyError:
- try:
- return super().__getattribute__(name)
- except AttributeError:
- raise AttributeError(name) from None
-
def __getitem__(cls, name: str) -> typing.Any:
- return cls._name2member_map_[name]
+ return cls._name_to_member_map_[name]
def __iter__(cls) -> typing.Iterator[str]:
- yield cls._name2member_map_
+ yield from cls._name_to_member_map_
@staticmethod
def __new__(
mcs: typing.Type[_T],
name: str,
bases: typing.Tuple[typing.Type[typing.Any], ...],
- namespace: _EnumNamespace,
+ namespace: typing.Union[typing.Dict[str, typing.Any], _EnumNamespace],
) -> _T:
global _Enum
- if name == "Enum" and _Enum is NotImplemented:
+ if _Enum is NotImplemented:
# noinspection PyRedundantParentheses
return (_Enum := super().__new__(mcs, name, bases, namespace))
- try:
- base, enum_type = bases
- except ValueError:
- raise TypeError("Expected two base classes for an enum") from None
+ assert isinstance(namespace, _EnumNamespace)
- if not issubclass(enum_type, _Enum):
- raise TypeError("second base type for enum must be derived from Enum")
+ base, enum_type = bases
new_namespace = {
"__objtype__": base,
"__enumtype__": enum_type,
- "_name2member_map_": (name2member := {}),
- "_value2member_map_": (value2member := {}),
+ "_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),
**namespace,
+ **{
+ name: value
+ for name, value in Enum.__dict__.items()
+ if name not in ("__class__", "__module__", "__doc__")
+ },
}
cls = super().__new__(mcs, name, bases, new_namespace)
@@ -187,18 +167,24 @@ def __new__(
# 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
- name2member[name] = member
- value2member[value] = member
-
- cls.__setattr__ = _attr_mutator
- cls.__delattr__ = _attr_mutator
+ 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], ...] = ()) -> _EnumNamespace:
+ 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 {}
+
try:
# Fails if Enum is not defined. We check this in `__new__` properly.
base, enum_type = bases
@@ -208,9 +194,14 @@ def __prepare__(mcs, name: str, bases: typing.Tuple[typing.Type[typing.Any], ...
if not isinstance(enum_type, _EnumMeta):
raise TypeError("Second base to an enum must be the enum type (derived from _EnumMeta) to be used")
+ if not issubclass(enum_type, _Enum):
+ raise TypeError("second base type for enum must be derived from Enum")
+
return _EnumNamespace(base)
except ValueError:
- return _EnumNamespace(object)
+ if name == "Enum" and _Enum is NotImplemented:
+ return _EnumNamespace(object)
+ raise TypeError("Expected two base classes for an enum") from None
def __repr__(cls) -> str:
return f""
@@ -219,18 +210,567 @@ def __repr__(cls) -> str:
class Enum(metaclass=_EnumMeta):
- """Re-implementation of parts of Python's `enum` to be faster."""
-
- def __getattr__(self, name: str) -> typing.Any:
- return getattr(self.value, 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__` :
+ 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__)
+
+ ```
+
+ Operators on the class
+ ----------------------
+ * `EnumType["FOO"]` :
+ Return the member that has the name `FOO`, raising a `builtins.KeyError`
+ if it is not present.
+ * `EnumType.FOO` :
+ Return the member that has the name `FOO`, raising a
+ `builtins.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
+ `builtins.ValueError` to be raised.
+
+ Operators on each enum member
+ -----------------------------
+ * `e1 == e2` : `builtins.bool`
+ Compare equality.
+ * `e1 != e2` : `builtins.bool`
+ Compare inequality.
+ * `builtins.repr(e)` : `builtins.str`
+ Get the machine readable representation of the enum member `e`.
+ * `builtins.str(e)` : `builtins.str`
+ Get the `builtins.str` name of the enum member `e`.
+
+ Special properties on each enum member
+ --------------------------------------
+ * `name` : `builtins.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 `builtins.int` would
+ be able to be used as an `int` type outside these overridden definitions.
+ """
+
+ _name_to_member_map_: typing.Final[typing.ClassVar[typing.Mapping[str, Enum]]]
+ _value_to_member_map_: typing.Final[typing.ClassVar[typing.Mapping[int, Enum]]]
+ _member_names_: typing.Final[typing.ClassVar[typing.Sequence[str]]]
+ __members__: typing.Final[typing.ClassVar[typing.Mapping[str, Enum]]]
+ __objtype__: typing.Final[typing.ClassVar[typing.Type[typing.Any]]]
+ __enumtype__: typing.Final[typing.ClassVar[typing.Type[Enum]]]
+ _name_: typing.Final[str]
+ _value_: typing.Final[typing.Any]
+
+ @property
+ def name(self) -> str:
+ """Return the name of the enum member as a `builtins.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}>"
+ return f"<{type(self).__name__}.{self._name_}: {self._value_!r}>"
def __str__(self) -> str:
- return f"{type(self).__name__}.{self.name}"
+ return self._name_ or "NO_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: typing.Any = 0) -> typing.Any:
+ 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 cant find the value, just return what got casted in by generating a pseudomember
+ # and caching it. We cant use weakref because int is not weak referenceable, annoyingly.
+ # TODO: make the cache update thread-safe by using setdefault instead of assignment.
+ pseudomember = cls.__new__(cls, value)
+ temp_members[value] = pseudomember
+ pseudomember._name_ = None
+ pseudomember._value_ = value
+ 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[str]:
+ 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)
+
+ try:
+ # Fails if Enum is not defined.
+ if len(bases) == 1 and bases[0] == Flag:
+ return _EnumNamespace(int)
+ except ValueError:
+ pass
+ raise TypeError("Cannot define another Flag base type") from None
+
+ @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 cant 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),
+ **namespace,
+ # 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__")
+ },
+ }
+
+ 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
-# We have to use this fallback, or Pdoc will fail to document some stuff correctly...
-if os.getenv("PDOC3_GENERATING") == "1": # pragma: no cover
- from enum import Enum # noqa: F811 - Redefinition intended
+ def __repr__(cls) -> str:
+ return f""
+
+ __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 `builtins.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 `builtins.set` as well.
+
+ !!! warning
+ Despite wrapping `builtins.int` values, conceptually this does not
+ behave as if it were a subclass of `int`.
+
+ !!! danger
+ 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!
+
+ 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 `builtins.int`.
+
+ Operators on the class
+ ----------------------
+ * `FlagType["FOO"]` :
+ Return the member that has the name `FOO`, raising a `builtins.KeyError`
+ if it is not present.
+ * `FlagType.FOO` :
+ Return the member that has the name `FOO`, raising a
+ `builtins.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 `builtins.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 `builtins.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 `builtins.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)` : `builtins.bool`
+ Return `builtins.True` if `e` has a non-zero value, otherwise
+ `builtins.False`.
+ * `E.A in e`: `builtins.bool`
+ `builtins.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` : `builtins.bool`
+ Compare equality.
+ * `e1 != e2` : `builtins.bool`
+ Compare inequality.
+ * `e1 < e2` : `builtins.bool`
+ Compare by ordering.
+ * `builtins.int(e)` : `builtins.int`
+ Get the integer value of this flag
+ * `builtins.repr(e)` : `builtins.str`
+ Get the machine readable representation of the flag member `e`.
+ * `builtins.str(e)` : `builtins.str`
+ Get the `builtins.str` name of the flag member `e`.
+
+ Special properties on each flag member
+ --------------------------------------
+ * `e.name` : `builtins.str`
+ The name of the member. For composite members, this will be generated.
+ * `e.value` : `builtins.int`
+ The value of the member.
+
+ Special members on each flag member
+ -----------------------------------
+ * `e.all(E.A, E.B, E.C, ...)` : `builtins.bool`
+ Returns `builtins.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, ...)` : `builtins.bool`
+ Returns `builtins.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, ...)` : `builtins.bool`
+ Returns `builtins.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.Final[typing.ClassVar[typing.Mapping[str, Flag]]]
+ _value_to_member_map_: typing.Final[typing.ClassVar[typing.Mapping[int, Flag]]]
+ _powers_of_2_to_member_map_: typing.Final[typing.ClassVar[typing.Mapping[int, Flag]]]
+ _temp_members_: typing.Final[typing.ClassVar[typing.Mapping[int, Flag]]]
+ _member_names_: typing.Final[typing.ClassVar[typing.Sequence[str]]]
+ __members__: typing.Final[typing.ClassVar[typing.Mapping[str, Flag]]]
+ __objtype__: typing.Final[typing.ClassVar[typing.Type[int]]]
+ __enumtype__: typing.Final[typing.ClassVar[typing.Type[Flag]]]
+ _name_: typing.Final[str]
+ _value_: typing.Final[int]
+
+ @property
+ def name(self) -> str:
+ """Return the name of the flag combination as a `builtins.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 `builtins.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
+ -------
+ builtins.bool
+ `builtins.True` if any of the given flags are part of this value.
+ Otherwise, return `builtins.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
+ -------
+ builtins.bool
+ `builtins.True` if any of the given flags are part of this value.
+ Otherwise, return `builtins.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
+ `builtins.False`. If no common flag values exist between them, then
+ this returns `builtins.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
+ -------
+ builtins.bool
+ `builtins.True` if none of the given flags are part of this value.
+ Otherwise, return `builtins.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 membe
+ """
+ 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.
+ if not isinstance(other, self.__class__):
+ other = self.__class__(other)
+ return 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
diff --git a/hikari/internal/enums.pyi b/hikari/internal/enums.pyi
index 9abb63a6cb..59a325cc4b 100644
--- a/hikari/internal/enums.pyi
+++ b/hikari/internal/enums.pyi
@@ -30,7 +30,64 @@
# we are using are just aliases from the enum types in the standard library.
import enum as __enum
+from typing import Any as __Any
+from typing import Iterator as __Iterator
+from typing import Sequence as __Sequence
+from typing import Type as __Type
+from typing import TypeVar as __TypeVar
+from typing import Union as __Union
Enum = __enum.Enum
-__all__ = ["Enum"]
+__FlagT = __TypeVar("__FlagT", bound=__enum.IntFlag)
+
+
+class Flag(__enum.IntFlag):
+ def all(self: __FlagT, *flags: __FlagT) -> bool:
+ ...
+ def any(self: __FlagT, *flags: __FlagT) -> bool:
+ ...
+ def difference(self: __FlagT, other: __Union[int, __FlagT]) -> __FlagT:
+ ...
+ def intersection(self: __FlagT, other: __Union[int, __FlagT]) -> __FlagT:
+ ...
+ def invert(self: __FlagT) -> __FlagT:
+ ...
+ def is_disjoint(self: __FlagT, other: __Union[int, __FlagT]) -> bool:
+ ...
+ def is_subset(self: __FlagT, other: __Union[int, __FlagT]) -> bool:
+ ...
+ def is_superset(self: __FlagT, other: __Union[int, __FlagT]) -> bool:
+ ...
+ def none(self, *flags: __FlagT) -> bool:
+ ...
+ def split(self: __FlagT) -> __Sequence[__FlagT]:
+ ...
+ def symmetric_difference(self: __FlagT, other: __Union[int, __FlagT]) -> __FlagT:
+ ...
+ def union(self: __FlagT, other: __Union[int, __FlagT]) -> __FlagT:
+ ...
+ def __bool__(self) -> bool:
+ ...
+ def __int__(self) -> int:
+ ...
+ def __iter__(self: __FlagT) -> __Iterator[__FlagT]:
+ ...
+ def __len__(self) -> int:
+ ...
+ @staticmethod
+ def __new__(cls: __Type[__FlagT], value: __Any = 0) -> __FlagT:
+ ...
+
+ isdisjoint = is_disjoint
+ issuperset = is_superset
+ symmetricdifference = symmetric_difference
+ __contains__ = issubset = is_subset
+ __rand__ = __and__ = intersection
+ __ror__ = __or__ = union
+ __rsub__ = __sub__ = difference
+ __rxor__ = __xor__ = symmetric_difference
+ __invert__ = invert
+
+
+__all__ = ["Enum", "Flag"]
diff --git a/hikari/internal/flag.py b/hikari/internal/flag.py
deleted file mode 100644
index 52cef4ce2a..0000000000
--- a/hikari/internal/flag.py
+++ /dev/null
@@ -1,169 +0,0 @@
-# -*- coding: utf-8 -*-
-# cython: language_level=3
-# Copyright (c) 2020 Nekokatt
-#
-# 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.
-"""Stuff to make working with enum flags a bit easier."""
-from __future__ import annotations
-
-__all__: typing.List[str] = ["Flag"]
-
-import enum
-import inspect
-import math
-import typing
-
-FlagT = typing.TypeVar("FlagT", bound="Flag")
-
-
-class Flag(enum.IntFlag):
- """Base type for an enum integer flag in Hikari.
-
- Provides a consistent way of producing human-readable strings, extra
- inspection utilities, and injects some boilerplate documentation. This
- should make the concept of using flags a little less daunting to those
- who are not yet used to the idea.
-
- Example
- -------
-
- ```py
- >>> class Permission(Flag):
- ... CREATE = enum.auto()
- ... READ = enum.auto()
- ... UPDATE = enum.auto()
- ... DELETE = enum.auto()
-
- >>> perms = Permissions.CREATE | Permissions.READ
- >>> print(perms.split())
- [Permissions.CREATE, Permissions.READ]
- ```
-
- This also provides two operators for clearer semantics of combining and
- removing flag members.
-
- ```py
- perms = Permissions.CREATE + Permissions.READ
- assert perms == Permissions.CREATE | Permissions.READ
-
- perms -= Permissions.CREATE
- assert perms == Permissions.READ
- ```
-
- Members will be iterable if you wish to inspect each individual flag.
-
- ```py
- for p in Permissions.CREATE + Permissions.READ:
- print(p)
- ```
- """
-
- __slots__: typing.Sequence[str] = ()
-
- def __init_subclass__(cls, **kwargs: typing.Any) -> None:
- doc = inspect.getdoc(cls) or ""
-
- doc += "\n".join(
- (
- "",
- "",
- "This flag type has several additional operations that can be used",
- "compared to normal enum types. These are applied to instances of",
- "this enum directly, or can be used as a class method by passing the",
- "flag as the first parameter.",
- "",
- f" - `def split() -> typing.Sequence[{cls.__name__}]: ...`
",
- " Will split the combined flag up into individual atomic flags and",
- " return them in a `typing.Sequence`.",
- "```py",
- ">>> (FOO | BAR | BAZ).split()",
- "[FOO, BAR, BAZ]",
- "```",
- "",
- f" - `def has_any(*flags: {cls.__name__}) -> bool: ...`
",
- " Returns `builtins.True` if any of the given flags are present",
- " in the combined flag this is applied to. Otherwise, returns",
- " `builtins.False` instead.",
- "",
- f" - `def has_all(*flags: {cls.__name__}) -> bool: ...`
",
- " Returns `builtins.True` if all of the given flags are present",
- " in the combined flag this is applied to. Otherwise, returns",
- " `builtins.False` instead.",
- "",
- f" - `def has_none(*flags: {cls.__name__}) -> bool: ...`
",
- " Returns `builtins.True` if none of the given flags are present",
- " in the combined flag this is applied to. Otherwise, returns",
- " `builtins.False` instead.",
- "",
- "In addition, new operators are overloaded. `+` will combine flags",
- "in the same way `|` would usually, and `-` will remove specific ",
- "flags from this instance. This is equivalent to using the `&` " "operator.",
- "",
- "Finally, combined flag types can be iterated across as if they",
- "were a collection.",
- "```py",
- ">>> for f in FOO | BAR | BAZ:",
- "... print(f)",
- "FOO",
- "BAR",
- "BAZ",
- "```",
- )
- )
- cls.__doc__ = doc
-
- def split(self: FlagT) -> typing.Sequence[FlagT]:
- """Return a list of all atomic values for this flag."""
- members: typing.List[FlagT] = []
-
- for member in type(self).__members__.values():
- # Don't show `NONE` values, it breaks stuff and makes no sense here.
- if not member.value:
- continue
-
- # If it is not a combined value, and it is contained in the bitfield:
- if math.log2(member.value).is_integer() and member & self:
- members.append(member)
-
- return sorted(members, key=lambda m: m.name)
-
- def has_any(self, *flags: FlagT) -> bool:
- return any((flag & self) == flag for flag in flags)
-
- def has_all(self, *flags: FlagT) -> bool:
- return all((flag & self) == flag for flag in flags)
-
- def has_none(self, *flags: FlagT) -> bool:
- return not self.has_any(*flags)
-
- def __add__(self: FlagT, other: typing.Union[int, FlagT]) -> FlagT:
- return type(self)(self | other)
-
- __radd__ = __add__
-
- def __sub__(self: FlagT, other: typing.Union[int, FlagT]) -> FlagT:
- return type(self)(self & ~other)
-
- def __str__(self) -> str:
- if hasattr(self, "name") and self.name is not None:
- return self.name
- return " | ".join(m.name for m in self.split())
-
- def __iter__(self: FlagT) -> typing.Iterator[FlagT]:
- return iter(self.split())
diff --git a/hikari/messages.py b/hikari/messages.py
index 5a635bffa7..6906e2a3bc 100644
--- a/hikari/messages.py
+++ b/hikari/messages.py
@@ -35,7 +35,6 @@
"Message",
]
-import enum
import typing
import attr
@@ -46,7 +45,6 @@
from hikari import urls
from hikari.internal import attr_extensions
from hikari.internal import enums
-from hikari.internal import flag
if typing.TYPE_CHECKING:
import datetime
@@ -107,9 +105,8 @@ def __str__(self) -> str:
return self.name
-@enum.unique
@typing.final
-class MessageFlag(flag.Flag):
+class MessageFlag(enums.Flag):
"""Additional flags for message options."""
NONE = 0
@@ -353,31 +350,15 @@ class PartialMessage(snowflakes.Unique):
message_reference: undefined.UndefinedNoneOr[MessageCrosspost] = attr.ib(repr=False)
"""The message's cross-posted reference data."""
+ flags: undefined.UndefinedNoneOr[MessageFlag] = attr.ib(repr=False)
+ """The message's flags."""
+
nonce: undefined.UndefinedNoneOr[str] = attr.ib(repr=False)
"""The message nonce.
This is a string used for validating a message was sent.
"""
- # Flags are lazily loaded, due to the IntFlag mechanism being overly slow
- # to execute.
- _flags: undefined.UndefinedNoneOr[int] = attr.ib(repr=False)
-
- @property
- def flags(self) -> undefined.UndefinedNoneOr[MessageFlag]:
- """Return flags for thge message if known.
-
- If no flags are set, this returns `builtins.None`.
-
- If unknown, this returns `hikari.undefined.UNDEFINED`
-
- Returns
- -------
- hikari.undefined.UndefinedNoneOr[MessageFlag]
- The message flags, if known and set.
- """
- return MessageFlag(self._flags) if isinstance(self._flags, int) else self._flags
-
@property
def link(self) -> str:
"""Jump link to the message.
@@ -914,9 +895,8 @@ class Message(PartialMessage):
message_reference: typing.Optional[MessageCrosspost]
"""The message crossposted reference data."""
- nonce: typing.Optional[str]
- """The message nonce. This is a string used for validating a message was sent."""
-
- _flags = typing.Optional[int]
flags: typing.Optional[MessageFlag]
"""The message flags."""
+
+ nonce: typing.Optional[str]
+ """The message nonce. This is a string used for validating a message was sent."""
diff --git a/hikari/permissions.py b/hikari/permissions.py
index 37d07c2de5..f6ab8509ea 100644
--- a/hikari/permissions.py
+++ b/hikari/permissions.py
@@ -25,15 +25,13 @@
__all__: typing.List[str] = ["Permissions"]
-import enum
import typing
-from hikari.internal import flag
+from hikari.internal import enums
-@enum.unique
@typing.final
-class Permissions(flag.Flag):
+class Permissions(enums.Flag):
"""Represents the permissions available in a given channel or guild.
This enum is an `enum.IntFlag`. This means that you can **combine multiple
diff --git a/hikari/presences.py b/hikari/presences.py
index a0f038529b..49717d6c7d 100644
--- a/hikari/presences.py
+++ b/hikari/presences.py
@@ -37,7 +37,6 @@
"Status",
]
-import enum
import typing
import attr
@@ -45,7 +44,6 @@
from hikari import snowflakes
from hikari.internal import attr_extensions
from hikari.internal import enums
-from hikari.internal import flag
if typing.TYPE_CHECKING:
import datetime
@@ -160,9 +158,8 @@ class ActivitySecret:
"""The secret used for matching a party, if applicable."""
-@enum.unique
@typing.final
-class ActivityFlag(flag.Flag):
+class ActivityFlag(enums.Flag):
"""Flags that describe what an activity includes.
This can be more than one using bitwise-combinations.
@@ -242,20 +239,8 @@ class RichActivity(Activity):
is_instance: typing.Optional[bool] = attr.ib(repr=False)
"""Whether this activity is an instanced game session."""
- # Flags are lazily loaded, due to the IntFlag mechanism being overly slow
- # to execute.
- _flags: typing.Optional[int] = attr.ib(repr=False)
-
- @property
- def flags(self) -> typing.Optional[ActivityFlag]:
- """Return flags describing the activity type.
-
- Returns
- -------
- typing.Optional[ActivityFlag]
- Flags, if present, otherwise `builtins.None`.
- """
- return ActivityFlag(self._flags) if self._flags is not None else None
+ flags: typing.Optional[ActivityFlag] = attr.ib(repr=False)
+ """Flags that describe what the activity includes, if present."""
@typing.final
diff --git a/hikari/users.py b/hikari/users.py
index dda20d8cd0..e1bfb24795 100644
--- a/hikari/users.py
+++ b/hikari/users.py
@@ -26,7 +26,6 @@
__all__: typing.List[str] = ["PartialUser", "User", "OwnUser", "UserFlag", "PremiumType"]
import abc
-import enum
import typing
import attr
@@ -37,16 +36,14 @@
from hikari import urls
from hikari.internal import attr_extensions
from hikari.internal import enums
-from hikari.internal import flag
from hikari.internal import routes
if typing.TYPE_CHECKING:
from hikari import traits
-@enum.unique
@typing.final
-class UserFlag(flag.Flag):
+class UserFlag(enums.Flag):
"""The known user flags that represent account badges."""
NONE = 0
@@ -355,12 +352,8 @@ class PartialUserImpl(PartialUser):
is_system: undefined.UndefinedOr[bool] = attr.ib(eq=False, hash=False, repr=False)
"""Whether this user is a system account."""
- _flags: undefined.UndefinedOr[int] = attr.ib(eq=False, hash=False)
-
- @property
- def flags(self) -> undefined.UndefinedOr[UserFlag]:
- """Public flags for this user."""
- return UserFlag(self._flags) if self._flags is not undefined.UNDEFINED else undefined.UNDEFINED
+ flags: undefined.UndefinedOr[UserFlag] = attr.ib(eq=False, hash=False)
+ """Public flags for this user."""
@property
def mention(self) -> str:
@@ -413,7 +406,6 @@ class UserImpl(PartialUserImpl, User):
is_system: bool
"""`builtins.True` if this user is a system account, `builtins.False` otherwise."""
- _flags: int
flags: UserFlag
"""The public flags for this user."""
diff --git a/scripts/enum_benchmark.py b/scripts/enum_benchmark.py
index ef2b6b55dd..8e562d8233 100644
--- a/scripts/enum_benchmark.py
+++ b/scripts/enum_benchmark.py
@@ -94,7 +94,7 @@ class BasicHikariEnum(str, hikari_enum.Enum):
"BasicPyEnum._value2member_map_['25']", number=1_000_000, globals=globals()
)
hikari_enum_delegate_to_map_time = timeit.timeit(
- "BasicHikariEnum._value2member_map_['25']", number=1_000_000, globals=globals()
+ "BasicHikariEnum._value_to_member_map_['25']", number=1_000_000, globals=globals()
)
py_enum_getitem_time = timeit.timeit("BasicPyEnum['z']", number=1_000_000, globals=globals())
hikari_enum_getitem_time = timeit.timeit("BasicHikariEnum['z']", number=1_000_000, globals=globals())
@@ -102,7 +102,7 @@ class BasicHikariEnum(str, hikari_enum.Enum):
print("BasicPyEnum.__call__('25')", py_enum_call_time, "µs")
print("BasicHikariEnum.__call__('25')", hikari_enum_call_time, "µs")
print("BasicPyEnum._value2member_map_['25']", py_enum_delegate_to_map_time, "µs")
-print("BasicHikariEnum._value2member_map['25']", hikari_enum_delegate_to_map_time, "µs")
+print("BasicHikariEnum._value_to_member_map['25']", hikari_enum_delegate_to_map_time, "µs")
print("BasicPyEnum.__getitem__['z']", py_enum_getitem_time, "µs")
print("BasicHikariEnum.__getitem__['z']", hikari_enum_getitem_time, "µs")
diff --git a/scripts/flag_benchmark.py b/scripts/flag_benchmark.py
new file mode 100644
index 0000000000..ad71bc1e29
--- /dev/null
+++ b/scripts/flag_benchmark.py
@@ -0,0 +1,150 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020 Nekokatt
+#
+# 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.
+
+import enum as py_enum
+import os
+import sys
+import timeit
+
+from hikari.internal import enums as hikari_enum
+
+PyIntFlag = None
+HikariIntFlag = None
+
+
+def build_enums():
+ global PyIntFlag
+ global HikariIntFlag
+
+ class PyIntFlag(py_enum.IntFlag):
+ a = 1
+ b = 2
+ c = 4
+ d = 8
+ e = 16
+ f = 32
+ g = 64
+ ab = 3
+ cde = 28
+
+ class HikariIntFlag(hikari_enum.Flag):
+ a = 1
+ b = 2
+ c = 4
+ d = 8
+ e = 16
+ f = 32
+ g = 64
+ ab = 3
+ cde = 28
+
+ # we do this otherwise we never gc the old class objects before a new
+ # run, which takes up more and more RAM until none is left. Not ideal but
+ # hopefully given how nothing else is running in the process, this count
+ # will eventually be the same roughly on each iteration and be roughly the
+ # same for each average collected offset.
+ # gc.enable()
+ # gc.collect(0)
+ # gc.collect(1)
+ # gc.collect(2)
+ # gc.disable()
+
+
+if os.name != "nt":
+ print("Making highest priority, os.SCHED_RR")
+ try:
+ pid = os.getpid()
+ niceValue = os.nice(-20)
+ sys.setswitchinterval(0.5)
+ print("sys.getswitchinterval", sys.getswitchinterval())
+ os.sched_setaffinity(pid, [(os.cpu_count() or 1) - 1])
+ os.sched_setscheduler(pid, os.SCHED_RR, os.sched_param(1))
+ print("sched_getscheduler", os.sched_getscheduler(pid))
+ print("sched_getparam", os.sched_getparam(pid))
+ print("sched_getaffinity", os.sched_getaffinity(pid))
+ print("sched_getprioritymax", os.sched_get_priority_max(0))
+ print("sched_getprioritymin", os.sched_get_priority_min(0))
+ print("sched_rr_getinterval", os.sched_rr_get_interval(pid))
+ print("nice", os.nice(0))
+ except PermissionError:
+ print("run as root to make top OS priority for more accurate results.")
+else:
+ print("lol windows good luck")
+
+for i in range(5):
+ print("pass", i + 1)
+
+ for j in range(1_000_000):
+ if sum(j for j in range(10)) < 0:
+ raise RuntimeError
+
+ py_intflag_call_time_member = timeit.timeit(
+ setup="build_enums()", stmt="PyIntFlag(4)", number=10_000_000, globals=globals()
+ )
+ hikari_intflag_call_time_member = timeit.timeit(
+ setup="build_enums()", stmt="HikariIntFlag(4)", number=10_000_000, globals=globals()
+ )
+
+ for j in range(1_000_000):
+ if sum(j for j in range(10)) < 0:
+ raise RuntimeError
+
+ py_intflag_call_time_existing_composite = (
+ timeit.timeit(stmt="PyIntFlag(71)", number=10_000_000, globals=globals()) / 10
+ )
+ hikari_intflag_call_time_existing_composite = (
+ timeit.timeit(stmt="HikariIntFlag(71)", number=10_000_000, globals=globals()) / 10
+ )
+
+ for j in range(1_000_000):
+ if sum(j for j in range(10)) < 0:
+ raise RuntimeError
+
+ build_enums_time = timeit.timeit(stmt="build_enums()", number=10_000, globals=globals())
+ py_intflag_call_time_new_composite = timeit.timeit(
+ stmt="build_enums(); PyIntFlag(71)", number=10_000, globals=globals()
+ )
+ build_enums_time = min(timeit.timeit(stmt="build_enums()", number=10_000, globals=globals()), build_enums_time)
+ py_intflag_call_time_new_composite -= build_enums_time
+ py_intflag_call_time_new_composite *= 100
+
+ for j in range(1_000_000):
+ if sum(j for j in range(10)) < 0:
+ raise RuntimeError
+
+ build_enums_time = timeit.timeit(stmt="build_enums()", number=10_000, globals=globals())
+ hikari_intflag_call_time_new_composite = timeit.timeit(
+ stmt="build_enums(); HikariIntFlag(71)", number=10_000, globals=globals()
+ )
+ build_enums_time = min(timeit.timeit(stmt="build_enums()", number=10_000, globals=globals()), build_enums_time)
+ hikari_intflag_call_time_new_composite -= build_enums_time
+ hikari_intflag_call_time_new_composite *= 100
+
+ print("PyIntFlag.__call__(4) (existing member)", py_intflag_call_time_member, "µs")
+ print("HikariIntFlag.__call__(4) (existing member)", hikari_intflag_call_time_member, "µs")
+
+ print("PyIntFlag.__call__(71) (new composite member)", py_intflag_call_time_new_composite, "µs")
+ print("HikariIntFlag.__call__(71) (new composite member)", hikari_intflag_call_time_new_composite, "µs")
+
+ print("PyIntFlag.__call__(71) (existing composite member)", py_intflag_call_time_existing_composite, "µs")
+ print("HikariIntFlag.__call__(71) (existing composite member)", hikari_intflag_call_time_existing_composite, "µs")
+
+ print()
diff --git a/tests/hikari/internal/test_enums.py b/tests/hikari/internal/test_enums.py
index 94eb77ce7c..4eed26eaaf 100644
--- a/tests/hikari/internal/test_enums.py
+++ b/tests/hikari/internal/test_enums.py
@@ -132,7 +132,7 @@ def foo(self):
assert isinstance(Enum.foo, property)
- def test_init_enum_type_maps_names_in___members__(self):
+ def test_init_enum_type_maps_names_in_members(self):
class Enum(int, enums.Enum):
foo = 9
bar = 18
@@ -160,7 +160,7 @@ def p(self):
assert Enum.__members__ == {"foo": 9, "bar": 18, "baz": 27}
- def test___call___when_member(self):
+ def test_call_when_member(self):
class Enum(int, enums.Enum):
foo = 9
bar = 18
@@ -170,7 +170,7 @@ class Enum(int, enums.Enum):
assert returned == Enum.foo
assert type(returned) == Enum
- def test___call___when_not_member(self):
+ def test_call_when_not_member(self):
class Enum(int, enums.Enum):
foo = 9
bar = 18
@@ -180,7 +180,7 @@ class Enum(int, enums.Enum):
assert returned == 69
assert type(returned) != Enum
- def test___getitem__(self):
+ def test_getitem(self):
class Enum(int, enums.Enum):
foo = 9
bar = 18
@@ -189,3 +189,902 @@ class Enum(int, enums.Enum):
returned = Enum["foo"]
assert returned == Enum.foo
assert type(returned) == Enum
+
+
+class TestIntFlag:
+ @mock.patch.object(enums, "_Flag", new=NotImplemented)
+ def test_init_first_flag_type_populates_Flag(self):
+ class Flag(metaclass=enums._FlagMeta):
+ a = 1
+
+ assert enums._Flag is Flag
+
+ @mock.patch.object(enums, "_Flag", new=NotImplemented)
+ def test_init_first_flag_type_with_wrong_name_and_no_bases_raises_TypeError(self):
+ with pytest.raises(TypeError):
+
+ class Potato(metaclass=enums._FlagMeta):
+ a = 1
+
+ assert enums._Flag is NotImplemented
+
+ def test_init_second_flag_type_with_no_bases_does_not_change_Flag_attribute_and_raises_TypeError(self):
+ expect = enums._Flag
+
+ with pytest.raises(TypeError):
+
+ class Flag(metaclass=enums._FlagMeta):
+ a = 1
+
+ assert enums._Flag is expect
+
+ def test_init_flag_type_default_docstring_set(self):
+ class Flag(enums.Flag):
+ a = 1
+
+ assert Flag.__doc__ == "An enumeration."
+
+ def test_init_flag_type_disallows_objects_that_are_not_instances_int(self):
+ with pytest.raises(TypeError):
+
+ class Flag(enums.Flag):
+ a = 1
+ foo = "hi"
+
+ def test_init_flag_type_disallows_other_bases(self):
+ with pytest.raises(TypeError):
+
+ class Flag(float, enums.Flag):
+ a = 1
+
+ def test_init_flag_type_allows_any_object_if_it_has_a_dunder_name(self):
+ class Flag(enums.Flag):
+ __foo__ = 1
+ __bar = 2
+ a = 3
+
+ assert Flag is not None
+
+ def test_init_flag_type_allows_any_object_if_it_has_a_sunder_name(self):
+ class Flag(enums.Flag):
+ _foo_ = 1
+ _bar = 2
+ a = 3
+
+ assert Flag is not None
+
+ def test_init_flag_type_allows_methods(self):
+ class Flag(enums.Flag):
+ A = 0x1
+
+ def foo(self):
+ return "foo"
+
+ assert Flag.foo(12) == "foo"
+
+ def test_init_flag_type_allows_classmethods(self):
+ class Flag(enums.Flag):
+ A = 0x1
+
+ @classmethod
+ def foo(cls):
+ assert cls is Flag
+ return "foo"
+
+ assert Flag.foo() == "foo"
+
+ def test_init_flag_type_allows_staticmethods(self):
+ class Flag(enums.Flag):
+ A = 0x1
+
+ @staticmethod
+ def foo():
+ return "foo"
+
+ assert Flag.foo() == "foo"
+
+ def test_init_flag_type_allows_descriptors(self):
+ class Flag(enums.Flag):
+ A = 0x1
+
+ @property
+ def foo(self):
+ return "foo"
+
+ assert isinstance(Flag.foo, property)
+
+ def test_name_to_member_map(self):
+ class Flag(enums.Flag):
+ foo = 9
+ bar = 18
+ baz = 27
+
+ @staticmethod
+ def sm():
+ pass
+
+ @classmethod
+ def cm(cls):
+ pass
+
+ def m(self):
+ pass
+
+ @property
+ def p(self):
+ pass
+
+ assert Flag._name_to_member_map_["foo"].__class__ is Flag
+ assert Flag._name_to_member_map_["foo"] is Flag.foo
+
+ assert Flag._name_to_member_map_["bar"].__class__ is Flag
+ assert Flag._name_to_member_map_["bar"] is Flag.bar
+
+ assert Flag._name_to_member_map_["baz"].__class__ is Flag
+ assert Flag._name_to_member_map_["baz"] is Flag.baz
+
+ assert len(Flag._name_to_member_map_) == 3
+
+ def test_value_to_member_map(self):
+ class Flag(enums.Flag):
+ foo = 9
+ bar = 18
+ baz = 27
+
+ @staticmethod
+ def sm():
+ pass
+
+ @classmethod
+ def cm(cls):
+ pass
+
+ def m(self):
+ pass
+
+ @property
+ def p(self):
+ pass
+
+ assert Flag._value_to_member_map_[9].__class__ is Flag
+ assert Flag._value_to_member_map_[9] is Flag.foo
+
+ assert Flag._value_to_member_map_[18].__class__ is Flag
+ assert Flag._value_to_member_map_[18] is Flag.bar
+
+ assert Flag._value_to_member_map_[27].__class__ is Flag
+ assert Flag._value_to_member_map_[27] is Flag.baz
+
+ def test_member_names(self):
+ class Flag(enums.Flag):
+ foo = 9
+ bar = 18
+ baz = 27
+
+ @staticmethod
+ def sm():
+ pass
+
+ @classmethod
+ def cm(cls):
+ pass
+
+ def m(self):
+ pass
+
+ @property
+ def p(self):
+ pass
+
+ assert Flag._member_names_ == ["foo", "bar", "baz"]
+
+ def test_members(self):
+ class Flag(enums.Flag):
+ foo = 9
+ bar = 18
+ baz = 27
+
+ @staticmethod
+ def sm():
+ pass
+
+ @classmethod
+ def cm(cls):
+ pass
+
+ def m(self):
+ pass
+
+ @property
+ def p(self):
+ pass
+
+ assert Flag.__members__["foo"].__class__ is int
+ assert Flag.__members__["foo"] == 9
+
+ assert Flag.__members__["bar"].__class__ is int
+ assert Flag.__members__["bar"] == 18
+
+ assert Flag.__members__["baz"].__class__ is int
+ assert Flag.__members__["baz"] == 27
+
+ assert len(Flag.__members__) == 3
+
+ def test_call_on_existing_value(self):
+ class Flag(enums.Flag):
+ foo = 9
+ bar = 18
+ baz = 27
+
+ assert Flag(9) is Flag.foo
+ assert Flag(Flag.foo) is Flag.foo
+
+ def test_call_on_composite_value(self):
+ class Flag(enums.Flag):
+ foo = 1
+ bar = 2
+ baz = 4
+
+ assert Flag(3) is Flag.foo | Flag.bar
+ assert Flag(Flag.foo | Flag.bar) is Flag.foo | Flag.bar
+
+ def test_call_on_named_composite_value(self):
+ class Flag(enums.Flag):
+ foo = 1
+ bar = 2
+ baz = 3
+
+ assert Flag(3) is Flag.baz
+ assert Flag(Flag.foo | Flag.bar) is Flag.baz
+
+ def test_call_on_invalid_value(self):
+ class Flag(enums.Flag):
+ foo = 1
+ bar = 2
+ baz = 3
+
+ assert Flag(4) == 4
+
+ def test_cache(self):
+ class Flag(enums.Flag):
+ foo = 1
+ bar = 2
+ baz = 4
+
+ assert Flag._temp_members_ == {}
+ # Cache something. Remember the dict is evaluated before the equality
+ # so this will populate the cache.
+ assert Flag._temp_members_ == {3: Flag.foo | Flag.bar}
+ assert Flag._temp_members_ == {3: Flag.foo | Flag.bar, 7: Flag.foo | Flag.bar | Flag.baz}
+
+ # Shouldn't mutate for existing items.
+ assert Flag._temp_members_ == {3: Flag.foo | Flag.bar, 7: Flag.foo | Flag.bar | Flag.baz}
+ assert Flag._temp_members_ == {3: Flag.foo | Flag.bar, 7: Flag.foo | Flag.bar | Flag.baz}
+
+ def test_bitwise_name(self):
+ class Flag(enums.Flag):
+ foo = 1
+ bar = 2
+ baz = 4
+
+ assert Flag.foo.name == "foo"
+
+ def test_combined_bitwise_name(self):
+ class Flag(enums.Flag):
+ foo = 1
+ bar = 2
+ baz = 4
+
+ assert (Flag.foo | Flag.bar).name == "foo|bar"
+
+ def test_combined_known_bitwise_name(self):
+ class Flag(enums.Flag):
+ foo = 1
+ bar = 2
+ baz = 3
+
+ assert (Flag.foo | Flag.bar).name == "baz"
+
+ def test_combined_partially_known_name(self):
+ class Flag(enums.Flag):
+ doo = 1
+ laa = 2
+ dee = 3
+
+ # This is fine because it is not an identity or exact value.
+ assert (Flag.laa | 4 | Flag.doo).name == "doo|laa|0x4"
+
+ def test_combined_partially_known_combined_bitwise_name(self):
+ class Flag(enums.Flag):
+ foo = 1
+ bar = 2
+ baz = 3
+
+ # This is fine because it is not an identity or exact value.
+ assert (Flag.baz | 4).name == "foo|bar|0x4"
+
+ def test_unknown_name(self):
+ class Flag(enums.Flag):
+ foo = 1
+ bar = 2
+ baz = 3
+
+ assert Flag(4).name == "UNKNOWN 0x4"
+
+ def test_value(self):
+ class Flag(enums.Flag):
+ foo = 1
+ bar = 2
+ baz = 3
+
+ assert Flag.foo.value.__class__ is int
+ assert Flag.foo.value == 1
+
+ assert Flag.bar.value.__class__ is int
+ assert Flag.bar.value == 2
+
+ assert Flag.baz.value.__class__ is int
+ assert Flag.baz.value == 3
+
+ def test_is_instance_of_declaring_type(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+ QUXX = QUX | BORK
+
+ assert isinstance(TestFlag.BORK, TestFlag)
+ assert isinstance(TestFlag.BORK, int)
+
+ assert isinstance(TestFlag.QUXX, TestFlag)
+ assert isinstance(TestFlag.QUXX, int)
+
+ def test_and(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+ QUXX = QUX | BORK
+
+ assert TestFlag.QUXX & TestFlag.QUX == TestFlag.QUX
+ assert TestFlag.QUXX & TestFlag.QUX == 0x8
+ assert TestFlag.QUXX & 0x8 == 0x8
+ assert isinstance(TestFlag.QUXX & TestFlag.QUX, TestFlag)
+ assert isinstance(TestFlag.QUXX & TestFlag.QUX, int)
+
+ def test_rand(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+ QUXX = QUX | BORK
+
+ assert 0x8 & TestFlag.QUXX == TestFlag.QUX
+ assert 0x8 & TestFlag.QUXX == 0x8
+ assert isinstance(0x8 & TestFlag.QUXX, TestFlag)
+ assert isinstance(0x8 & TestFlag.QUXX, int)
+
+ def test_all_positive_case(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+
+ val = TestFlag.BAZ | TestFlag.BORK
+
+ assert val.all(TestFlag.FOO)
+
+ assert val.all(TestFlag.FOO, TestFlag.BAR, TestFlag.BAZ, TestFlag.BORK)
+
+ def test_all_negative_case(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+ QUXX = 0x10
+
+ val = TestFlag.BAZ | TestFlag.BORK
+
+ assert not val.all(TestFlag.QUX)
+ assert not val.all(TestFlag.BAZ, TestFlag.QUX, TestFlag.QUXX)
+
+ def test_any_positive_case(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+
+ val = TestFlag.BAZ | TestFlag.BORK
+
+ assert val.any(TestFlag.FOO)
+ assert val.any(TestFlag.BAR)
+ assert val.any(TestFlag.BAZ)
+ assert val.any(TestFlag.BORK)
+ # All present
+ assert val.any(TestFlag.FOO, TestFlag.BAR, TestFlag.BAZ, TestFlag.BORK)
+ # One present, one not
+ assert val.any(
+ TestFlag.FOO,
+ TestFlag.QUX,
+ )
+
+ def test_any_negative_case(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+ QUXX = 0x10
+
+ val = TestFlag.BAZ | TestFlag.BORK
+
+ assert not val.any(TestFlag.QUX)
+
+ def test_bool(self):
+ class TestFlag(enums.Flag):
+ BLEH = 0x0
+ FOO = 0x1
+ BAR = 0x2
+
+ assert not TestFlag.BLEH
+ assert TestFlag.FOO
+ assert TestFlag.BAR
+
+ def test_contains(self):
+ class TestFlag(enums.Flag):
+ BLEH = 0x1
+ FOO = 0x2
+ BAR = 0x4
+ BAZ = 0x8
+
+ f = TestFlag.FOO | TestFlag.BLEH | TestFlag.BAZ
+ assert TestFlag.FOO in f
+ assert TestFlag.BLEH in f
+ assert TestFlag.BAZ in f
+ assert TestFlag.BAR not in f
+
+ def test_difference(self):
+ class TestFlag(enums.Flag):
+ A = 0x1
+ B = 0x2
+ C = 0x4
+ D = 0x8
+ E = 0x10
+ F = 0x20
+ G = 0x40
+ H = 0x80
+
+ a = TestFlag.A | TestFlag.B | TestFlag.D | TestFlag.F | TestFlag.G
+ b = TestFlag.A | TestFlag.B | TestFlag.E
+ c = 0x13
+ expect_asubb = TestFlag.D | TestFlag.F | TestFlag.G
+ expect_bsuba = TestFlag.E
+ expect_asubc = 0x68
+
+ assert a.difference(b) == expect_asubb
+ assert b.difference(a) == expect_bsuba
+ assert a.difference(c) == expect_asubc
+
+ assert isinstance(a.difference(b), int)
+ assert isinstance(a.difference(b), TestFlag)
+ assert isinstance(a.difference(c), int)
+ assert isinstance(a.difference(c), TestFlag)
+
+ def test_int(self):
+ class TestFlag(enums.Flag):
+ BLEH = 0x0
+ FOO = 0x1
+ BAR = 0x2
+
+ assert int(TestFlag.BLEH) == 0x0
+ assert int(TestFlag.FOO) == 0x1
+ assert int(TestFlag.BAR) == 0x2
+
+ assert type(int(TestFlag.BAR)) is int
+
+ def test_intersection(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+ QUXX = QUX | BORK
+
+ assert TestFlag.QUXX.intersection(TestFlag.QUX) == TestFlag.QUX
+ assert TestFlag.QUXX.intersection(TestFlag.QUX) == 0x8
+ assert TestFlag.QUXX.intersection(0x8) == 0x8
+ assert isinstance(TestFlag.QUXX.intersection(TestFlag.QUX), TestFlag)
+ assert isinstance(TestFlag.QUXX.intersection(TestFlag.QUX), int)
+
+ def test_invert(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x4
+
+ assert TestFlag.BAR.invert() == TestFlag.FOO | TestFlag.BAZ
+
+ def test_invert_op(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x4
+
+ assert ~TestFlag.BAR == TestFlag.FOO | TestFlag.BAZ
+
+ def test_is_disjoint(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x4
+ BORK = 0x8
+ QUX = 0x10
+
+ assert (TestFlag.FOO | TestFlag.BAR).is_disjoint(TestFlag.BAZ | TestFlag.BORK)
+ assert not (TestFlag.FOO | TestFlag.BAR).is_disjoint(TestFlag.BAR | TestFlag.BORK)
+ assert (TestFlag.FOO | TestFlag.BAR).is_disjoint(0xC)
+ assert not (TestFlag.FOO | TestFlag.BAR).is_disjoint(0xA)
+
+ def test_isdisjoint(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x4
+ BORK = 0x8
+ QUX = 0x10
+
+ assert (TestFlag.FOO | TestFlag.BAR).isdisjoint(TestFlag.BAZ | TestFlag.BORK)
+ assert not (TestFlag.FOO | TestFlag.BAR).isdisjoint(TestFlag.BAR | TestFlag.BORK)
+ assert (TestFlag.FOO | TestFlag.BAR).isdisjoint(0xC)
+ assert not (TestFlag.FOO | TestFlag.BAR).isdisjoint(0xA)
+
+ def test_is_subset(self):
+ class TestFlag(enums.Flag):
+ BLEH = 0x1
+ FOO = 0x2
+ BAR = 0x4
+ BAZ = 0x8
+ BORK = 0x10
+
+ f = TestFlag.FOO | TestFlag.BLEH | TestFlag.BAZ
+ assert f.is_subset(TestFlag.FOO)
+ assert f.is_subset(TestFlag.BLEH)
+ assert f.is_subset(TestFlag.BAZ)
+ assert not f.is_subset(TestFlag.BAR)
+ assert f.is_subset(0x2)
+ assert f.is_subset(0x1)
+ assert f.is_subset(0x8)
+ assert not f.is_subset(0x4)
+ assert f.is_subset(TestFlag.FOO | TestFlag.BLEH)
+ assert f.is_subset(0x3)
+ assert f.is_subset(TestFlag.FOO | TestFlag.BLEH)
+ assert not f.is_subset(TestFlag.BAR | TestFlag.BORK)
+ assert not f.is_subset(0x14)
+
+ def test_issubset(self):
+ class TestFlag(enums.Flag):
+ BLEH = 0x1
+ FOO = 0x2
+ BAR = 0x4
+ BAZ = 0x8
+ BORK = 0x10
+
+ f = TestFlag.FOO | TestFlag.BLEH | TestFlag.BAZ
+ assert f.issubset(TestFlag.FOO)
+ assert f.issubset(TestFlag.BLEH)
+ assert f.issubset(TestFlag.BAZ)
+ assert not f.issubset(TestFlag.BAR)
+ assert f.issubset(0x2)
+ assert f.issubset(0x1)
+ assert f.issubset(0x8)
+ assert not f.issubset(0x4)
+ assert f.issubset(TestFlag.FOO | TestFlag.BLEH)
+ assert f.issubset(0x3)
+ assert not f.issubset(TestFlag.BAR | TestFlag.BORK)
+ assert not f.issubset(0x14)
+
+ def test_is_superset(self):
+ class TestFlag(enums.Flag):
+ BLEH = 0x1
+ FOO = 0x2
+ BAR = 0x4
+ BAZ = 0x8
+ BORK = 0x10
+ QUX = 0x10
+
+ f = TestFlag.FOO | TestFlag.BLEH | TestFlag.BAZ
+
+ assert f.is_superset(TestFlag.BLEH | TestFlag.FOO | TestFlag.BAR | TestFlag.BAZ | TestFlag.BORK)
+ assert f.is_superset(0x1F)
+ assert not f.is_superset(TestFlag.QUX)
+ assert not f.is_superset(0x20)
+
+ def test_issuperset(self):
+ class TestFlag(enums.Flag):
+ BLEH = 0x1
+ FOO = 0x2
+ BAR = 0x4
+ BAZ = 0x8
+ BORK = 0x10
+ QUX = 0x10
+
+ f = TestFlag.FOO | TestFlag.BLEH | TestFlag.BAZ
+
+ assert f.issuperset(TestFlag.BLEH | TestFlag.FOO | TestFlag.BAR | TestFlag.BAZ | TestFlag.BORK)
+ assert f.issuperset(0x1F)
+ assert not f.issuperset(TestFlag.QUX)
+ assert not f.issuperset(0x20)
+
+ def test_iter(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+
+ val = TestFlag.BAZ | TestFlag.BORK
+ val_iter = iter(val)
+ assert next(val_iter) == TestFlag.BAR
+ assert next(val_iter) == TestFlag.BORK
+ assert next(val_iter) == TestFlag.FOO
+ with pytest.raises(StopIteration):
+ next(val_iter)
+
+ def test_len(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+
+ val0 = TestFlag(0)
+ val1 = TestFlag.FOO
+ val2 = TestFlag.FOO | TestFlag.BORK
+ val3 = TestFlag.FOO | TestFlag.BAR | TestFlag.BORK
+ val3_comb = TestFlag.BAZ | TestFlag.BORK
+
+ assert len(val0) == 0
+ assert len(val1) == 1
+ assert len(val2) == 2
+ assert len(val3) == 3
+ assert len(val3_comb) == 3
+
+ def test_or(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x4
+ BORK = 0x8
+ QUX = 0x10
+
+ assert isinstance(TestFlag.FOO | TestFlag.BAR, int)
+ assert isinstance(TestFlag.FOO | TestFlag.BAR, TestFlag)
+ assert isinstance(TestFlag.FOO | 0x2, int)
+ assert isinstance(TestFlag.FOO | 0x2, TestFlag)
+
+ assert TestFlag.FOO | TestFlag.BAR == 0x3
+ assert TestFlag.FOO | TestFlag.BAR == TestFlag(0x3)
+ assert TestFlag.FOO | 0x2 == 0x3
+ assert TestFlag.FOO | 0x2 == TestFlag(0x3)
+
+ def test_ror(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x4
+ BORK = 0x8
+ QUX = 0x10
+
+ assert isinstance(0x2 | TestFlag.FOO, int)
+ assert isinstance(0x2 | TestFlag.FOO, TestFlag)
+
+ assert 0x2 | TestFlag.FOO == 0x3
+ assert 0x2 | TestFlag.FOO == TestFlag(0x3)
+
+ def test_none_positive_case(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+ QUXX = 0x10
+
+ val = TestFlag.BAZ | TestFlag.BORK
+
+ assert val.none(TestFlag.QUX)
+
+ def test_none_negative_case(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+
+ val = TestFlag.BAZ | TestFlag.BORK
+
+ assert not val.none(TestFlag.FOO)
+ assert not val.none(TestFlag.BAR)
+ assert not val.none(TestFlag.BAZ)
+ assert not val.none(TestFlag.BORK)
+ # All present
+ assert not val.none(TestFlag.FOO, TestFlag.BAR, TestFlag.BAZ, TestFlag.BORK)
+ # One present, one not
+ assert not val.none(
+ TestFlag.FOO,
+ TestFlag.QUX,
+ )
+
+ def test_split(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+
+ val = TestFlag.BAZ | TestFlag.BORK
+
+ # Baz is a combined field technically, so we don't expect it to be output here
+ assert val.split() == [TestFlag.BAR, TestFlag.BORK, TestFlag.FOO]
+
+ def test_str_operator(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x3
+ BORK = 0x4
+ QUX = 0x8
+
+ val = TestFlag.BAZ | TestFlag.BORK
+
+ assert str(val) == "FOO|BAR|BORK"
+
+ def test_symmetric_difference(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x4
+ BORK = 0x8
+ QUX = 0x10
+
+ a = TestFlag.FOO | TestFlag.BAR | TestFlag.BAZ
+ b = TestFlag.BAZ | TestFlag.BORK | TestFlag.QUX
+
+ assert isinstance(a.symmetric_difference(b), int)
+ assert isinstance(a.symmetric_difference(b), TestFlag)
+ assert isinstance(a.symmetric_difference(0x1C), int)
+ assert isinstance(a.symmetric_difference(0x1C), TestFlag)
+
+ assert a.symmetric_difference(b) == b.symmetric_difference(a)
+ assert a.symmetric_difference(a) == 0
+ assert b.symmetric_difference(b) == 0
+
+ assert a.symmetric_difference(b) == TestFlag.FOO | TestFlag.BAR | TestFlag.BORK | TestFlag.QUX
+ assert a.symmetric_difference(b) == 0x1B
+
+ def test_sub(self):
+ class TestFlag(enums.Flag):
+ A = 0x1
+ B = 0x2
+ C = 0x4
+ D = 0x8
+ E = 0x10
+ F = 0x20
+ G = 0x40
+ H = 0x80
+
+ a = TestFlag.A | TestFlag.B | TestFlag.D | TestFlag.F | TestFlag.G
+ b = TestFlag.A | TestFlag.B | TestFlag.E
+ c = 0x13
+ expect_asubb = TestFlag.D | TestFlag.F | TestFlag.G
+ expect_bsuba = TestFlag.E
+ expect_asubc = 0x68
+
+ assert a - b == expect_asubb
+ assert b - a == expect_bsuba
+ assert a - c == expect_asubc
+
+ assert isinstance(a - b, int)
+ assert isinstance(a - b, TestFlag)
+ assert isinstance(a - c, int)
+ assert isinstance(a - c, TestFlag)
+
+ def test_rsub(self):
+ class TestFlag(enums.Flag):
+ A = 0x1
+ B = 0x2
+ C = 0x4
+ D = 0x8
+ E = 0x10
+ F = 0x20
+ G = 0x40
+ H = 0x80
+
+ a = TestFlag.A | TestFlag.B | TestFlag.D | TestFlag.F | TestFlag.G
+ c = 0x13
+ expect_csuba = 0x10
+
+ assert c - a == expect_csuba
+
+ assert isinstance(c - a, int)
+ assert isinstance(c - a, TestFlag)
+
+ def test_union(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x4
+ BORK = 0x8
+ QUX = 0x10
+
+ assert isinstance(TestFlag.FOO.union(TestFlag.BAR), int)
+ assert isinstance(TestFlag.FOO.union(TestFlag.BAR), TestFlag)
+ assert isinstance(TestFlag.FOO.union(TestFlag.BAR).union(TestFlag.BAZ), int)
+ assert isinstance(TestFlag.FOO.union(TestFlag.BAR).union(TestFlag.BAZ), TestFlag)
+ assert isinstance(TestFlag.FOO.union(0x2).union(TestFlag.BAZ), int)
+ assert isinstance(TestFlag.FOO.union(0x2).union(TestFlag.BAZ), TestFlag)
+ assert isinstance(TestFlag.FOO.union(0x2), int)
+ assert isinstance(TestFlag.FOO.union(0x2), TestFlag)
+
+ assert TestFlag.FOO.union(TestFlag.BAR) == 0x3
+ assert TestFlag.FOO.union(TestFlag.BAR) == TestFlag(0x3)
+ assert TestFlag.FOO.union(0x2) == 0x3
+ assert TestFlag.FOO.union(0x2) == TestFlag(0x3)
+
+ def test_xor(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x4
+ BORK = 0x8
+ QUX = 0x10
+
+ a = TestFlag.FOO | TestFlag.BAR | TestFlag.BAZ
+ b = TestFlag.BAZ | TestFlag.BORK | TestFlag.QUX
+
+ assert isinstance(a ^ b, int)
+ assert isinstance(a ^ b, TestFlag)
+ assert isinstance(a ^ 0x1C, int)
+ assert isinstance(a ^ 0x1C, TestFlag)
+
+ assert a ^ b == b ^ a
+ assert a ^ a == 0
+ assert b ^ b == 0
+
+ assert a ^ b == TestFlag.FOO | TestFlag.BAR | TestFlag.BORK | TestFlag.QUX
+ assert a ^ b == 0x1B
+ assert a ^ 0x1C == TestFlag.FOO | TestFlag.BAR | TestFlag.BORK | TestFlag.QUX
+ assert a ^ 0x1C == 0x1B
+
+ def test_rxor(self):
+ class TestFlag(enums.Flag):
+ FOO = 0x1
+ BAR = 0x2
+ BAZ = 0x4
+ BORK = 0x8
+ QUX = 0x10
+
+ a = TestFlag.FOO | TestFlag.BAR | TestFlag.BAZ
+
+ assert isinstance(0x1C ^ a, int)
+ assert isinstance(0x1C ^ a, TestFlag)
+ assert 0x1C ^ a == TestFlag.FOO | TestFlag.BAR | TestFlag.BORK | TestFlag.QUX
+ assert 0x1C ^ a == 0x1B
diff --git a/tests/hikari/internal/test_flag.py b/tests/hikari/internal/test_flag.py
deleted file mode 100644
index c5499c9006..0000000000
--- a/tests/hikari/internal/test_flag.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2020 Nekokatt
-#
-# 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.
-import pytest
-
-from hikari.internal import flag
-
-
-class TestFlag:
- def test_flag_is_IntFlag(self):
- import enum
-
- TestFlagType = flag.Flag("TestFlagType", "a b c")
- assert isinstance(TestFlagType, enum.EnumMeta)
- assert issubclass(TestFlagType, enum.IntFlag)
-
- def test_split(self):
- class TestFlagType(flag.Flag):
- FOO = 0x1
- BAR = 0x2
- BAZ = 0x3
- BORK = 0x4
- QUX = 0x8
-
- val = TestFlagType.BAZ | TestFlagType.BORK
-
- # Baz is a combined field technically, so we don't expect it to be output here
- assert val.split() == [TestFlagType.BAR, TestFlagType.BORK, TestFlagType.FOO]
-
- def test_has_any_positive_case(self):
- class TestFlagType(flag.Flag):
- FOO = 0x1
- BAR = 0x2
- BAZ = 0x3
- BORK = 0x4
- QUX = 0x8
-
- val = TestFlagType.BAZ | TestFlagType.BORK
-
- assert val.has_any(TestFlagType.FOO)
- assert val.has_any(TestFlagType.BAR)
- assert val.has_any(TestFlagType.BAZ)
- assert val.has_any(TestFlagType.BORK)
- # All present
- assert val.has_any(TestFlagType.FOO, TestFlagType.BAR, TestFlagType.BAZ, TestFlagType.BORK)
- # One present, one not
- assert val.has_any(
- TestFlagType.FOO,
- TestFlagType.QUX,
- )
-
- def test_has_any_negative_case(self):
- class TestFlagType(flag.Flag):
- FOO = 0x1
- BAR = 0x2
- BAZ = 0x3
- BORK = 0x4
- QUX = 0x8
- QUXX = 0x10
-
- val = TestFlagType.BAZ | TestFlagType.BORK
-
- assert not val.has_any(TestFlagType.QUX)
-
- def test_has_all_positive_case(self):
- class TestFlagType(flag.Flag):
- FOO = 0x1
- BAR = 0x2
- BAZ = 0x3
- BORK = 0x4
- QUX = 0x8
-
- val = TestFlagType.BAZ | TestFlagType.BORK
-
- assert val.has_all(TestFlagType.FOO)
-
- assert val.has_all(TestFlagType.FOO, TestFlagType.BAR, TestFlagType.BAZ, TestFlagType.BORK)
-
- def test_has_all_negative_case(self):
- class TestFlagType(flag.Flag):
- FOO = 0x1
- BAR = 0x2
- BAZ = 0x3
- BORK = 0x4
- QUX = 0x8
- QUXX = 0x10
-
- val = TestFlagType.BAZ | TestFlagType.BORK
-
- assert not val.has_all(TestFlagType.QUX)
- assert not val.has_all(TestFlagType.BAZ, TestFlagType.QUX, TestFlagType.QUXX)
-
- def test_has_none_positive_case(self):
- class TestFlagType(flag.Flag):
- FOO = 0x1
- BAR = 0x2
- BAZ = 0x3
- BORK = 0x4
- QUX = 0x8
- QUXX = 0x10
-
- val = TestFlagType.BAZ | TestFlagType.BORK
-
- assert val.has_none(TestFlagType.QUX)
-
- def test_has_none_negative_case(self):
- class TestFlagType(flag.Flag):
- FOO = 0x1
- BAR = 0x2
- BAZ = 0x3
- BORK = 0x4
- QUX = 0x8
-
- val = TestFlagType.BAZ | TestFlagType.BORK
-
- assert not val.has_none(TestFlagType.FOO)
- assert not val.has_none(TestFlagType.BAR)
- assert not val.has_none(TestFlagType.BAZ)
- assert not val.has_none(TestFlagType.BORK)
- # All present
- assert not val.has_none(TestFlagType.FOO, TestFlagType.BAR, TestFlagType.BAZ, TestFlagType.BORK)
- # One present, one not
- assert not val.has_none(
- TestFlagType.FOO,
- TestFlagType.QUX,
- )
-
- def test_add_operator(self):
- class TestFlagType(flag.Flag):
- FOO = 0x1
- BAR = 0x2
- BAZ = 0x3
- BORK = 0x4
- QUX = 0x8
-
- assert TestFlagType.BAZ + TestFlagType.BORK == TestFlagType.BAZ | TestFlagType.BORK
- assert TestFlagType.BORK + TestFlagType.BAZ == TestFlagType.BAZ | TestFlagType.BORK
- assert TestFlagType.BAZ + 4 == TestFlagType.BAZ | TestFlagType.BORK
- assert 4 + TestFlagType.BAZ == TestFlagType.BAZ | TestFlagType.BORK
-
- def test_sub_operator(self):
- class TestFlagType(flag.Flag):
- FOO = 0x1
- BAR = 0x2
- BAZ = 0x3
- BORK = 0x4
- QUX = 0x8
-
- val = TestFlagType.BAZ | TestFlagType.BORK
- assert val - TestFlagType.BAZ == TestFlagType.BORK
- assert val - TestFlagType.QUX == val
- assert (TestFlagType.BAZ | TestFlagType.QUX) - val == TestFlagType.QUX
-
- def test_str_operator(self):
- class TestFlagType(flag.Flag):
- FOO = 0x1
- BAR = 0x2
- BAZ = 0x3
- BORK = 0x4
- QUX = 0x8
-
- val = TestFlagType.BAZ | TestFlagType.BORK
-
- assert str(val) == "BAR | BORK | FOO"
-
- def test_iter(self):
- class TestFlagType(flag.Flag):
- FOO = 0x1
- BAR = 0x2
- BAZ = 0x3
- BORK = 0x4
- QUX = 0x8
-
- val = TestFlagType.BAZ | TestFlagType.BORK
- val_iter = iter(val)
- assert next(val_iter) == TestFlagType.BAR
- assert next(val_iter) == TestFlagType.BORK
- assert next(val_iter) == TestFlagType.FOO
- with pytest.raises(StopIteration):
- next(val_iter)
diff --git a/tests/hikari/test_channels.py b/tests/hikari/test_channels.py
index df597f25c6..3e67ea7be8 100644
--- a/tests/hikari/test_channels.py
+++ b/tests/hikari/test_channels.py
@@ -94,8 +94,8 @@ def test_unset(self):
overwrite = channels.PermissionOverwrite(
type=channels.PermissionOverwriteType.MEMBER, id=snowflakes.Snowflake(1234321)
)
- overwrite._allow = permissions.Permissions.CREATE_INSTANT_INVITE
- overwrite._deny = permissions.Permissions.CHANGE_NICKNAME
+ overwrite.allow = permissions.Permissions.CREATE_INSTANT_INVITE
+ overwrite.deny = permissions.Permissions.CHANGE_NICKNAME
assert overwrite.unset == permissions.Permissions(-67108866)
diff --git a/tests/hikari/test_errors.py b/tests/hikari/test_errors.py
index a2c47aa038..59b79cbbef 100644
--- a/tests/hikari/test_errors.py
+++ b/tests/hikari/test_errors.py
@@ -78,4 +78,4 @@ def error(self):
return errors.MissingIntentError(intents.Intents.GUILD_BANS | intents.Intents.GUILD_EMOJIS)
def test_str(self, error):
- assert str(error) == "You are missing the following intent(s): GUILD_BANS | GUILD_EMOJIS"
+ assert str(error) == "You are missing the following intent(s): GUILD_BANS, GUILD_EMOJIS"
diff --git a/tests/hikari/test_permissions.py b/tests/hikari/test_permissions.py
index f5fa51ce07..952b991062 100644
--- a/tests/hikari/test_permissions.py
+++ b/tests/hikari/test_permissions.py
@@ -18,20 +18,3 @@
# 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.
-
-from hikari import permissions
-
-
-def test_Permission_str_operator_on_zero_value():
- permission = permissions.Permissions.NONE
- assert str(permission) == "NONE"
-
-
-def test_Permission_str_operator():
- permission = permissions.Permissions.MANAGE_EMOJIS
- assert str(permission) == "MANAGE_EMOJIS"
-
-
-def test_combined_Permission_str_operator():
- permission = permissions.Permissions.MANAGE_CHANNELS | permissions.Permissions.MANAGE_EMOJIS
- assert str(permission) == "MANAGE_CHANNELS | MANAGE_EMOJIS"