Skip to content

Auto Freeze

auto_freeze

Auto-freeze decorator for enforcing immutability on class instances.

Provides the :func:auto_freeze decorator that automatically freezes instances
after __init__ completes. The decorator injects a frozen state marker and
a __setattr__ override to prevent attribute modifications.

Can be used as @auto_freeze, @auto_freeze(), or
@auto_freeze(attrs=[...]) for selective freezing.

Useful for: Entities, value objects, and any domain type that
should be immutable after construction.

Example
from forging_blocks.foundation.autofreeze import auto_freeze
from forging_blocks.foundation.errors import CantModifyImmutableAttributeError


@auto_freeze
class Money:
    def __init__(self, amount: int, currency: str) -> None:
        if amount < 0:
            raise ValueError("Amount cannot be negative")
        self._amount = amount
        self._currency = currency.upper()

    @property
    def amount(self) -> int:
        return self._amount

    @property
    def currency(self) -> str:
        return self._currency

With selective freezing (e.g., for Entities):

@auto_freeze(attrs=["_id"])
class User:
    def __init__(self, user_id: str, name: str) -> None:
        self._id = user_id
        self._name = name

    @property
    def id(self) -> str:
        return self._id

    @property
    def name(self) -> str:
        return self._name

auto_freeze(class_: type[T] | None = None, *, attrs: Sequence[str] | None = None) -> type[T] | Callable[[type[T]], type[T]]

auto_freeze(class_: type[T]) -> type[T]
auto_freeze(
    class_: type[T], *, attrs: Sequence[str] | None = None
) -> type[T]
auto_freeze(
    class_: None = None,
    *,
    attrs: Sequence[str] | None = None,
) -> Callable[[type[T]], type[T]]

Automatically freeze class instances after __init__ completes.

Can be used as @auto_freeze, @auto_freeze(), or
@auto_freeze(attrs=[...]). No protocol implementation is required
on the target class - the decorator handles freezing internally.

Parameters:

Name Type Description Default
class_ type[T] | None

The target class (when used directly as @auto_freeze).
None when used with parentheses (@auto_freeze()).

None
attrs Sequence[str] | None

Optional attribute names for selective freezing. If None,
the whole instance is frozen. When provided, only those attributes
are frozen.

None

Returns:

Type Description
type[T] | Callable[[type[T]], type[T]]

The decorated class if class_ is provided; otherwise a callable

type[T] | Callable[[type[T]], type[T]]

that can be used as a decorator.

Source code in src/forging_blocks/foundation/autofreeze/auto_freeze.py
def auto_freeze[T](
    class_: type[T] | None = None,
    *,
    attrs: Sequence[str] | None = None,
) -> type[T] | Callable[[type[T]], type[T]]:
    """Automatically freeze class instances after ``__init__`` completes.

    Can be used as ``@auto_freeze``, ``@auto_freeze()``, or
    ``@auto_freeze(attrs=[...])``. No protocol implementation is required
    on the target class - the decorator handles freezing internally.

    Args:
        class_: The target class (when used directly as ``@auto_freeze``).
            ``None`` when used with parentheses (``@auto_freeze()``).
        attrs: Optional attribute names for selective freezing. If ``None``,
            the whole instance is frozen. When provided, only those attributes
            are frozen.

    Returns:
        The decorated class if *class_* is provided; otherwise a callable
        that can be used as a decorator.
    """
    decorator = _AutoFreezeDecorator(attrs=attrs)

    if class_ is not None:
        return decorator(class_)

    return decorator