[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:
		
				
					committed by
					
						 Rapptz
						Rapptz
					
				
			
			
				
	
			
			
			
						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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user