looks like I changed stuff?

This commit is contained in:
iDutchy
2021-06-25 20:29:27 -05:00
78 changed files with 2136 additions and 2539 deletions

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
discord.ext.commands
~~~~~~~~~~~~~~~~~~~~~
@@ -10,8 +8,8 @@ An extension module to facilitate creation of bot commands.
:license: MIT, see LICENSE for more details.
"""
from .bot import Bot, AutoShardedBot, when_mentioned, when_mentioned_or
from .context import Context
from .bot import *
from .context import *
from .core import *
from .errors import *
from .help import *

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
The MIT License (MIT)

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
The MIT License (MIT)
@@ -42,12 +40,19 @@ from . import errors
from .help import HelpCommand, DefaultHelpCommand
from .cog import Cog
__all__ = (
'when_mentioned',
'when_mentioned_or',
'Bot',
'AutoShardedBot',
)
def when_mentioned(bot, msg):
"""A callable that implements a command prefix equivalent to being mentioned.
These are meant to be passed into the :attr:`.Bot.command_prefix` attribute.
"""
return [bot.user.mention + ' ', '<@!%s> ' % bot.user.id]
return [f'<@{bot.user.id}> ', f'<@!{bot.user.id}> ']
def when_mentioned_or(*prefixes):
"""A callable that implements when mentioned or other prefixes provided.
@@ -116,7 +121,7 @@ class BotBase(GroupMixin):
raise TypeError('Both owner_id and owner_ids are set.')
if self.owner_ids and not isinstance(self.owner_ids, collections.abc.Collection):
raise TypeError('owner_ids must be a collection not {0.__class__!r}'.format(self.owner_ids))
raise TypeError(f'owner_ids must be a collection not {self.owner_ids.__class__!r}')
if options.pop('self_bot', False):
self._skip_check = lambda x, y: x != y
@@ -194,7 +199,7 @@ class BotBase(GroupMixin):
if cog and Cog._get_overridden_method(cog.cog_command_error) is not None:
return
print('Ignoring exception in command {}:'.format(context.command), file=sys.stderr)
print(f'Ignoring exception in command {context.command}:', file=sys.stderr)
traceback.print_exception(type(exception), exception, exception.__traceback__, file=sys.stderr)
# global check registration
@@ -986,7 +991,7 @@ class BotBase(GroupMixin):
else:
self.dispatch('command_completion', ctx)
elif ctx.invoked_with:
exc = errors.CommandNotFound('Command "{}" is not found'.format(ctx.invoked_with))
exc = errors.CommandNotFound(f'Command "{ctx.invoked_with}" is not found')
self.dispatch('command_error', ctx, exc)
async def process_commands(self, message):

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
The MIT License (MIT)
@@ -130,7 +128,7 @@ class CogMeta(type):
value = value.__func__
if isinstance(value, _BaseCommand):
if is_static_method:
raise TypeError('Command in method {0}.{1!r} must not be staticmethod.'.format(base, elem))
raise TypeError(f'Command in method {base}.{elem!r} must not be staticmethod.')
if elem.startswith(('cog_', 'bot_')):
raise TypeError(no_bot_cog.format(base, elem))
commands[elem] = value
@@ -285,7 +283,7 @@ class Cog(metaclass=CogMeta):
"""
if name is not None and not isinstance(name, str):
raise TypeError('Cog.listener expected str but received {0.__class__.__name__!r} instead.'.format(name))
raise TypeError(f'Cog.listener expected str but received {name.__class__.__name__!r} instead.')
def decorator(func):
actual = func

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
The MIT License (MIT)
@@ -28,6 +26,10 @@ import re
import discord.abc
import discord.utils
__all__ = (
'Context',
)
class Context(discord.abc.Messageable):
r"""Represents the context in which a command is being invoked under.
@@ -103,7 +105,7 @@ class Context(discord.abc.Messageable):
pattern = re.compile(r"<@!?%s>" % user.id)
return pattern.sub("@%s" % user.display_name.replace('\\', r'\\'), self.prefix)
async def invoke(self, *args, **kwargs):
async def invoke(self, command, /, *args, **kwargs):
r"""|coro|
Calls a command with the arguments given.
@@ -120,10 +122,6 @@ class Context(discord.abc.Messageable):
You must take care in passing the proper arguments when
using this function.
.. warning::
The first parameter passed **must** be the command being invoked.
Parameters
-----------
command: :class:`.Command`
@@ -138,18 +136,12 @@ class Context(discord.abc.Messageable):
TypeError
The command argument to invoke is missing.
"""
try:
command = args[0]
except IndexError:
raise TypeError('Missing command to invoke.') from None
arguments = []
if command.cog is not None:
arguments.append(command.cog)
arguments.append(self)
arguments.extend(args[1:])
arguments.extend(args)
ret = await command.callback(*arguments, **kwargs)
return ret

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
The MIT License (MIT)
@@ -24,14 +22,19 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""
from __future__ import annotations
import re
import inspect
import typing
from typing import TYPE_CHECKING, Generic, Protocol, TypeVar, Union, runtime_checkable
import discord
from .errors import *
if TYPE_CHECKING:
from .context import Context
__all__ = (
'Converter',
'MemberConverter',
@@ -56,6 +59,7 @@ __all__ = (
'Greedy',
)
def _get_from_guilds(bot, getter, argument):
result = None
for guild in bot.guilds:
@@ -64,9 +68,13 @@ def _get_from_guilds(bot, getter, argument):
return result
return result
_utils_get = discord.utils.get
class Converter:
_utils_get = discord.utils.get
T = TypeVar('T', covariant=True)
@runtime_checkable
class Converter(Protocol[T]):
"""The base class of custom converters that require the :class:`.Context`
to be passed to be useful.
@@ -77,7 +85,7 @@ class Converter:
method to do its conversion logic. This method must be a :ref:`coroutine <coroutine>`.
"""
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> T:
"""|coro|
The method to override to do conversion logic.
@@ -102,7 +110,7 @@ class Converter:
"""
raise NotImplementedError('Derived classes need to implement this.')
class IDConverter(Converter):
class IDConverter(Converter[T]):
def __init__(self):
self._id_regex = re.compile(r'([0-9]{15,20})$')
super().__init__()
@@ -110,7 +118,7 @@ class IDConverter(Converter):
def _get_id_match(self, argument):
return self._id_regex.match(argument)
class MemberConverter(IDConverter):
class MemberConverter(IDConverter[discord.Member]):
"""Converts to a :class:`~discord.Member`.
All lookups are via the local guild. If in a DM context, then the lookup
@@ -163,7 +171,7 @@ class MemberConverter(IDConverter):
return None
return members[0]
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.Member:
bot = ctx.bot
match = self._get_id_match(argument) or re.match(r'<@!?([0-9]+)>$', argument)
guild = ctx.guild
@@ -196,7 +204,7 @@ class MemberConverter(IDConverter):
return result
class UserConverter(IDConverter):
class UserConverter(IDConverter[discord.User]):
"""Converts to a :class:`~discord.User`.
All lookups are via the global user cache.
@@ -215,7 +223,7 @@ class UserConverter(IDConverter):
This converter now lazily fetches users from the HTTP APIs if an ID is passed
and it's not available in cache.
"""
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.User:
match = self._get_id_match(argument) or re.match(r'<@!?([0-9]+)>$', argument)
result = None
state = ctx._state
@@ -255,7 +263,7 @@ class UserConverter(IDConverter):
return result
class PartialMessageConverter(Converter):
class PartialMessageConverter(Converter[discord.PartialMessage]):
"""Converts to a :class:`discord.PartialMessage`.
.. versionadded:: 1.7
@@ -266,7 +274,8 @@ class PartialMessageConverter(Converter):
2. By message ID (The message is assumed to be in the context channel.)
3. By message URL
"""
def _get_id_matches(self, argument):
@staticmethod
def _get_id_matches(argument):
id_regex = re.compile(r'(?:(?P<channel_id>[0-9]{15,20})-)?(?P<message_id>[0-9]{15,20})$')
link_regex = re.compile(
r'https?://(?:(ptb|canary|www)\.)?discord(?:app)?\.com/channels/'
@@ -279,14 +288,14 @@ class PartialMessageConverter(Converter):
channel_id = match.group("channel_id")
return int(match.group("message_id")), int(channel_id) if channel_id else None
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.PartialMessage:
message_id, channel_id = self._get_id_matches(argument)
channel = ctx.bot.get_channel(channel_id) if channel_id else ctx.channel
if not channel:
raise ChannelNotFound(channel_id)
return discord.PartialMessage(channel=channel, id=message_id)
class MessageConverter(PartialMessageConverter):
class MessageConverter(IDConverter[discord.Message]):
"""Converts to a :class:`discord.Message`.
.. versionadded:: 1.1
@@ -300,8 +309,8 @@ class MessageConverter(PartialMessageConverter):
.. versionchanged:: 1.5
Raise :exc:`.ChannelNotFound`, :exc:`.MessageNotFound` or :exc:`.ChannelNotReadable` instead of generic :exc:`.BadArgument`
"""
async def convert(self, ctx, argument):
message_id, channel_id = self._get_id_matches(argument)
async def convert(self, ctx: Context, argument: str) -> discord.Message:
message_id, channel_id = PartialMessageConverter._get_id_matches(argument)
message = ctx.bot._connection._get_message(message_id)
if message:
return message
@@ -315,7 +324,7 @@ class MessageConverter(PartialMessageConverter):
except discord.Forbidden:
raise ChannelNotReadable(channel)
class TextChannelConverter(IDConverter):
class TextChannelConverter(IDConverter[discord.TextChannel]):
"""Converts to a :class:`~discord.TextChannel`.
All lookups are via the local guild. If in a DM context, then the lookup
@@ -330,7 +339,7 @@ class TextChannelConverter(IDConverter):
.. versionchanged:: 1.5
Raise :exc:`.ChannelNotFound` instead of generic :exc:`.BadArgument`
"""
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.TextChannel:
bot = ctx.bot
match = self._get_id_match(argument) or re.match(r'<#([0-9]+)>$', argument)
@@ -357,7 +366,7 @@ class TextChannelConverter(IDConverter):
return result
class VoiceChannelConverter(IDConverter):
class VoiceChannelConverter(IDConverter[discord.VoiceChannel]):
"""Converts to a :class:`~discord.VoiceChannel`.
All lookups are via the local guild. If in a DM context, then the lookup
@@ -372,7 +381,7 @@ class VoiceChannelConverter(IDConverter):
.. versionchanged:: 1.5
Raise :exc:`.ChannelNotFound` instead of generic :exc:`.BadArgument`
"""
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.VoiceChannel:
bot = ctx.bot
match = self._get_id_match(argument) or re.match(r'<#([0-9]+)>$', argument)
result = None
@@ -398,7 +407,7 @@ class VoiceChannelConverter(IDConverter):
return result
class StageChannelConverter(IDConverter):
class StageChannelConverter(IDConverter[discord.StageChannel]):
"""Converts to a :class:`~discord.StageChannel`.
.. versionadded:: 1.7
@@ -412,7 +421,7 @@ class StageChannelConverter(IDConverter):
2. Lookup by mention.
3. Lookup by name
"""
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.StageChannel:
bot = ctx.bot
match = self._get_id_match(argument) or re.match(r'<#([0-9]+)>$', argument)
result = None
@@ -438,7 +447,7 @@ class StageChannelConverter(IDConverter):
return result
class CategoryChannelConverter(IDConverter):
class CategoryChannelConverter(IDConverter[discord.CategoryChannel]):
"""Converts to a :class:`~discord.CategoryChannel`.
All lookups are via the local guild. If in a DM context, then the lookup
@@ -453,7 +462,7 @@ class CategoryChannelConverter(IDConverter):
.. versionchanged:: 1.5
Raise :exc:`.ChannelNotFound` instead of generic :exc:`.BadArgument`
"""
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.CategoryChannel:
bot = ctx.bot
match = self._get_id_match(argument) or re.match(r'<#([0-9]+)>$', argument)
@@ -480,7 +489,7 @@ class CategoryChannelConverter(IDConverter):
return result
class StoreChannelConverter(IDConverter):
class StoreChannelConverter(IDConverter[discord.StoreChannel]):
"""Converts to a :class:`~discord.StoreChannel`.
All lookups are via the local guild. If in a DM context, then the lookup
@@ -495,7 +504,7 @@ class StoreChannelConverter(IDConverter):
.. versionadded:: 1.7
"""
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.StoreChannel:
bot = ctx.bot
match = self._get_id_match(argument) or re.match(r'<#([0-9]+)>$', argument)
result = None
@@ -521,7 +530,7 @@ class StoreChannelConverter(IDConverter):
return result
class ColourConverter(Converter):
class ColourConverter(Converter[discord.Colour]):
"""Converts to a :class:`~discord.Colour`.
.. versionchanged:: 1.5
@@ -582,7 +591,7 @@ class ColourConverter(Converter):
blue = self.parse_rgb_number(argument, match.group('b'))
return discord.Color.from_rgb(red, green, blue)
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.Colour:
if argument[0] == '#':
return self.parse_hex_number(argument[1:])
@@ -605,7 +614,7 @@ class ColourConverter(Converter):
ColorConverter = ColourConverter
class RoleConverter(IDConverter):
class RoleConverter(IDConverter[discord.Role]):
"""Converts to a :class:`~discord.Role`.
All lookups are via the local guild. If in a DM context, the converter raises
@@ -620,7 +629,7 @@ class RoleConverter(IDConverter):
.. versionchanged:: 1.5
Raise :exc:`.RoleNotFound` instead of generic :exc:`.BadArgument`
"""
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.Role:
guild = ctx.guild
if not guild:
raise NoPrivateMessage()
@@ -635,12 +644,12 @@ class RoleConverter(IDConverter):
raise RoleNotFound(argument)
return result
class GameConverter(Converter):
class GameConverter(Converter[discord.Game]):
"""Converts to :class:`~discord.Game`."""
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.Game:
return discord.Game(name=argument)
class InviteConverter(Converter):
class InviteConverter(Converter[discord.Invite]):
"""Converts to a :class:`~discord.Invite`.
This is done via an HTTP request using :meth:`.Bot.fetch_invite`.
@@ -648,14 +657,14 @@ class InviteConverter(Converter):
.. versionchanged:: 1.5
Raise :exc:`.BadInviteArgument` instead of generic :exc:`.BadArgument`
"""
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.Invite:
try:
invite = await ctx.bot.fetch_invite(argument)
return invite
except Exception as exc:
raise BadInviteArgument() from exc
class GuildConverter(IDConverter):
class GuildConverter(IDConverter[discord.Guild]):
"""Converts to a :class:`~discord.Guild`.
The lookup strategy is as follows (in order):
@@ -666,7 +675,7 @@ class GuildConverter(IDConverter):
.. versionadded:: 1.7
"""
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.Guild:
match = self._get_id_match(argument)
result = None
@@ -681,7 +690,7 @@ class GuildConverter(IDConverter):
raise GuildNotFound(argument)
return result
class EmojiConverter(IDConverter):
class EmojiConverter(IDConverter[discord.Emoji]):
"""Converts to a :class:`~discord.Emoji`.
All lookups are done for the local guild first, if available. If that lookup
@@ -696,7 +705,7 @@ class EmojiConverter(IDConverter):
.. versionchanged:: 1.5
Raise :exc:`.EmojiNotFound` instead of generic :exc:`.BadArgument`
"""
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.Emoji:
match = self._get_id_match(argument) or re.match(r'<a?:[a-zA-Z0-9\_]+:([0-9]+)>$', argument)
result = None
bot = ctx.bot
@@ -724,7 +733,7 @@ class EmojiConverter(IDConverter):
return result
class PartialEmojiConverter(Converter):
class PartialEmojiConverter(Converter[discord.PartialEmoji]):
"""Converts to a :class:`~discord.PartialEmoji`.
This is done by extracting the animated flag, name and ID from the emoji.
@@ -732,7 +741,7 @@ class PartialEmojiConverter(Converter):
.. versionchanged:: 1.5
Raise :exc:`.PartialEmojiConversionFailure` instead of generic :exc:`.BadArgument`
"""
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> discord.PartialEmoji:
match = re.match(r'<(a?):([a-zA-Z0-9\_]+):([0-9]+)>$', argument)
if match:
@@ -745,7 +754,7 @@ class PartialEmojiConverter(Converter):
raise PartialEmojiConversionFailure(argument)
class clean_content(Converter):
class clean_content(Converter[str]):
"""Converts the argument to mention scrubbed version of
said content.
@@ -770,14 +779,14 @@ class clean_content(Converter):
self.escape_markdown = escape_markdown
self.remove_markdown = remove_markdown
async def convert(self, ctx, argument):
async def convert(self, ctx: Context, argument: str) -> str:
message = ctx.message
transformations = {}
if self.fix_channel_mentions and ctx.guild:
def resolve_channel(id, *, _get=ctx.guild.get_channel):
ch = _get(id)
return ('<#%s>' % id), ('#' + ch.name if ch else '#deleted-channel')
return f'<#{id}>', ('#' + ch.name if ch else '#deleted-channel')
transformations.update(resolve_channel(channel) for channel in message.raw_channel_mentions)
@@ -792,12 +801,12 @@ class clean_content(Converter):
transformations.update(
('<@%s>' % member_id, resolve_member(member_id))
(f'<@{member_id}>', resolve_member(member_id))
for member_id in message.raw_mentions
)
transformations.update(
('<@!%s>' % member_id, resolve_member(member_id))
(f'<@!{member_id}>', resolve_member(member_id))
for member_id in message.raw_mentions
)
@@ -807,7 +816,7 @@ class clean_content(Converter):
return '@' + r.name if r else '@deleted-role'
transformations.update(
('<@&%s>' % role_id, resolve_role(role_id))
(f'<@&{role_id}>', resolve_role(role_id))
for role_id in message.raw_role_mentions
)
@@ -842,10 +851,10 @@ class _Greedy:
raise TypeError('Greedy[...] expects a type or a Converter instance.')
if converter is str or converter is type(None) or converter is _Greedy:
raise TypeError('Greedy[%s] is invalid.' % converter.__name__)
raise TypeError(f'Greedy[{converter.__name__}] is invalid.')
if getattr(converter, '__origin__', None) is typing.Union and type(None) in converter.__args__:
raise TypeError('Greedy[%r] is invalid.' % converter)
if getattr(converter, '__origin__', None) is Union and type(None) in converter.__args__:
raise TypeError(f'Greedy[{converter!r}] is invalid.')
return self.__class__(converter=converter)

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
The MIT License (MIT)
@@ -255,7 +253,7 @@ class MaxConcurrency:
raise ValueError('max_concurrency \'number\' cannot be less than 1')
if not isinstance(per, BucketType):
raise TypeError('max_concurrency \'per\' must be of type BucketType not %r' % type(per))
raise TypeError(f'max_concurrency \'per\' must be of type BucketType not {type(per)!r}')
def copy(self):
return self.__class__(self.number, per=self.per, wait=self.wait)

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
The MIT License (MIT)
@@ -29,6 +27,7 @@ import functools
import inspect
import typing
import datetime
import sys
import discord
@@ -301,17 +300,37 @@ class Command(_BaseCommand):
signature = inspect.signature(function)
self.params = signature.parameters.copy()
# PEP-563 allows postponing evaluation of annotations with a __future__
# import. When postponed, Parameter.annotation will be a string and must
# be replaced with the real value for the converters to work later on
# see: https://bugs.python.org/issue41341
resolve = self._recursive_resolve if sys.version_info < (3, 9) else self._return_resolved
try:
type_hints = {k: resolve(v) for k, v in typing.get_type_hints(function).items()}
except NameError as e:
raise NameError(f'unresolved forward reference: {e.args[0]}') from None
for key, value in self.params.items():
if isinstance(value.annotation, str):
self.params[key] = value = value.replace(annotation=eval(value.annotation, function.__globals__))
# coalesce the forward references
if key in type_hints:
self.params[key] = value = value.replace(annotation=type_hints[key])
# fail early for when someone passes an unparameterized Greedy type
if value.annotation is converters.Greedy:
raise TypeError('Unparameterized Greedy[...] is disallowed in signature.')
def _return_resolved(self, type, **kwargs):
return type
def _recursive_resolve(self, type, *, globals=None):
if not isinstance(type, typing.ForwardRef):
return type
resolved = eval(type.__forward_arg__, globals)
args = typing.get_args(resolved)
for index, arg in enumerate(args):
inner_resolve_result = self._recursive_resolve(arg, globals=globals)
resolved[index] = inner_resolve_result
return resolved
def add_check(self, func):
"""Adds a check to the command.
@@ -445,19 +464,13 @@ class Command(_BaseCommand):
converter = getattr(converters, converter.__name__ + 'Converter', converter)
try:
if inspect.isclass(converter):
if issubclass(converter, converters.Converter):
instance = converter()
ret = await instance.convert(ctx, argument)
return ret
if inspect.isclass(converter) and issubclass(converter, converters.Converter):
if inspect.ismethod(converter.convert):
return await converter.convert(ctx, argument)
else:
method = getattr(converter, 'convert', None)
if method is not None and inspect.ismethod(method):
ret = await method(ctx, argument)
return ret
return await converter().convert(ctx, argument)
elif isinstance(converter, converters.Converter):
ret = await converter.convert(ctx, argument)
return ret
return await converter.convert(ctx, argument)
except CommandError:
raise
except Exception as exc:
@@ -473,7 +486,7 @@ class Command(_BaseCommand):
except AttributeError:
name = converter.__class__.__name__
raise BadArgument('Converting to "{}" failed for parameter "{}".'.format(name, param.name)) from exc
raise BadArgument(f'Converting to "{name}" failed for parameter "{param.name}".') from exc
async def do_conversion(self, ctx, converter, argument, param):
try:
@@ -775,7 +788,7 @@ class Command(_BaseCommand):
ctx.command = self
if not await self.can_run(ctx):
raise CheckFailure('The check functions for command {0.qualified_name} failed.'.format(self))
raise CheckFailure(f'The check functions for command {self.qualified_name} failed.')
if self._max_concurrency is not None:
await self._max_concurrency.acquire(ctx)
@@ -1014,23 +1027,23 @@ class Command(_BaseCommand):
# do [name] since [name=None] or [name=] are not exactly useful for the user.
should_print = param.default if isinstance(param.default, str) else param.default is not None
if should_print:
result.append('[%s=%s]' % (name, param.default) if not greedy else
'[%s=%s]...' % (name, param.default))
result.append(f'[{name}={param.default}]' if not greedy else
f'[{name}={param.default}]...')
continue
else:
result.append('[%s]' % name)
result.append(f'[{name}]')
elif param.kind == param.VAR_POSITIONAL:
if self.require_var_positional:
result.append('<%s...>' % name)
result.append(f'<{name}...>')
else:
result.append('[%s...]' % name)
result.append(f'[{name}...]')
elif greedy:
result.append('[%s]...' % name)
result.append(f'[{name}]...')
elif self._is_typing_optional(param.annotation):
result.append('[%s]' % name)
result.append(f'[{name}]')
else:
result.append('<%s>' % name)
result.append(f'<{name}>')
return ' '.join(result)
@@ -1062,14 +1075,14 @@ class Command(_BaseCommand):
"""
if not self.enabled:
raise DisabledCommand('{0.name} command is disabled'.format(self))
raise DisabledCommand(f'{self.name} command is disabled')
original = ctx.command
ctx.command = self
try:
if not await ctx.bot.can_run(ctx):
raise CheckFailure('The global check functions for command {0.qualified_name} failed.'.format(self))
raise CheckFailure(f'The global check functions for command {self.qualified_name} failed.')
cog = self.cog
if cog is not None:
@@ -1588,7 +1601,7 @@ def check_any(*checks):
try:
pred = wrapped.predicate
except AttributeError:
raise TypeError('%r must be wrapped by commands.check decorator' % wrapped) from None
raise TypeError(f'{wrapped!r} must be wrapped by commands.check decorator') from None
else:
unwrapped.append(pred)
@@ -1776,7 +1789,7 @@ def has_permissions(**perms):
invalid = set(perms) - set(discord.Permissions.VALID_FLAGS)
if invalid:
raise TypeError('Invalid permission(s): %s' % (', '.join(invalid)))
raise TypeError(f"Invalid permission(s): {', '.join(invalid)}")
def predicate(ctx):
ch = ctx.channel
@@ -1801,7 +1814,7 @@ def bot_has_permissions(**perms):
invalid = set(perms) - set(discord.Permissions.VALID_FLAGS)
if invalid:
raise TypeError('Invalid permission(s): %s' % (', '.join(invalid)))
raise TypeError(f"Invalid permission(s): {', '.join(invalid)}")
def predicate(ctx):
guild = ctx.guild
@@ -1829,7 +1842,7 @@ def has_guild_permissions(**perms):
invalid = set(perms) - set(discord.Permissions.VALID_FLAGS)
if invalid:
raise TypeError('Invalid permission(s): %s' % (', '.join(invalid)))
raise TypeError(f"Invalid permission(s): {', '.join(invalid)}")
def predicate(ctx):
if not ctx.guild:
@@ -1854,7 +1867,7 @@ def bot_has_guild_permissions(**perms):
invalid = set(perms) - set(discord.Permissions.VALID_FLAGS)
if invalid:
raise TypeError('Invalid permission(s): %s' % (', '.join(invalid)))
raise TypeError(f"Invalid permission(s): {', '.join(invalid)}")
def predicate(ctx):
if not ctx.guild:
@@ -1961,7 +1974,7 @@ def cooldown(rate, per, type=BucketType.default):
The amount of seconds to wait for a cooldown when it's been triggered.
type: Union[:class:`.BucketType`, Callable[[:class:`.Message`], Any]]
The type of cooldown to have. If callable, should return a key for the mapping.
.. versionchanged:: 1.7
Callables are now supported for custom bucket types.
"""

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
The MIT License (MIT)
@@ -143,7 +141,7 @@ class MissingRequiredArgument(UserInputError):
"""
def __init__(self, param):
self.param = param
super().__init__('{0.name} is a required argument that is missing.'.format(param))
super().__init__(f'{param.name} is a required argument that is missing.')
class TooManyArguments(UserInputError):
"""Exception raised when the command was passed too many arguments and its
@@ -229,7 +227,7 @@ class MemberNotFound(BadArgument):
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Member "{}" not found.'.format(argument))
super().__init__(f'Member "{argument}" not found.')
class GuildNotFound(BadArgument):
"""Exception raised when the guild provided was not found in the bot's cache.
@@ -245,7 +243,7 @@ class GuildNotFound(BadArgument):
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Guild "{}" not found.'.format(argument))
super().__init__(f'Guild "{argument}" not found.')
class UserNotFound(BadArgument):
"""Exception raised when the user provided was not found in the bot's
@@ -262,7 +260,7 @@ class UserNotFound(BadArgument):
"""
def __init__(self, argument):
self.argument = argument
super().__init__('User "{}" not found.'.format(argument))
super().__init__(f'User "{argument}" not found.')
class MessageNotFound(BadArgument):
"""Exception raised when the message provided was not found in the channel.
@@ -278,7 +276,7 @@ class MessageNotFound(BadArgument):
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Message "{}" not found.'.format(argument))
super().__init__(f'Message "{argument}" not found.')
class ChannelNotReadable(BadArgument):
"""Exception raised when the bot does not have permission to read messages
@@ -295,7 +293,7 @@ class ChannelNotReadable(BadArgument):
"""
def __init__(self, argument):
self.argument = argument
super().__init__("Can't read messages in {}.".format(argument.mention))
super().__init__(f"Can't read messages in {argument.mention}.")
class ChannelNotFound(BadArgument):
"""Exception raised when the bot can not find the channel.
@@ -311,7 +309,7 @@ class ChannelNotFound(BadArgument):
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Channel "{}" not found.'.format(argument))
super().__init__(f'Channel "{argument}" not found.')
class BadColourArgument(BadArgument):
"""Exception raised when the colour is not valid.
@@ -327,7 +325,7 @@ class BadColourArgument(BadArgument):
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Colour "{}" is invalid.'.format(argument))
super().__init__(f'Colour "{argument}" is invalid.')
BadColorArgument = BadColourArgument
@@ -345,7 +343,7 @@ class RoleNotFound(BadArgument):
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Role "{}" not found.'.format(argument))
super().__init__(f'Role "{argument}" not found.')
class BadInviteArgument(BadArgument):
"""Exception raised when the invite is invalid or expired.
@@ -371,7 +369,7 @@ class EmojiNotFound(BadArgument):
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Emoji "{}" not found.'.format(argument))
super().__init__(f'Emoji "{argument}" not found.')
class PartialEmojiConversionFailure(BadArgument):
"""Exception raised when the emoji provided does not match the correct
@@ -388,7 +386,7 @@ class PartialEmojiConversionFailure(BadArgument):
"""
def __init__(self, argument):
self.argument = argument
super().__init__('Couldn\'t convert "{}" to PartialEmoji.'.format(argument))
super().__init__(f'Couldn\'t convert "{argument}" to PartialEmoji.')
class BadBoolArgument(BadArgument):
"""Exception raised when a boolean argument was not convertable.
@@ -404,7 +402,7 @@ class BadBoolArgument(BadArgument):
"""
def __init__(self, argument):
self.argument = argument
super().__init__('{} is not a recognised boolean option'.format(argument))
super().__init__(f'{argument} is not a recognised boolean option')
class DisabledCommand(CommandError):
"""Exception raised when the command being invoked is disabled.
@@ -444,7 +442,7 @@ class CommandOnCooldown(CommandError):
def __init__(self, cooldown, retry_after):
self.cooldown = cooldown
self.retry_after = retry_after
super().__init__('You are on cooldown. Try again in {:.2f}s'.format(retry_after))
super().__init__(f'You are on cooldown. Try again in {retry_after:.2f}s')
class MaxConcurrencyReached(CommandError):
"""Exception raised when the command being invoked has reached its maximum concurrency.
@@ -466,7 +464,7 @@ class MaxConcurrencyReached(CommandError):
suffix = 'per %s' % name if per.name != 'default' else 'globally'
plural = '%s times %s' if number > 1 else '%s time %s'
fmt = plural % (number, suffix)
super().__init__('Too many people using this command. It can only be used {} concurrently.'.format(fmt))
super().__init__(f'Too many people using this command. It can only be used {fmt} concurrently.')
class MissingRole(CheckFailure):
"""Exception raised when the command invoker lacks a role to run a command.
@@ -483,7 +481,7 @@ class MissingRole(CheckFailure):
"""
def __init__(self, missing_role):
self.missing_role = missing_role
message = 'Role {0!r} is required to run this command.'.format(missing_role)
message = f'Role {missing_role!r} is required to run this command.'
super().__init__(message)
class BotMissingRole(CheckFailure):
@@ -501,7 +499,7 @@ class BotMissingRole(CheckFailure):
"""
def __init__(self, missing_role):
self.missing_role = missing_role
message = 'Bot requires the role {0!r} to run this command'.format(missing_role)
message = f'Bot requires the role {missing_role!r} to run this command'
super().__init__(message)
class MissingAnyRole(CheckFailure):
@@ -521,14 +519,14 @@ class MissingAnyRole(CheckFailure):
def __init__(self, missing_roles):
self.missing_roles = missing_roles
missing = ["'{}'".format(role) for role in missing_roles]
missing = [f"'{role}'" for role in missing_roles]
if len(missing) > 2:
fmt = '{}, or {}'.format(", ".join(missing[:-1]), missing[-1])
else:
fmt = ' or '.join(missing)
message = "You are missing at least one of the required roles: {}".format(fmt)
message = f"You are missing at least one of the required roles: {fmt}"
super().__init__(message)
@@ -550,14 +548,14 @@ class BotMissingAnyRole(CheckFailure):
def __init__(self, missing_roles):
self.missing_roles = missing_roles
missing = ["'{}'".format(role) for role in missing_roles]
missing = [f"'{role}'" for role in missing_roles]
if len(missing) > 2:
fmt = '{}, or {}'.format(", ".join(missing[:-1]), missing[-1])
else:
fmt = ' or '.join(missing)
message = "Bot is missing at least one of the required roles: {}".format(fmt)
message = f"Bot is missing at least one of the required roles: {fmt}"
super().__init__(message)
class NSFWChannelRequired(CheckFailure):
@@ -574,7 +572,7 @@ class NSFWChannelRequired(CheckFailure):
"""
def __init__(self, channel):
self.channel = channel
super().__init__("Channel '{}' needs to be NSFW for this command to work.".format(channel))
super().__init__(f"Channel '{channel}' needs to be NSFW for this command to work.")
class MissingPermissions(CheckFailure):
"""Exception raised when the command invoker lacks permissions to run a
@@ -596,7 +594,7 @@ class MissingPermissions(CheckFailure):
fmt = '{}, and {}'.format(", ".join(missing[:-1]), missing[-1])
else:
fmt = ' and '.join(missing)
message = 'You are missing {} permission(s) to run this command.'.format(fmt)
message = f'You are missing {fmt} permission(s) to run this command.'
super().__init__(message, *args)
class BotMissingPermissions(CheckFailure):
@@ -619,7 +617,7 @@ class BotMissingPermissions(CheckFailure):
fmt = '{}, and {}'.format(", ".join(missing[:-1]), missing[-1])
else:
fmt = ' and '.join(missing)
message = 'Bot requires {} permission(s) to run this command.'.format(fmt)
message = f'Bot requires {fmt} permission(s) to run this command.'
super().__init__(message, *args)
class BadUnionArgument(UserInputError):
@@ -654,7 +652,7 @@ class BadUnionArgument(UserInputError):
else:
fmt = ' or '.join(to_string)
super().__init__('Could not convert "{0.name}" into {1}.'.format(param, fmt))
super().__init__(f'Could not convert "{param.name}" into {fmt}.')
class ArgumentParsingError(UserInputError):
"""An exception raised when the parser fails to parse a user's input.
@@ -678,7 +676,7 @@ class UnexpectedQuoteError(ArgumentParsingError):
"""
def __init__(self, quote):
self.quote = quote
super().__init__('Unexpected quote mark, {0!r}, in non-quoted string'.format(quote))
super().__init__(f'Unexpected quote mark, {quote!r}, in non-quoted string')
class InvalidEndOfQuotedStringError(ArgumentParsingError):
"""An exception raised when a space is expected after the closing quote in a string
@@ -693,7 +691,7 @@ class InvalidEndOfQuotedStringError(ArgumentParsingError):
"""
def __init__(self, char):
self.char = char
super().__init__('Expected space after closing quotation but received {0!r}'.format(char))
super().__init__(f'Expected space after closing quotation but received {char!r}')
class ExpectedClosingQuoteError(ArgumentParsingError):
"""An exception raised when a quote character is expected but not found.
@@ -708,7 +706,7 @@ class ExpectedClosingQuoteError(ArgumentParsingError):
def __init__(self, close_quote):
self.close_quote = close_quote
super().__init__('Expected closing {}.'.format(close_quote))
super().__init__(f'Expected closing {close_quote}.')
class ExtensionError(DiscordException):
"""Base exception for extension related errors.
@@ -722,7 +720,7 @@ class ExtensionError(DiscordException):
"""
def __init__(self, message=None, *args, name):
self.name = name
message = message or 'Extension {!r} had an error.'.format(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')
super().__init__(m, *args)
@@ -733,7 +731,7 @@ class ExtensionAlreadyLoaded(ExtensionError):
This inherits from :exc:`ExtensionError`
"""
def __init__(self, name):
super().__init__('Extension {!r} is already loaded.'.format(name), name=name)
super().__init__(f'Extension {name!r} is already loaded.', name=name)
class ExtensionNotLoaded(ExtensionError):
"""An exception raised when an extension was not loaded.
@@ -741,7 +739,7 @@ class ExtensionNotLoaded(ExtensionError):
This inherits from :exc:`ExtensionError`
"""
def __init__(self, name):
super().__init__('Extension {!r} has not been loaded.'.format(name), name=name)
super().__init__(f'Extension {name!r} has not been loaded.', name=name)
class NoEntryPointError(ExtensionError):
"""An exception raised when an extension does not have a ``setup`` entry point function.
@@ -749,7 +747,7 @@ class NoEntryPointError(ExtensionError):
This inherits from :exc:`ExtensionError`
"""
def __init__(self, name):
super().__init__("Extension {!r} has no 'setup' function.".format(name), name=name)
super().__init__(f"Extension {name!r} has no 'setup' function.", name=name)
class ExtensionFailed(ExtensionError):
"""An exception raised when an extension failed to load during execution of the module or ``setup`` entry point.
@@ -808,4 +806,4 @@ class CommandRegistrationError(ClientException):
self.name = name
self.alias_conflict = alias_conflict
type_ = 'alias' if alias_conflict else 'command'
super().__init__('The {} {} is already an existing command or alias.'.format(type_, name))
super().__init__(f'The {type_} {name} is already an existing command or alias.')

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
The MIT License (MIT)
@@ -132,7 +130,7 @@ class Paginator:
"""
max_page_size = self.max_size - self._prefix_len - self._suffix_len - 2 * self._linesep_len
if len(line) > max_page_size:
raise RuntimeError('Line exceeds maximum page size %s' % (max_page_size))
raise RuntimeError(f'Line exceeds maximum page size {max_page_size}')
if self._count + len(line) + self._linesep_len > self.max_size - self._suffix_len:
self.close_page()
@@ -386,8 +384,9 @@ class HelpCommand:
# consider this to be an *incredibly* strange use case. I'd rather go
# for this common use case rather than waste performance for the
# odd one.
pattern = re.compile(r"<@!?%s>" % user.id)
return pattern.sub("@%s" % user.display_name.replace('\\', r'\\'), self.context.prefix)
pattern = re.compile(fr"<@!?{user.id}>")
display_name = user.display_name.replace('\\', r'\\')
return pattern.sub('@' + display_name, self.context.prefix)
@property
def invoked_with(self):
@@ -436,14 +435,14 @@ class HelpCommand:
if len(command.aliases) > 0:
aliases = '|'.join(command.aliases)
fmt = '[%s|%s]' % (command.name, aliases)
fmt = f'[{command.name}|{aliases}]'
if parent_sig:
fmt = parent_sig + ' ' + fmt
alias = fmt
else:
alias = command.name if not parent_sig else parent_sig + ' ' + command.name
return '%s%s %s' % (self.clean_prefix, alias, command.signature)
return f'{self.clean_prefix}{alias} {command.signature}'
def remove_mentions(self, string):
"""Removes mentions from the string to prevent abuse.
@@ -506,7 +505,7 @@ class HelpCommand:
:class:`str`
The string to use when a command has not been found.
"""
return 'No command called "{}" found.'.format(string)
return f'No command called "{string}" found.'
def subcommand_not_found(self, command, string):
"""|maybecoro|
@@ -535,8 +534,8 @@ class HelpCommand:
The string to use when the command did not have the subcommand requested.
"""
if isinstance(command, Group) and len(command.all_commands) > 0:
return 'Command "{0.qualified_name}" has no subcommand named {1}'.format(command, string)
return 'Command "{0.qualified_name}" has no subcommands.'.format(command)
return f'Command "{command.qualified_name}" has no subcommand named {string}'
return f'Command "{command.qualified_name}" has no subcommands.'
async def filter_commands(self, commands, *, sort=False, key=None):
"""|coro|
@@ -941,8 +940,8 @@ class DefaultHelpCommand(HelpCommand):
def get_ending_note(self):
""":class:`str`: Returns help command's ending note. This is mainly useful to override for i18n purposes."""
command_name = self.invoked_with
return "Type {0}{1} command for more info on a command.\n" \
"You can also type {0}{1} category for more info on a category.".format(self.clean_prefix, command_name)
return f"Type {self.clean_prefix}{command_name} command for more info on a command.\n" \
f"You can also type {self.clean_prefix}{command_name} category for more info on a category."
def add_indented_commands(self, commands, *, heading, max_size=None):
"""Indents a list of commands after the specified heading.
@@ -977,7 +976,7 @@ class DefaultHelpCommand(HelpCommand):
for command in commands:
name = command.name
width = max_size - (get_width(name) - len(name))
entry = '{0}{1:<{width}} {2}'.format(self.indent * ' ', name, command.short_doc, width=width)
entry = f'{self.indent * " "}{name:<{width}} {command.short_doc}'
self.paginator.add_line(self.shorten_text(entry))
async def send_pages(self):
@@ -1030,7 +1029,7 @@ class DefaultHelpCommand(HelpCommand):
# <description> portion
self.paginator.add_line(bot.description, empty=True)
no_category = '\u200b{0.no_category}:'.format(self)
no_category = f'\u200b{self.no_category}:'
def get_category(command, *, no_category=no_category):
cog = command.cog
return cog.qualified_name + ':' if cog is not None else no_category
@@ -1154,7 +1153,7 @@ class MinimalHelpCommand(HelpCommand):
"You can also use `{0}{1} [category]` for more info on a category.".format(self.clean_prefix, command_name)
def get_command_signature(self, command):
return '%s%s %s' % (self.clean_prefix, command.qualified_name, command.signature)
return f'{self.clean_prefix}{command.qualified_name} {command.signature}'
def get_ending_note(self):
"""Return the help command's ending note. This is mainly useful to override for i18n purposes.
@@ -1186,7 +1185,7 @@ class MinimalHelpCommand(HelpCommand):
if commands:
# U+2002 Middle Dot
joined = '\u2002'.join(c.name for c in commands)
self.paginator.add_line('__**%s**__' % heading)
self.paginator.add_line(f'__**{heading}**__')
self.paginator.add_line(joined)
def add_subcommand_formatting(self, command):
@@ -1220,7 +1219,7 @@ class MinimalHelpCommand(HelpCommand):
aliases: Sequence[:class:`str`]
A list of aliases to format.
"""
self.paginator.add_line('**%s** %s' % (self.aliases_heading, ', '.join(aliases)), empty=True)
self.paginator.add_line(f'**{self.aliases_heading}** {", ".join(aliases)}', empty=True)
def add_command_formatting(self, command):
"""A utility function to format commands and groups.
@@ -1273,7 +1272,7 @@ class MinimalHelpCommand(HelpCommand):
if note:
self.paginator.add_line(note, empty=True)
no_category = '\u200b{0.no_category}'.format(self)
no_category = f'\u200b{self.no_category}'
def get_category(command, *, no_category=no_category):
cog = command.cog
return cog.qualified_name if cog is not None else no_category
@@ -1306,7 +1305,7 @@ class MinimalHelpCommand(HelpCommand):
filtered = await self.filter_commands(cog.get_commands(), sort=self.sort_commands)
if filtered:
self.paginator.add_line('**%s %s**' % (cog.qualified_name, self.commands_heading))
self.paginator.add_line(f'**{cog.qualified_name} {self.commands_heading}**')
for command in filtered:
self.add_subcommand_formatting(command)
@@ -1326,7 +1325,7 @@ class MinimalHelpCommand(HelpCommand):
if note:
self.paginator.add_line(note, empty=True)
self.paginator.add_line('**%s**' % self.commands_heading)
self.paginator.add_line(f'**{self.commands_heading}**')
for command in filtered:
self.add_subcommand_formatting(command)

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
The MIT License (MIT)

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
"""
The MIT License (MIT)
@@ -37,6 +35,10 @@ from discord.backoff import ExponentialBackoff
log = logging.getLogger(__name__)
__all__ = (
'loop',
)
class Loop:
"""A background task helper that abstracts the loop and reconnection logic for you.
@@ -289,9 +291,9 @@ class Loop:
for exc in exceptions:
if not inspect.isclass(exc):
raise TypeError('{0!r} must be a class.'.format(exc))
raise TypeError(f'{exc!r} must be a class.')
if not issubclass(exc, BaseException):
raise TypeError('{0!r} must inherit from BaseException.'.format(exc))
raise TypeError(f'{exc!r} must inherit from BaseException.')
self._valid_exception = (*self._valid_exception, *exceptions)
@@ -345,7 +347,7 @@ class Loop:
async def _error(self, *args):
exception = args[-1]
print('Unhandled exception in internal background task {0.__name__!r}.'.format(self.coro), file=sys.stderr)
print(f'Unhandled exception in internal background task {self.coro.__name__!r}.', file=sys.stderr)
traceback.print_exception(type(exception), exception, exception.__traceback__, file=sys.stderr)
def before_loop(self, coro):