From a8db8546dbb8bc45d9417d16dd828e745f2d681e Mon Sep 17 00:00:00 2001 From: Rapptz Date: Sat, 3 Jul 2021 00:53:30 -0400 Subject: [PATCH] Typehint error.py --- discord/errors.py | 90 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/discord/errors.py b/discord/errors.py index ca9538489..398cdc556 100644 --- a/discord/errors.py +++ b/discord/errors.py @@ -22,6 +22,21 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +from __future__ import annotations +from typing import Dict, List, Optional, TYPE_CHECKING, Any, Tuple, Union + +if TYPE_CHECKING: + from aiohttp import ClientResponse, ClientWebSocketResponse + + try: + from requests import Response + + ResponseType = Union[ClientResponse, Response] + except ModuleNotFoundError: + ResponseType = ClientResponse + + from .interactions import Interaction + __all__ = ( 'DiscordException', 'ClientException', @@ -39,41 +54,49 @@ __all__ = ( 'InteractionResponded', ) + class DiscordException(Exception): """Base exception class for discord.py Ideally speaking, this could be caught to handle any exceptions raised from this library. """ + pass + class ClientException(DiscordException): """Exception that's raised when an operation in the :class:`Client` fails. These are usually for exceptions that happened due to user input. """ + pass + class NoMoreItems(DiscordException): - """Exception that is raised when an async iteration operation has no more - items.""" + """Exception that is raised when an async iteration operation has no more items.""" + pass + class GatewayNotFound(DiscordException): """An exception that is raised when the gateway for Discord could not be found""" + def __init__(self): message = 'The gateway to connect to discord was not found.' super().__init__(message) -def flatten_error_dict(d, key=''): - items = [] + +def _flatten_error_dict(d: Dict[str, Any], key: str = '') -> Dict[str, str]: + items: List[Tuple[str, str]] = [] for k, v in d.items(): new_key = key + '.' + k if key else k if isinstance(v, dict): try: - _errors = v['_errors'] + _errors: List[Dict[str, Any]] = v['_errors'] except KeyError: - items.extend(flatten_error_dict(v, new_key).items()) + items.extend(_flatten_error_dict(v, new_key).items()) else: items.append((new_key, ' '.join(x.get('message', '') for x in _errors))) else: @@ -81,6 +104,7 @@ def flatten_error_dict(d, key=''): return dict(items) + class HTTPException(DiscordException): """Exception that's raised when an HTTP request operation fails. @@ -99,21 +123,23 @@ class HTTPException(DiscordException): The Discord specific error code for the failure. """ - def __init__(self, response, message): - self.response = response - self.status = response.status + def __init__(self, response: ResponseType, message: Optional[Union[str, Dict[str, Any]]]): + self.response: ResponseType = response + self.status: int = response.status # type: ignore + self.code: int + self.text: str if isinstance(message, dict): self.code = message.get('code', 0) base = message.get('message', '') errors = message.get('errors') if errors: - errors = flatten_error_dict(errors) + errors = _flatten_error_dict(errors) helpful = '\n'.join('In %s: %s' % t for t in errors.items()) self.text = base + '\n' + helpful else: self.text = base else: - self.text = message + self.text = message or '' self.code = 0 fmt = '{0.status} {0.reason} (error code: {1})' @@ -122,20 +148,25 @@ class HTTPException(DiscordException): super().__init__(fmt.format(self.response, self.code, self.text)) + class Forbidden(HTTPException): """Exception that's raised for when status code 403 occurs. Subclass of :exc:`HTTPException` """ + pass + class NotFound(HTTPException): """Exception that's raised for when status code 404 occurs. Subclass of :exc:`HTTPException` """ + pass + class DiscordServerError(HTTPException): """Exception that's raised for when a 500 range status code occurs. @@ -143,14 +174,18 @@ class DiscordServerError(HTTPException): .. versionadded:: 1.5 """ + pass + class InvalidData(ClientException): """Exception that's raised when the library encounters unknown or invalid data from Discord. """ + pass + class InvalidArgument(ClientException): """Exception that's raised when an argument to a function is invalid some way (e.g. wrong value or wrong type). @@ -159,15 +194,19 @@ class InvalidArgument(ClientException): ``TypeError`` except inherited from :exc:`ClientException` and thus :exc:`DiscordException`. """ + pass + class LoginFailure(ClientException): """Exception that's raised when the :meth:`Client.login` function fails to log you in from improper credentials or some other misc. failure. """ + pass + class ConnectionClosed(ClientException): """Exception that's raised when the gateway connection is closed for reasons that could not be handled internally. @@ -181,15 +220,17 @@ class ConnectionClosed(ClientException): shard_id: Optional[:class:`int`] The shard ID that got closed if applicable. """ - def __init__(self, socket, *, shard_id, code=None): + + def __init__(self, socket: ClientWebSocketResponse, *, shard_id: Optional[int], code: Optional[int] = None): # This exception is just the same exception except # reconfigured to subclass ClientException for users - self.code = code or socket.close_code + self.code: int = code or socket.close_code or -1 # aiohttp doesn't seem to consistently provide close reason - self.reason = '' - self.shard_id = shard_id + self.reason: str = '' + self.shard_id: Optional[int] = shard_id super().__init__(f'Shard ID {self.shard_id} WebSocket closed with {self.code}') + class PrivilegedIntentsRequired(ClientException): """Exception that's raised when the gateway is requesting privileged intents but they're not ticked in the developer page yet. @@ -206,14 +247,17 @@ class PrivilegedIntentsRequired(ClientException): The shard ID that got closed if applicable. """ - def __init__(self, shard_id): - self.shard_id = shard_id - msg = 'Shard ID %s is requesting privileged intents that have not been explicitly enabled in the ' \ - 'developer portal. It is recommended to go to https://discord.com/developers/applications/ ' \ - 'and explicitly enable the privileged intents within your application\'s page. If this is not ' \ - 'possible, then consider disabling the privileged intents instead.' + def __init__(self, shard_id: Optional[int]): + self.shard_id: Optional[int] = shard_id + msg = ( + 'Shard ID %s is requesting privileged intents that have not been explicitly enabled in the ' + 'developer portal. It is recommended to go to https://discord.com/developers/applications/ ' + 'and explicitly enable the privileged intents within your application\'s page. If this is not ' + 'possible, then consider disabling the privileged intents instead.' + ) super().__init__(msg % shard_id) + class InteractionResponded(ClientException): """Exception that's raised when sending another interaction response using :class:`InteractionResponse` when one has already been done before. @@ -228,6 +272,6 @@ class InteractionResponded(ClientException): The interaction that's already been responded to. """ - def __init__(self, interaction): - self.interaction = interaction + def __init__(self, interaction: Interaction): + self.interaction: Interaction = interaction super().__init__('This interaction has already been responded to before')