diff --git a/discord/audit_logs.py b/discord/audit_logs.py index faf05f20b..49a5bb5ab 100644 --- a/discord/audit_logs.py +++ b/discord/audit_logs.py @@ -216,7 +216,7 @@ class AuditLogEntry: self._from_data(data) def _from_data(self, data): - self.action = enums.AuditLogAction(data['action_type']) + self.action = enums.try_enum(enums.AuditLogAction, data['action_type']) self.id = int(data['id']) # this key is technically not usually present diff --git a/discord/enums.py b/discord/enums.py index ff5266fab..4c59dbf90 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -32,6 +32,20 @@ __all__ = ['ChannelType', 'MessageType', 'VoiceRegion', 'SpeakingState', 'UserFlags', 'ActivityType', 'HypeSquadHouse', 'NotificationLevel', 'PremiumType', 'UserContentFilter', 'FriendFlags', 'Theme'] +def fast_lookup(cls): + # NOTE: implies hashable + try: + lookup = cls._value2member_map_ + except AttributeError: + lookup = { + member.value: member + for member in cls.__members__ + } + finally: + cls.__fast_value_lookup__ = lookup + return cls + +@fast_lookup class ChannelType(Enum): text = 0 private = 1 @@ -44,6 +58,7 @@ class ChannelType(Enum): def __str__(self): return self.name +@fast_lookup class MessageType(Enum): default = 0 recipient_add = 1 @@ -54,6 +69,7 @@ class MessageType(Enum): pins_add = 6 new_member = 7 +@fast_lookup class VoiceRegion(Enum): us_west = 'us-west' us_east = 'us-east' @@ -78,6 +94,7 @@ class VoiceRegion(Enum): def __str__(self): return self.value +@fast_lookup class SpeakingState(Enum): none = 0 voice = 1 @@ -90,6 +107,7 @@ class SpeakingState(Enum): def __int__(self): return self.value +@fast_lookup class VerificationLevel(Enum): none = 0 low = 1 @@ -102,6 +120,7 @@ class VerificationLevel(Enum): def __str__(self): return self.name +@fast_lookup class ContentFilter(Enum): disabled = 0 no_role = 1 @@ -110,11 +129,13 @@ class ContentFilter(Enum): def __str__(self): return self.name +@fast_lookup class UserContentFilter(Enum): disabled = 0 friends = 1 all_messages = 2 +@fast_lookup class FriendFlags(Enum): noone = 0 mutual_guilds = 1 @@ -122,10 +143,12 @@ class FriendFlags(Enum): guild_and_friends = 3 everyone = 4 +@fast_lookup class Theme(Enum): light = 'light' dark = 'dark' +@fast_lookup class Status(Enum): online = 'online' offline = 'offline' @@ -137,6 +160,7 @@ class Status(Enum): def __str__(self): return self.value +@fast_lookup class DefaultAvatar(Enum): blurple = 0 grey = 1 @@ -148,21 +172,25 @@ class DefaultAvatar(Enum): def __str__(self): return self.name +@fast_lookup class RelationshipType(Enum): friend = 1 blocked = 2 incoming_request = 3 outgoing_request = 4 +@fast_lookup class NotificationLevel(Enum): all_messages = 0 only_mentions = 1 +@fast_lookup class AuditLogActionCategory(Enum): create = 1 delete = 2 update = 3 +@fast_lookup class AuditLogAction(Enum): guild_update = 1 channel_create = 10 @@ -245,6 +273,7 @@ class AuditLogAction(Enum): elif v < 80: return 'message' +@fast_lookup class UserFlags(Enum): staff = 1 partner = 2 @@ -255,6 +284,7 @@ class UserFlags(Enum): hypesquad_balance = 256 early_supporter = 512 +@fast_lookup class ActivityType(Enum): unknown = -1 playing = 0 @@ -262,11 +292,13 @@ class ActivityType(Enum): listening = 2 watching = 3 +@fast_lookup class HypeSquadHouse(Enum): bravery = 1 brilliance = 2 balance = 3 +@fast_lookup class PremiumType(Enum): nitro_classic = 1 nitro = 2 @@ -276,7 +308,28 @@ def try_enum(cls, val): If it fails it returns the value instead. """ + + # For some ungodly reason, `cls(x)` is *really* slow + # For most use cases it's about 750ns per call + # Internally this is dispatched like follows: + # cls(x) + # cls.__new__(cls, x) + # cls._value2member_map[x] + # if above fails ^ + # find it in cls._member_map.items() + + # Accessing the _value2member_map directly gives the biggest + # boost to performance, from 750ns to 130ns + + # Now, the weird thing is that regular dict access is approx 31ns + # So there's a slowdown in the attribute access somewhere in the + # __getattr__ chain that I can't do much about + + # Since this relies on internals the enums have an internal shim + # decorator that defines an alias for my own purposes or creates + # it for me under __fast_value_lookup__ + try: - return cls(val) - except ValueError: + return cls.__fast_value_lookup__[val] + except (KeyError, AttributeError): return val diff --git a/discord/user.py b/discord/user.py index 2b0b52631..b1d34868d 100644 --- a/discord/user.py +++ b/discord/user.py @@ -164,7 +164,7 @@ class BaseUser(_BaseUser): @property def default_avatar(self): """Returns the default avatar for a given user. This is calculated by the user's discriminator""" - return DefaultAvatar(int(self.discriminator) % len(DefaultAvatar)) + return try_enum(DefaultAvatar, int(self.discriminator) % len(DefaultAvatar)) @property def default_avatar_url(self):