mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-05-13 17:29:50 +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 inspect
|
||||||
import importlib
|
import importlib
|
||||||
import sys
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
from .core import GroupMixin, Command, command
|
from .core import GroupMixin, Command, command
|
||||||
from .view import StringView
|
from .view import StringView
|
||||||
from .context import Context
|
from .context import Context
|
||||||
from .errors import CommandNotFound
|
from .errors import CommandNotFound, CommandError
|
||||||
from .formatter import HelpFormatter
|
from .formatter import HelpFormatter
|
||||||
|
|
||||||
def _get_variable(name):
|
def _get_variable(name):
|
||||||
@ -247,6 +248,26 @@ class Bot(GroupMixin, discord.Client):
|
|||||||
coro = self._run_extra(event, event_name, *args, **kwargs)
|
coro = self._run_extra(event, event_name, *args, **kwargs)
|
||||||
discord.compat.create_task(coro, loop=self.loop)
|
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
|
# utility "send_*" functions
|
||||||
|
|
||||||
def say(self, *args, **kwargs):
|
def say(self, *args, **kwargs):
|
||||||
@ -618,8 +639,13 @@ class Bot(GroupMixin, discord.Client):
|
|||||||
command = self.commands[invoker]
|
command = self.commands[invoker]
|
||||||
self.dispatch('command', command, ctx)
|
self.dispatch('command', command, ctx)
|
||||||
ctx.command = command
|
ctx.command = command
|
||||||
yield from command.invoke(ctx)
|
try:
|
||||||
self.dispatch('command_completion', command, ctx)
|
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:
|
else:
|
||||||
exc = CommandNotFound('Command "{}" is not found'.format(invoker))
|
exc = CommandNotFound('Command "{}" is not found'.format(invoker))
|
||||||
self.dispatch('command_error', exc, ctx)
|
self.dispatch('command_error', exc, ctx)
|
||||||
|
@ -44,7 +44,10 @@ def inject_context(ctx, coro):
|
|||||||
_internal_channel = ctx.message.channel
|
_internal_channel = ctx.message.channel
|
||||||
_internal_author = ctx.message.author
|
_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 ret
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
@ -306,72 +309,60 @@ class Command:
|
|||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def _parse_arguments(self, ctx):
|
def _parse_arguments(self, ctx):
|
||||||
try:
|
ctx.args = [] if self.instance is None else [self.instance]
|
||||||
ctx.args = [] if self.instance is None else [self.instance]
|
ctx.kwargs = {}
|
||||||
ctx.kwargs = {}
|
args = ctx.args
|
||||||
args = ctx.args
|
kwargs = ctx.kwargs
|
||||||
kwargs = ctx.kwargs
|
|
||||||
|
|
||||||
first = True
|
first = True
|
||||||
view = ctx.view
|
view = ctx.view
|
||||||
iterator = iter(self.params.items())
|
iterator = iter(self.params.items())
|
||||||
|
|
||||||
if self.instance is not None:
|
if self.instance is not None:
|
||||||
# we have 'self' as the first parameter so just advance
|
# we have 'self' as the first parameter so just advance
|
||||||
# the iterator and resume parsing
|
# the iterator and resume parsing
|
||||||
try:
|
try:
|
||||||
next(iterator)
|
next(iterator)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
fmt = 'Callback for {0.name} command is missing "self" parameter.'
|
fmt = 'Callback for {0.name} command is missing "self" parameter.'
|
||||||
raise discord.ClientException(fmt.format(self))
|
raise discord.ClientException(fmt.format(self))
|
||||||
|
|
||||||
for name, param in iterator:
|
for name, param in iterator:
|
||||||
if first and self.pass_context:
|
if first and self.pass_context:
|
||||||
args.append(ctx)
|
args.append(ctx)
|
||||||
first = False
|
first = False
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if param.kind == param.POSITIONAL_OR_KEYWORD:
|
if param.kind == param.POSITIONAL_OR_KEYWORD:
|
||||||
transformed = yield from self.transform(ctx, param)
|
transformed = yield from self.transform(ctx, param)
|
||||||
args.append(transformed)
|
args.append(transformed)
|
||||||
elif param.kind == param.KEYWORD_ONLY:
|
elif param.kind == param.KEYWORD_ONLY:
|
||||||
# kwarg only param denotes "consume rest" semantics
|
# kwarg only param denotes "consume rest" semantics
|
||||||
if self.rest_is_raw:
|
if self.rest_is_raw:
|
||||||
converter = self._get_converter(param)
|
converter = self._get_converter(param)
|
||||||
argument = view.read_rest()
|
argument = view.read_rest()
|
||||||
kwargs[name] = yield from self.do_conversion(ctx.bot, ctx.message, converter, argument)
|
kwargs[name] = yield from self.do_conversion(ctx.bot, ctx.message, converter, argument)
|
||||||
else:
|
else:
|
||||||
kwargs[name] = yield from self.transform(ctx, param)
|
kwargs[name] = yield from self.transform(ctx, param)
|
||||||
break
|
break
|
||||||
elif param.kind == param.VAR_POSITIONAL:
|
elif param.kind == param.VAR_POSITIONAL:
|
||||||
while not view.eof:
|
while not view.eof:
|
||||||
try:
|
try:
|
||||||
transformed = yield from self.transform(ctx, param)
|
transformed = yield from self.transform(ctx, param)
|
||||||
args.append(transformed)
|
args.append(transformed)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
break
|
break
|
||||||
|
|
||||||
except CommandError as e:
|
|
||||||
self.handle_local_error(e, ctx)
|
|
||||||
ctx.bot.dispatch('command_error', e, ctx)
|
|
||||||
return False
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _verify_checks(self, ctx):
|
def _verify_checks(self, ctx):
|
||||||
try:
|
if not self.enabled:
|
||||||
if not self.enabled:
|
raise DisabledCommand('{0.name} command is disabled'.format(self))
|
||||||
raise DisabledCommand('{0.name} command is disabled'.format(self))
|
|
||||||
|
|
||||||
if self.no_pm and ctx.message.channel.is_private:
|
if self.no_pm and ctx.message.channel.is_private:
|
||||||
raise NoPrivateMessage('This command cannot be used in private messages.')
|
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 not self.can_run(ctx):
|
||||||
|
raise CheckFailure('The check functions for command {0.name} failed.'.format(self))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
Loading…
x
Reference in New Issue
Block a user