diff --git a/discord/ext/commands/errors.py b/discord/ext/commands/errors.py index 55bc9a124..3609d6d92 100644 --- a/discord/ext/commands/errors.py +++ b/discord/ext/commands/errors.py @@ -22,8 +22,23 @@ 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 Optional, Any, TYPE_CHECKING, List, Callable, Type, Tuple, Union + from discord.errors import ClientException, DiscordException +if TYPE_CHECKING: + from inspect import Parameter + + from .converter import Converter + from .context import Context + from .cooldowns import Cooldown, BucketType + from .flags import Flag + from discord.abc import GuildChannel + from discord.threads import Thread + from discord.types.snowflake import Snowflake, SnowflakeList + __all__ = ( 'CommandError', @@ -94,7 +109,7 @@ class CommandError(DiscordException): in a special way as they are caught and passed into a special event from :class:`.Bot`\, :func:`.on_command_error`. """ - def __init__(self, message=None, *args): + def __init__(self, message: Optional[str] = None, *args: Any) -> None: if message is not None: # clean-up @everyone and @here mentions m = message.replace('@everyone', '@\u200beveryone').replace('@here', '@\u200bhere') @@ -115,9 +130,9 @@ class ConversionError(CommandError): The original exception that was raised. You can also get this via the ``__cause__`` attribute. """ - def __init__(self, converter, original): - self.converter = converter - self.original = original + def __init__(self, converter: Converter, original: Exception) -> None: + self.converter: Converter = converter + self.original: Exception = original class UserInputError(CommandError): """The base exception type for errors that involve errors @@ -149,8 +164,8 @@ class MissingRequiredArgument(UserInputError): param: :class:`inspect.Parameter` The argument that is missing. """ - def __init__(self, param): - self.param = param + def __init__(self, param: Parameter) -> None: + self.param: Parameter = param super().__init__(f'{param.name} is a required argument that is missing.') class TooManyArguments(UserInputError): @@ -191,9 +206,9 @@ class CheckAnyFailure(CheckFailure): A list of check predicates that failed. """ - def __init__(self, checks, errors): - self.checks = checks - self.errors = errors + def __init__(self, checks: List[CheckFailure], errors: List[Callable[[Context], bool]]) -> None: + self.checks: List[CheckFailure] = checks + self.errors: List[Callable[[Context], bool]] = errors super().__init__('You do not have permission to run this command.') class PrivateMessageOnly(CheckFailure): @@ -202,7 +217,7 @@ class PrivateMessageOnly(CheckFailure): This inherits from :exc:`CheckFailure` """ - def __init__(self, message=None): + def __init__(self, message: Optional[str] = None) -> None: super().__init__(message or 'This command can only be used in private messages.') class NoPrivateMessage(CheckFailure): @@ -212,7 +227,7 @@ class NoPrivateMessage(CheckFailure): This inherits from :exc:`CheckFailure` """ - def __init__(self, message=None): + def __init__(self, message: Optional[str] = None) -> None: super().__init__(message or 'This command cannot be used in private messages.') class NotOwner(CheckFailure): @@ -235,8 +250,8 @@ class ObjectNotFound(BadArgument): argument: :class:`str` The argument supplied by the caller that was not matched """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'{argument!r} does not follow a valid ID or mention format.') class MemberNotFound(BadArgument): @@ -252,8 +267,8 @@ class MemberNotFound(BadArgument): argument: :class:`str` The member supplied by the caller that was not found """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'Member "{argument}" not found.') class GuildNotFound(BadArgument): @@ -268,8 +283,8 @@ class GuildNotFound(BadArgument): argument: :class:`str` The guild supplied by the called that was not found """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'Guild "{argument}" not found.') class UserNotFound(BadArgument): @@ -285,8 +300,8 @@ class UserNotFound(BadArgument): argument: :class:`str` The user supplied by the caller that was not found """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'User "{argument}" not found.') class MessageNotFound(BadArgument): @@ -301,8 +316,8 @@ class MessageNotFound(BadArgument): argument: :class:`str` The message supplied by the caller that was not found """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'Message "{argument}" not found.') class ChannelNotReadable(BadArgument): @@ -315,11 +330,11 @@ class ChannelNotReadable(BadArgument): Attributes ----------- - argument: :class:`.abc.GuildChannel` + argument: Union[:class:`.abc.GuildChannel`, :class:`Thread`] The channel supplied by the caller that was not readable """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: Union[GuildChannel, Thread]) -> None: + self.argument: Union[GuildChannel, Thread] = argument super().__init__(f"Can't read messages in {argument.mention}.") class ChannelNotFound(BadArgument): @@ -334,8 +349,8 @@ class ChannelNotFound(BadArgument): argument: :class:`str` The channel supplied by the caller that was not found """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'Channel "{argument}" not found.') class ThreadNotFound(BadArgument): @@ -350,8 +365,8 @@ class ThreadNotFound(BadArgument): argument: :class:`str` The thread supplied by the caller that was not found """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'Thread "{argument}" not found.') class BadColourArgument(BadArgument): @@ -366,8 +381,8 @@ class BadColourArgument(BadArgument): argument: :class:`str` The colour supplied by the caller that was not valid """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'Colour "{argument}" is invalid.') BadColorArgument = BadColourArgument @@ -384,8 +399,8 @@ class RoleNotFound(BadArgument): argument: :class:`str` The role supplied by the caller that was not found """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'Role "{argument}" not found.') class BadInviteArgument(BadArgument): @@ -395,8 +410,8 @@ class BadInviteArgument(BadArgument): .. versionadded:: 1.5 """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'Invite "{argument}" is invalid or expired.') class EmojiNotFound(BadArgument): @@ -411,8 +426,8 @@ class EmojiNotFound(BadArgument): argument: :class:`str` The emoji supplied by the caller that was not found """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'Emoji "{argument}" not found.') class PartialEmojiConversionFailure(BadArgument): @@ -428,8 +443,8 @@ class PartialEmojiConversionFailure(BadArgument): argument: :class:`str` The emoji supplied by the caller that did not match the regex """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'Couldn\'t convert "{argument}" to PartialEmoji.') class GuildStickerNotFound(BadArgument): @@ -444,8 +459,8 @@ class GuildStickerNotFound(BadArgument): argument: :class:`str` The sticker supplied by the caller that was not found """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'Sticker "{argument}" not found.') class BadBoolArgument(BadArgument): @@ -460,8 +475,8 @@ class BadBoolArgument(BadArgument): argument: :class:`str` The boolean argument supplied by the caller that is not in the predefined list """ - def __init__(self, argument): - self.argument = argument + def __init__(self, argument: str) -> None: + self.argument: str = argument super().__init__(f'{argument} is not a recognised boolean option') class DisabledCommand(CommandError): @@ -482,8 +497,8 @@ class CommandInvokeError(CommandError): The original exception that was raised. You can also get this via the ``__cause__`` attribute. """ - def __init__(self, e): - self.original = e + def __init__(self, e: Exception) -> None: + self.original: Exception = e super().__init__(f'Command raised an exception: {e.__class__.__name__}: {e}') class CommandOnCooldown(CommandError): @@ -501,10 +516,10 @@ class CommandOnCooldown(CommandError): retry_after: :class:`float` The amount of seconds to wait before you can retry again. """ - def __init__(self, cooldown, retry_after, type): - self.cooldown = cooldown - self.retry_after = retry_after - self.type = type + def __init__(self, cooldown: Cooldown, retry_after: float, type: BucketType) -> None: + self.cooldown: Cooldown = cooldown + self.retry_after: float = retry_after + self.type: BucketType = type super().__init__(f'You are on cooldown. Try again in {retry_after:.2f}s') class MaxConcurrencyReached(CommandError): @@ -520,9 +535,9 @@ class MaxConcurrencyReached(CommandError): The bucket type passed to the :func:`.max_concurrency` decorator. """ - def __init__(self, number, per): - self.number = number - self.per = per + def __init__(self, number: int, per: BucketType) -> None: + self.number: int = number + self.per: BucketType = per name = per.name suffix = 'per %s' % name if per.name != 'default' else 'globally' plural = '%s times %s' if number > 1 else '%s time %s' @@ -542,8 +557,8 @@ class MissingRole(CheckFailure): The required role that is missing. This is the parameter passed to :func:`~.commands.has_role`. """ - def __init__(self, missing_role): - self.missing_role = missing_role + def __init__(self, missing_role: Snowflake) -> None: + self.missing_role: Snowflake = missing_role message = f'Role {missing_role!r} is required to run this command.' super().__init__(message) @@ -560,8 +575,8 @@ class BotMissingRole(CheckFailure): The required role that is missing. This is the parameter passed to :func:`~.commands.has_role`. """ - def __init__(self, missing_role): - self.missing_role = missing_role + def __init__(self, missing_role: Snowflake) -> None: + self.missing_role: Snowflake = missing_role message = f'Bot requires the role {missing_role!r} to run this command' super().__init__(message) @@ -579,8 +594,8 @@ class MissingAnyRole(CheckFailure): The roles that the invoker is missing. These are the parameters passed to :func:`~.commands.has_any_role`. """ - def __init__(self, missing_roles): - self.missing_roles = missing_roles + def __init__(self, missing_roles: SnowflakeList) -> None: + self.missing_roles: SnowflakeList = missing_roles missing = [f"'{role}'" for role in missing_roles] @@ -608,8 +623,8 @@ class BotMissingAnyRole(CheckFailure): These are the parameters passed to :func:`~.commands.has_any_role`. """ - def __init__(self, missing_roles): - self.missing_roles = missing_roles + def __init__(self, missing_roles: SnowflakeList) -> None: + self.missing_roles: SnowflakeList = missing_roles missing = [f"'{role}'" for role in missing_roles] @@ -630,11 +645,11 @@ class NSFWChannelRequired(CheckFailure): Parameters ----------- - channel: :class:`discord.abc.GuildChannel` + channel: Union[:class:`.abc.GuildChannel`, :class:`Thread`] The channel that does not have NSFW enabled. """ - def __init__(self, channel): - self.channel = channel + def __init__(self, channel: Union[GuildChannel, Thread]) -> None: + self.channel: Union[GuildChannel, Thread] = channel super().__init__(f"Channel '{channel}' needs to be NSFW for this command to work.") class MissingPermissions(CheckFailure): @@ -645,11 +660,11 @@ class MissingPermissions(CheckFailure): Attributes ----------- - missing_permissions: :class:`list` + missing_permissions: List[:class:`str`] The required permissions that are missing. """ - def __init__(self, missing_permissions, *args): - self.missing_permissions = missing_permissions + def __init__(self, missing_permissions: List[str], *args: Any) -> None: + self.missing_permissions: List[str] = missing_permissions missing = [perm.replace('_', ' ').replace('guild', 'server').title() for perm in missing_permissions] @@ -668,11 +683,11 @@ class BotMissingPermissions(CheckFailure): Attributes ----------- - missing_permissions: :class:`list` + missing_permissions: List[:class:`str`] The required permissions that are missing. """ - def __init__(self, missing_permissions, *args): - self.missing_permissions = missing_permissions + def __init__(self, missing_permissions: List[str], *args: Any) -> None: + self.missing_permissions: List[str] = missing_permissions missing = [perm.replace('_', ' ').replace('guild', 'server').title() for perm in missing_permissions] @@ -698,10 +713,10 @@ class BadUnionArgument(UserInputError): errors: List[:class:`CommandError`] A list of errors that were caught from failing the conversion. """ - def __init__(self, param, converters, errors): - self.param = param - self.converters = converters - self.errors = errors + def __init__(self, param: Parameter, converters: Tuple[Type, ...], errors: List[CommandError]) -> None: + self.param: Parameter = param + self.converters: Tuple[Type, ...] = converters + self.errors: List[CommandError] = errors def _get_name(x): try: @@ -736,10 +751,10 @@ class BadLiteralArgument(UserInputError): errors: List[:class:`CommandError`] A list of errors that were caught from failing the conversion. """ - def __init__(self, param, literals, errors): - self.param = param - self.literals = literals - self.errors = errors + def __init__(self, param: Parameter, literals: Tuple[Any, ...], errors: List[CommandError]) -> None: + self.param: Parameter = param + self.literals: Tuple[Any, ...] = literals + self.errors: List[CommandError] = errors to_string = [repr(l) for l in literals] if len(to_string) > 2: @@ -769,8 +784,8 @@ class UnexpectedQuoteError(ArgumentParsingError): quote: :class:`str` The quote mark that was found inside the non-quoted string. """ - def __init__(self, quote): - self.quote = quote + def __init__(self, quote: str) -> None: + self.quote: str = quote super().__init__(f'Unexpected quote mark, {quote!r}, in non-quoted string') class InvalidEndOfQuotedStringError(ArgumentParsingError): @@ -784,8 +799,8 @@ class InvalidEndOfQuotedStringError(ArgumentParsingError): char: :class:`str` The character found instead of the expected string. """ - def __init__(self, char): - self.char = char + def __init__(self, char: str) -> None: + self.char: str = char super().__init__(f'Expected space after closing quotation but received {char!r}') class ExpectedClosingQuoteError(ArgumentParsingError): @@ -799,8 +814,8 @@ class ExpectedClosingQuoteError(ArgumentParsingError): The quote character expected. """ - def __init__(self, close_quote): - self.close_quote = close_quote + def __init__(self, close_quote: str) -> None: + self.close_quote: str = close_quote super().__init__(f'Expected closing {close_quote}.') class ExtensionError(DiscordException): @@ -813,8 +828,8 @@ class ExtensionError(DiscordException): name: :class:`str` The extension that had an error. """ - def __init__(self, message=None, *args, name): - self.name = name + def __init__(self, message: Optional[str] = None, *args: Any, name: str) -> None: + self.name: str = name message = message or f'Extension {name!r} had an error.' # clean-up @everyone and @here mentions m = message.replace('@everyone', '@\u200beveryone').replace('@here', '@\u200bhere') @@ -825,7 +840,7 @@ class ExtensionAlreadyLoaded(ExtensionError): This inherits from :exc:`ExtensionError` """ - def __init__(self, name): + def __init__(self, name: str) -> None: super().__init__(f'Extension {name!r} is already loaded.', name=name) class ExtensionNotLoaded(ExtensionError): @@ -833,7 +848,7 @@ class ExtensionNotLoaded(ExtensionError): This inherits from :exc:`ExtensionError` """ - def __init__(self, name): + def __init__(self, name: str) -> None: super().__init__(f'Extension {name!r} has not been loaded.', name=name) class NoEntryPointError(ExtensionError): @@ -841,7 +856,7 @@ class NoEntryPointError(ExtensionError): This inherits from :exc:`ExtensionError` """ - def __init__(self, name): + def __init__(self, name: str) -> None: super().__init__(f"Extension {name!r} has no 'setup' function.", name=name) class ExtensionFailed(ExtensionError): @@ -857,8 +872,8 @@ class ExtensionFailed(ExtensionError): The original exception that was raised. You can also get this via the ``__cause__`` attribute. """ - def __init__(self, name, original): - self.original = original + def __init__(self, name: str, original: Exception) -> None: + self.original: Exception = original msg = f'Extension {name!r} raised an error: {original.__class__.__name__}: {original}' super().__init__(msg, name=name) @@ -875,7 +890,7 @@ class ExtensionNotFound(ExtensionError): name: :class:`str` The extension that had the error. """ - def __init__(self, name): + def __init__(self, name: str) -> None: msg = f'Extension {name!r} could not be loaded.' super().__init__(msg, name=name) @@ -894,9 +909,9 @@ class CommandRegistrationError(ClientException): alias_conflict: :class:`bool` Whether the name that conflicts is an alias of the command we try to add. """ - def __init__(self, name, *, alias_conflict=False): - self.name = name - self.alias_conflict = alias_conflict + def __init__(self, name: str, *, alias_conflict: bool = False) -> None: + self.name: str = name + self.alias_conflict: bool = alias_conflict type_ = 'alias' if alias_conflict else 'command' super().__init__(f'The {type_} {name} is already an existing command or alias.') @@ -923,9 +938,9 @@ class TooManyFlags(FlagError): values: List[:class:`str`] The values that were passed. """ - def __init__(self, flag, values): - self.flag = flag - self.values = values + def __init__(self, flag: Flag, values: List[str]) -> None: + self.flag: Flag = flag + self.values: List[str] = values super().__init__(f'Too many flag values, expected {flag.max_args} but received {len(values)}.') class BadFlagArgument(FlagError): @@ -940,8 +955,8 @@ class BadFlagArgument(FlagError): flag: :class:`~discord.ext.commands.Flag` The flag that failed to convert. """ - def __init__(self, flag): - self.flag = flag + def __init__(self, flag: Flag) -> None: + self.flag: Flag = flag try: name = flag.annotation.__name__ except AttributeError: @@ -961,8 +976,8 @@ class MissingRequiredFlag(FlagError): flag: :class:`~discord.ext.commands.Flag` The required flag that was not found. """ - def __init__(self, flag): - self.flag = flag + def __init__(self, flag: Flag) -> None: + self.flag: Flag = flag super().__init__(f'Flag {flag.name!r} is required and missing') class MissingFlagArgument(FlagError): @@ -977,6 +992,6 @@ class MissingFlagArgument(FlagError): flag: :class:`~discord.ext.commands.Flag` The flag that did not get a value. """ - def __init__(self, flag): - self.flag = flag + def __init__(self, flag: Flag) -> None: + self.flag: Flag = flag super().__init__(f'Flag {flag.name!r} does not have an argument')