urwid 2.6.0.post0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of urwid might be problematic. Click here for more details.
- urwid/__init__.py +333 -0
- urwid/canvas.py +1413 -0
- urwid/command_map.py +137 -0
- urwid/container.py +59 -0
- urwid/decoration.py +65 -0
- urwid/display/__init__.py +97 -0
- urwid/display/_posix_raw_display.py +413 -0
- urwid/display/_raw_display_base.py +914 -0
- urwid/display/_web.css +12 -0
- urwid/display/_web.js +462 -0
- urwid/display/_win32.py +171 -0
- urwid/display/_win32_raw_display.py +269 -0
- urwid/display/common.py +1219 -0
- urwid/display/curses.py +690 -0
- urwid/display/escape.py +624 -0
- urwid/display/html_fragment.py +251 -0
- urwid/display/lcd.py +518 -0
- urwid/display/raw.py +37 -0
- urwid/display/web.py +636 -0
- urwid/event_loop/__init__.py +55 -0
- urwid/event_loop/abstract_loop.py +175 -0
- urwid/event_loop/asyncio_loop.py +231 -0
- urwid/event_loop/glib_loop.py +294 -0
- urwid/event_loop/main_loop.py +721 -0
- urwid/event_loop/select_loop.py +230 -0
- urwid/event_loop/tornado_loop.py +206 -0
- urwid/event_loop/trio_loop.py +302 -0
- urwid/event_loop/twisted_loop.py +269 -0
- urwid/event_loop/zmq_loop.py +275 -0
- urwid/font.py +695 -0
- urwid/graphics.py +96 -0
- urwid/highlight.css +19 -0
- urwid/listbox.py +1899 -0
- urwid/monitored_list.py +522 -0
- urwid/numedit.py +376 -0
- urwid/signals.py +330 -0
- urwid/split_repr.py +130 -0
- urwid/str_util.py +358 -0
- urwid/text_layout.py +632 -0
- urwid/treetools.py +515 -0
- urwid/util.py +557 -0
- urwid/version.py +16 -0
- urwid/vterm.py +1806 -0
- urwid/widget/__init__.py +181 -0
- urwid/widget/attr_map.py +161 -0
- urwid/widget/attr_wrap.py +140 -0
- urwid/widget/bar_graph.py +649 -0
- urwid/widget/big_text.py +77 -0
- urwid/widget/box_adapter.py +126 -0
- urwid/widget/columns.py +1145 -0
- urwid/widget/constants.py +574 -0
- urwid/widget/container.py +227 -0
- urwid/widget/divider.py +110 -0
- urwid/widget/edit.py +718 -0
- urwid/widget/filler.py +403 -0
- urwid/widget/frame.py +539 -0
- urwid/widget/grid_flow.py +539 -0
- urwid/widget/line_box.py +194 -0
- urwid/widget/overlay.py +829 -0
- urwid/widget/padding.py +597 -0
- urwid/widget/pile.py +971 -0
- urwid/widget/popup.py +170 -0
- urwid/widget/progress_bar.py +141 -0
- urwid/widget/scrollable.py +597 -0
- urwid/widget/solid_fill.py +44 -0
- urwid/widget/text.py +354 -0
- urwid/widget/widget.py +852 -0
- urwid/widget/widget_decoration.py +166 -0
- urwid/widget/wimp.py +792 -0
- urwid/wimp.py +23 -0
- urwid-2.6.0.post0.dist-info/COPYING +504 -0
- urwid-2.6.0.post0.dist-info/METADATA +332 -0
- urwid-2.6.0.post0.dist-info/RECORD +75 -0
- urwid-2.6.0.post0.dist-info/WHEEL +5 -0
- urwid-2.6.0.post0.dist-info/top_level.txt +1 -0
urwid/monitored_list.py
ADDED
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
# Urwid MonitoredList class
|
|
2
|
+
# Copyright (C) 2004-2011 Ian Ward
|
|
3
|
+
#
|
|
4
|
+
# This library is free software; you can redistribute it and/or
|
|
5
|
+
# modify it under the terms of the GNU Lesser General Public
|
|
6
|
+
# License as published by the Free Software Foundation; either
|
|
7
|
+
# version 2.1 of the License, or (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
12
|
+
# Lesser General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
|
15
|
+
# License along with this library; if not, write to the Free Software
|
|
16
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
17
|
+
#
|
|
18
|
+
# Urwid web site: https://urwid.org/
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import functools
|
|
23
|
+
import typing
|
|
24
|
+
import warnings
|
|
25
|
+
|
|
26
|
+
if typing.TYPE_CHECKING:
|
|
27
|
+
from collections.abc import Callable, Collection, Iterator
|
|
28
|
+
|
|
29
|
+
from typing_extensions import Concatenate, ParamSpec
|
|
30
|
+
|
|
31
|
+
ArgSpec = ParamSpec("ArgSpec")
|
|
32
|
+
Ret = typing.TypeVar("Ret")
|
|
33
|
+
|
|
34
|
+
_T = typing.TypeVar("_T")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _call_modified(
|
|
38
|
+
fn: Callable[Concatenate[MonitoredList, ArgSpec], Ret]
|
|
39
|
+
) -> Callable[Concatenate[MonitoredList, ArgSpec], Ret]:
|
|
40
|
+
@functools.wraps(fn)
|
|
41
|
+
def call_modified_wrapper(self: MonitoredList, *args: ArgSpec.args, **kwargs: ArgSpec.kwargs) -> Ret:
|
|
42
|
+
rval = fn(self, *args, **kwargs)
|
|
43
|
+
self._modified() # pylint: disable=protected-access
|
|
44
|
+
return rval
|
|
45
|
+
|
|
46
|
+
return call_modified_wrapper
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class MonitoredList(typing.List[_T], typing.Generic[_T]):
|
|
50
|
+
"""
|
|
51
|
+
This class can trigger a callback any time its contents are changed
|
|
52
|
+
with the usual list operations append, extend, etc.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def _modified(self) -> None: # pylint: disable=method-hidden # monkeypatch used
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
def set_modified_callback(self, callback: Callable[[], typing.Any]) -> None:
|
|
59
|
+
"""
|
|
60
|
+
Assign a callback function with no parameters that is called any
|
|
61
|
+
time the list is modified. Callback's return value is ignored.
|
|
62
|
+
|
|
63
|
+
>>> import sys
|
|
64
|
+
>>> ml = MonitoredList([1,2,3])
|
|
65
|
+
>>> ml.set_modified_callback(lambda: sys.stdout.write("modified\\n"))
|
|
66
|
+
>>> ml
|
|
67
|
+
MonitoredList([1, 2, 3])
|
|
68
|
+
>>> ml.append(10)
|
|
69
|
+
modified
|
|
70
|
+
>>> len(ml)
|
|
71
|
+
4
|
|
72
|
+
>>> ml += [11, 12, 13]
|
|
73
|
+
modified
|
|
74
|
+
>>> ml[:] = ml[:2] + ml[-2:]
|
|
75
|
+
modified
|
|
76
|
+
>>> ml
|
|
77
|
+
MonitoredList([1, 2, 12, 13])
|
|
78
|
+
"""
|
|
79
|
+
self._modified = callback # monkeypatch
|
|
80
|
+
|
|
81
|
+
def __repr__(self) -> str:
|
|
82
|
+
return f"{self.__class__.__name__}({list(self)!r})"
|
|
83
|
+
|
|
84
|
+
# noinspection PyMethodParameters
|
|
85
|
+
def __rich_repr__(self) -> Iterator[tuple[str | None, typing.Any] | typing.Any]:
|
|
86
|
+
for item in self:
|
|
87
|
+
yield None, item
|
|
88
|
+
|
|
89
|
+
__add__ = _call_modified(list.__add__) # type: ignore[assignment] # magic like old __super__
|
|
90
|
+
__delitem__ = _call_modified(list.__delitem__) # type: ignore[assignment] # magic like old __super__
|
|
91
|
+
|
|
92
|
+
__iadd__ = _call_modified(list.__iadd__) # type: ignore[assignment] # magic like old __super__
|
|
93
|
+
__imul__ = _call_modified(list.__imul__) # type: ignore[assignment] # magic like old __super__
|
|
94
|
+
__rmul__ = _call_modified(list.__rmul__) # type: ignore[assignment] # magic like old __super__
|
|
95
|
+
__setitem__ = _call_modified(list.__setitem__) # type: ignore[assignment] # magic like old __super__
|
|
96
|
+
|
|
97
|
+
append = _call_modified(list.append) # type: ignore[assignment] # magic like old __super__
|
|
98
|
+
extend = _call_modified(list.extend) # type: ignore[assignment] # magic like old __super__
|
|
99
|
+
insert = _call_modified(list.insert) # type: ignore[assignment] # magic like old __super__
|
|
100
|
+
pop = _call_modified(list.pop) # type: ignore[assignment] # magic like old __super__
|
|
101
|
+
remove = _call_modified(list.remove) # type: ignore[assignment] # magic like old __super__
|
|
102
|
+
reverse = _call_modified(list.reverse) # type: ignore[assignment] # magic like old __super__
|
|
103
|
+
sort = _call_modified(list.sort) # type: ignore[assignment] # magic like old __super__
|
|
104
|
+
if hasattr(list, "clear"):
|
|
105
|
+
clear = _call_modified(list.clear) # type: ignore[assignment] # magic like old __super__
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class MonitoredFocusList(MonitoredList[_T], typing.Generic[_T]):
|
|
109
|
+
"""
|
|
110
|
+
This class can trigger a callback any time its contents are modified,
|
|
111
|
+
before and/or after modification, and any time the focus index is changed.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
def __init__(self, *args, focus: int = 0, **kwargs) -> None:
|
|
115
|
+
"""
|
|
116
|
+
This is a list that tracks one item as the focus item. If items
|
|
117
|
+
are inserted or removed it will update the focus.
|
|
118
|
+
|
|
119
|
+
>>> ml = MonitoredFocusList([10, 11, 12, 13, 14], focus=3)
|
|
120
|
+
>>> ml
|
|
121
|
+
MonitoredFocusList([10, 11, 12, 13, 14], focus=3)
|
|
122
|
+
>>> del(ml[1])
|
|
123
|
+
>>> ml
|
|
124
|
+
MonitoredFocusList([10, 12, 13, 14], focus=2)
|
|
125
|
+
>>> ml[:2] = [50, 51, 52, 53]
|
|
126
|
+
>>> ml
|
|
127
|
+
MonitoredFocusList([50, 51, 52, 53, 13, 14], focus=4)
|
|
128
|
+
>>> ml[4] = 99
|
|
129
|
+
>>> ml
|
|
130
|
+
MonitoredFocusList([50, 51, 52, 53, 99, 14], focus=4)
|
|
131
|
+
>>> ml[:] = []
|
|
132
|
+
>>> ml
|
|
133
|
+
MonitoredFocusList([], focus=None)
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
super().__init__(*args, **kwargs)
|
|
137
|
+
|
|
138
|
+
self._focus = focus
|
|
139
|
+
self._focus_modified = lambda ml, indices, new_items: None
|
|
140
|
+
|
|
141
|
+
def __repr__(self) -> str:
|
|
142
|
+
return f"{self.__class__.__name__}({list(self)!r}, focus={self.focus!r})"
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def focus(self) -> int | None:
|
|
146
|
+
"""
|
|
147
|
+
Get/set the focus index. This value is read as None when the list
|
|
148
|
+
is empty, and may only be set to a value between 0 and len(self)-1
|
|
149
|
+
or an IndexError will be raised.
|
|
150
|
+
|
|
151
|
+
Return the index of the item "in focus" or None if
|
|
152
|
+
the list is empty.
|
|
153
|
+
|
|
154
|
+
>>> MonitoredFocusList([1,2,3], focus=2).focus
|
|
155
|
+
2
|
|
156
|
+
>>> MonitoredFocusList().focus
|
|
157
|
+
"""
|
|
158
|
+
if not self:
|
|
159
|
+
return None
|
|
160
|
+
return self._focus
|
|
161
|
+
|
|
162
|
+
@focus.setter
|
|
163
|
+
def focus(self, index: int) -> None:
|
|
164
|
+
"""
|
|
165
|
+
index -- index into this list, any index out of range will
|
|
166
|
+
raise an IndexError, except when the list is empty and
|
|
167
|
+
the index passed is ignored.
|
|
168
|
+
|
|
169
|
+
This function may call self._focus_changed when the focus
|
|
170
|
+
is modified, passing the new focus position to the
|
|
171
|
+
callback just before changing the old focus setting.
|
|
172
|
+
That method may be overridden on the
|
|
173
|
+
instance with set_focus_changed_callback().
|
|
174
|
+
|
|
175
|
+
>>> ml = MonitoredFocusList([9, 10, 11])
|
|
176
|
+
>>> ml.focus = 2; ml.focus
|
|
177
|
+
2
|
|
178
|
+
>>> ml.focus = 0; ml.focus
|
|
179
|
+
0
|
|
180
|
+
>>> ml.focus = -2
|
|
181
|
+
Traceback (most recent call last):
|
|
182
|
+
...
|
|
183
|
+
IndexError: focus index is out of range: -2
|
|
184
|
+
"""
|
|
185
|
+
if not self:
|
|
186
|
+
self._focus = 0
|
|
187
|
+
return
|
|
188
|
+
if not isinstance(index, int):
|
|
189
|
+
raise TypeError("index must be an integer")
|
|
190
|
+
if index < 0 or index >= len(self):
|
|
191
|
+
raise IndexError(f"focus index is out of range: {index}")
|
|
192
|
+
|
|
193
|
+
if index != self._focus:
|
|
194
|
+
self._focus_changed(index)
|
|
195
|
+
self._focus = index
|
|
196
|
+
|
|
197
|
+
def _get_focus(self) -> int | None:
|
|
198
|
+
warnings.warn(
|
|
199
|
+
f"method `{self.__class__.__name__}._get_focus` is deprecated, "
|
|
200
|
+
f"please use `{self.__class__.__name__}.focus` property",
|
|
201
|
+
DeprecationWarning,
|
|
202
|
+
stacklevel=3,
|
|
203
|
+
)
|
|
204
|
+
return self.focus
|
|
205
|
+
|
|
206
|
+
def _set_focus(self, index: int) -> None:
|
|
207
|
+
warnings.warn(
|
|
208
|
+
f"method `{self.__class__.__name__}._set_focus` is deprecated, "
|
|
209
|
+
f"please use `{self.__class__.__name__}.focus` property",
|
|
210
|
+
DeprecationWarning,
|
|
211
|
+
stacklevel=3,
|
|
212
|
+
)
|
|
213
|
+
self.focus = index
|
|
214
|
+
|
|
215
|
+
def _focus_changed(self, new_focus: int) -> None: # pylint: disable=method-hidden # monkeypatch used
|
|
216
|
+
pass
|
|
217
|
+
|
|
218
|
+
def set_focus_changed_callback(self, callback: Callable[[int], typing.Any]) -> None:
|
|
219
|
+
"""
|
|
220
|
+
Assign a callback to be called when the focus index changes
|
|
221
|
+
for any reason. The callback is in the form:
|
|
222
|
+
|
|
223
|
+
callback(new_focus)
|
|
224
|
+
new_focus -- new focus index
|
|
225
|
+
|
|
226
|
+
>>> import sys
|
|
227
|
+
>>> ml = MonitoredFocusList([1,2,3], focus=1)
|
|
228
|
+
>>> ml.set_focus_changed_callback(lambda f: sys.stdout.write("focus: %d\\n" % (f,)))
|
|
229
|
+
>>> ml
|
|
230
|
+
MonitoredFocusList([1, 2, 3], focus=1)
|
|
231
|
+
>>> ml.append(10)
|
|
232
|
+
>>> ml.insert(1, 11)
|
|
233
|
+
focus: 2
|
|
234
|
+
>>> ml
|
|
235
|
+
MonitoredFocusList([1, 11, 2, 3, 10], focus=2)
|
|
236
|
+
>>> del ml[:2]
|
|
237
|
+
focus: 0
|
|
238
|
+
>>> ml[:0] = [12, 13, 14]
|
|
239
|
+
focus: 3
|
|
240
|
+
>>> ml.focus = 5
|
|
241
|
+
focus: 5
|
|
242
|
+
>>> ml
|
|
243
|
+
MonitoredFocusList([12, 13, 14, 2, 3, 10], focus=5)
|
|
244
|
+
"""
|
|
245
|
+
self._focus_changed = callback # Monkeypatch
|
|
246
|
+
|
|
247
|
+
def _validate_contents_modified( # pylint: disable=method-hidden # monkeypatch used
|
|
248
|
+
self,
|
|
249
|
+
indices: tuple[int, int, int],
|
|
250
|
+
new_items: Collection[_T],
|
|
251
|
+
) -> int | None:
|
|
252
|
+
return None
|
|
253
|
+
|
|
254
|
+
def set_validate_contents_modified(self, callback: Callable[[tuple[int, int, int], Collection[_T]], int | None]):
|
|
255
|
+
"""
|
|
256
|
+
Assign a callback function to handle validating changes to the list.
|
|
257
|
+
This may raise an exception if the change should not be performed.
|
|
258
|
+
It may also return an integer position to be the new focus after the
|
|
259
|
+
list is modified, or None to use the default behaviour.
|
|
260
|
+
|
|
261
|
+
The callback is in the form:
|
|
262
|
+
|
|
263
|
+
callback(indices, new_items)
|
|
264
|
+
indices -- a (start, stop, step) tuple whose range covers the
|
|
265
|
+
items being modified
|
|
266
|
+
new_items -- an iterable of items replacing those at range(*indices),
|
|
267
|
+
empty if items are being removed, if step==1 this list may
|
|
268
|
+
contain any number of items
|
|
269
|
+
"""
|
|
270
|
+
self._validate_contents_modified = callback # Monkeypatch
|
|
271
|
+
|
|
272
|
+
def _adjust_focus_on_contents_modified(self, slc: slice, new_items: Collection[_T] = ()) -> int:
|
|
273
|
+
"""
|
|
274
|
+
Default behaviour is to move the focus to the item following
|
|
275
|
+
any removed items, unless that item was simply replaced.
|
|
276
|
+
|
|
277
|
+
Failing that choose the last item in the list.
|
|
278
|
+
|
|
279
|
+
returns focus position for after change is applied
|
|
280
|
+
"""
|
|
281
|
+
num_new_items = len(new_items)
|
|
282
|
+
start, stop, step = indices = slc.indices(len(self))
|
|
283
|
+
num_removed = len(list(range(*indices)))
|
|
284
|
+
|
|
285
|
+
focus = self._validate_contents_modified(indices, new_items)
|
|
286
|
+
if focus is not None:
|
|
287
|
+
return focus
|
|
288
|
+
|
|
289
|
+
focus = self._focus
|
|
290
|
+
if step == 1:
|
|
291
|
+
if start + num_new_items <= focus < stop:
|
|
292
|
+
focus = stop
|
|
293
|
+
# adjust for added/removed items
|
|
294
|
+
if stop <= focus:
|
|
295
|
+
focus += num_new_items - (stop - start)
|
|
296
|
+
|
|
297
|
+
else: # noqa: PLR5501 # pylint: disable=else-if-used # readability
|
|
298
|
+
if not num_new_items:
|
|
299
|
+
# extended slice being removed
|
|
300
|
+
if focus in range(start, stop, step):
|
|
301
|
+
focus += 1
|
|
302
|
+
|
|
303
|
+
# adjust for removed items
|
|
304
|
+
focus -= len(list(range(start, min(focus, stop), step)))
|
|
305
|
+
|
|
306
|
+
return min(focus, len(self) + num_new_items - num_removed - 1)
|
|
307
|
+
|
|
308
|
+
# override all the list methods that modify the list
|
|
309
|
+
|
|
310
|
+
def __delitem__(self, y: int | slice) -> None:
|
|
311
|
+
"""
|
|
312
|
+
>>> ml = MonitoredFocusList([0,1,2,3,4], focus=2)
|
|
313
|
+
>>> del ml[3]; ml
|
|
314
|
+
MonitoredFocusList([0, 1, 2, 4], focus=2)
|
|
315
|
+
>>> del ml[-1]; ml
|
|
316
|
+
MonitoredFocusList([0, 1, 2], focus=2)
|
|
317
|
+
>>> del ml[0]; ml
|
|
318
|
+
MonitoredFocusList([1, 2], focus=1)
|
|
319
|
+
>>> del ml[1]; ml
|
|
320
|
+
MonitoredFocusList([1], focus=0)
|
|
321
|
+
>>> del ml[0]; ml
|
|
322
|
+
MonitoredFocusList([], focus=None)
|
|
323
|
+
>>> ml = MonitoredFocusList([5,4,6,4,5,4,6,4,5], focus=4)
|
|
324
|
+
>>> del ml[1::2]; ml
|
|
325
|
+
MonitoredFocusList([5, 6, 5, 6, 5], focus=2)
|
|
326
|
+
>>> del ml[::2]; ml
|
|
327
|
+
MonitoredFocusList([6, 6], focus=1)
|
|
328
|
+
>>> ml = MonitoredFocusList([0,1,2,3,4,6,7], focus=2)
|
|
329
|
+
>>> del ml[-2:]; ml
|
|
330
|
+
MonitoredFocusList([0, 1, 2, 3, 4], focus=2)
|
|
331
|
+
>>> del ml[-4:-2]; ml
|
|
332
|
+
MonitoredFocusList([0, 3, 4], focus=1)
|
|
333
|
+
>>> del ml[:]; ml
|
|
334
|
+
MonitoredFocusList([], focus=None)
|
|
335
|
+
"""
|
|
336
|
+
if isinstance(y, slice):
|
|
337
|
+
focus = self._adjust_focus_on_contents_modified(y)
|
|
338
|
+
else:
|
|
339
|
+
focus = self._adjust_focus_on_contents_modified(slice(y, y + 1 or None))
|
|
340
|
+
super().__delitem__(y)
|
|
341
|
+
self.focus = focus
|
|
342
|
+
|
|
343
|
+
@typing.overload
|
|
344
|
+
def __setitem__(self, i: int, y: _T) -> None: ...
|
|
345
|
+
|
|
346
|
+
@typing.overload
|
|
347
|
+
def __setitem__(self, i: slice, y: Collection[_T]) -> None: ...
|
|
348
|
+
|
|
349
|
+
def __setitem__(self, i: int | slice, y: _T | Collection[_T]) -> None:
|
|
350
|
+
"""
|
|
351
|
+
>>> def modified(indices, new_items):
|
|
352
|
+
... print(f"range{indices!r} <- {new_items!r}" )
|
|
353
|
+
>>> ml = MonitoredFocusList([0,1,2,3], focus=2)
|
|
354
|
+
>>> ml.set_validate_contents_modified(modified)
|
|
355
|
+
>>> ml[0] = 9
|
|
356
|
+
range(0, 1, 1) <- [9]
|
|
357
|
+
>>> ml[2] = 6
|
|
358
|
+
range(2, 3, 1) <- [6]
|
|
359
|
+
>>> ml.focus
|
|
360
|
+
2
|
|
361
|
+
>>> ml[-1] = 8
|
|
362
|
+
range(3, 4, 1) <- [8]
|
|
363
|
+
>>> ml
|
|
364
|
+
MonitoredFocusList([9, 1, 6, 8], focus=2)
|
|
365
|
+
>>> ml[1::2] = [12, 13]
|
|
366
|
+
range(1, 4, 2) <- [12, 13]
|
|
367
|
+
>>> ml[::2] = [10, 11]
|
|
368
|
+
range(0, 4, 2) <- [10, 11]
|
|
369
|
+
>>> ml[-3:-1] = [21, 22, 23]
|
|
370
|
+
range(1, 3, 1) <- [21, 22, 23]
|
|
371
|
+
>>> ml
|
|
372
|
+
MonitoredFocusList([10, 21, 22, 23, 13], focus=2)
|
|
373
|
+
>>> ml[:] = []
|
|
374
|
+
range(0, 5, 1) <- []
|
|
375
|
+
>>> ml
|
|
376
|
+
MonitoredFocusList([], focus=None)
|
|
377
|
+
"""
|
|
378
|
+
if isinstance(i, slice):
|
|
379
|
+
focus = self._adjust_focus_on_contents_modified(i, y)
|
|
380
|
+
else:
|
|
381
|
+
focus = self._adjust_focus_on_contents_modified(slice(i, i + 1 or None), [y])
|
|
382
|
+
super().__setitem__(i, y)
|
|
383
|
+
self.focus = focus
|
|
384
|
+
|
|
385
|
+
def __imul__(self, n: int):
|
|
386
|
+
"""
|
|
387
|
+
>>> def modified(indices, new_items):
|
|
388
|
+
... print(f"range{indices!r} <- {list(new_items)!r}" )
|
|
389
|
+
>>> ml = MonitoredFocusList([0,1,2], focus=2)
|
|
390
|
+
>>> ml.set_validate_contents_modified(modified)
|
|
391
|
+
>>> ml *= 3
|
|
392
|
+
range(3, 3, 1) <- [0, 1, 2, 0, 1, 2]
|
|
393
|
+
>>> ml
|
|
394
|
+
MonitoredFocusList([0, 1, 2, 0, 1, 2, 0, 1, 2], focus=2)
|
|
395
|
+
>>> ml *= 0
|
|
396
|
+
range(0, 9, 1) <- []
|
|
397
|
+
>>> print(ml.focus)
|
|
398
|
+
None
|
|
399
|
+
"""
|
|
400
|
+
if n > 0:
|
|
401
|
+
focus = self._adjust_focus_on_contents_modified(slice(len(self), len(self)), list(self) * (n - 1))
|
|
402
|
+
else: # all contents are being removed
|
|
403
|
+
focus = self._adjust_focus_on_contents_modified(slice(0, len(self)))
|
|
404
|
+
rval = super().__imul__(n)
|
|
405
|
+
self.focus = focus
|
|
406
|
+
return rval
|
|
407
|
+
|
|
408
|
+
def append(self, item: _T) -> None:
|
|
409
|
+
"""
|
|
410
|
+
>>> def modified(indices, new_items):
|
|
411
|
+
... print(f"range{indices!r} <- {new_items!r}" )
|
|
412
|
+
>>> ml = MonitoredFocusList([0,1,2], focus=2)
|
|
413
|
+
>>> ml.set_validate_contents_modified(modified)
|
|
414
|
+
>>> ml.append(6)
|
|
415
|
+
range(3, 3, 1) <- [6]
|
|
416
|
+
"""
|
|
417
|
+
focus = self._adjust_focus_on_contents_modified(slice(len(self), len(self)), [item])
|
|
418
|
+
super().append(item)
|
|
419
|
+
self.focus = focus
|
|
420
|
+
|
|
421
|
+
def extend(self, items: Collection[_T]) -> None:
|
|
422
|
+
"""
|
|
423
|
+
>>> def modified(indices, new_items):
|
|
424
|
+
... print(f"range{indices!r} <- {list(new_items)!r}" )
|
|
425
|
+
>>> ml = MonitoredFocusList([0,1,2], focus=2)
|
|
426
|
+
>>> ml.set_validate_contents_modified(modified)
|
|
427
|
+
>>> ml.extend((6,7,8))
|
|
428
|
+
range(3, 3, 1) <- [6, 7, 8]
|
|
429
|
+
"""
|
|
430
|
+
focus = self._adjust_focus_on_contents_modified(slice(len(self), len(self)), items)
|
|
431
|
+
super().extend(items)
|
|
432
|
+
self.focus = focus
|
|
433
|
+
|
|
434
|
+
def insert(self, index: int, item: _T) -> None:
|
|
435
|
+
"""
|
|
436
|
+
>>> ml = MonitoredFocusList([0,1,2,3], focus=2)
|
|
437
|
+
>>> ml.insert(-1, -1); ml
|
|
438
|
+
MonitoredFocusList([0, 1, 2, -1, 3], focus=2)
|
|
439
|
+
>>> ml.insert(0, -2); ml
|
|
440
|
+
MonitoredFocusList([-2, 0, 1, 2, -1, 3], focus=3)
|
|
441
|
+
>>> ml.insert(3, -3); ml
|
|
442
|
+
MonitoredFocusList([-2, 0, 1, -3, 2, -1, 3], focus=4)
|
|
443
|
+
"""
|
|
444
|
+
focus = self._adjust_focus_on_contents_modified(slice(index, index), [item])
|
|
445
|
+
super().insert(index, item)
|
|
446
|
+
self.focus = focus
|
|
447
|
+
|
|
448
|
+
def pop(self, index: int = -1) -> _T:
|
|
449
|
+
"""
|
|
450
|
+
>>> ml = MonitoredFocusList([-2,0,1,-3,2,3], focus=4)
|
|
451
|
+
>>> ml.pop(3); ml
|
|
452
|
+
-3
|
|
453
|
+
MonitoredFocusList([-2, 0, 1, 2, 3], focus=3)
|
|
454
|
+
>>> ml.pop(0); ml
|
|
455
|
+
-2
|
|
456
|
+
MonitoredFocusList([0, 1, 2, 3], focus=2)
|
|
457
|
+
>>> ml.pop(-1); ml
|
|
458
|
+
3
|
|
459
|
+
MonitoredFocusList([0, 1, 2], focus=2)
|
|
460
|
+
>>> ml.pop(2); ml
|
|
461
|
+
2
|
|
462
|
+
MonitoredFocusList([0, 1], focus=1)
|
|
463
|
+
"""
|
|
464
|
+
focus = self._adjust_focus_on_contents_modified(slice(index, index + 1 or None))
|
|
465
|
+
rval = super().pop(index)
|
|
466
|
+
self.focus = focus
|
|
467
|
+
return rval
|
|
468
|
+
|
|
469
|
+
def remove(self, value: _T) -> None:
|
|
470
|
+
"""
|
|
471
|
+
>>> ml = MonitoredFocusList([-2,0,1,-3,2,-1,3], focus=4)
|
|
472
|
+
>>> ml.remove(-3); ml
|
|
473
|
+
MonitoredFocusList([-2, 0, 1, 2, -1, 3], focus=3)
|
|
474
|
+
>>> ml.remove(-2); ml
|
|
475
|
+
MonitoredFocusList([0, 1, 2, -1, 3], focus=2)
|
|
476
|
+
>>> ml.remove(3); ml
|
|
477
|
+
MonitoredFocusList([0, 1, 2, -1], focus=2)
|
|
478
|
+
"""
|
|
479
|
+
index = self.index(value)
|
|
480
|
+
focus = self._adjust_focus_on_contents_modified(slice(index, index + 1 or None))
|
|
481
|
+
super().remove(value)
|
|
482
|
+
self.focus = focus
|
|
483
|
+
|
|
484
|
+
def reverse(self) -> None:
|
|
485
|
+
"""
|
|
486
|
+
>>> ml = MonitoredFocusList([0,1,2,3,4], focus=1)
|
|
487
|
+
>>> ml.reverse(); ml
|
|
488
|
+
MonitoredFocusList([4, 3, 2, 1, 0], focus=3)
|
|
489
|
+
"""
|
|
490
|
+
rval = super().reverse()
|
|
491
|
+
self.focus = max(0, len(self) - self._focus - 1)
|
|
492
|
+
return rval
|
|
493
|
+
|
|
494
|
+
def sort(self, **kwargs) -> None:
|
|
495
|
+
"""
|
|
496
|
+
>>> ml = MonitoredFocusList([-2,0,1,-3,2,-1,3], focus=4)
|
|
497
|
+
>>> ml.sort(); ml
|
|
498
|
+
MonitoredFocusList([-3, -2, -1, 0, 1, 2, 3], focus=5)
|
|
499
|
+
"""
|
|
500
|
+
if not self:
|
|
501
|
+
return None
|
|
502
|
+
value = self[self._focus]
|
|
503
|
+
rval = super().sort(**kwargs)
|
|
504
|
+
self.focus = self.index(value)
|
|
505
|
+
return rval
|
|
506
|
+
|
|
507
|
+
if hasattr(list, "clear"):
|
|
508
|
+
|
|
509
|
+
def clear(self) -> None:
|
|
510
|
+
focus = self._adjust_focus_on_contents_modified(slice(0, 0))
|
|
511
|
+
super().clear()
|
|
512
|
+
self.focus = focus
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def _test():
|
|
516
|
+
import doctest
|
|
517
|
+
|
|
518
|
+
doctest.testmod()
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
if __name__ == "__main__":
|
|
522
|
+
_test()
|