mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-05-13 09:19:48 +00:00
[commands] Dispatch command_error on command exec error.
Provide fallback on_command_error - will only fire if no cog handlers and no local handler. Propagate exceptions in checks and argument parsing to bot.
This commit is contained in:
parent
329f916e10
commit
33a69681fc
@ -29,11 +29,12 @@ import discord
|
||||
import inspect
|
||||
import importlib
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from .core import GroupMixin, Command, command
|
||||
from .view import StringView
|
||||
from .context import Context
|
||||
from .errors import CommandNotFound
|
||||
from .errors import CommandNotFound, CommandError
|
||||
from .formatter import HelpFormatter
|
||||
|
||||
def _get_variable(name):
|
||||
@ -247,6 +248,26 @@ class Bot(GroupMixin, discord.Client):
|
||||
coro = self._run_extra(event, event_name, *args, **kwargs)
|
||||
discord.compat.create_task(coro, loop=self.loop)
|
||||
|
||||
@asyncio.coroutine
|
||||
def on_command_error(self, exception, context):
|
||||
"""|coro|
|
||||
|
||||
The default command error handler provided by the bot.
|
||||
|
||||
By default this prints to ``sys.stderr`` however it could be
|
||||
overridden to have a different implementation.
|
||||
|
||||
This only fires if you do not specify any listeners for command error.
|
||||
"""
|
||||
if self.extra_events.get('on_command_error', None):
|
||||
return
|
||||
|
||||
if hasattr(context.command, "on_error"):
|
||||
return
|
||||
|
||||
print('Ignoring exception in command {}'.format(context.command), file=sys.stderr)
|
||||
traceback.print_exception(type(exception), exception, exception.__traceback__, file=sys.stderr)
|
||||
|
||||
# utility "send_*" functions
|
||||
|
||||
def say(self, *args, **kwargs):
|
||||
@ -618,8 +639,13 @@ class Bot(GroupMixin, discord.Client):
|
||||
command = self.commands[invoker]
|
||||
self.dispatch('command', command, ctx)
|
||||
ctx.command = command
|
||||
yield from command.invoke(ctx)
|
||||
self.dispatch('command_completion', command, ctx)
|
||||
try:
|
||||
yield from command.invoke(ctx)
|
||||
except CommandError as e:
|
||||
command.handle_local_error(e, ctx)
|
||||
self.dispatch('command_error', e, ctx)
|
||||
else:
|
||||
self.dispatch('command_completion', command, ctx)
|
||||
else:
|
||||
exc = CommandNotFound('Command "{}" is not found'.format(invoker))
|
||||
self.dispatch('command_error', exc, ctx)
|
||||
|
@ -44,7 +44,10 @@ def inject_context(ctx, coro):
|
||||
_internal_channel = ctx.message.channel
|
||||
_internal_author = ctx.message.author
|
||||
|
||||
ret = yield from coro(*args, **kwargs)
|
||||
try:
|
||||
ret = yield from coro(*args, **kwargs)
|
||||
except Exception as e:
|
||||
raise CommandError("Exception raised while executing command") from e
|
||||
return ret
|
||||
return wrapped
|
||||
|
||||
@ -306,72 +309,60 @@ class Command:
|
||||
|
||||
@asyncio.coroutine
|
||||
def _parse_arguments(self, ctx):
|
||||
try:
|
||||
ctx.args = [] if self.instance is None else [self.instance]
|
||||
ctx.kwargs = {}
|
||||
args = ctx.args
|
||||
kwargs = ctx.kwargs
|
||||
ctx.args = [] if self.instance is None else [self.instance]
|
||||
ctx.kwargs = {}
|
||||
args = ctx.args
|
||||
kwargs = ctx.kwargs
|
||||
|
||||
first = True
|
||||
view = ctx.view
|
||||
iterator = iter(self.params.items())
|
||||
first = True
|
||||
view = ctx.view
|
||||
iterator = iter(self.params.items())
|
||||
|
||||
if self.instance is not None:
|
||||
# we have 'self' as the first parameter so just advance
|
||||
# the iterator and resume parsing
|
||||
try:
|
||||
next(iterator)
|
||||
except StopIteration:
|
||||
fmt = 'Callback for {0.name} command is missing "self" parameter.'
|
||||
raise discord.ClientException(fmt.format(self))
|
||||
if self.instance is not None:
|
||||
# we have 'self' as the first parameter so just advance
|
||||
# the iterator and resume parsing
|
||||
try:
|
||||
next(iterator)
|
||||
except StopIteration:
|
||||
fmt = 'Callback for {0.name} command is missing "self" parameter.'
|
||||
raise discord.ClientException(fmt.format(self))
|
||||
|
||||
for name, param in iterator:
|
||||
if first and self.pass_context:
|
||||
args.append(ctx)
|
||||
first = False
|
||||
continue
|
||||
for name, param in iterator:
|
||||
if first and self.pass_context:
|
||||
args.append(ctx)
|
||||
first = False
|
||||
continue
|
||||
|
||||
if param.kind == param.POSITIONAL_OR_KEYWORD:
|
||||
transformed = yield from self.transform(ctx, param)
|
||||
args.append(transformed)
|
||||
elif param.kind == param.KEYWORD_ONLY:
|
||||
# kwarg only param denotes "consume rest" semantics
|
||||
if self.rest_is_raw:
|
||||
converter = self._get_converter(param)
|
||||
argument = view.read_rest()
|
||||
kwargs[name] = yield from self.do_conversion(ctx.bot, ctx.message, converter, argument)
|
||||
else:
|
||||
kwargs[name] = yield from self.transform(ctx, param)
|
||||
break
|
||||
elif param.kind == param.VAR_POSITIONAL:
|
||||
while not view.eof:
|
||||
try:
|
||||
transformed = yield from self.transform(ctx, param)
|
||||
args.append(transformed)
|
||||
except RuntimeError:
|
||||
break
|
||||
|
||||
except CommandError as e:
|
||||
self.handle_local_error(e, ctx)
|
||||
ctx.bot.dispatch('command_error', e, ctx)
|
||||
return False
|
||||
if param.kind == param.POSITIONAL_OR_KEYWORD:
|
||||
transformed = yield from self.transform(ctx, param)
|
||||
args.append(transformed)
|
||||
elif param.kind == param.KEYWORD_ONLY:
|
||||
# kwarg only param denotes "consume rest" semantics
|
||||
if self.rest_is_raw:
|
||||
converter = self._get_converter(param)
|
||||
argument = view.read_rest()
|
||||
kwargs[name] = yield from self.do_conversion(ctx.bot, ctx.message, converter, argument)
|
||||
else:
|
||||
kwargs[name] = yield from self.transform(ctx, param)
|
||||
break
|
||||
elif param.kind == param.VAR_POSITIONAL:
|
||||
while not view.eof:
|
||||
try:
|
||||
transformed = yield from self.transform(ctx, param)
|
||||
args.append(transformed)
|
||||
except RuntimeError:
|
||||
break
|
||||
return True
|
||||
|
||||
def _verify_checks(self, ctx):
|
||||
try:
|
||||
if not self.enabled:
|
||||
raise DisabledCommand('{0.name} command is disabled'.format(self))
|
||||
if not self.enabled:
|
||||
raise DisabledCommand('{0.name} command is disabled'.format(self))
|
||||
|
||||
if self.no_pm and ctx.message.channel.is_private:
|
||||
raise NoPrivateMessage('This command cannot be used in private messages.')
|
||||
|
||||
if not self.can_run(ctx):
|
||||
raise CheckFailure('The check functions for command {0.name} failed.'.format(self))
|
||||
except CommandError as exc:
|
||||
self.handle_local_error(exc, ctx)
|
||||
ctx.bot.dispatch('command_error', exc, ctx)
|
||||
return False
|
||||
if self.no_pm and ctx.message.channel.is_private:
|
||||
raise NoPrivateMessage('This command cannot be used in private messages.')
|
||||
|
||||
if not self.can_run(ctx):
|
||||
raise CheckFailure('The check functions for command {0.name} failed.'.format(self))
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
|
Loading…
x
Reference in New Issue
Block a user