Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
Toggle table of contents sidebar

# Source code for betty.error

    """
    Provide error handling utilities.
    """

    import traceback
    from typing import TypeVar

    from betty.locale.localizable import Localizable
    from betty.locale.localizer import Localizer
    from typing_extensions import override

    _BaseExceptionT = TypeVar("_BaseExceptionT", bound=BaseException)

    def serialize(error: _BaseExceptionT) -> _BaseExceptionT:
        """
        Serialize an exception.

        This replaces the exception's traceback object with the traceback formatted as a string.
        """
        formatted_traceback = f'\n"""\n{"".join(traceback.format_exception(type(error), error, error.__traceback__))}"""'
        error.__cause__ = _SerializedTraceback(formatted_traceback)
        error.__traceback__ = None
        return error

    class _SerializedTraceback(Exception):
        def __init__(self, formatted_traceback: str):
            self._formatted_traceback = formatted_traceback

        def __str__(self) -> str:
            return self._formatted_traceback

    class UserFacingError(Exception, Localizable):
        """
        A localizable, user-facing error.

        This type of error is fatal, but fixing it does not require knowledge of Betty's internals or the stack trace
        leading to the error. It must therefore have an end-user-friendly message, and its stack trace must not be shown.
        """

        def __init__(self, message: Localizable):
            from betty.locale.localizer import DEFAULT_LOCALIZER

            super().__init__(
                # Provide a default localization so this exception can be displayed like any other.
                message.localize(DEFAULT_LOCALIZER),
            )
            self._localizable_message = message

        def __str__(self) -> str:
            from betty.locale.localizer import DEFAULT_LOCALIZER

            return self.localize(DEFAULT_LOCALIZER)

        @override
        def localize(self, localizer: Localizer) -> str:
            return self._localizable_message.localize(localizer)