Source code for advanced_alchemy.types.mutables

from typing import Any, TypeVar, cast, no_type_check

from sqlalchemy.ext.mutable import Mutable
from sqlalchemy.ext.mutable import MutableList as SQLMutableList
from typing_extensions import Self

T = TypeVar("T", bound="Any")


[docs] class MutableList(SQLMutableList[T]): # pragma: no cover """A list type that implements :class:`Mutable`. The :class:`MutableList` object implements a list that will emit change events to the underlying mapping when the contents of the list are altered, including when values are added or removed. This is a replication of default Mutablelist provide by SQLAlchemy. The difference here is the properties _removed which keep every element removed from the list in order to be able to delete them after commit and keep them when session rolled back. """
[docs] def __init__(self, *args: "Any", **kwargs: "Any") -> None: super().__init__(*args, **kwargs) self._pending_removed: set[T] = set() self._pending_append: list[T] = []
[docs] @classmethod def coerce(cls, key: "Any", value: "Any") -> "Any": # pragma: no cover if not isinstance(value, MutableList): if isinstance(value, list): return MutableList[T](value) # this call will raise ValueError return Mutable.coerce(key, value) return cast("MutableList[T]", value)
@no_type_check def __reduce_ex__(self, proto: int) -> "tuple[type[MutableList[T]], tuple[list[T]]]": # pragma: no cover return self.__class__, (list(self),) # needed for backwards compatibility with # older pickles def __getstate__(self) -> "tuple[list[T], set[T]]": # pragma: no cover return list(self), self._pending_removed def __setstate__(self, state: "Any") -> None: # pragma: no cover self[:] = state[0] self._pending_removed = state[1]
[docs] def __setitem__(self, index: "Any", value: "Any") -> None: """Detect list set events and emit change events.""" old_value = self[index] if isinstance(index, slice) else [self[index]] list.__setitem__(self, index, value) # pyright: ignore[reportUnknownMemberType,reportUnknownArgumentType] self.changed() self._pending_removed.update(old_value) # pyright: ignore[reportArgumentType]
[docs] def __delitem__(self, index: "Any") -> None: """Detect list del events and emit change events.""" old_value = self[index] if isinstance(index, slice) else [self[index]] list.__delitem__(self, index) # pyright: ignore[reportUnknownMemberType,reportUnknownArgumentType] self.changed() self._pending_removed.update(old_value) # pyright: ignore[reportArgumentType]
[docs] def pop(self, *arg: "Any") -> "T": result = list.pop(self, *arg) # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType] self.changed() self._pending_removed.add(result) # pyright: ignore[reportArgumentType,reportUnknownArgumentType] return result # pyright: ignore[reportUnknownVariableType]
[docs] def append(self, x: "Any") -> None: list.append(self, x) # pyright: ignore[reportUnknownMemberType] self._pending_append.append(x) self.changed()
[docs] def extend(self, x: "Any") -> None: list.extend(self, x) # pyright: ignore[reportUnknownMemberType] self._pending_append.extend(x) self.changed()
@no_type_check def __iadd__(self, x: "Any") -> "Self": self.extend(x) return self
[docs] def insert(self, i: "Any", x: "Any") -> None: list.insert(self, i, x) # pyright: ignore[reportUnknownMemberType] self._pending_append.append(x) self.changed()
[docs] def remove(self, i: "T") -> None: list.remove(self, i) # pyright: ignore[reportUnknownMemberType] self._pending_removed.add(i) self.changed()
[docs] def clear(self) -> None: self._pending_removed.update(self) list.clear(self) # type: ignore[arg-type] # pyright: ignore[reportUnknownMemberType] self.changed()
[docs] def sort(self, **kw: "Any") -> None: list.sort(self, **kw) # pyright: ignore[reportUnknownMemberType] self.changed()
[docs] def reverse(self) -> None: list.reverse(self) # type: ignore[arg-type] # pyright: ignore[reportUnknownMemberType] self.changed()
def _finalize_pending(self) -> None: """Finalize pending changes by clearing the pending append list.""" self._pending_append.clear()