urwid 2.6.15__py3-none-any.whl → 3.0.5__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.
- urwid/__init__.py +30 -20
- urwid/canvas.py +34 -53
- urwid/command_map.py +6 -4
- urwid/container.py +1 -1
- urwid/decoration.py +1 -1
- urwid/display/__init__.py +53 -48
- urwid/display/_posix_raw_display.py +20 -8
- urwid/display/_raw_display_base.py +21 -16
- urwid/display/_win32_raw_display.py +16 -17
- urwid/display/common.py +45 -74
- urwid/display/curses.py +3 -5
- urwid/display/escape.py +28 -13
- urwid/display/lcd.py +8 -10
- urwid/display/web.py +11 -16
- urwid/event_loop/asyncio_loop.py +35 -15
- urwid/event_loop/main_loop.py +18 -23
- urwid/event_loop/tornado_loop.py +4 -5
- urwid/event_loop/trio_loop.py +1 -1
- urwid/font.py +19 -22
- urwid/numedit.py +65 -65
- urwid/signals.py +19 -27
- urwid/split_repr.py +9 -3
- urwid/str_util.py +105 -60
- urwid/text_layout.py +14 -13
- urwid/util.py +8 -19
- urwid/version.py +22 -4
- urwid/vterm.py +20 -47
- urwid/widget/__init__.py +0 -6
- urwid/widget/attr_map.py +10 -10
- urwid/widget/attr_wrap.py +11 -13
- urwid/widget/bar_graph.py +3 -8
- urwid/widget/big_text.py +8 -9
- urwid/widget/box_adapter.py +6 -6
- urwid/widget/columns.py +52 -83
- urwid/widget/container.py +29 -75
- urwid/widget/divider.py +6 -6
- urwid/widget/edit.py +50 -50
- urwid/widget/filler.py +14 -14
- urwid/widget/frame.py +31 -40
- urwid/widget/grid_flow.py +25 -110
- urwid/widget/line_box.py +31 -18
- urwid/widget/listbox.py +16 -51
- urwid/widget/monitored_list.py +75 -49
- urwid/widget/overlay.py +4 -37
- urwid/widget/padding.py +31 -68
- urwid/widget/pile.py +179 -158
- urwid/widget/popup.py +2 -2
- urwid/widget/progress_bar.py +17 -18
- urwid/widget/scrollable.py +26 -34
- urwid/widget/solid_fill.py +3 -3
- urwid/widget/text.py +44 -30
- urwid/widget/treetools.py +27 -48
- urwid/widget/widget.py +13 -130
- urwid/widget/widget_decoration.py +6 -35
- urwid/widget/wimp.py +61 -61
- urwid/wimp.py +1 -1
- {urwid-2.6.15.dist-info → urwid-3.0.5.dist-info}/METADATA +24 -24
- urwid-3.0.5.dist-info/RECORD +74 -0
- {urwid-2.6.15.dist-info → urwid-3.0.5.dist-info}/WHEEL +1 -1
- urwid-2.6.15.dist-info/RECORD +0 -74
- {urwid-2.6.15.dist-info → urwid-3.0.5.dist-info/licenses}/COPYING +0 -0
- {urwid-2.6.15.dist-info → urwid-3.0.5.dist-info}/top_level.txt +0 -0
urwid/__init__.py
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
from __future__ import annotations
|
|
23
23
|
|
|
24
|
-
import importlib
|
|
24
|
+
import importlib.util
|
|
25
25
|
import sys
|
|
26
26
|
import types
|
|
27
27
|
import typing
|
|
@@ -150,7 +150,6 @@ from .widget import (
|
|
|
150
150
|
BigText,
|
|
151
151
|
BoxAdapter,
|
|
152
152
|
BoxAdapterError,
|
|
153
|
-
BoxWidget,
|
|
154
153
|
Button,
|
|
155
154
|
CheckBox,
|
|
156
155
|
CheckBoxError,
|
|
@@ -161,8 +160,6 @@ from .widget import (
|
|
|
161
160
|
EditError,
|
|
162
161
|
Filler,
|
|
163
162
|
FillerError,
|
|
164
|
-
FixedWidget,
|
|
165
|
-
FlowWidget,
|
|
166
163
|
Frame,
|
|
167
164
|
FrameError,
|
|
168
165
|
GraphVScale,
|
|
@@ -242,7 +239,7 @@ except ImportError:
|
|
|
242
239
|
|
|
243
240
|
# OS Specific
|
|
244
241
|
if sys.platform != "win32":
|
|
245
|
-
from .vterm import TermCanvas, TermCharset, Terminal, TermModes
|
|
242
|
+
from .vterm import TermCanvas, TermCharset, Terminal, TermModes
|
|
246
243
|
|
|
247
244
|
# ZMQEventLoop cause interpreter crash on windows
|
|
248
245
|
try:
|
|
@@ -268,12 +265,28 @@ _moved_warn: dict[str, str] = {
|
|
|
268
265
|
}
|
|
269
266
|
# Backward compatible lazy load without any warnings
|
|
270
267
|
# Before DeprecationWarning need to start PendingDeprecationWarning process.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
"
|
|
275
|
-
|
|
276
|
-
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def lazy_import(name: str, package: str | None = None) -> types.ModuleType:
|
|
271
|
+
"""Lazy import implementation from Python documentation.
|
|
272
|
+
|
|
273
|
+
Useful for cases where no warnings expected for moved modules.
|
|
274
|
+
"""
|
|
275
|
+
spec = importlib.util.find_spec(name, package)
|
|
276
|
+
if not spec:
|
|
277
|
+
raise ImportError(f"No module named {name!r}")
|
|
278
|
+
if not spec.loader:
|
|
279
|
+
raise ImportError(f"Module named {name!r} is invalid")
|
|
280
|
+
|
|
281
|
+
loader = importlib.util.LazyLoader(spec.loader)
|
|
282
|
+
spec.loader = loader
|
|
283
|
+
module = importlib.util.module_from_spec(spec)
|
|
284
|
+
if not package:
|
|
285
|
+
sys.modules[name] = module
|
|
286
|
+
else:
|
|
287
|
+
sys.modules[f"{package.rstrip('.')}.{name.lstrip('.')}"] = module
|
|
288
|
+
loader.exec_module(module)
|
|
289
|
+
return module
|
|
277
290
|
|
|
278
291
|
|
|
279
292
|
class _MovedModule(types.ModuleType):
|
|
@@ -296,6 +309,12 @@ class _MovedModule(types.ModuleType):
|
|
|
296
309
|
return getattr(real_module, name)
|
|
297
310
|
|
|
298
311
|
|
|
312
|
+
display_common = lazy_import("urwid.display.common")
|
|
313
|
+
raw_display = lazy_import("urwid.display.raw")
|
|
314
|
+
curses_display = lazy_import("urwid.display.curses")
|
|
315
|
+
escape = lazy_import("urwid.display.escape")
|
|
316
|
+
|
|
317
|
+
|
|
299
318
|
class _MovedModuleWarn(_MovedModule):
|
|
300
319
|
"""Special class to handle moved modules.
|
|
301
320
|
|
|
@@ -313,10 +332,6 @@ class _MovedModuleWarn(_MovedModule):
|
|
|
313
332
|
return super().__getattr__(name)
|
|
314
333
|
|
|
315
334
|
|
|
316
|
-
for _name, _module in _moved_no_warn.items():
|
|
317
|
-
_module_path = f"{__name__}.{_name}"
|
|
318
|
-
sys.modules[_module_path] = _MovedModule(_module_path, _module)
|
|
319
|
-
|
|
320
335
|
for _name, _module in _moved_warn.items():
|
|
321
336
|
_module_path = f"{__name__}.{_name}"
|
|
322
337
|
sys.modules[_module_path] = _MovedModuleWarn(_module_path, _module)
|
|
@@ -328,11 +343,6 @@ def __getattr__(name: str) -> typing.Any:
|
|
|
328
343
|
:return: attribute by name
|
|
329
344
|
:raises AttributeError: attribute is not defined for lazy load
|
|
330
345
|
"""
|
|
331
|
-
if name in _moved_no_warn:
|
|
332
|
-
mod = importlib.import_module(_moved_no_warn[name])
|
|
333
|
-
__locals[name] = mod
|
|
334
|
-
return mod
|
|
335
|
-
|
|
336
346
|
if name in _moved_warn:
|
|
337
347
|
warnings.warn(
|
|
338
348
|
f"{name} is moved to {_moved_warn[name]}",
|
urwid/canvas.py
CHANGED
|
@@ -23,7 +23,6 @@ from __future__ import annotations
|
|
|
23
23
|
import contextlib
|
|
24
24
|
import dataclasses
|
|
25
25
|
import typing
|
|
26
|
-
import warnings
|
|
27
26
|
import weakref
|
|
28
27
|
from contextlib import suppress
|
|
29
28
|
|
|
@@ -40,7 +39,7 @@ from urwid.util import (
|
|
|
40
39
|
)
|
|
41
40
|
|
|
42
41
|
if typing.TYPE_CHECKING:
|
|
43
|
-
from collections.abc import Hashable, Iterable, Sequence
|
|
42
|
+
from collections.abc import Hashable, Iterable, Iterator, Sequence
|
|
44
43
|
|
|
45
44
|
from typing_extensions import Literal
|
|
46
45
|
|
|
@@ -85,7 +84,7 @@ class CanvasCache:
|
|
|
85
84
|
cleanups = 0
|
|
86
85
|
|
|
87
86
|
@classmethod
|
|
88
|
-
def store(cls, wcls, canvas: Canvas) -> None:
|
|
87
|
+
def store(cls, wcls: type[Widget], canvas: Canvas) -> None:
|
|
89
88
|
"""
|
|
90
89
|
Store a weakref to canvas in the cache.
|
|
91
90
|
|
|
@@ -99,7 +98,7 @@ class CanvasCache:
|
|
|
99
98
|
raise TypeError("Can't store canvas without widget_info")
|
|
100
99
|
widget, size, focus = canvas.widget_info
|
|
101
100
|
|
|
102
|
-
def walk_depends(canv):
|
|
101
|
+
def walk_depends(canv: Canvas) -> list[Widget]:
|
|
103
102
|
"""
|
|
104
103
|
Collect all child widgets for determining who we
|
|
105
104
|
depend on.
|
|
@@ -126,10 +125,10 @@ class CanvasCache:
|
|
|
126
125
|
|
|
127
126
|
ref = weakref.ref(canvas, cls.cleanup)
|
|
128
127
|
cls._refs[ref] = (widget, wcls, size, focus)
|
|
129
|
-
cls._widgets.setdefault(widget, {})[
|
|
128
|
+
cls._widgets.setdefault(widget, {})[wcls, size, focus] = ref
|
|
130
129
|
|
|
131
130
|
@classmethod
|
|
132
|
-
def fetch(cls, widget, wcls, size, focus) -> Canvas | None:
|
|
131
|
+
def fetch(cls, widget: Widget, wcls: type[Widget], size, focus: bool) -> Canvas | None:
|
|
133
132
|
"""
|
|
134
133
|
Return the cached canvas or None.
|
|
135
134
|
|
|
@@ -151,7 +150,7 @@ class CanvasCache:
|
|
|
151
150
|
return canv
|
|
152
151
|
|
|
153
152
|
@classmethod
|
|
154
|
-
def invalidate(cls, widget):
|
|
153
|
+
def invalidate(cls, widget: Widget) -> None:
|
|
155
154
|
"""
|
|
156
155
|
Remove all canvases cached for widget.
|
|
157
156
|
"""
|
|
@@ -182,7 +181,7 @@ class CanvasCache:
|
|
|
182
181
|
if not sizes:
|
|
183
182
|
return
|
|
184
183
|
with suppress(KeyError):
|
|
185
|
-
del sizes[
|
|
184
|
+
del sizes[wcls, size, focus]
|
|
186
185
|
if not sizes:
|
|
187
186
|
with contextlib.suppress(KeyError):
|
|
188
187
|
del cls._widgets[widget]
|
|
@@ -215,7 +214,7 @@ class Canvas:
|
|
|
215
214
|
|
|
216
215
|
def __init__(self) -> None:
|
|
217
216
|
"""Base Canvas class"""
|
|
218
|
-
self._widget_info = None
|
|
217
|
+
self._widget_info: tuple[Widget, tuple[[]] | tuple[int] | tuple[int, int], bool] | None = None
|
|
219
218
|
self.coords: dict[str, tuple[int, int, tuple[Widget, int, int]] | tuple[int, int, None]] = {}
|
|
220
219
|
self.shortcuts: dict[str, str] = {}
|
|
221
220
|
|
|
@@ -241,18 +240,9 @@ class Canvas:
|
|
|
241
240
|
self._widget_info = widget, size, focus
|
|
242
241
|
|
|
243
242
|
@property
|
|
244
|
-
def widget_info(self):
|
|
243
|
+
def widget_info(self) -> tuple[Widget, tuple[[]] | tuple[int] | tuple[int, int], bool] | None:
|
|
245
244
|
return self._widget_info
|
|
246
245
|
|
|
247
|
-
def _get_widget_info(self):
|
|
248
|
-
warnings.warn(
|
|
249
|
-
f"Method `{self.__class__.__name__}._get_widget_info` is deprecated, "
|
|
250
|
-
f"please use property `{self.__class__.__name__}.widget_info`",
|
|
251
|
-
DeprecationWarning,
|
|
252
|
-
stacklevel=2,
|
|
253
|
-
)
|
|
254
|
-
return self.widget_info
|
|
255
|
-
|
|
256
246
|
@property
|
|
257
247
|
def text(self) -> list[bytes]:
|
|
258
248
|
"""
|
|
@@ -266,15 +256,6 @@ class Canvas:
|
|
|
266
256
|
encoding = get_encoding()
|
|
267
257
|
return tuple(line.decode(encoding) for line in self.text)
|
|
268
258
|
|
|
269
|
-
def _text_content(self):
|
|
270
|
-
warnings.warn(
|
|
271
|
-
f"Method `{self.__class__.__name__}._text_content` is deprecated, "
|
|
272
|
-
f"please use property `{self.__class__.__name__}.text`",
|
|
273
|
-
DeprecationWarning,
|
|
274
|
-
stacklevel=2,
|
|
275
|
-
)
|
|
276
|
-
return self.text
|
|
277
|
-
|
|
278
259
|
def content(
|
|
279
260
|
self,
|
|
280
261
|
trim_left: int = 0,
|
|
@@ -282,7 +263,7 @@ class Canvas:
|
|
|
282
263
|
cols: int | None = None,
|
|
283
264
|
rows: int | None = None,
|
|
284
265
|
attr=None,
|
|
285
|
-
) ->
|
|
266
|
+
) -> Iterator[list[tuple[object, Literal["0", "U"] | None, bytes]]]:
|
|
286
267
|
raise NotImplementedError()
|
|
287
268
|
|
|
288
269
|
def cols(self) -> int:
|
|
@@ -295,10 +276,10 @@ class Canvas:
|
|
|
295
276
|
raise NotImplementedError()
|
|
296
277
|
|
|
297
278
|
def get_cursor(self) -> tuple[int, int] | None:
|
|
298
|
-
c
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return
|
|
279
|
+
if c := self.coords.get("cursor", None):
|
|
280
|
+
return c[:2] # trim off data part
|
|
281
|
+
|
|
282
|
+
return None
|
|
302
283
|
|
|
303
284
|
def set_cursor(self, c: tuple[int, int] | None) -> None:
|
|
304
285
|
if self.widget_info and self.cacheable:
|
|
@@ -312,10 +293,10 @@ class Canvas:
|
|
|
312
293
|
cursor = property(get_cursor, set_cursor)
|
|
313
294
|
|
|
314
295
|
def get_pop_up(self) -> tuple[int, int, tuple[Widget, int, int]] | None:
|
|
315
|
-
c
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
return
|
|
296
|
+
if c := self.coords.get("pop up", None):
|
|
297
|
+
return c
|
|
298
|
+
|
|
299
|
+
return None
|
|
319
300
|
|
|
320
301
|
def set_pop_up(self, w: Widget, left: int, top: int, overlay_width: int, overlay_height: int) -> None:
|
|
321
302
|
"""
|
|
@@ -376,7 +357,7 @@ class TextCanvas(Canvas):
|
|
|
376
357
|
|
|
377
358
|
def __init__(
|
|
378
359
|
self,
|
|
379
|
-
text:
|
|
360
|
+
text: list[bytes] | None = None,
|
|
380
361
|
attr: list[list[tuple[Hashable | None, int]]] | None = None,
|
|
381
362
|
cs: list[list[tuple[Literal["0", "U"] | None, int]]] | None = None,
|
|
382
363
|
cursor: tuple[int, int] | None = None,
|
|
@@ -477,7 +458,7 @@ class TextCanvas(Canvas):
|
|
|
477
458
|
cols: int | None = 0,
|
|
478
459
|
rows: int | None = 0,
|
|
479
460
|
attr=None,
|
|
480
|
-
) ->
|
|
461
|
+
) -> Iterator[tuple[object, Literal["0", "U"] | None, bytes]]:
|
|
481
462
|
"""
|
|
482
463
|
Return the canvas content as a list of rows where each row
|
|
483
464
|
is a list of (attr, cs, text) tuples.
|
|
@@ -551,7 +532,7 @@ class BlankCanvas(Canvas):
|
|
|
551
532
|
cols: int | None = 0,
|
|
552
533
|
rows: int | None = 0,
|
|
553
534
|
attr=None,
|
|
554
|
-
) ->
|
|
535
|
+
) -> Iterator[list[tuple[object, Literal["0", "U"] | None, bytes]]]:
|
|
555
536
|
"""
|
|
556
537
|
return (cols, rows) of spaces with default attributes.
|
|
557
538
|
"""
|
|
@@ -603,7 +584,7 @@ class SolidCanvas(Canvas):
|
|
|
603
584
|
cols: int | None = None,
|
|
604
585
|
rows: int | None = None,
|
|
605
586
|
attr=None,
|
|
606
|
-
) ->
|
|
587
|
+
) -> Iterator[list[tuple[object, Literal["0", "U"] | None, bytes]]]:
|
|
607
588
|
if cols is None:
|
|
608
589
|
cols = self.size[0]
|
|
609
590
|
if rows is None:
|
|
@@ -704,7 +685,7 @@ class CompositeCanvas(Canvas):
|
|
|
704
685
|
cols: int | None = None,
|
|
705
686
|
rows: int | None = None,
|
|
706
687
|
attr=None,
|
|
707
|
-
) ->
|
|
688
|
+
) -> Iterator[list[tuple[object, Literal["0", "U"] | None, bytes]]]:
|
|
708
689
|
"""
|
|
709
690
|
Return the canvas content as a list of rows where each row
|
|
710
691
|
is a list of (attr, cs, text) tuples.
|
|
@@ -808,7 +789,7 @@ class CompositeCanvas(Canvas):
|
|
|
808
789
|
|
|
809
790
|
if right > 0:
|
|
810
791
|
new_top_cviews.append((0, 0, right, rows, None, blank_canvas))
|
|
811
|
-
shards = [(top_rows, new_top_cviews)
|
|
792
|
+
shards = [(top_rows, new_top_cviews), *shards[1:]]
|
|
812
793
|
|
|
813
794
|
self.coords = self.translate_coords(left, 0)
|
|
814
795
|
self.shards = shards
|
|
@@ -833,7 +814,7 @@ class CompositeCanvas(Canvas):
|
|
|
833
814
|
|
|
834
815
|
if bottom > 0:
|
|
835
816
|
if orig_shards is self.shards:
|
|
836
|
-
self.shards = self.shards
|
|
817
|
+
self.shards = self.shards.copy()
|
|
837
818
|
self.shards.append((bottom, [(0, 0, cols, bottom, None, blank_canvas)]))
|
|
838
819
|
|
|
839
820
|
def overlay(self, other: CompositeCanvas, left: int, top: int) -> None:
|
|
@@ -902,11 +883,11 @@ class CompositeCanvas(Canvas):
|
|
|
902
883
|
for cv in original_cviews:
|
|
903
884
|
# cv[4] == attr_map
|
|
904
885
|
if cv[4] is None:
|
|
905
|
-
new_cviews.append(cv[:4]
|
|
886
|
+
new_cviews.append((*cv[:4], mapping, *cv[5:]))
|
|
906
887
|
else:
|
|
907
888
|
combined = mapping.copy()
|
|
908
889
|
combined.update([(k, mapping.get(v, v)) for k, v in cv[4].items()])
|
|
909
|
-
new_cviews.append(cv[:4]
|
|
890
|
+
new_cviews.append((*cv[:4], combined, *cv[5:]))
|
|
910
891
|
shards.append((num_rows, new_cviews))
|
|
911
892
|
self.shards = shards
|
|
912
893
|
|
|
@@ -1002,7 +983,7 @@ def shard_cviews_delta(cviews, other_cviews):
|
|
|
1002
983
|
continue
|
|
1003
984
|
# top-left-aligned cviews, compare them
|
|
1004
985
|
if cv[5] is other_cv[5] and cv[:5] == other_cv[:5]:
|
|
1005
|
-
yield cv[:5]
|
|
986
|
+
yield (*cv[:5], None, *cv[6:])
|
|
1006
987
|
else:
|
|
1007
988
|
yield cv
|
|
1008
989
|
other_cols += other_cv[2]
|
|
@@ -1185,19 +1166,19 @@ def shards_join(shard_lists):
|
|
|
1185
1166
|
|
|
1186
1167
|
|
|
1187
1168
|
def cview_trim_rows(cv, rows: int):
|
|
1188
|
-
return cv[:3]
|
|
1169
|
+
return (*cv[:3], rows, *cv[4:])
|
|
1189
1170
|
|
|
1190
1171
|
|
|
1191
1172
|
def cview_trim_top(cv, trim: int):
|
|
1192
|
-
return (cv[0], trim + cv[1], cv[2], cv[3] - trim
|
|
1173
|
+
return (cv[0], trim + cv[1], cv[2], cv[3] - trim, *cv[4:])
|
|
1193
1174
|
|
|
1194
1175
|
|
|
1195
1176
|
def cview_trim_left(cv, trim: int):
|
|
1196
|
-
return (cv[0] + trim, cv[1], cv[2] - trim
|
|
1177
|
+
return (cv[0] + trim, cv[1], cv[2] - trim, *cv[3:])
|
|
1197
1178
|
|
|
1198
1179
|
|
|
1199
1180
|
def cview_trim_cols(cv, cols: int):
|
|
1200
|
-
return cv[:2]
|
|
1181
|
+
return (*cv[:2], cols, *cv[3:])
|
|
1201
1182
|
|
|
1202
1183
|
|
|
1203
1184
|
def CanvasCombine(canvas_info: Iterable[tuple[Canvas, typing.Any, bool]]) -> CompositeCanvas:
|
|
@@ -1229,7 +1210,7 @@ def CanvasCombine(canvas_info: Iterable[tuple[Canvas, typing.Any, bool]]) -> Com
|
|
|
1229
1210
|
row += canv.rows()
|
|
1230
1211
|
|
|
1231
1212
|
if focus_index:
|
|
1232
|
-
children = [children[focus_index]
|
|
1213
|
+
children = [children[focus_index], *children[:focus_index], *children[focus_index + 1 :]]
|
|
1233
1214
|
|
|
1234
1215
|
combined_canvas.shards = shards
|
|
1235
1216
|
combined_canvas.children = children
|
|
@@ -1295,7 +1276,7 @@ def CanvasJoin(canvas_info: Iterable[tuple[Canvas, typing.Any, bool, int]]) -> C
|
|
|
1295
1276
|
col += composite_canvas.cols()
|
|
1296
1277
|
|
|
1297
1278
|
if focus_item:
|
|
1298
|
-
children = [children[focus_item]
|
|
1279
|
+
children = [children[focus_item], *children[:focus_item], *children[focus_item + 1 :]]
|
|
1299
1280
|
|
|
1300
1281
|
joined_canvas.shards = shards_join(shard_lists)
|
|
1301
1282
|
joined_canvas.children = children
|
urwid/command_map.py
CHANGED
|
@@ -21,9 +21,11 @@ from __future__ import annotations
|
|
|
21
21
|
|
|
22
22
|
import enum
|
|
23
23
|
import typing
|
|
24
|
-
from
|
|
24
|
+
from collections.abc import MutableMapping
|
|
25
25
|
|
|
26
26
|
if typing.TYPE_CHECKING:
|
|
27
|
+
from collections.abc import Iterator
|
|
28
|
+
|
|
27
29
|
from typing_extensions import Self
|
|
28
30
|
|
|
29
31
|
|
|
@@ -55,7 +57,7 @@ CURSOR_MAX_RIGHT = Command.MAX_RIGHT
|
|
|
55
57
|
ACTIVATE = Command.ACTIVATE
|
|
56
58
|
|
|
57
59
|
|
|
58
|
-
class CommandMap(
|
|
60
|
+
class CommandMap(MutableMapping[str, typing.Union[str, Command, None]]):
|
|
59
61
|
"""
|
|
60
62
|
dict-like object for looking up commands from keystrokes
|
|
61
63
|
|
|
@@ -105,10 +107,10 @@ class CommandMap(typing.Mapping[str, typing.Union[str, Command, None]]):
|
|
|
105
107
|
}
|
|
106
108
|
|
|
107
109
|
def __init__(self) -> None:
|
|
108
|
-
self._command =
|
|
110
|
+
self._command = self._command_defaults.copy()
|
|
109
111
|
|
|
110
112
|
def restore_defaults(self) -> None:
|
|
111
|
-
self._command =
|
|
113
|
+
self._command = self._command_defaults.copy()
|
|
112
114
|
|
|
113
115
|
def __getitem__(self, key: str) -> str | Command | None:
|
|
114
116
|
return self._command.get(key, None)
|
urwid/container.py
CHANGED
|
@@ -53,7 +53,7 @@ __all__ = (
|
|
|
53
53
|
warnings.warn(
|
|
54
54
|
f"{__name__!r} is not expected to be imported directly. "
|
|
55
55
|
'Please use public access from "urwid" package. '
|
|
56
|
-
f"Module {__name__!r} is deprecated and will be removed in the
|
|
56
|
+
f"Module {__name__!r} is deprecated and will be removed in the version 4.0.",
|
|
57
57
|
DeprecationWarning,
|
|
58
58
|
stacklevel=3,
|
|
59
59
|
)
|
urwid/decoration.py
CHANGED
|
@@ -59,7 +59,7 @@ __all__ = (
|
|
|
59
59
|
warnings.warn(
|
|
60
60
|
f"{__name__!r} is not expected to be imported directly. "
|
|
61
61
|
'Please use public access from "urwid" package. '
|
|
62
|
-
f"Module {__name__!r} is deprecated and will be removed in the
|
|
62
|
+
f"Module {__name__!r} is deprecated and will be removed in the version 4.0.",
|
|
63
63
|
DeprecationWarning,
|
|
64
64
|
stacklevel=3,
|
|
65
65
|
)
|
urwid/display/__init__.py
CHANGED
|
@@ -2,9 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import importlib
|
|
5
|
+
import importlib.util
|
|
6
|
+
import sys
|
|
6
7
|
import typing
|
|
7
8
|
|
|
9
|
+
from . import raw
|
|
10
|
+
from .common import (
|
|
11
|
+
BLACK,
|
|
12
|
+
BROWN,
|
|
13
|
+
DARK_BLUE,
|
|
14
|
+
DARK_CYAN,
|
|
15
|
+
DARK_GRAY,
|
|
16
|
+
DARK_GREEN,
|
|
17
|
+
DARK_MAGENTA,
|
|
18
|
+
DARK_RED,
|
|
19
|
+
DEFAULT,
|
|
20
|
+
LIGHT_BLUE,
|
|
21
|
+
LIGHT_CYAN,
|
|
22
|
+
LIGHT_GRAY,
|
|
23
|
+
LIGHT_GREEN,
|
|
24
|
+
LIGHT_MAGENTA,
|
|
25
|
+
LIGHT_RED,
|
|
26
|
+
UPDATE_PALETTE_ENTRY,
|
|
27
|
+
WHITE,
|
|
28
|
+
YELLOW,
|
|
29
|
+
AttrSpec,
|
|
30
|
+
AttrSpecError,
|
|
31
|
+
BaseScreen,
|
|
32
|
+
RealTerminal,
|
|
33
|
+
ScreenError,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if typing.TYPE_CHECKING:
|
|
37
|
+
import types
|
|
38
|
+
|
|
8
39
|
__all__ = (
|
|
9
40
|
"BLACK",
|
|
10
41
|
"BROWN",
|
|
@@ -36,33 +67,6 @@ __all__ = (
|
|
|
36
67
|
"web",
|
|
37
68
|
)
|
|
38
69
|
|
|
39
|
-
from . import raw
|
|
40
|
-
from .common import (
|
|
41
|
-
BLACK,
|
|
42
|
-
BROWN,
|
|
43
|
-
DARK_BLUE,
|
|
44
|
-
DARK_CYAN,
|
|
45
|
-
DARK_GRAY,
|
|
46
|
-
DARK_GREEN,
|
|
47
|
-
DARK_MAGENTA,
|
|
48
|
-
DARK_RED,
|
|
49
|
-
DEFAULT,
|
|
50
|
-
LIGHT_BLUE,
|
|
51
|
-
LIGHT_CYAN,
|
|
52
|
-
LIGHT_GRAY,
|
|
53
|
-
LIGHT_GREEN,
|
|
54
|
-
LIGHT_MAGENTA,
|
|
55
|
-
LIGHT_RED,
|
|
56
|
-
UPDATE_PALETTE_ENTRY,
|
|
57
|
-
WHITE,
|
|
58
|
-
YELLOW,
|
|
59
|
-
AttrSpec,
|
|
60
|
-
AttrSpecError,
|
|
61
|
-
BaseScreen,
|
|
62
|
-
RealTerminal,
|
|
63
|
-
ScreenError,
|
|
64
|
-
)
|
|
65
|
-
|
|
66
70
|
try:
|
|
67
71
|
from . import curses
|
|
68
72
|
|
|
@@ -70,28 +74,29 @@ try:
|
|
|
70
74
|
except ImportError:
|
|
71
75
|
pass
|
|
72
76
|
|
|
73
|
-
# Moved modules handling
|
|
74
|
-
__locals: dict[str, typing.Any] = locals() # use mutable access for pure lazy loading
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
(
|
|
79
|
-
"html_fragment",
|
|
80
|
-
"lcd",
|
|
81
|
-
"web",
|
|
82
|
-
)
|
|
83
|
-
)
|
|
78
|
+
def lazy_import(name: str, package: str | None = None) -> types.ModuleType:
|
|
79
|
+
"""Lazy import implementation from Python documentation.
|
|
84
80
|
|
|
81
|
+
Useful for cases where no warnings expected for moved modules.
|
|
82
|
+
"""
|
|
83
|
+
spec = importlib.util.find_spec(name, package)
|
|
84
|
+
if not spec:
|
|
85
|
+
raise ImportError(f"No module named {name!r}")
|
|
86
|
+
if not spec.loader:
|
|
87
|
+
raise ImportError(f"Module named {name!r} is invalid")
|
|
85
88
|
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
loader = importlib.util.LazyLoader(spec.loader)
|
|
90
|
+
spec.loader = loader
|
|
91
|
+
module = importlib.util.module_from_spec(spec)
|
|
92
|
+
if not package:
|
|
93
|
+
sys.modules[name] = module
|
|
94
|
+
else:
|
|
95
|
+
sys.modules[f"{package.rstrip('.')}.{name.lstrip('.')}"] = module
|
|
96
|
+
loader.exec_module(module)
|
|
97
|
+
return module
|
|
88
98
|
|
|
89
|
-
:return: attribute by name
|
|
90
|
-
:raises AttributeError: attribute is not defined for lazy load
|
|
91
|
-
"""
|
|
92
|
-
if name in _lazy_load:
|
|
93
|
-
mod = importlib.import_module(f"{__package__}.{name}")
|
|
94
|
-
__locals[name] = mod
|
|
95
|
-
return mod
|
|
96
99
|
|
|
97
|
-
|
|
100
|
+
html_fragment = lazy_import(".html_fragment", __package__)
|
|
101
|
+
lcd = lazy_import(".lcd", __package__)
|
|
102
|
+
web = lazy_import(".web", __package__)
|
|
@@ -56,6 +56,7 @@ class Screen(_raw_display_base.Screen):
|
|
|
56
56
|
input: typing.TextIO = sys.stdin, # noqa: A002 # pylint: disable=redefined-builtin
|
|
57
57
|
output: typing.TextIO = sys.stdout,
|
|
58
58
|
bracketed_paste_mode=False,
|
|
59
|
+
focus_reporting=False,
|
|
59
60
|
) -> None:
|
|
60
61
|
"""Initialize a screen that directly prints escape codes to an output
|
|
61
62
|
terminal.
|
|
@@ -63,11 +64,15 @@ class Screen(_raw_display_base.Screen):
|
|
|
63
64
|
bracketed_paste_mode -- enable bracketed paste mode in the host terminal.
|
|
64
65
|
If the host terminal supports it, the application will receive `begin paste`
|
|
65
66
|
and `end paste` keystrokes when the user pastes text.
|
|
67
|
+
focus_reporting -- enable focus reporting in the host terminal.
|
|
68
|
+
If the host terminal supports it, the application will receive `focus in`
|
|
69
|
+
and `focus out` keystrokes when the application gains and loses focus.
|
|
66
70
|
"""
|
|
67
71
|
super().__init__(input, output)
|
|
68
72
|
self.gpm_mev: Popen | None = None
|
|
69
73
|
self.gpm_event_pending: bool = False
|
|
70
74
|
self.bracketed_paste_mode = bracketed_paste_mode
|
|
75
|
+
self.focus_reporting = focus_reporting
|
|
71
76
|
|
|
72
77
|
# These store the previous signal handlers after setting ours
|
|
73
78
|
self._prev_sigcont_handler = None
|
|
@@ -79,7 +84,8 @@ class Screen(_raw_display_base.Screen):
|
|
|
79
84
|
f"<{self.__class__.__name__}("
|
|
80
85
|
f"input={self._term_input_file}, "
|
|
81
86
|
f"output={self._term_output_file}, "
|
|
82
|
-
f"bracketed_paste_mode={self.bracketed_paste_mode}
|
|
87
|
+
f"bracketed_paste_mode={self.bracketed_paste_mode}, "
|
|
88
|
+
f"focus_reporting={self.focus_reporting})>"
|
|
83
89
|
)
|
|
84
90
|
|
|
85
91
|
def _sigwinch_handler(self, signum: int = 28, frame: FrameType | None = None) -> None:
|
|
@@ -148,7 +154,7 @@ class Screen(_raw_display_base.Screen):
|
|
|
148
154
|
if not os.environ.get("TERM", "").lower().startswith("linux"):
|
|
149
155
|
return
|
|
150
156
|
|
|
151
|
-
m = Popen( #
|
|
157
|
+
m = Popen( # pylint: disable=consider-using-with
|
|
152
158
|
["/usr/bin/mev", "-e", "158"],
|
|
153
159
|
stdin=PIPE,
|
|
154
160
|
stdout=PIPE,
|
|
@@ -169,7 +175,7 @@ class Screen(_raw_display_base.Screen):
|
|
|
169
175
|
"""
|
|
170
176
|
Initialize the screen and input mode.
|
|
171
177
|
|
|
172
|
-
alternate_buffer -- use alternate screen buffer
|
|
178
|
+
alternate_buffer -- use an alternate screen buffer
|
|
173
179
|
"""
|
|
174
180
|
if alternate_buffer:
|
|
175
181
|
self.write(escape.SWITCH_TO_ALTERNATE_BUFFER)
|
|
@@ -180,6 +186,9 @@ class Screen(_raw_display_base.Screen):
|
|
|
180
186
|
if self.bracketed_paste_mode:
|
|
181
187
|
self.write(escape.ENABLE_BRACKETED_PASTE_MODE)
|
|
182
188
|
|
|
189
|
+
if self.focus_reporting:
|
|
190
|
+
self.write(escape.ENABLE_FOCUS_REPORTING)
|
|
191
|
+
|
|
183
192
|
fd = self._input_fileno()
|
|
184
193
|
if fd is not None and os.isatty(fd):
|
|
185
194
|
self._old_termios_settings = termios.tcgetattr(fd)
|
|
@@ -207,15 +216,18 @@ class Screen(_raw_display_base.Screen):
|
|
|
207
216
|
if self.bracketed_paste_mode:
|
|
208
217
|
self.write(escape.DISABLE_BRACKETED_PASTE_MODE)
|
|
209
218
|
|
|
219
|
+
if self.focus_reporting:
|
|
220
|
+
self.write(escape.DISABLE_FOCUS_REPORTING)
|
|
221
|
+
|
|
210
222
|
signals.emit_signal(self, INPUT_DESCRIPTORS_CHANGED)
|
|
211
223
|
|
|
212
224
|
self.signal_restore()
|
|
213
225
|
|
|
226
|
+
self._stop_mouse_restore_buffer()
|
|
227
|
+
|
|
214
228
|
fd = self._input_fileno()
|
|
215
229
|
if fd is not None and os.isatty(fd):
|
|
216
|
-
termios.tcsetattr(fd, termios.
|
|
217
|
-
|
|
218
|
-
self._stop_mouse_restore_buffer()
|
|
230
|
+
termios.tcsetattr(fd, termios.TCSAFLUSH, self._old_termios_settings)
|
|
219
231
|
|
|
220
232
|
if self._old_signal_keys:
|
|
221
233
|
self.tty_signal_keys(*self._old_signal_keys, fd)
|
|
@@ -391,8 +403,8 @@ class Screen(_raw_display_base.Screen):
|
|
|
391
403
|
y, x = super().get_cols_rows()
|
|
392
404
|
with contextlib.suppress(OSError): # Term size could not be determined
|
|
393
405
|
if hasattr(self._term_output_file, "fileno"):
|
|
394
|
-
buf = fcntl.ioctl(self._term_output_file.fileno(), termios.TIOCGWINSZ, b" " *
|
|
395
|
-
y, x = struct.unpack("
|
|
406
|
+
buf = fcntl.ioctl(self._term_output_file.fileno(), termios.TIOCGWINSZ, b" " * 8)
|
|
407
|
+
y, x, _, _ = struct.unpack("hhhh", buf)
|
|
396
408
|
|
|
397
409
|
# Provide some lightweight fallbacks in case the TIOCWINSZ doesn't
|
|
398
410
|
# give sane answers
|