Module raffiot.utils

Expand source code
from __future__ import annotations

from collections import abc
from dataclasses import dataclass
from enum import IntEnum
from traceback import format_exc, format_stack
from typing import Any, List, Generic, TypeVar, Iterable

from typing_extensions import final

__all__ = [
    "TracedException",
    "MatchError",
    "MultipleExceptions",
    "ComputationStatus",
    "seq",
    "DomainErrors",
]

E = TypeVar("E", covariant=True)


@final
@dataclass
class TracedException:
    __slots__ = ("exception", "stack_trace")

    exception: Exception
    """
    The exception that was raised.
    """

    stack_trace: str
    """
    Its stack trace.
    """

    def __str__(self):
        return f"{self.exception}\n{self.stack_trace}"

    @classmethod
    def in_except_clause(cls, exn: Exception) -> TracedException:
        """
        Collect the stack trace of the exception.

        BEWARE: this method should only be used in the except clause
        of a try-except block and called with the caught exception!

        :param exn:
        :return:
        """
        if isinstance(exn, TracedException):
            return exn
        return TracedException(exception=exn, stack_trace=format_exc())

    @classmethod
    def with_stack_trace(cls, exn: Exception) -> TracedException:
        """
        Collect the stack trace at the current position.

        :param exn:
        :return:
        """
        if isinstance(exn, TracedException):
            return exn
        return TracedException(exception=exn, stack_trace="".join(format_stack()))

    @classmethod
    def ensure_traced(cls, exception: Exception) -> TracedException:
        return cls.with_stack_trace(exception)

    @classmethod
    def ensure_list_traced(
        cls, exceptions: Iterable[Exception]
    ) -> List[TracedException]:
        return [cls.ensure_traced(exn) for exn in exceptions]


@dataclass
class MatchError(Exception):
    """
    Exception for pattern matching errors (used internally, should NEVER happen).
    """

    message: str


@final
@dataclass
class MultipleExceptions(Exception, Generic[E]):
    """
    Represents
    """

    exceptions: List[TracedException]
    """
    The list exceptions encountered
    """

    errors: List[E]
    """
    The list of errors encountered
    """

    @classmethod
    def merge(cls, *exceptions: TracedException, errors: List[E] = None) -> Exception:
        """
        Merge some exceptions, retuning the exceptions if there is only one
        or a  `MultipleExceptions` otherwise.

        :param exceptions:
        :param errors:
        :return:
        """
        stack = [exn for exn in exceptions]
        base_exceptions = []
        errs = [x for x in errors] if errors else []

        while stack:
            item = stack.pop()
            if isinstance(item, MultipleExceptions):
                stack.extend(item.exceptions)
                errs.extend(item.errors)
                continue
            if isinstance(item, abc.Iterable) and not isinstance(item, str):
                stack.extend(item)
                continue
            base_exceptions.append(TracedException.ensure_traced(item))

        base_exceptions.reverse()
        return MultipleExceptions(base_exceptions, errs)

    def __str__(self):
        msg = ""
        for traced in self.exceptions:
            msg += f"\nException: {traced.exception}\n{traced.stack_trace}"
        for err in self.errors:
            msg += f"\nError: {err}"
        return msg


@final
@dataclass
class DomainErrors(Exception, Generic[E]):
    """
    Errors from the business domain
    """

    errors: List[E]


@final
class ComputationStatus(IntEnum):
    FAILED = 0
    SUCCEEDED = 1


def seq(*a: Any) -> Any:
    """
    The result is the result of the last argument.

    Accepts a single list or multiple arguments.
    :param a:
    :return:
    """
    if len(a) == 1 and isinstance(a[0], abc.Iterable):
        return a[0][-1]  # type: ignore
    return a[-1]

Functions

def seq(*a: Any) ‑> Any

The result is the result of the last argument.

Accepts a single list or multiple arguments. :param a: :return:

Expand source code
def seq(*a: Any) -> Any:
    """
    The result is the result of the last argument.

    Accepts a single list or multiple arguments.
    :param a:
    :return:
    """
    if len(a) == 1 and isinstance(a[0], abc.Iterable):
        return a[0][-1]  # type: ignore
    return a[-1]

Classes

class ComputationStatus (value, names=None, *, module=None, qualname=None, type=None, start=1)

An enumeration.

Expand source code
@final
class ComputationStatus(IntEnum):
    FAILED = 0
    SUCCEEDED = 1

Ancestors

  • enum.IntEnum
  • builtins.int
  • enum.Enum

Class variables

var FAILED
var SUCCEEDED
class DomainErrors (errors: List[E])

Errors from the business domain

Expand source code
@final
@dataclass
class DomainErrors(Exception, Generic[E]):
    """
    Errors from the business domain
    """

    errors: List[E]

Ancestors

  • builtins.Exception
  • builtins.BaseException
  • typing.Generic

Class variables

var errors : List[+E]
class MatchError (message: str)

Exception for pattern matching errors (used internally, should NEVER happen).

Expand source code
@dataclass
class MatchError(Exception):
    """
    Exception for pattern matching errors (used internally, should NEVER happen).
    """

    message: str

Ancestors

  • builtins.Exception
  • builtins.BaseException

Class variables

var message : str
class MultipleExceptions (exceptions: List[TracedException], errors: List[E])

Represents

Expand source code
@final
@dataclass
class MultipleExceptions(Exception, Generic[E]):
    """
    Represents
    """

    exceptions: List[TracedException]
    """
    The list exceptions encountered
    """

    errors: List[E]
    """
    The list of errors encountered
    """

    @classmethod
    def merge(cls, *exceptions: TracedException, errors: List[E] = None) -> Exception:
        """
        Merge some exceptions, retuning the exceptions if there is only one
        or a  `MultipleExceptions` otherwise.

        :param exceptions:
        :param errors:
        :return:
        """
        stack = [exn for exn in exceptions]
        base_exceptions = []
        errs = [x for x in errors] if errors else []

        while stack:
            item = stack.pop()
            if isinstance(item, MultipleExceptions):
                stack.extend(item.exceptions)
                errs.extend(item.errors)
                continue
            if isinstance(item, abc.Iterable) and not isinstance(item, str):
                stack.extend(item)
                continue
            base_exceptions.append(TracedException.ensure_traced(item))

        base_exceptions.reverse()
        return MultipleExceptions(base_exceptions, errs)

    def __str__(self):
        msg = ""
        for traced in self.exceptions:
            msg += f"\nException: {traced.exception}\n{traced.stack_trace}"
        for err in self.errors:
            msg += f"\nError: {err}"
        return msg

Ancestors

  • builtins.Exception
  • builtins.BaseException
  • typing.Generic

Class variables

var errors : List[+E]

The list of errors encountered

var exceptions : List[TracedException]

The list exceptions encountered

Static methods

def merge(*exceptions: TracedException, errors: List[E] = None) ‑> Exception

Merge some exceptions, retuning the exceptions if there is only one or a MultipleExceptions otherwise.

:param exceptions: :param errors: :return:

Expand source code
@classmethod
def merge(cls, *exceptions: TracedException, errors: List[E] = None) -> Exception:
    """
    Merge some exceptions, retuning the exceptions if there is only one
    or a  `MultipleExceptions` otherwise.

    :param exceptions:
    :param errors:
    :return:
    """
    stack = [exn for exn in exceptions]
    base_exceptions = []
    errs = [x for x in errors] if errors else []

    while stack:
        item = stack.pop()
        if isinstance(item, MultipleExceptions):
            stack.extend(item.exceptions)
            errs.extend(item.errors)
            continue
        if isinstance(item, abc.Iterable) and not isinstance(item, str):
            stack.extend(item)
            continue
        base_exceptions.append(TracedException.ensure_traced(item))

    base_exceptions.reverse()
    return MultipleExceptions(base_exceptions, errs)
class TracedException (exception: Exception, stack_trace: str)

TracedException(exception: 'Exception', stack_trace: 'str')

Expand source code
@final
@dataclass
class TracedException:
    __slots__ = ("exception", "stack_trace")

    exception: Exception
    """
    The exception that was raised.
    """

    stack_trace: str
    """
    Its stack trace.
    """

    def __str__(self):
        return f"{self.exception}\n{self.stack_trace}"

    @classmethod
    def in_except_clause(cls, exn: Exception) -> TracedException:
        """
        Collect the stack trace of the exception.

        BEWARE: this method should only be used in the except clause
        of a try-except block and called with the caught exception!

        :param exn:
        :return:
        """
        if isinstance(exn, TracedException):
            return exn
        return TracedException(exception=exn, stack_trace=format_exc())

    @classmethod
    def with_stack_trace(cls, exn: Exception) -> TracedException:
        """
        Collect the stack trace at the current position.

        :param exn:
        :return:
        """
        if isinstance(exn, TracedException):
            return exn
        return TracedException(exception=exn, stack_trace="".join(format_stack()))

    @classmethod
    def ensure_traced(cls, exception: Exception) -> TracedException:
        return cls.with_stack_trace(exception)

    @classmethod
    def ensure_list_traced(
        cls, exceptions: Iterable[Exception]
    ) -> List[TracedException]:
        return [cls.ensure_traced(exn) for exn in exceptions]

Static methods

def ensure_list_traced(exceptions: Iterable[Exception]) ‑> List[TracedException]
Expand source code
@classmethod
def ensure_list_traced(
    cls, exceptions: Iterable[Exception]
) -> List[TracedException]:
    return [cls.ensure_traced(exn) for exn in exceptions]
def ensure_traced(exception: Exception) ‑> TracedException
Expand source code
@classmethod
def ensure_traced(cls, exception: Exception) -> TracedException:
    return cls.with_stack_trace(exception)
def in_except_clause(exn: Exception) ‑> TracedException

Collect the stack trace of the exception.

BEWARE: this method should only be used in the except clause of a try-except block and called with the caught exception!

:param exn: :return:

Expand source code
@classmethod
def in_except_clause(cls, exn: Exception) -> TracedException:
    """
    Collect the stack trace of the exception.

    BEWARE: this method should only be used in the except clause
    of a try-except block and called with the caught exception!

    :param exn:
    :return:
    """
    if isinstance(exn, TracedException):
        return exn
    return TracedException(exception=exn, stack_trace=format_exc())
def with_stack_trace(exn: Exception) ‑> TracedException

Collect the stack trace at the current position.

:param exn: :return:

Expand source code
@classmethod
def with_stack_trace(cls, exn: Exception) -> TracedException:
    """
    Collect the stack trace at the current position.

    :param exn:
    :return:
    """
    if isinstance(exn, TracedException):
        return exn
    return TracedException(exception=exn, stack_trace="".join(format_stack()))

Instance variables

var exception : Exception

The exception that was raised.

var stack_trace : str

Its stack trace.