Browse Source

Create ClientStatus type to improve Member memory usage

pull/7523/head
Lilly Rose Berner 3 years ago
committed by GitHub
parent
commit
85b6175137
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 68
      discord/member.py
  2. 6
      discord/types/activity.py

68
discord/member.py

@ -55,7 +55,10 @@ if TYPE_CHECKING:
from .channel import DMChannel, VoiceChannel, StageChannel from .channel import DMChannel, VoiceChannel, StageChannel
from .flags import PublicUserFlags from .flags import PublicUserFlags
from .guild import Guild from .guild import Guild
from .types.activity import PartialPresenceUpdate from .types.activity import (
ClientStatus as ClientStatusPayload,
PartialPresenceUpdate,
)
from .types.member import ( from .types.member import (
MemberWithUser as MemberWithUserPayload, MemberWithUser as MemberWithUserPayload,
Member as MemberPayload, Member as MemberPayload,
@ -163,6 +166,46 @@ class VoiceState:
return f'<{self.__class__.__name__} {inner}>' return f'<{self.__class__.__name__} {inner}>'
class _ClientStatus:
__slots__ = ('_status', 'desktop', 'mobile', 'web')
def __init__(self):
self._status: str = 'offline'
self.desktop: Optional[str] = None
self.mobile: Optional[str] = None
self.web: Optional[str] = None
def __repr__(self) -> str:
attrs = [
('_status', self._status),
('desktop', self.desktop),
('mobile', self.mobile),
('web', self.web),
]
inner = ' '.join('%s=%r' % t for t in attrs)
return f'<{self.__class__.__name__} {inner}>'
def _update(self, status: str, data: ClientStatusPayload, /) -> None:
self._status = status
self.desktop = data.get('desktop')
self.mobile = data.get('mobile')
self.web = data.get('web')
@classmethod
def _copy(cls, client_status: Self, /) -> Self:
self = cls.__new__(cls) # bypass __init__
self._status = client_status._status
self.desktop = client_status.desktop
self.mobile = client_status.mobile
self.web = client_status.web
return self
def flatten_user(cls): def flatten_user(cls):
for attr, value in itertools.chain(BaseUser.__dict__.items(), User.__dict__.items()): for attr, value in itertools.chain(BaseUser.__dict__.items(), User.__dict__.items()):
# ignore private/special methods # ignore private/special methods
@ -303,7 +346,7 @@ class Member(discord.abc.Messageable, _UserTag):
self.joined_at: Optional[datetime.datetime] = utils.parse_time(data.get('joined_at')) self.joined_at: Optional[datetime.datetime] = utils.parse_time(data.get('joined_at'))
self.premium_since: Optional[datetime.datetime] = utils.parse_time(data.get('premium_since')) self.premium_since: Optional[datetime.datetime] = utils.parse_time(data.get('premium_since'))
self._roles: utils.SnowflakeList = utils.SnowflakeList(map(int, data['roles'])) self._roles: utils.SnowflakeList = utils.SnowflakeList(map(int, data['roles']))
self._client_status: Dict[Optional[str], str] = {None: 'offline'} self._client_status: _ClientStatus = _ClientStatus()
self.activities: Tuple[ActivityTypes, ...] = tuple() self.activities: Tuple[ActivityTypes, ...] = tuple()
self.nick: Optional[str] = data.get('nick', None) self.nick: Optional[str] = data.get('nick', None)
self.pending: bool = data.get('pending', False) self.pending: bool = data.get('pending', False)
@ -366,7 +409,7 @@ class Member(discord.abc.Messageable, _UserTag):
self._roles = utils.SnowflakeList(member._roles, is_sorted=True) self._roles = utils.SnowflakeList(member._roles, is_sorted=True)
self.joined_at = member.joined_at self.joined_at = member.joined_at
self.premium_since = member.premium_since self.premium_since = member.premium_since
self._client_status = member._client_status.copy() self._client_status = _ClientStatus._copy(member._client_status)
self.guild = member.guild self.guild = member.guild
self.nick = member.nick self.nick = member.nick
self.pending = member.pending self.pending = member.pending
@ -405,10 +448,7 @@ class Member(discord.abc.Messageable, _UserTag):
def _presence_update(self, data: PartialPresenceUpdate, user: UserPayload) -> Optional[Tuple[User, User]]: def _presence_update(self, data: PartialPresenceUpdate, user: UserPayload) -> Optional[Tuple[User, User]]:
self.activities = tuple(map(create_activity, data['activities'])) self.activities = tuple(map(create_activity, data['activities']))
self._client_status = { self._client_status._update(data['status'], data['client_status'])
sys.intern(key): sys.intern(value) for key, value in data.get('client_status', {}).items() # type: ignore
}
self._client_status[None] = sys.intern(data['status'])
if len(user) > 1: if len(user) > 1:
return self._update_inner_user(user) return self._update_inner_user(user)
@ -428,7 +468,7 @@ class Member(discord.abc.Messageable, _UserTag):
@property @property
def status(self) -> Status: def status(self) -> Status:
""":class:`Status`: The member's overall status. If the value is unknown, then it will be a :class:`str` instead.""" """:class:`Status`: The member's overall status. If the value is unknown, then it will be a :class:`str` instead."""
return try_enum(Status, self._client_status[None]) return try_enum(Status, self._client_status._status)
@property @property
def raw_status(self) -> str: def raw_status(self) -> str:
@ -436,31 +476,31 @@ class Member(discord.abc.Messageable, _UserTag):
.. versionadded:: 1.5 .. versionadded:: 1.5
""" """
return self._client_status[None] return self._client_status._status
@status.setter @status.setter
def status(self, value: Status) -> None: def status(self, value: Status) -> None:
# internal use only # internal use only
self._client_status[None] = str(value) self._client_status._status = str(value)
@property @property
def mobile_status(self) -> Status: def mobile_status(self) -> Status:
""":class:`Status`: The member's status on a mobile device, if applicable.""" """:class:`Status`: The member's status on a mobile device, if applicable."""
return try_enum(Status, self._client_status.get('mobile', 'offline')) return try_enum(Status, self._client_status.mobile or 'offline')
@property @property
def desktop_status(self) -> Status: def desktop_status(self) -> Status:
""":class:`Status`: The member's status on the desktop client, if applicable.""" """:class:`Status`: The member's status on the desktop client, if applicable."""
return try_enum(Status, self._client_status.get('desktop', 'offline')) return try_enum(Status, self._client_status.desktop or 'offline')
@property @property
def web_status(self) -> Status: def web_status(self) -> Status:
""":class:`Status`: The member's status on the web client, if applicable.""" """:class:`Status`: The member's status on the web client, if applicable."""
return try_enum(Status, self._client_status.get('web', 'offline')) return try_enum(Status, self._client_status.web or 'offline')
def is_on_mobile(self) -> bool: def is_on_mobile(self) -> bool:
""":class:`bool`: A helper function that determines if a member is active on a mobile device.""" """:class:`bool`: A helper function that determines if a member is active on a mobile device."""
return 'mobile' in self._client_status return self._client_status.mobile is not None
@property @property
def colour(self) -> Colour: def colour(self) -> Colour:

6
discord/types/activity.py

@ -41,9 +41,9 @@ class PartialPresenceUpdate(TypedDict):
class ClientStatus(TypedDict, total=False): class ClientStatus(TypedDict, total=False):
desktop: str desktop: StatusType
mobile: str mobile: StatusType
web: str web: StatusType
class ActivityTimestamps(TypedDict, total=False): class ActivityTimestamps(TypedDict, total=False):

Loading…
Cancel
Save