implement error accumulation

This way, multiple ConfigurationErrors are caught during setup, accumulated and printed all at once
This commit is contained in:
Finn-Thorben Sell 2022-03-24 10:42:06 +01:00
parent 7de2615441
commit a3b720f31a
No known key found for this signature in database
GPG key ID: A78A03C25A3A3825
3 changed files with 39 additions and 9 deletions

View file

@ -4,6 +4,7 @@ import re
from django.conf import global_settings
from django.core.exceptions import ImproperlyConfigured
from .errors import ConfigurationError, SetupError
from .utils import uppercase_attributes
from .values import Value, setup_value
@ -142,6 +143,13 @@ class Configuration(metaclass=ConfigurationBase):
@classmethod
def setup(cls):
exceptions = []
for name, value in uppercase_attributes(cls).items():
if isinstance(value, Value):
setup_value(cls, name, value)
try:
setup_value(cls, name, value)
except ConfigurationError as err:
exceptions.append(err)
if len(exceptions) > 0:
raise SetupError(f"Couldn't setup values of configuration {cls.__name__}", exceptions)

View file

@ -13,6 +13,20 @@ class TermStyles:
END = "\033[0m" if os.isatty(sys.stderr.fileno()) else ""
class SetupError(Exception):
"""
Exception that gets raised when a configuration class cannot be set up by the importer
"""
def __init__(self, msg: str, child_errors: List['ConfigurationError'] = None) -> None:
"""
:param step_verb: Which step the importer tried to perform (e.g. import, setup)
:param configuration_path: The full module path of the configuration that was supposed to be set up
:param child_errors: Optional child configuration errors that caused this error
"""
super().__init__(msg)
self.child_errors = child_errors or []
class ConfigurationError(ValueError):
"""
Base error class that is used to indicate that something went wrong during configuration.
@ -95,10 +109,13 @@ def with_error_handler(callee: Callable) -> Callable:
def wrapper(*args, **kwargs):
try:
return callee(*args, **kwargs)
except ConfigurationError as e:
msg = "{}{}{}".format(TermStyles.RED + TermStyles.BOLD, e, TermStyles.END)
for line in e.explanation_lines:
msg += f"\n {line}"
except SetupError as e:
msg = f"{str(e)}"
for child_error in e.child_errors:
msg += f"\n * {child_error.main_error_msg}"
for explanation_line in child_error.explanation_lines:
msg += f"\n - {explanation_line}"
print(msg, file=sys.stderr)
return wrapper

View file

@ -8,6 +8,7 @@ from django.conf import ENVIRONMENT_VARIABLE as SETTINGS_ENVIRONMENT_VARIABLE
from django.core.exceptions import ImproperlyConfigured
from django.core.management import base
from .errors import SetupError, ConfigurationError
from .utils import uppercase_attributes, reraise
from .values import Value, setup_value
@ -149,10 +150,10 @@ class ConfigurationLoader:
try:
cls = getattr(mod, self.name)
except AttributeError as err: # pragma: no cover
reraise(err, "Couldn't find configuration '{0}' "
"in module '{1}'".format(self.name,
mod.__package__))
except AttributeError: # pragma: no cover
raise SetupError(f"Couldn't find configuration '{self.name}' in module {mod.__package__}.\n"
f"Hint: '{self.name}' is taken from the environment variable '{CONFIGURATION_ENVIRONMENT_VARIABLE}'"
f"and '{mod.__package__}' from the environment variable '{SETTINGS_ENVIRONMENT_VARIABLE}'.")
try:
cls.pre_setup()
cls.setup()
@ -172,6 +173,10 @@ class ConfigurationLoader:
self.name))
cls.post_setup()
except SetupError:
raise
except ConfigurationError as err:
raise SetupError(f"Couldn't setup configuration '{cls_path}'", [err])
except Exception as err:
reraise(err, "Couldn't setup configuration '{0}'".format(cls_path))