Skip to content

Commit

Permalink
Task/intflag (#265)
Browse files Browse the repository at this point in the history
* Started IntFlagMeta.

* _IntFlagNamespace getitem/setitem/contains operators.

* Initial intflag implementation completed (other than operators).

Average times:

```
Making highest priority, os.SCHED_RR
sys.getswitchinterval 0.5
sched_getscheduler 2
sched_getparam posix.sched_param(sched_priority=1)
sched_getaffinity {7}
sched_getprioritymax 0
sched_getprioritymin 0
sched_rr_getinterval 0.09999999000000001
nice -20
warming up
benchmark starts
PyIntFlag.__call__(4) (existing member) 0.4027703850006219 µs
BasicHikariEnum.__call__(4) (existing member) 0.11296135399970808 µs
PyIntFlag.__call__(71) (new composite member) 18.099853999956395 µs
BasicHikariEnum.__call__(71) (new composite member) 2.48106900107814 µs
PyIntFlag.__call__(71) (existing composite member) 0.39969006100000115 µs
BasicHikariEnum.__call__(71) (existing composite member) 0.26146456899914483 µs
```

* Added disabling of cache for large flag types like permissions which could use gigabytes of memory for each possible combination.

* Adjusted caching strategy for int enums.

* Adjusted caching strategy for int enums.

* Refined IntFlag implementation, which is now a normal Flag and acts like a set.

* Revert "Made flag enums lazily load, speeding up cache and event ops."

This reverts commit aecccc5.

* Fixed multiple flag bugs.

* Flake8

* Fixed edge-case error.

* Tidied up some documentation.

* Finished testing enums.

* Variables now show before methods in docs.

* Fixed #256 -- issues with documentation function anchors.

* PR feedback.

* Fixed a MyPy issue and added an override for symmetricdifference.

* Removed commented out lines from mako docs.
  • Loading branch information
Nekokatt authored Oct 4, 2020
1 parent 55a9673 commit 06908ba
Show file tree
Hide file tree
Showing 23 changed files with 1,800 additions and 591 deletions.
54 changes: 32 additions & 22 deletions docs/documentation.mako
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"<small class='text-muted'><em>{prefix}{QUAL_CONST}</em></small> "
elif is_descriptor:
qual = QUAL_CACHED_PROPERTY if isinstance(descriptor, functools.cached_property) else QUAL_PROPERTY
prefix = f"<small class='text-muted'><em>{prefix}{qual}</em></small> "
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"<small class='text-muted'><em>{prefix}{QUAL_TYPEHINT} </em></small> "
elif all(not c.isalpha() or c.isupper() for c in dobj.name):
prefix = f"<small class='text-muted'><em>{prefix}{QUAL_CONST}</em></small> "
else:
prefix = f"<small class='text-muted'><em>{prefix}{QUAL_VAR}</em></small> "
Expand All @@ -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
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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}"
Expand Down Expand Up @@ -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'<a title="{f.name}" href="{get_url_to_object_maybe_module(f)}" id="{f.refname}">{f.name}</a>'
example_str = qual + f.name + "(" + ", ".join(params) + ")" + return_type
Expand All @@ -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:
Expand Down Expand Up @@ -708,21 +718,21 @@
<div class="sep"></div>
% endif
% if methods:
<h5>Methods</h5>
% if variables:
<h5>Variables and properties</h5>
<dl>
% for m in methods:
${show_func(m)}
% for i in variables:
${show_var(i)}
% endfor
</dl>
<div class="sep"></div>
% endif
% if variables:
<h5>Variables and properties</h5>
% if methods:
<h5>Methods</h5>
<dl>
% for i in variables:
${show_var(i)}
% for m in methods:
${show_func(m)}
% endfor
</dl>
<div class="sep"></div>
Expand Down
39 changes: 13 additions & 26 deletions hikari/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion hikari/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
33 changes: 12 additions & 21 deletions hikari/guilds.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
]

import abc
import enum
import typing

import attr
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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."""
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion hikari/impl/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
11 changes: 7 additions & 4 deletions hikari/impl/entity_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions hikari/impl/event_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions hikari/intents.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion hikari/internal/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading

0 comments on commit 06908ba

Please sign in to comment.