Skip to content

Commit e456a9e

Browse files
feat: implement magic methods (#633)
* feat: ``__setattr__`` implementation for components to allow editing * feat: ``__str__`` and ``__int__`` implementation for models. * feat: ``__repr`` implementation for models. * feat: setattr method for embeds * Update gateway.py * Update component.py * ci: correct from checks. Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 6b2fbf9 commit e456a9e

File tree

14 files changed

+197
-69
lines changed

14 files changed

+197
-69
lines changed

interactions/api/gateway.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,8 @@ def _dispatch_event(self, event: str, data: dict) -> None: # sourcery no-metric
365365
_name: str = _event_path[0] if len(_event_path) < 3 else "".join(_event_path[:-1])
366366
__obj: object = getattr(__import__(path), _name)
367367

368-
if name in {"_create", "_add"}:
368+
# name in {"_create", "_add"} returns False (tested w message_create)
369+
if any(_ in name for _ in {"_create", "_update", "_add", "_remove", "_delete"}):
369370
data["_client"] = self._http
370371

371372
self._dispatch.dispatch(f"on_{name}", __obj(**data)) # noqa

interactions/api/models/channel.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ def __init__(self, **kwargs):
181181
else None
182182
)
183183

184+
def __repr__(self) -> str:
185+
return self.name
186+
184187
@property
185188
def mention(self) -> str:
186189
"""

interactions/api/models/channel.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class Channel(DictSerializerMixin):
6868
default_auto_archive_duration: Optional[int]
6969
permissions: Optional[str]
7070
def __init__(self, **kwargs): ...
71+
def __repr__(self) -> str: ...
7172
@property
7273
def mention(self) -> str: ...
7374
async def send(

interactions/api/models/guild.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,9 @@ def __init__(self, **kwargs):
328328
else None
329329
)
330330

331+
def __repr__(self) -> str:
332+
return self.name
333+
331334
async def ban(
332335
self,
333336
member_id: int,

interactions/api/models/guild.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ class Guild(DictSerializerMixin):
131131
lazy: Any
132132
application_command_counts: Any
133133
def __init__(self, **kwargs): ...
134+
def __repr__(self) -> str: ...
134135
async def ban(
135136
self,
136137
member_id: int,

interactions/api/models/gw.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@ class Presence(DictSerializerMixin):
726726
:ivar ClientStatus client_status: The client status across platforms in the event.
727727
"""
728728

729-
__slots__ = ("_json", "user", "guild_id", "status", "activities", "client_status")
729+
__slots__ = ("_json", "user", "guild_id", "status", "activities", "client_status", "_client")
730730

731731
def __init__(self, **kwargs):
732732
super().__init__(**kwargs)

interactions/api/models/gw.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ class Integration(DictSerializerMixin):
181181
def __init__(self, **kwargs): ...
182182

183183
class Presence(DictSerializerMixin):
184+
_client: HTTPClient
184185
_json: dict
185186
user: User
186187
guild_id: Snowflake

interactions/api/models/member.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ def __init__(self, **kwargs):
8181
if not self.avatar and self.user:
8282
self.avatar = self.user.avatar
8383

84+
def __repr__(self) -> str:
85+
return self.user.username if self.user else self.nick
86+
8487
@property
8588
def id(self) -> Snowflake:
8689
"""

interactions/api/models/member.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class Member(DictSerializerMixin):
2727
communication_disabled_until: Optional[datetime.isoformat]
2828
hoisted_role: Any # TODO: post-v4: Investigate what this is for when documented by Discord.
2929
def __init__(self, **kwargs): ...
30+
def __repr__(self) -> str: ...
3031
@property
3132
def mention(self) -> str: ...
3233
@property

interactions/api/models/message.py

Lines changed: 82 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ def __init__(self, **kwargs):
299299
)
300300
self.thread = Channel(**self.thread) if self._json.get("thread") else None
301301

302+
def __repr__(self) -> str:
303+
return self.content
304+
302305
async def get_channel(self) -> Channel:
303306
"""
304307
Gets the channel where the message was sent.
@@ -854,6 +857,15 @@ class EmbedImageStruct(DictSerializerMixin):
854857
def __init__(self, **kwargs):
855858
super().__init__(**kwargs)
856859

860+
def __setattr__(self, key, value) -> None:
861+
super().__setattr__(key, value)
862+
if key != "_json" and (key not in self._json or value != self._json.get(key)):
863+
if value is not None and value is not MISSING:
864+
self._json.update({key: value})
865+
866+
elif value is None and key in self._json.keys():
867+
del self._json[key]
868+
857869

858870
class EmbedProvider(DictSerializerMixin):
859871
"""
@@ -868,6 +880,15 @@ class EmbedProvider(DictSerializerMixin):
868880
def __init__(self, **kwargs):
869881
super().__init__(**kwargs)
870882

883+
def __setattr__(self, key, value) -> None:
884+
super().__setattr__(key, value)
885+
if key != "_json" and (key not in self._json or value != self._json.get(key)):
886+
if value is not None and value is not MISSING:
887+
self._json.update({key: value})
888+
889+
elif value is None and key in self._json.keys():
890+
del self._json[key]
891+
871892

872893
class EmbedAuthor(DictSerializerMixin):
873894
"""
@@ -892,6 +913,15 @@ class EmbedAuthor(DictSerializerMixin):
892913
def __init__(self, **kwargs):
893914
super().__init__(**kwargs)
894915

916+
def __setattr__(self, key, value) -> None:
917+
super().__setattr__(key, value)
918+
if key != "_json" and (key not in self._json or value != self._json.get(key)):
919+
if value is not None and value is not MISSING:
920+
self._json.update({key: value})
921+
922+
elif value is None and key in self._json.keys():
923+
del self._json[key]
924+
895925

896926
class EmbedFooter(DictSerializerMixin):
897927
"""
@@ -915,6 +945,15 @@ class EmbedFooter(DictSerializerMixin):
915945
def __init__(self, **kwargs):
916946
super().__init__(**kwargs)
917947

948+
def __setattr__(self, key, value) -> None:
949+
super().__setattr__(key, value)
950+
if key != "_json" and (key not in self._json or value != self._json.get(key)):
951+
if value is not None and value is not MISSING:
952+
self._json.update({key: value})
953+
954+
elif value is None and key in self._json.keys():
955+
del self._json[key]
956+
918957

919958
class EmbedField(DictSerializerMixin):
920959
"""
@@ -940,6 +979,15 @@ class EmbedField(DictSerializerMixin):
940979
def __init__(self, **kwargs):
941980
super().__init__(**kwargs)
942981

982+
def __setattr__(self, key, value) -> None:
983+
super().__setattr__(key, value)
984+
if key != "_json" and (key not in self._json or value != self._json.get(key)):
985+
if value is not None and value is not MISSING:
986+
self._json.update({key: value})
987+
988+
elif value is None and key in self._json.keys():
989+
del self._json[key]
990+
943991

944992
class Embed(DictSerializerMixin):
945993
"""
@@ -1037,30 +1085,36 @@ def __init__(self, **kwargs):
10371085
else None
10381086
)
10391087

1040-
# TODO: Complete partial fix.
1088+
# (Complete partial fix.)
10411089
# The issue seems to be that this itself is not updating
10421090
# JSON result correctly. After numerous attempts I seem to
10431091
# have the attribute to do it, but _json won't budge at all.
10441092
# a genexpr is a poor way to go about this, but I know later
10451093
# on we'll be refactoring this anyhow. What the fuck is breaking
10461094
# it?
1047-
if self.fields:
1048-
self._json.update({"fields": [field._json for field in self.fields]})
1049-
1050-
if self.author:
1051-
self._json.update({"author": self.author._json})
1052-
1053-
if self.footer:
1054-
self._json.update({"footer": self.footer._json})
10551095

1056-
if self.thumbnail:
1057-
self._json.update({"thumbnail": self.thumbnail._json})
1096+
# the __setattr__ method fixes this issue :)
10581097

1059-
if self.image:
1060-
self._json.update({"image": self.image._json})
1061-
1062-
if self.video:
1063-
self._json.update({"video": self.video._json})
1098+
def __setattr__(self, key, value) -> None:
1099+
super().__setattr__(key, value)
1100+
if key != "_json" and (
1101+
key not in self._json
1102+
or (
1103+
value != self._json.get(key)
1104+
or not isinstance(value, dict)
1105+
# we don't need this instance check in components because serialisation works for them
1106+
)
1107+
):
1108+
if value is not None and value is not MISSING:
1109+
try:
1110+
value = [val._json for val in value] if isinstance(value, list) else value._json
1111+
except AttributeError:
1112+
if isinstance(value, datetime):
1113+
value = value.isoformat()
1114+
self._json.update({key: value})
1115+
1116+
elif value is None and key in self._json.keys():
1117+
del self._json[key]
10641118

10651119
def add_field(self, name: str, value: str, inline: Optional[bool] = False) -> None:
10661120
"""
@@ -1074,19 +1128,20 @@ def add_field(self, name: str, value: str, inline: Optional[bool] = False) -> No
10741128
:type inline?: Optional[bool]
10751129
"""
10761130

1077-
if self.fields is None:
1078-
self.fields = []
1131+
fields = self.fields or []
1132+
fields.append(EmbedField(name=name, value=value, inline=inline))
10791133

1080-
self.fields.append(EmbedField(name=name, value=value, inline=inline))
1081-
self._json.update({"fields": [field._json for field in self.fields]})
1134+
self.fields = fields
1135+
# We must use "=" here to call __setattr__. Append does not call any magic, making it impossible to modify the
1136+
# json when using it, so the object what would be sent wouldn't be modified.
1137+
# Imo this is still better than doing a `self._json.update({"fields": [field._json for ...]})`
10821138

10831139
def clear_fields(self) -> None:
10841140
"""
10851141
Clears all the fields of the embed
10861142
"""
10871143

10881144
self.fields = []
1089-
self._json.update({"fields": []})
10901145

10911146
def insert_field_at(
10921147
self, index: int, name: str = None, value: str = None, inline: Optional[bool] = False
@@ -1104,13 +1159,9 @@ def insert_field_at(
11041159
:type inline?: Optional[bool]
11051160
"""
11061161

1107-
try:
1108-
self.fields.insert(index, EmbedField(name=name, value=value, inline=inline))
1109-
1110-
except AttributeError:
1111-
self.fields = [EmbedField(name=name, value=value, inline=inline)]
1112-
1113-
self._json.update({"fields": [field._json for field in self.fields]})
1162+
fields = self.fields or []
1163+
fields.insert(index, EmbedField(name=name, value=value, inline=inline))
1164+
self.fields = fields
11141165

11151166
def set_field_at(
11161167
self, index: int, name: str, value: str, inline: Optional[bool] = False
@@ -1130,7 +1181,6 @@ def set_field_at(
11301181

11311182
try:
11321183
self.fields[index] = EmbedField(name=name, value=value, inline=inline)
1133-
self._json.update({"fields": [field._json for field in self.fields]})
11341184

11351185
except AttributeError as e:
11361186
raise AttributeError("No fields found in Embed") from e
@@ -1147,8 +1197,9 @@ def remove_field(self, index: int) -> None:
11471197
"""
11481198

11491199
try:
1150-
self.fields.pop(index)
1151-
self._json.update({"fields": [field._json for field in self.fields]})
1200+
fields = self.fields
1201+
fields.pop(index)
1202+
self.fields = fields
11521203

11531204
except AttributeError as e:
11541205
raise AttributeError("No fields found in Embed") from e
@@ -1163,7 +1214,6 @@ def remove_author(self) -> None:
11631214

11641215
try:
11651216
del self.author
1166-
self._json.update({"author": None})
11671217
except AttributeError:
11681218
pass
11691219

@@ -1190,7 +1240,6 @@ def set_author(
11901240
self.author = EmbedAuthor(
11911241
name=name, url=url, icon_url=icon_url, proxy_icon_url=proxy_icon_url
11921242
)
1193-
self._json.update({"author": self.author._json})
11941243

11951244
def set_footer(
11961245
self, text: str, icon_url: Optional[str] = None, proxy_icon_url: Optional[str] = None
@@ -1207,7 +1256,6 @@ def set_footer(
12071256
"""
12081257

12091258
self.footer = EmbedFooter(text=text, icon_url=icon_url, proxy_icon_url=proxy_icon_url)
1210-
self._json.update({"footer": self.footer._json})
12111259

12121260
def set_image(
12131261
self,
@@ -1230,7 +1278,6 @@ def set_image(
12301278
"""
12311279

12321280
self.image = EmbedImageStruct(url=url, proxy_url=proxy_url, height=height, width=width)
1233-
self._json.update({"image": self.image._json})
12341281

12351282
def set_thumbnail(
12361283
self,
@@ -1253,4 +1300,3 @@ def set_thumbnail(
12531300
"""
12541301

12551302
self.thumbnail = EmbedImageStruct(url=url, proxy_url=proxy_url, height=height, width=width)
1256-
self._json.update({"thumbnail": self.thumbnail._json})

0 commit comments

Comments
 (0)