aboutsummaryrefslogtreecommitdiff
path: root/venv/lib/python3.8/site-packages/dash/_patch.py
blob: 99a4a919e8ca2a3b7213963c58a7e88c6ad944a7 (plain)
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
from typing import List, Union, Optional, Any


def _operation(name, location, **kwargs):
    return {"operation": name, "location": location, "params": kwargs}


_noop = object()

_KeyType = Union[str, int]


def validate_slice(obj: Any):
    if isinstance(obj, slice):
        raise TypeError("a slice is not a valid index for patch")


class Patch:
    """
    Patch a callback output value

    Act like a proxy of the output prop value on the frontend.

    Supported prop types: Dictionaries and lists.
    """

    def __init__(
        self,
        location: Optional[List[_KeyType]] = None,
        parent: Optional["Patch"] = None,
    ):
        if location is not None:
            self._location = location
        else:
            # pylint: disable=consider-using-ternary
            self._location = (parent and parent._location) or []
        if parent is not None:
            self._operations = parent._operations
        else:
            self._operations = []

    def __getstate__(self):
        return vars(self)

    def __setstate__(self, state):
        vars(self).update(state)

    def __getitem__(self, item: _KeyType) -> "Patch":
        validate_slice(item)
        return Patch(location=self._location + [item], parent=self)

    def __getattr__(self, item: _KeyType) -> "Patch":
        if item == "tolist":
            # to_json fix
            raise AttributeError
        if item == "_location":
            return self._location  # type: ignore
        if item == "_operations":
            return self._operations  # type: ignore
        return self.__getitem__(item)

    def __setattr__(self, key: _KeyType, value: Any):
        if key in ("_location", "_operations"):
            self.__dict__[key] = value
        else:
            self.__setitem__(key, value)

    def __delattr__(self, item: _KeyType):
        self.__delitem__(item)

    def __setitem__(self, key: _KeyType, value: Any):
        validate_slice(key)
        if value is _noop:
            # The += set themselves.
            return
        self._operations.append(
            _operation(
                "Assign",
                self._location + [key],
                value=value,
            )
        )

    def __delitem__(self, key: _KeyType):
        validate_slice(key)
        self._operations.append(_operation("Delete", self._location + [key]))

    def __iadd__(self, other: Any):
        if isinstance(other, (list, tuple)):
            self.extend(other)
        else:
            self._operations.append(_operation("Add", self._location, value=other))
        if not self._location:
            return self
        return _noop

    def __isub__(self, other: Any):
        self._operations.append(_operation("Sub", self._location, value=other))
        if not self._location:
            return self
        return _noop

    def __imul__(self, other: Any) -> "Patch":
        self._operations.append(_operation("Mul", self._location, value=other))
        if not self._location:
            return self
        return _noop

    def __itruediv__(self, other: Any):
        self._operations.append(_operation("Div", self._location, value=other))
        if not self._location:
            return self
        return _noop

    def __ior__(self, other: Any):
        self.update(E=other)
        if not self._location:
            return self
        return _noop

    def __iter__(self):
        raise TypeError("Patch objects are write-only, you cannot iterate them.")

    def __repr__(self):
        return f"<write-only dash.Patch object at {self._location}>"

    def append(self, item: Any) -> None:
        """Add the item to the end of a list"""
        self._operations.append(_operation("Append", self._location, value=item))

    def prepend(self, item: Any) -> None:
        """Add the item to the start of a list"""
        self._operations.append(_operation("Prepend", self._location, value=item))

    def insert(self, index: int, item: Any) -> None:
        """Add the item at the index of a list"""
        self._operations.append(
            _operation("Insert", self._location, value=item, index=index)
        )

    def clear(self) -> None:
        """Remove all items in a list"""
        self._operations.append(_operation("Clear", self._location))

    def reverse(self) -> None:
        """Reversal of the order of items in a list"""
        self._operations.append(_operation("Reverse", self._location))

    def extend(self, item: Union[list, tuple]) -> None:
        """Add all the items to the end of a list"""
        if not isinstance(item, (list, tuple)):
            raise TypeError(f"{item} should be a list or tuple")
        self._operations.append(_operation("Extend", self._location, value=item))

    def remove(self, item: Any) -> None:
        """filter the item out of a list on the frontend"""
        self._operations.append(_operation("Remove", self._location, value=item))

    def update(self, E: Any = None, **F) -> None:
        """Merge a dict or keyword arguments with another dictionary"""
        value = E or {}
        value.update(F)
        self._operations.append(_operation("Merge", self._location, value=value))

    # pylint: disable=no-self-use
    def sort(self):
        raise KeyError(
            "sort is reserved for future use, use brackets to access this key on your object"
        )

    def to_plotly_json(self) -> Any:
        return {
            "__dash_patch_update": "__dash_patch_update",
            "operations": self._operations,
        }