File: mutables.py

package info (click to toggle)
python-advanced-alchemy 1.4.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 3,708 kB
  • sloc: python: 25,811; makefile: 162; javascript: 123; sh: 4
file content (111 lines) | stat: -rw-r--r-- 4,525 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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")


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.

    """

    def __init__(self, *args: "Any", **kwargs: "Any") -> None:
        super().__init__(*args, **kwargs)
        self._pending_removed: set[T] = set()
        self._pending_append: list[T] = []

    @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]

    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]

    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]

    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]

    def append(self, x: "Any") -> None:
        list.append(self, x)  # pyright: ignore[reportUnknownMemberType]
        self._pending_append.append(x)
        self.changed()

    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

    def insert(self, i: "Any", x: "Any") -> None:
        list.insert(self, i, x)  # pyright: ignore[reportUnknownMemberType]
        self._pending_append.append(x)
        self.changed()

    def remove(self, i: "T") -> None:
        list.remove(self, i)  # pyright: ignore[reportUnknownMemberType]
        self._pending_removed.add(i)
        self.changed()

    def clear(self) -> None:
        self._pending_removed.update(self)
        list.clear(self)  # type: ignore[arg-type] # pyright: ignore[reportUnknownMemberType]
        self.changed()

    def sort(self, **kw: "Any") -> None:
        list.sort(self, **kw)  # pyright: ignore[reportUnknownMemberType]
        self.changed()

    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()