Skip to content

Errorloom

Base class for automatic error handling.

Classes inheriting from ErrorLoom will have all their methods automatically wrapped in a try/except block. Caught exceptions are converted to BlueError objects and passed to an error_handler method if one is defined on the instance.

To exempt a method from this behavior, use the @skip_error_loom decorator.

Source code in blue/errorloom.py
184
185
186
187
188
189
190
191
192
193
194
195
196
class ErrorLoom(metaclass=CatcherMeta):
    """
    Base class for automatic error handling.

    Classes inheriting from `ErrorLoom` will have all their methods automatically
    wrapped in a try/except block. Caught exceptions are converted to `BlueError`
    objects and passed to an `error_handler` method if one is defined on the instance.

    To exempt a method from this behavior, use the `@skip_error_loom` decorator.
    """

    def __init__(self):
        pass
Example - MyService
class MyService(ErrorLoom):
    def do_work(self):
        raise ValueError("Something went wrong")

    def error_handler(self, error: BlueError, exception: Exception):
        # Handle the error centrally
        print(f"Caught error: {error}")

CatcherMeta

Bases: type

Metaclass that automatically wraps all methods of a class with error handling logic.

When a class uses CatcherMeta (or inherits from ErrorLoom), this metaclass:

  1. Iterates through all attributes defined in the class body.
  2. Identifies callable methods (excluding static/class methods).
  3. Wraps them with _create_async_method_wrapper or _create_sync_method_wrapper.
  4. Respects the @skip_error_loom decorator to bypass wrapping.
  5. Checks base classes to see if a method was explicitly skipped in a higher class, preserving that exemption in the lower class.
Source code in blue/errorloom.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
class CatcherMeta(type):
    """
    Metaclass that automatically wraps all methods of a class with error handling logic.

    When a class uses `CatcherMeta` (or inherits from `ErrorLoom`), this metaclass:

    1. Iterates through all attributes defined in the class body.
    2. Identifies callable methods (excluding static/class methods).
    3. Wraps them with `_create_async_method_wrapper` or `_create_sync_method_wrapper`.
    4. Respects the `@skip_error_loom` decorator to bypass wrapping.
    5. Checks base classes to see if a method was explicitly skipped in a higher class, preserving that exemption in the lower class.
    """

    def _is_base_method_skipped(attr_name: str, bases: tuple) -> bool:
        """
        Checks if a method with the given name was marked to be skipped in any of the base classes.

        Parameters:
            attr_name (str): The name of the method to check.
            bases (tuple): The base classes of the class being created.

        Returns:
            bool: True if the method is skipped in any base class, False otherwise.
        """
        for base in bases:
            if hasattr(base, attr_name):
                base_method = getattr(base, attr_name)
                if getattr(base_method, '__skip_error_loom__', False):
                    return True
        return False

    def __new__(mcs, name: str, bases: tuple, attrs: Dict[str, Any]):
        """
        Constructs the new class, intercepting and wrapping its methods.
        """
        new_attrs = {}
        for attr_name, attr_value in attrs.items():
            is_method = callable(attr_value) and not isinstance(attr_value, (staticmethod, classmethod))
            is_explicitly_skipped = getattr(attr_value, '__skip_error_loom__', False)
            is_inherited_skipped = mcs._is_base_method_skipped(attr_name, bases)
            if is_method and attr_name != 'error_handler':
                if is_explicitly_skipped:
                    new_attrs[attr_name] = attr_value
                elif is_inherited_skipped:
                    new_attrs[attr_name] = skip_error_loom(attr_value)
                else:
                    if inspect.iscoroutinefunction(attr_value):
                        new_attrs[attr_name] = _create_async_method_wrapper(attr_value)
                    else:
                        new_attrs[attr_name] = _create_sync_method_wrapper(attr_value)
            else:
                new_attrs[attr_name] = attr_value
        return super().__new__(mcs, name, bases, new_attrs)

__new__(mcs, name, bases, attrs)

Constructs the new class, intercepting and wrapping its methods.

Source code in blue/errorloom.py
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
def __new__(mcs, name: str, bases: tuple, attrs: Dict[str, Any]):
    """
    Constructs the new class, intercepting and wrapping its methods.
    """
    new_attrs = {}
    for attr_name, attr_value in attrs.items():
        is_method = callable(attr_value) and not isinstance(attr_value, (staticmethod, classmethod))
        is_explicitly_skipped = getattr(attr_value, '__skip_error_loom__', False)
        is_inherited_skipped = mcs._is_base_method_skipped(attr_name, bases)
        if is_method and attr_name != 'error_handler':
            if is_explicitly_skipped:
                new_attrs[attr_name] = attr_value
            elif is_inherited_skipped:
                new_attrs[attr_name] = skip_error_loom(attr_value)
            else:
                if inspect.iscoroutinefunction(attr_value):
                    new_attrs[attr_name] = _create_async_method_wrapper(attr_value)
                else:
                    new_attrs[attr_name] = _create_sync_method_wrapper(attr_value)
        else:
            new_attrs[attr_name] = attr_value
    return super().__new__(mcs, name, bases, new_attrs)
Last update: 2026-02-04