Browse Source

[commands] Add subclasses of BadArgument for converters

pull/5793/head
Simon Beal 5 years ago
committed by GitHub
parent
commit
6ebd2e13a1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 65
      discord/ext/commands/converter.py
  2. 2
      discord/ext/commands/core.py
  3. 187
      discord/ext/commands/errors.py
  4. 44
      docs/ext/commands/api.rst

65
discord/ext/commands/converter.py

@ -30,7 +30,7 @@ import typing
import discord import discord
from .errors import BadArgument, NoPrivateMessage from .errors import *
__all__ = ( __all__ = (
'Converter', 'Converter',
@ -119,6 +119,9 @@ class MemberConverter(IDConverter):
3. Lookup by name#discrim 3. Lookup by name#discrim
4. Lookup by name 4. Lookup by name
5. Lookup by nickname 5. Lookup by nickname
.. versionchanged:: 1.5
Raise :exc:`.MemberNotFound` instead of generic :exc:`.BadArgument`
""" """
async def convert(self, ctx, argument): async def convert(self, ctx, argument):
@ -140,7 +143,7 @@ class MemberConverter(IDConverter):
result = _get_from_guilds(bot, 'get_member', user_id) result = _get_from_guilds(bot, 'get_member', user_id)
if result is None: if result is None:
raise BadArgument('Member "{}" not found'.format(argument)) raise MemberNotFound(argument)
return result return result
@ -155,6 +158,9 @@ class UserConverter(IDConverter):
2. Lookup by mention. 2. Lookup by mention.
3. Lookup by name#discrim 3. Lookup by name#discrim
4. Lookup by name 4. Lookup by name
.. versionchanged:: 1.5
Raise :exc:`.UserNotFound` instead of generic :exc:`.BadArgument`
""" """
async def convert(self, ctx, argument): async def convert(self, ctx, argument):
match = self._get_id_match(argument) or re.match(r'<@!?([0-9]+)>$', argument) match = self._get_id_match(argument) or re.match(r'<@!?([0-9]+)>$', argument)
@ -185,7 +191,7 @@ class UserConverter(IDConverter):
result = discord.utils.find(predicate, state._users.values()) result = discord.utils.find(predicate, state._users.values())
if result is None: if result is None:
raise BadArgument('User "{}" not found'.format(argument)) raise UserNotFound(argument)
return result return result
@ -199,6 +205,9 @@ class MessageConverter(Converter):
1. Lookup by "{channel ID}-{message ID}" (retrieved by shift-clicking on "Copy ID") 1. Lookup by "{channel ID}-{message ID}" (retrieved by shift-clicking on "Copy ID")
2. Lookup by message ID (the message **must** be in the context channel) 2. Lookup by message ID (the message **must** be in the context channel)
3. Lookup by message URL 3. Lookup by message URL
.. versionchanged:: 1.5
Raise :exc:`.ChannelNotFound`, `MessageNotFound` or `ChannelNotReadable` instead of generic :exc:`.BadArgument`
""" """
async def convert(self, ctx, argument): async def convert(self, ctx, argument):
@ -210,7 +219,7 @@ class MessageConverter(Converter):
) )
match = id_regex.match(argument) or link_regex.match(argument) match = id_regex.match(argument) or link_regex.match(argument)
if not match: if not match:
raise BadArgument('Message "{msg}" not found.'.format(msg=argument)) raise MessageNotFound(argument)
message_id = int(match.group("message_id")) message_id = int(match.group("message_id"))
channel_id = match.group("channel_id") channel_id = match.group("channel_id")
message = ctx.bot._connection._get_message(message_id) message = ctx.bot._connection._get_message(message_id)
@ -218,13 +227,13 @@ class MessageConverter(Converter):
return message return message
channel = ctx.bot.get_channel(int(channel_id)) if channel_id else ctx.channel channel = ctx.bot.get_channel(int(channel_id)) if channel_id else ctx.channel
if not channel: if not channel:
raise BadArgument('Channel "{channel}" not found.'.format(channel=channel_id)) raise ChannelNotFound(channel_id)
try: try:
return await channel.fetch_message(message_id) return await channel.fetch_message(message_id)
except discord.NotFound: except discord.NotFound:
raise BadArgument('Message "{msg}" not found.'.format(msg=argument)) raise MessageNotFound(argument)
except discord.Forbidden: except discord.Forbidden:
raise BadArgument("Can't read messages in {channel}".format(channel=channel.mention)) raise ChannelNotReadable(channel)
class TextChannelConverter(IDConverter): class TextChannelConverter(IDConverter):
"""Converts to a :class:`~discord.TextChannel`. """Converts to a :class:`~discord.TextChannel`.
@ -237,6 +246,9 @@ class TextChannelConverter(IDConverter):
1. Lookup by ID. 1. Lookup by ID.
2. Lookup by mention. 2. Lookup by mention.
3. Lookup by name 3. Lookup by name
.. versionchanged:: 1.5
Raise :exc:`.ChannelNotFound` instead of generic :exc:`.BadArgument`
""" """
async def convert(self, ctx, argument): async def convert(self, ctx, argument):
bot = ctx.bot bot = ctx.bot
@ -261,7 +273,7 @@ class TextChannelConverter(IDConverter):
result = _get_from_guilds(bot, 'get_channel', channel_id) result = _get_from_guilds(bot, 'get_channel', channel_id)
if not isinstance(result, discord.TextChannel): if not isinstance(result, discord.TextChannel):
raise BadArgument('Channel "{}" not found.'.format(argument)) raise ChannelNotFound(argument)
return result return result
@ -276,6 +288,9 @@ class VoiceChannelConverter(IDConverter):
1. Lookup by ID. 1. Lookup by ID.
2. Lookup by mention. 2. Lookup by mention.
3. Lookup by name 3. Lookup by name
.. versionchanged:: 1.5
Raise :exc:`.ChannelNotFound` instead of generic :exc:`.BadArgument`
""" """
async def convert(self, ctx, argument): async def convert(self, ctx, argument):
bot = ctx.bot bot = ctx.bot
@ -299,7 +314,7 @@ class VoiceChannelConverter(IDConverter):
result = _get_from_guilds(bot, 'get_channel', channel_id) result = _get_from_guilds(bot, 'get_channel', channel_id)
if not isinstance(result, discord.VoiceChannel): if not isinstance(result, discord.VoiceChannel):
raise BadArgument('Channel "{}" not found.'.format(argument)) raise ChannelNotFound(argument)
return result return result
@ -314,6 +329,9 @@ class CategoryChannelConverter(IDConverter):
1. Lookup by ID. 1. Lookup by ID.
2. Lookup by mention. 2. Lookup by mention.
3. Lookup by name 3. Lookup by name
.. versionchanged:: 1.5
Raise :exc:`.ChannelNotFound` instead of generic :exc:`.BadArgument`
""" """
async def convert(self, ctx, argument): async def convert(self, ctx, argument):
bot = ctx.bot bot = ctx.bot
@ -338,7 +356,7 @@ class CategoryChannelConverter(IDConverter):
result = _get_from_guilds(bot, 'get_channel', channel_id) result = _get_from_guilds(bot, 'get_channel', channel_id)
if not isinstance(result, discord.CategoryChannel): if not isinstance(result, discord.CategoryChannel):
raise BadArgument('Channel "{}" not found.'.format(argument)) raise ChannelNotFound(argument)
return result return result
@ -355,6 +373,9 @@ class ColourConverter(Converter):
- Any of the ``classmethod`` in :class:`Colour` - Any of the ``classmethod`` in :class:`Colour`
- The ``_`` in the name can be optionally replaced with spaces. - The ``_`` in the name can be optionally replaced with spaces.
.. versionchanged:: 1.5
Raise :exc:`.BadColourArgument` instead of generic :exc:`.BadArgument`
""" """
async def convert(self, ctx, argument): async def convert(self, ctx, argument):
arg = argument.replace('0x', '').lower() arg = argument.replace('0x', '').lower()
@ -364,13 +385,13 @@ class ColourConverter(Converter):
try: try:
value = int(arg, base=16) value = int(arg, base=16)
if not (0 <= value <= 0xFFFFFF): if not (0 <= value <= 0xFFFFFF):
raise BadArgument('Colour "{}" is invalid.'.format(arg)) raise BadColourArgument(arg)
return discord.Colour(value=value) return discord.Colour(value=value)
except ValueError: except ValueError:
arg = arg.replace(' ', '_') arg = arg.replace(' ', '_')
method = getattr(discord.Colour, arg, None) method = getattr(discord.Colour, arg, None)
if arg.startswith('from_') or method is None or not inspect.ismethod(method): if arg.startswith('from_') or method is None or not inspect.ismethod(method):
raise BadArgument('Colour "{}" is invalid.'.format(arg)) raise BadColourArgument(arg)
return method() return method()
ColorConverter = ColourConverter ColorConverter = ColourConverter
@ -386,6 +407,9 @@ class RoleConverter(IDConverter):
1. Lookup by ID. 1. Lookup by ID.
2. Lookup by mention. 2. Lookup by mention.
3. Lookup by name 3. Lookup by name
.. versionchanged:: 1.5
Raise :exc:`.RoleNotFound` instead of generic :exc:`.BadArgument`
""" """
async def convert(self, ctx, argument): async def convert(self, ctx, argument):
guild = ctx.guild guild = ctx.guild
@ -399,7 +423,7 @@ class RoleConverter(IDConverter):
result = discord.utils.get(guild._roles.values(), name=argument) result = discord.utils.get(guild._roles.values(), name=argument)
if result is None: if result is None:
raise BadArgument('Role "{}" not found.'.format(argument)) raise RoleNotFound(argument)
return result return result
class GameConverter(Converter): class GameConverter(Converter):
@ -411,13 +435,16 @@ class InviteConverter(Converter):
"""Converts to a :class:`~discord.Invite`. """Converts to a :class:`~discord.Invite`.
This is done via an HTTP request using :meth:`.Bot.fetch_invite`. This is done via an HTTP request using :meth:`.Bot.fetch_invite`.
.. versionchanged:: 1.5
Raise :exc:`.BadInviteArgument` instead of generic :exc:`.BadArgument`
""" """
async def convert(self, ctx, argument): async def convert(self, ctx, argument):
try: try:
invite = await ctx.bot.fetch_invite(argument) invite = await ctx.bot.fetch_invite(argument)
return invite return invite
except Exception as exc: except Exception as exc:
raise BadArgument('Invite is invalid or expired') from exc raise BadInviteArgument() from exc
class EmojiConverter(IDConverter): class EmojiConverter(IDConverter):
"""Converts to a :class:`~discord.Emoji`. """Converts to a :class:`~discord.Emoji`.
@ -430,6 +457,9 @@ class EmojiConverter(IDConverter):
1. Lookup by ID. 1. Lookup by ID.
2. Lookup by extracting ID from the emoji. 2. Lookup by extracting ID from the emoji.
3. Lookup by name 3. Lookup by name
.. versionchanged:: 1.5
Raise :exc:`.EmojiNotFound` instead of generic :exc:`.BadArgument`
""" """
async def convert(self, ctx, argument): async def convert(self, ctx, argument):
match = self._get_id_match(argument) or re.match(r'<a?:[a-zA-Z0-9\_]+:([0-9]+)>$', argument) match = self._get_id_match(argument) or re.match(r'<a?:[a-zA-Z0-9\_]+:([0-9]+)>$', argument)
@ -455,7 +485,7 @@ class EmojiConverter(IDConverter):
result = discord.utils.get(bot.emojis, id=emoji_id) result = discord.utils.get(bot.emojis, id=emoji_id)
if result is None: if result is None:
raise BadArgument('Emoji "{}" not found.'.format(argument)) raise EmojiNotFound(argument)
return result return result
@ -463,6 +493,9 @@ class PartialEmojiConverter(Converter):
"""Converts to a :class:`~discord.PartialEmoji`. """Converts to a :class:`~discord.PartialEmoji`.
This is done by extracting the animated flag, name and ID from the emoji. This is done by extracting the animated flag, name and ID from the emoji.
.. versionchanged:: 1.5
Raise :exc:`.PartialEmojiConversionFailure` instead of generic :exc:`.BadArgument`
""" """
async def convert(self, ctx, argument): async def convert(self, ctx, argument):
match = re.match(r'<(a?):([a-zA-Z0-9\_]+):([0-9]+)>$', argument) match = re.match(r'<(a?):([a-zA-Z0-9\_]+):([0-9]+)>$', argument)
@ -475,7 +508,7 @@ class PartialEmojiConverter(Converter):
return discord.PartialEmoji.with_state(ctx.bot._connection, animated=emoji_animated, name=emoji_name, return discord.PartialEmoji.with_state(ctx.bot._connection, animated=emoji_animated, name=emoji_name,
id=emoji_id) id=emoji_id)
raise BadArgument('Couldn\'t convert "{}" to PartialEmoji.'.format(argument)) raise PartialEmojiConversionFailure(argument)
class clean_content(Converter): class clean_content(Converter):
"""Converts the argument to mention scrubbed version of """Converts the argument to mention scrubbed version of

2
discord/ext/commands/core.py

@ -107,7 +107,7 @@ def _convert_to_bool(argument):
elif lowered in ('no', 'n', 'false', 'f', '0', 'disable', 'off'): elif lowered in ('no', 'n', 'false', 'f', '0', 'disable', 'off'):
return False return False
else: else:
raise BadArgument(lowered + ' is not a recognised boolean option') raise BadBooleanArgument(lowered)
class _CaseInsensitiveDict(dict): class _CaseInsensitiveDict(dict):
def __contains__(self, k): def __contains__(self, k):

187
discord/ext/commands/errors.py

@ -43,6 +43,17 @@ __all__ = (
'CommandOnCooldown', 'CommandOnCooldown',
'MaxConcurrencyReached', 'MaxConcurrencyReached',
'NotOwner', 'NotOwner',
'MessageNotFound',
'MemberNotFound',
'UserNotFound',
'ChannelNotFound',
'ChannelNotReadable',
'BadColourArgument',
'RoleNotFound',
'BadInviteArgument',
'EmojiNotFound',
'PartialEmojiConversionFailure',
'BadBooleanArgument',
'MissingRole', 'MissingRole',
'BotMissingRole', 'BotMissingRole',
'MissingAnyRole', 'MissingAnyRole',
@ -202,6 +213,182 @@ class NotOwner(CheckFailure):
""" """
pass pass
class MemberNotFound(BadArgument):
"""Exception raised when the member provided was not found in the bot's
cache.
This inherits from :exc:`BadArgument`
.. versionadded:: 1.5
Attributes
-----------
argument: :class:`str`
The member supplied by the caller that was not found
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Member "{}" not found.'.format(argument))
class UserNotFound(BadArgument):
"""Exception raised when the user provided was not found in the bot's
cache.
This inherits from :exc:`BadArgument`
.. versionadded:: 1.5
Attributes
-----------
argument: :class:`str`
The user supplied by the caller that was not found
"""
def __init__(self, argument):
self.argument = argument
super().__init__('User "{}" not found.'.format(argument))
class MessageNotFound(BadArgument):
"""Exception raised when the message provided was not found in the channel.
This inherits from :exc:`BadArgument`
.. versionadded:: 1.5
Attributes
-----------
argument: :class:`str`
The message supplied by the caller that was not found
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Message "{}" not found.'.format(argument))
class ChannelNotReadable(BadArgument):
"""Exception raised when the bot does not have permission to read messages
in the channel.
This inherits from :exc:`BadArgument`
.. versionadded:: 1.5
Attributes
-----------
argument: :class:`Channel`
The channel supplied by the caller that was not readable
"""
def __init__(self, argument):
self.argument = argument
super().__init__("Can't read messages in {}.".format(argument.mention))
class ChannelNotFound(BadArgument):
"""Exception raised when the bot can not find the channel.
This inherits from :exc:`BadArgument`
.. versionadded:: 1.5
Attributes
-----------
channel: :class:`str`
The channel supplied by the caller that was not found
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Channel "{}" not found.'.format(argument))
class BadColourArgument(BadArgument):
"""Exception raised when the colour is not valid.
This inherits from :exc:`BadArgument`
.. versionadded:: 1.5
Attributes
-----------
argument: :class:`str`
The colour supplied by the caller that was not valid
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Colour "{}" is invalid.'.format(argument))
BadColorArgument = BadColourArgument
class RoleNotFound(BadArgument):
"""Exception raised when the bot can not find the role.
This inherits from :exc:`BadArgument`
.. versionadded:: 1.5
Attributes
-----------
argument: :class:`str`
The role supplied by the caller that was not found
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Role "{}" not found.'.format(argument))
class BadInviteArgument(BadArgument):
"""Exception raised when the invite is invalid or expired.
This inherits from :exc:`BadArgument`
.. versionadded:: 1.5
"""
def __init__(self):
super().__init__('Invite is invalid or expired.')
class EmojiNotFound(BadArgument):
"""Exception raised when the bot can not find the emoji.
This inherits from :exc:`BadArgument`
.. versionadded:: 1.5
Attributes
-----------
argument: :class:`str`
The emoji supplied by the caller that was not found
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Emoji "{}" not found.'.format(argument))
class PartialEmojiConversionFailure(BadArgument):
"""Exception raised when the emoji provided does not match the correct
format.
This inherits from :exc:`BadArgument`
.. versionadded:: 1.5
Attributes
-----------
argument: :class:`str`
The emoji supplied by the caller that did not match the regex
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Couldn\'t convert "{}" to PartialEmoji.'.format(argument))
class BadBooleanArgument(BadArgument):
"""Exception raised when a boolean argument was not convertable.
This inherits from :exc:`BadArgument`
.. versionadded:: 1.5
Attributes
-----------
argument: :class:`str`
The boolean argument supplied by the caller that is not in the predefined list
"""
def __init__(self, argument):
self.argument = argument
super().__init__('{} is not a recognised boolean option'.format(argument))
class DisabledCommand(CommandError): class DisabledCommand(CommandError):
"""Exception raised when the command being invoked is disabled. """Exception raised when the command being invoked is disabled.

44
docs/ext/commands/api.rst

@ -338,6 +338,39 @@ Exceptions
.. autoexception:: discord.ext.commands.NotOwner .. autoexception:: discord.ext.commands.NotOwner
:members: :members:
.. autoexception:: discord.ext.commands.MessageNotFound
:members:
.. autoexception:: discord.ext.commands.MemberNotFound
:members:
.. autoexception:: discord.ext.commands.UserNotFound
:members:
.. autoexception:: discord.ext.commands.ChannelNotFound
:members:
.. autoexception:: discord.ext.commands.ChannelNotReadable
:members:
.. autoexception:: discord.ext.commands.InvalidColour
:members:
.. autoexception:: discord.ext.commands.RoleNotFound
:members:
.. autoexception:: discord.ext.commands.InvalidInvite
:members:
.. autoexception:: discord.ext.commands.EmojiNotFound
:members:
.. autoexception:: discord.ext.commands.PartialEmojiConversionFailure
:members:
.. autoexception:: discord.ext.commands.BadBooleanArgument
:members:
.. autoexception:: discord.ext.commands.MissingPermissions .. autoexception:: discord.ext.commands.MissingPermissions
:members: :members:
@ -393,6 +426,17 @@ Exception Hierarchy
- :exc:`~.commands.MissingRequiredArgument` - :exc:`~.commands.MissingRequiredArgument`
- :exc:`~.commands.TooManyArguments` - :exc:`~.commands.TooManyArguments`
- :exc:`~.commands.BadArgument` - :exc:`~.commands.BadArgument`
- :exc:`~.commands.MessageNotFound`
- :exc:`~.commands.MemberNotFound`
- :exc:`~.commands.UserNotFound`
- :exc:`~.commands.ChannelNotFound`
- :exc:`~.commands.ChannelNotReadable`
- :exc:`~.commands.BadColourArgument`
- :exc:`~.commands.RoleNotFound`
- :exc:`~.commands.BadInviteArgument`
- :exc:`~.commands.EmojiNotFound`
- :exc:`~.commands.PartialEmojiConversionFailure`
- :exc:`~.commands.BadBooleanArgument`
- :exc:`~.commands.BadUnionArgument` - :exc:`~.commands.BadUnionArgument`
- :exc:`~.commands.ArgumentParsingError` - :exc:`~.commands.ArgumentParsingError`
- :exc:`~.commands.UnexpectedQuoteError` - :exc:`~.commands.UnexpectedQuoteError`

Loading…
Cancel
Save