"""
The mutability API.
This provides tools to mark objects as mutable or immutable, and to guard against mutations.
"""
from __future__ import annotations
from typing import Any, TYPE_CHECKING
from betty.typing import internal
if TYPE_CHECKING:
from collections.abc import Iterable
[docs]
class MutabilityError(Exception):
"""
A generic mutability API error.
"""
pass
[docs]
@internal
class MutableError(MutabilityError, RuntimeError):
"""
An error raised because something was unexpectedly mutable.
"""
pass
[docs]
@internal
class ImmutableError(MutabilityError, RuntimeError):
"""
An error raised because something was unexpectedly immutable.
"""
pass
[docs]
class Mutable:
"""
A generic mutable type that can be marked immutable.
"""
[docs]
def get_mutable_instances(self) -> Iterable[Mutable]:
"""
Get any other :py:class:`betty.mutability.Mutable` instances contained by this one.
"""
return ()
@property
def is_mutable(self) -> bool:
"""
Whether the instance is mutable.
"""
return self._mutable
[docs]
def mutable(self) -> None:
"""
Mark the instance mutable.
"""
self._mutable = True
for instance in self.get_mutable_instances():
instance.mutable()
@property
def is_immutable(self) -> bool:
"""
Whether the instance is immutable.
"""
return not self._mutable
[docs]
def immutable(self) -> None:
"""
Mark the instance immutable.
"""
self._mutable = False
for instance in self.get_mutable_instances():
instance.immutable()
[docs]
def assert_mutable(self) -> None:
"""
Assert that the instance is mutable.
:raise ImmutableError: if the instance is immutable.
"""
if not self._mutable:
raise ImmutableError(
f"{self} was unexpectedly immutable, and cannot be modified."
)
[docs]
def assert_immutable(self) -> None:
"""
Assert that the instance is immutable.
:raise MutableError: if the instance is mutable.
"""
if self._mutable:
raise MutableError(f"{self} was unexpectedly mutable, and can be modified.")
[docs]
def mutable(*instances: Mutable) -> None:
"""
Mark the given instances mutable.
"""
for instance in instances:
instance.mutable()
[docs]
def immutable(*instances: Mutable) -> None:
"""
Mark the given instances immutable.
"""
for instance in instances:
instance.immutable()