Skip to content

Commit 646d966

Browse files
EepyElvyrapre-commit-ci[bot]Toricane
authored
refactor: Refactor all exceptions (#857)
* refactor: change the library's error handling * ci: fix * ok that didn't work out well * ok that didn't work out well * Update request.py * ci: correct from checks. * Update interactions/api/models/misc.py Co-authored-by: Toricane <73972068+Toricane@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Toricane <73972068+Toricane@users.noreply.github.com>
1 parent 0ac495d commit 646d966

File tree

14 files changed

+187
-151
lines changed

14 files changed

+187
-151
lines changed

interactions/api/error.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77

88

99
class LibraryException(Exception):
10+
"""
11+
A class object representing all errors.
12+
If you want more information on what a specific code means, use `e.lookup(code)`
13+
"""
14+
1015
code: Optional[int]
1116
severity: int
1217

@@ -33,11 +38,11 @@ def _inner(v, parent):
3338
else:
3439
for k, v in v.items():
3540
if isinstance(v, dict):
36-
_inner(v, parent + "." + k)
41+
_inner(v, f"{parent}.{k}")
3742
elif isinstance(v, list):
3843
for e in v:
3944
if isinstance(e, dict):
40-
_errors.append((e["code"], e["message"], parent + "." + k))
45+
_errors.append((e["code"], e["message"], f"{parent}.{k}"))
4146
elif isinstance(v, list) and parent == "_errors":
4247
for e in v:
4348
_errors.append((e["code"], e["message"], parent))
@@ -84,6 +89,8 @@ def lookup(code: int) -> str:
8489
9: "Incorrect data was passed to a slash command data object.",
8590
10: "The interaction was already responded to.",
8691
11: "Error creating your command.",
92+
12: "Invalid set of arguments specified.",
93+
13: "No HTTPClient set!",
8794
# HTTP errors
8895
400: "Bad Request. The request was improperly formatted, or the server couldn't understand it.",
8996
401: "Not authorized. Double check your token to see if it's valid.",
@@ -276,9 +283,15 @@ def __init__(self, message: str = None, code: int = 0, severity: int = 0, **kwar
276283
):
277284
_fmt_error: List[tuple] = self._parse(self.data["errors"])
278285

279-
super().__init__(
280-
f"{self.message} (code: {self.code}, severity {self.severity})\n"
281-
+ "\n".join([f"Error at {e[2]}: {e[0]} - {e[1]}" for e in _fmt_error])
282-
if _fmt_error
283-
else None
284-
)
286+
self.log(self.message)
287+
288+
if _fmt_error:
289+
super().__init__(
290+
f"{self.message} (code: {self.code}, severity {self.severity})\n"
291+
+ "\n".join([f"Error at {e[2]}: {e[0]} - {e[1]}" for e in _fmt_error])
292+
)
293+
else:
294+
super().__init__(
295+
f"An error occurred:\n"
296+
f"{self.message}, with code '{self.code}' and severity '{self.severity}'"
297+
)

interactions/api/error.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ class LibraryException(Exception):
44
message: Optional[str]
55
code: Optional[int]
66
severity: Optional[int]
7+
data: Optional[dict]
78

9+
def __init__(self, message: str = None, code: int = 0, severity: int = 0, **kwargs): ...
810
@staticmethod
911
def _parse(_data: dict) -> List[tuple]: ...
10-
1112
def log(self, message: str, *args) -> None: ...
12-
1313
@staticmethod
1414
def lookup(code: int) -> str: ...

interactions/api/http/channel.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import Dict, List, Optional, Union
22

33
from ...api.cache import Cache, Item
4+
from ..error import LibraryException
45
from ..models.channel import Channel
56
from ..models.message import Message
67
from .request import _Request
@@ -75,8 +76,9 @@ async def get_channel_messages(
7576
params["around"] = around
7677

7778
if params_used > 1:
78-
raise ValueError(
79-
"`before`, `after` and `around` are mutually exclusive. Please pass only one of them."
79+
raise LibraryException(
80+
message="`before`, `after` and `around` are mutually exclusive. Please pass only one of them.",
81+
code=12,
8082
)
8183

8284
request = await self._req.request(

interactions/api/http/request.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22
import traceback
33
from asyncio import AbstractEventLoop, Lock, get_event_loop, get_running_loop, new_event_loop
4+
from contextlib import suppress
45
from json import dumps
56
from logging import Logger
67
from sys import version_info
@@ -162,7 +163,6 @@ async def request(self, route: Route, **kwargs) -> Optional[Any]:
162163
if _bucket is not None:
163164
self.buckets[route.endpoint] = _bucket
164165
# real-time replacement/update/add if needed.
165-
166166
if isinstance(data, dict) and (
167167
data.get("errors") or (data.get("code") and data.get("code") != 429)
168168
):
@@ -171,8 +171,19 @@ async def request(self, route: Route, **kwargs) -> Optional[Any]:
171171
)
172172
# This "redundant" debug line is for debug use and tracing back the error codes.
173173

174-
raise LibraryException(message=data["message"], code=data["code"])
174+
raise LibraryException(
175+
message=data["message"], code=data["code"], severity=40, data=data
176+
)
177+
elif isinstance(data, dict) and data.get("code") == 0 and data.get("message"):
178+
log.debug(
179+
f"RETURN {response.status}: {dumps(data, indent=4, sort_keys=True)}"
180+
)
181+
# This "redundant" debug line is for debug use and tracing back the error codes.
175182

183+
raise LibraryException(
184+
message=f"'{data['message']}'. Make sure that your token is set properly.",
185+
severity=50,
186+
)
176187
if response.status == 429:
177188
if not is_global:
178189
log.warning(
@@ -206,18 +217,16 @@ async def request(self, route: Route, **kwargs) -> Optional[Any]:
206217
if tries < 4 and e.errno in (54, 10054):
207218
await asyncio.sleep(2 * tries + 1)
208219
continue
209-
try:
220+
with suppress(RuntimeError):
210221
_limiter.lock.release()
211-
except RuntimeError:
212-
pass
213222
raise
214223

215224
# For generic exceptions we give a traceback for debug reasons.
216225
except Exception as e:
217-
try:
226+
with suppress(RuntimeError):
218227
_limiter.lock.release()
219-
except RuntimeError:
220-
pass
228+
if isinstance(e, LibraryException):
229+
raise
221230
log.error("".join(traceback.format_exception(type(e), e, e.__traceback__)))
222231
break
223232

interactions/api/models/channel.py

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from enum import IntEnum
33
from typing import Any, Callable, List, Optional, Union
44

5+
from ..error import LibraryException
56
from .attrs_utils import (
67
MISSING,
78
ClientSerializerMixin,
@@ -210,7 +211,7 @@ async def send(
210211
:rtype: Message
211212
"""
212213
if not self._client:
213-
raise AttributeError("HTTPClient not found!")
214+
raise LibraryException(code=13)
214215
from ...client.models.component import _build_components
215216
from .message import Message
216217

@@ -264,7 +265,7 @@ async def delete(self) -> None:
264265
Deletes the channel.
265266
"""
266267
if not self._client:
267-
raise AttributeError("HTTPClient not found!")
268+
raise LibraryException(code=13)
268269
await self._client.delete_channel(channel_id=int(self.id))
269270

270271
async def modify(
@@ -319,7 +320,7 @@ async def modify(
319320
:rtype: Channel
320321
"""
321322
if not self._client:
322-
raise AttributeError("HTTPClient not found!")
323+
raise LibraryException(code=13)
323324
_name = self.name if name is MISSING else name
324325
_topic = self.topic if topic is MISSING else topic
325326
_bitrate = self.bitrate if bitrate is MISSING else bitrate
@@ -359,7 +360,7 @@ async def modify(
359360
if (
360361
archived is not MISSING or auto_archive_duration is not MISSING or locked is not MISSING
361362
) and not self.thread_metadata:
362-
raise ValueError("The specified channel is not a Thread!")
363+
raise LibraryException(message="The specified channel is not a Thread!", code=12)
363364

364365
if archived is not MISSING:
365366
payload["archived"] = archived
@@ -434,7 +435,7 @@ async def set_bitrate(
434435
"""
435436

436437
if self.type != ChannelType.GUILD_VOICE:
437-
raise TypeError("Bitrate is only available for VoiceChannels")
438+
raise LibraryException(message="Bitrate is only available for VoiceChannels", code=12)
438439

439440
return await self.modify(bitrate=bitrate, reason=reason)
440441

@@ -456,7 +457,9 @@ async def set_user_limit(
456457
"""
457458

458459
if self.type != ChannelType.GUILD_VOICE:
459-
raise TypeError("user_limit is only available for VoiceChannels")
460+
raise LibraryException(
461+
message="user_limit is only available for VoiceChannels", code=12
462+
)
460463

461464
return await self.modify(user_limit=user_limit, reason=reason)
462465

@@ -604,11 +607,9 @@ async def add_member(
604607
:type member_id: int
605608
"""
606609
if not self._client:
607-
raise AttributeError("HTTPClient not found!")
610+
raise LibraryException(code=13)
608611
if not self.thread_metadata:
609-
raise TypeError(
610-
"The Channel you specified is not a thread!"
611-
) # TODO: Move to new error formatter.
612+
raise LibraryException(message="The Channel you specified is not a thread!", code=12)
612613

613614
_member_id = (
614615
int(member_id) if isinstance(member_id, (int, Snowflake)) else int(member_id.id)
@@ -627,7 +628,7 @@ async def pin_message(
627628
:type message_id: Union[int, Snowflake, "Message"]
628629
"""
629630
if not self._client:
630-
raise AttributeError("HTTPClient not found!")
631+
raise LibraryException(code=13)
631632

632633
_message_id = (
633634
int(message_id) if isinstance(message_id, (int, Snowflake)) else int(message_id.id)
@@ -646,7 +647,7 @@ async def unpin_message(
646647
:type message_id: Union[int, Snowflake, "Message"]
647648
"""
648649
if not self._client:
649-
raise AttributeError("HTTPClient not found!")
650+
raise LibraryException(code=13)
650651

651652
_message_id = (
652653
int(message_id) if isinstance(message_id, (int, Snowflake)) else int(message_id.id)
@@ -666,7 +667,7 @@ async def publish_message(
666667
:rtype: Message
667668
"""
668669
if not self._client:
669-
raise AttributeError("HTTPClient not found!")
670+
raise LibraryException(code=13)
670671
from .message import Message
671672

672673
_message_id = (
@@ -685,7 +686,7 @@ async def get_pinned_messages(self) -> List["Message"]: # noqa
685686
:rtype: List[Message]
686687
"""
687688
if not self._client:
688-
raise AttributeError("HTTPClient not found!")
689+
raise LibraryException(code=13)
689690
from .message import Message
690691

691692
res = await self._client.get_pinned_messages(int(self.id))
@@ -744,7 +745,7 @@ def check_pinned(message):
744745
:rtype: List[Message]
745746
"""
746747
if not self._client:
747-
raise AttributeError("HTTPClient not found!")
748+
raise LibraryException(code=13)
748749
from .message import Message
749750

750751
_before = None if before is MISSING else before
@@ -942,13 +943,13 @@ async def create_thread(
942943
:rtype: Channel
943944
"""
944945
if not self._client:
945-
raise AttributeError("HTTPClient not found!")
946+
raise LibraryException(code=13)
946947
if type not in [
947948
ChannelType.GUILD_NEWS_THREAD,
948949
ChannelType.GUILD_PUBLIC_THREAD,
949950
ChannelType.GUILD_PRIVATE_THREAD,
950951
]:
951-
raise AttributeError("type must be a thread type!")
952+
raise LibraryException(message="type must be a thread type!", code=12)
952953

953954
_auto_archive_duration = None if auto_archive_duration is MISSING else auto_archive_duration
954955
_invitable = None if invitable is MISSING else invitable
@@ -1009,7 +1010,7 @@ async def create_invite(
10091010
"""
10101011

10111012
if not self._client:
1012-
raise AttributeError("HTTPClient not found!")
1013+
raise LibraryException(code=13)
10131014

10141015
payload = {
10151016
"max_age": max_age,
@@ -1021,16 +1022,17 @@ async def create_invite(
10211022
if (target_user_id is not MISSING and target_user_id) and (
10221023
target_application_id is not MISSING and target_application_id
10231024
):
1024-
raise ValueError(
1025-
"target user id and target application are mutually exclusive!"
1026-
) # TODO: move to custom error formatter
1025+
raise LibraryException(
1026+
message="target user id and target application are mutually exclusive!", code=12
1027+
)
10271028

10281029
elif (
10291030
(target_user_id is not MISSING and target_user_id)
10301031
or (target_application_id is not MISSING and target_application_id)
10311032
) and not target_type:
1032-
raise ValueError(
1033-
"you have to specify a target_type if you specify target_user-/target_application_id"
1033+
raise LibraryException(
1034+
message="you have to specify a target_type if you specify target_user-/target_application_id",
1035+
code=12,
10341036
)
10351037

10361038
if target_user_id is not MISSING:
@@ -1066,7 +1068,7 @@ async def get_history(self, limit: int = 100) -> Optional[List["Message"]]: # n
10661068
"""
10671069

10681070
if not self._client:
1069-
raise AttributeError("HTTPClient not found!")
1071+
raise LibraryException(code=13)
10701072

10711073
from .message import Message
10721074

@@ -1115,7 +1117,7 @@ async def get_webhooks(self) -> List[Webhook]:
11151117
"""
11161118

11171119
if not self._client:
1118-
raise AttributeError("HTTPClient not found!")
1120+
raise LibraryException(code=13)
11191121

11201122
res = await self._client.get_channel_webhooks(int(self.id))
11211123
return [Webhook(**_, _client=self._client) for _ in res]

0 commit comments

Comments
 (0)