mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-04-19 15:36:02 +00:00
[commands] Redesign extension exception flow.
Instead of raising a whole variety of exceptions, they are now wrapped into ExtensionError derived classes. * ExtensionAlreadyLoaded * Raised when an extension is already loaded in Bot.load_extension * ExtensionNotLoaded * Raised when an extension is not loaded, e.g. Bot.unload_extension * NoEntryPointError * Raised when an extension does not have a `setup` function. * ExtensionFailed * Raised when an extension's `setup` function fails. * ExtensionNotFound * Raised when an extension's module import fails.
This commit is contained in:
parent
26e9b5bfac
commit
d9e54d7dd3
@ -38,7 +38,7 @@ import discord
|
||||
from .core import GroupMixin, Command
|
||||
from .view import StringView
|
||||
from .context import Context
|
||||
from .errors import CommandNotFound, CommandError
|
||||
from . import errors
|
||||
from .help import HelpCommand, DefaultHelpCommand
|
||||
from .cog import Cog
|
||||
|
||||
@ -571,14 +571,14 @@ class BotBase(GroupMixin):
|
||||
setup = getattr(lib, 'setup')
|
||||
except AttributeError:
|
||||
del sys.modules[key]
|
||||
raise discord.ClientException('extension {!r} ({!r}) does not have a setup function.'.format(key, lib))
|
||||
raise errors.NoEntryPointError(key)
|
||||
|
||||
try:
|
||||
setup(self)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
self._remove_module_references(lib.__name__)
|
||||
self._call_module_finalizers(lib, key)
|
||||
raise
|
||||
raise errors.ExtensionFailed(key, e) from e
|
||||
else:
|
||||
self._extensions[key] = lib
|
||||
|
||||
@ -601,20 +601,25 @@ class BotBase(GroupMixin):
|
||||
|
||||
Raises
|
||||
--------
|
||||
ClientException
|
||||
The extension does not have a setup function.
|
||||
ImportError
|
||||
ExtensionNotFound
|
||||
The extension could not be imported.
|
||||
Exception
|
||||
Any other exception raised by the extension will be raised back
|
||||
to the caller.
|
||||
ExtensionAlreadyLoaded
|
||||
The extension is already loaded.
|
||||
NoEntryPointError
|
||||
The extension does not have a setup function.
|
||||
ExtensionFailed
|
||||
The extension setup function had an execution error.
|
||||
"""
|
||||
|
||||
if name in self._extensions:
|
||||
return
|
||||
raise errors.ExtensionAlreadyLoaded(name)
|
||||
|
||||
lib = importlib.import_module(name)
|
||||
self._load_from_module_spec(lib, name)
|
||||
try:
|
||||
lib = importlib.import_module(name)
|
||||
except ImportError as e:
|
||||
raise errors.ExtensionNotFound(name, e) from e
|
||||
else:
|
||||
self._load_from_module_spec(lib, name)
|
||||
|
||||
def unload_extension(self, name):
|
||||
"""Unloads an extension.
|
||||
@ -633,11 +638,16 @@ class BotBase(GroupMixin):
|
||||
The extension name to unload. It must be dot separated like
|
||||
regular Python imports if accessing a sub-module. e.g.
|
||||
``foo.test`` if you want to import ``foo/test.py``.
|
||||
|
||||
Raises
|
||||
-------
|
||||
ExtensionNotLoaded
|
||||
The extension was not loaded.
|
||||
"""
|
||||
|
||||
lib = self._extensions.get(name)
|
||||
if lib is None:
|
||||
return
|
||||
raise errors.ExtensionNotLoaded(name)
|
||||
|
||||
self._remove_module_references(lib.__name__)
|
||||
self._call_module_finalizers(lib, name)
|
||||
@ -659,14 +669,19 @@ class BotBase(GroupMixin):
|
||||
|
||||
Raises
|
||||
-------
|
||||
Exception
|
||||
Any exception raised by the extension will be raised back
|
||||
to the caller.
|
||||
ExtensionNotLoaded
|
||||
The extension was not loaded.
|
||||
ExtensionNotFound
|
||||
The extension could not be imported.
|
||||
NoEntryPointError
|
||||
The extension does not have a setup function.
|
||||
ExtensionFailed
|
||||
The extension setup function had an execution error.
|
||||
"""
|
||||
|
||||
lib = self._extensions.get(name)
|
||||
if lib is None:
|
||||
return
|
||||
raise errors.ExtensionNotLoaded(name)
|
||||
|
||||
# get the previous module states from sys modules
|
||||
modules = {
|
||||
@ -843,12 +858,12 @@ class BotBase(GroupMixin):
|
||||
try:
|
||||
if await self.can_run(ctx, call_once=True):
|
||||
await ctx.command.invoke(ctx)
|
||||
except CommandError as exc:
|
||||
except errors.CommandError as exc:
|
||||
await ctx.command.dispatch_error(ctx, exc)
|
||||
else:
|
||||
self.dispatch('command_completion', ctx)
|
||||
elif ctx.invoked_with:
|
||||
exc = CommandNotFound('Command "{}" is not found'.format(ctx.invoked_with))
|
||||
exc = errors.CommandNotFound('Command "{}" is not found'.format(ctx.invoked_with))
|
||||
self.dispatch('command_error', ctx, exc)
|
||||
|
||||
async def process_commands(self, message):
|
||||
|
@ -34,7 +34,9 @@ __all__ = ['CommandError', 'MissingRequiredArgument', 'BadArgument',
|
||||
'MissingPermissions', 'BotMissingPermissions', 'ConversionError',
|
||||
'BadUnionArgument', 'ArgumentParsingError',
|
||||
'UnexpectedQuoteError', 'InvalidEndOfQuotedStringError',
|
||||
'ExpectedClosingQuoteError', ]
|
||||
'ExpectedClosingQuoteError', 'ExtensionError', 'ExtensionAlreadyLoaded',
|
||||
'ExtensionNotLoaded', 'NoEntryPointError', 'ExtensionFailed',
|
||||
'ExtensionNotFound' ]
|
||||
|
||||
class CommandError(DiscordException):
|
||||
r"""The base exception type for all command related errors.
|
||||
@ -283,3 +285,80 @@ class ExpectedClosingQuoteError(ArgumentParsingError):
|
||||
def __init__(self, close_quote):
|
||||
self.close_quote = close_quote
|
||||
super().__init__('Expected closing {}.'.format(close_quote))
|
||||
|
||||
class ExtensionError(DiscordException):
|
||||
"""Base exception for extension related errors.
|
||||
|
||||
This inherits from :exc:`~discord.DiscordException`.
|
||||
|
||||
Parameter
|
||||
-----------
|
||||
name: :class:`str`
|
||||
The extension that had an error.
|
||||
"""
|
||||
def __init__(self, message=None, *args, name):
|
||||
self.name = name
|
||||
message = message or 'Extension {!r} had an error.'.format(name)
|
||||
# clean-up @everyone and @here mentions
|
||||
m = message.replace('@everyone', '@\u200beveryone').replace('@here', '@\u200bhere')
|
||||
super().__init__(m, *args)
|
||||
|
||||
class ExtensionAlreadyLoaded(ExtensionError):
|
||||
"""An exception raised when an extension has already been loaded.
|
||||
|
||||
This inherits from :exc:`ExtensionError`
|
||||
"""
|
||||
def __init__(self, name):
|
||||
super().__init__('Extension {!r} is already loaded.'.format(name), name=name)
|
||||
|
||||
class ExtensionNotLoaded(ExtensionError):
|
||||
"""An exception raised when an extension was not loaded.
|
||||
|
||||
This inherits from :exc:`ExtensionError`
|
||||
"""
|
||||
def __init__(self, name):
|
||||
super().__init__('Extension {!r} has not been loaded.'.format(name), name=name)
|
||||
|
||||
class NoEntryPointError(ExtensionError):
|
||||
"""An exception raised when an extension does not have a ``setup`` entry point function.
|
||||
|
||||
This inherits from :exc:`ExtensionError`
|
||||
"""
|
||||
def __init__(self, name):
|
||||
super().__init__("Extension {!r} has no 'setup' function.".format(name), name=name)
|
||||
|
||||
class ExtensionFailed(ExtensionError):
|
||||
"""An exception raised when an extension failed to load during execution of the ``setup`` entry point.
|
||||
|
||||
This inherits from :exc:`ExtensionError`
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
name: :class:`str`
|
||||
The extension that had the error.
|
||||
original: :exc:`Exception`
|
||||
The original exception that was raised. You can also get this via
|
||||
the ``__cause__`` attribute.
|
||||
"""
|
||||
def __init__(self, name, original):
|
||||
self.original = original
|
||||
fmt = 'Extension {0!r} raised an error: {1.__class__.__name__}: {1}'
|
||||
super().__init__(fmt.format(name, original), name=name)
|
||||
|
||||
class ExtensionNotFound(ExtensionError):
|
||||
"""An exception raised when an extension failed to be imported.
|
||||
|
||||
This inherits from :exc:`ExtensionError`
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
name: :class:`str`
|
||||
The extension that had the error.
|
||||
original: :exc:`ImportError`
|
||||
The original exception that was raised. You can also get this via
|
||||
the ``__cause__`` attribute.
|
||||
"""
|
||||
def __init__(self, name, original):
|
||||
self.original = original
|
||||
fmt = 'Extension {0!r} could not be loaded.'
|
||||
super().__init__(fmt.format(name), name=name)
|
||||
|
@ -285,6 +285,25 @@ Exceptions
|
||||
.. autoexception:: discord.ext.commands.BotMissingPermissions
|
||||
:members:
|
||||
|
||||
.. autoexception:: discord.ext.commands.ExtensionError
|
||||
:members:
|
||||
|
||||
.. autoexception:: discord.ext.commands.ExtensionAlreadyLoaded
|
||||
:members:
|
||||
|
||||
.. autoexception:: discord.ext.commands.ExtensionNotLoaded
|
||||
:members:
|
||||
|
||||
.. autoexception:: discord.ext.commands.NoEntryPointError
|
||||
:members:
|
||||
|
||||
.. autoexception:: discord.ext.commands.ExtensionFailed
|
||||
:members:
|
||||
|
||||
.. autoexception:: discord.ext.commands.ExtensionNotFound
|
||||
:members:
|
||||
|
||||
|
||||
Exception Hierarchy
|
||||
+++++++++++++++++++++
|
||||
|
||||
@ -311,3 +330,9 @@ Exception Hierarchy
|
||||
- :exc:`~.commands.DisabledCommand`
|
||||
- :exc:`~.commands.CommandInvokeError`
|
||||
- :exc:`~.commands.CommandOnCooldown`
|
||||
- :exc:`~.commands.ExtensionError`
|
||||
- :exc:`~.commands.ExtensionAlreadyLoaded`
|
||||
- :exc:`~.commands.ExtensionNotLoaded`
|
||||
- :exc:`~.commands.NoEntryPointError`
|
||||
- :exc:`~.commands.ExtensionFailed`
|
||||
- :exc:`~.commands.ExtensionNotFound`
|
||||
|
Loading…
x
Reference in New Issue
Block a user