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/display/web.py
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"""
|
|
22
22
|
Urwid web application display module
|
|
23
23
|
"""
|
|
24
|
+
|
|
24
25
|
from __future__ import annotations
|
|
25
26
|
|
|
26
27
|
import dataclasses
|
|
@@ -216,7 +217,7 @@ class Screen(BaseScreen):
|
|
|
216
217
|
sys.stdout.write("Status: 503 Sever Busy\r\n\r\n")
|
|
217
218
|
sys.exit(0)
|
|
218
219
|
|
|
219
|
-
urwid_id = f"{random.randrange(10
|
|
220
|
+
urwid_id = f"{random.randrange(10**9):09d}{random.randrange(10**9):09d}" # noqa: S311
|
|
220
221
|
self.pipe_name = os.path.join(_prefs.pipe_dir, f"urwid{urwid_id}")
|
|
221
222
|
os.mkfifo(f"{self.pipe_name}.in", 0o600)
|
|
222
223
|
signal.signal(signal.SIGTERM, self._cleanup_pipe)
|
|
@@ -224,11 +225,7 @@ class Screen(BaseScreen):
|
|
|
224
225
|
self.input_fd = os.open(f"{self.pipe_name}.in", os.O_NONBLOCK | os.O_RDONLY)
|
|
225
226
|
self.input_tail = ""
|
|
226
227
|
self.content_head = (
|
|
227
|
-
"Content-type: "
|
|
228
|
-
"multipart/x-mixed-replace;boundary=ZZ\r\n"
|
|
229
|
-
"X-Urwid-ID: " + urwid_id + "\r\n"
|
|
230
|
-
"\r\n\r\n"
|
|
231
|
-
"--ZZ\r\n"
|
|
228
|
+
f"Content-type: multipart/x-mixed-replace;boundary=ZZ\r\nX-Urwid-ID: {urwid_id}\r\n\r\n\r\n--ZZ\r\n"
|
|
232
229
|
)
|
|
233
230
|
if self.update_method == "polling":
|
|
234
231
|
self.content_head = f"Content-type: text/plain\r\nX-Urwid-ID: {urwid_id}\r\n\r\n\r\n"
|
|
@@ -328,8 +325,8 @@ class Screen(BaseScreen):
|
|
|
328
325
|
if y == cy:
|
|
329
326
|
sig = (*sig, cx)
|
|
330
327
|
new_screen[sig] = [*new_screen.get(sig, []), y]
|
|
331
|
-
|
|
332
|
-
if old_line_numbers is not None:
|
|
328
|
+
|
|
329
|
+
if (old_line_numbers := self.last_screen.get(sig, None)) is not None:
|
|
333
330
|
if y in old_line_numbers:
|
|
334
331
|
old_line = y
|
|
335
332
|
else:
|
|
@@ -459,7 +456,7 @@ class Screen(BaseScreen):
|
|
|
459
456
|
return pending_input
|
|
460
457
|
|
|
461
458
|
|
|
462
|
-
def code_span(s, fg, bg, cursor
|
|
459
|
+
def code_span(s: str, fg: str, bg: str, cursor: int = -1) -> str:
|
|
463
460
|
code_fg = _code_colours[fg]
|
|
464
461
|
code_bg = _code_colours[bg]
|
|
465
462
|
|
|
@@ -495,8 +492,8 @@ def is_web_request() -> bool:
|
|
|
495
492
|
def handle_short_request() -> bool:
|
|
496
493
|
"""
|
|
497
494
|
Handle short requests such as passing keystrokes to the application
|
|
498
|
-
or sending the initial
|
|
499
|
-
function
|
|
495
|
+
or sending the initial HTML page. If returns True, then this
|
|
496
|
+
function recognized and handled a short request, and the calling
|
|
500
497
|
script should immediately exit.
|
|
501
498
|
|
|
502
499
|
web_display.set_preferences(..) should be called before calling this
|
|
@@ -609,20 +606,18 @@ def daemonize(errfile: str) -> None:
|
|
|
609
606
|
"""
|
|
610
607
|
Detach process and become a daemon.
|
|
611
608
|
"""
|
|
612
|
-
|
|
613
|
-
if pid:
|
|
609
|
+
if os.fork():
|
|
614
610
|
os._exit(0)
|
|
615
611
|
|
|
616
612
|
os.setsid()
|
|
617
613
|
signal.signal(signal.SIGHUP, signal.SIG_IGN)
|
|
618
614
|
os.umask(0)
|
|
619
615
|
|
|
620
|
-
|
|
621
|
-
if pid:
|
|
616
|
+
if os.fork():
|
|
622
617
|
os._exit(0)
|
|
623
618
|
|
|
624
619
|
os.chdir("/")
|
|
625
|
-
for fd in range(
|
|
620
|
+
for fd in range(20):
|
|
626
621
|
with suppress(OSError):
|
|
627
622
|
os.close(fd)
|
|
628
623
|
|
urwid/event_loop/asyncio_loop.py
CHANGED
|
@@ -41,7 +41,6 @@ if typing.TYPE_CHECKING:
|
|
|
41
41
|
_T = typing.TypeVar("_T")
|
|
42
42
|
|
|
43
43
|
__all__ = ("AsyncioEventLoop",)
|
|
44
|
-
IS_WINDOWS = sys.platform == "win32"
|
|
45
44
|
|
|
46
45
|
|
|
47
46
|
class AsyncioEventLoop(EventLoop):
|
|
@@ -67,20 +66,39 @@ class AsyncioEventLoop(EventLoop):
|
|
|
67
66
|
def __init__(self, *, loop: asyncio.AbstractEventLoop | None = None, **kwargs) -> None:
|
|
68
67
|
super().__init__()
|
|
69
68
|
self.logger = logging.getLogger(__name__).getChild(self.__class__.__name__)
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
|
|
70
|
+
if sys.version_info[:2] < (3, 11):
|
|
72
71
|
self._event_loop_policy_altered: bool = False
|
|
73
72
|
self._original_event_loop_policy: asyncio.AbstractEventLoopPolicy | None = None
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
self.logger.debug("Set WindowsSelectorEventLoopPolicy as asyncio event loop policy")
|
|
78
|
-
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
79
|
-
self._event_loop_policy_altered = True
|
|
73
|
+
|
|
74
|
+
if loop:
|
|
75
|
+
self._loop: asyncio.AbstractEventLoop = loop
|
|
80
76
|
else:
|
|
81
|
-
self.
|
|
77
|
+
self._original_event_loop_policy = asyncio.get_event_loop_policy()
|
|
78
|
+
if sys.platform == "win32" and not isinstance(
|
|
79
|
+
self._original_event_loop_policy, asyncio.WindowsSelectorEventLoopPolicy
|
|
80
|
+
):
|
|
81
|
+
self.logger.debug("Set WindowsSelectorEventLoopPolicy as asyncio event loop policy")
|
|
82
|
+
asyncio.set_event_loop_policy(
|
|
83
|
+
asyncio.WindowsSelectorEventLoopPolicy() # pylint: disable=deprecated-class
|
|
84
|
+
)
|
|
85
|
+
self._event_loop_policy_altered = True
|
|
86
|
+
else:
|
|
87
|
+
self._event_loop_policy_altered = False
|
|
88
|
+
|
|
89
|
+
self._loop = asyncio.get_event_loop()
|
|
82
90
|
|
|
83
|
-
|
|
91
|
+
else:
|
|
92
|
+
self._runner: asyncio.Runner | None = None
|
|
93
|
+
|
|
94
|
+
if loop:
|
|
95
|
+
self._loop: asyncio.AbstractEventLoop = loop
|
|
96
|
+
else:
|
|
97
|
+
try:
|
|
98
|
+
self._loop = asyncio.get_running_loop()
|
|
99
|
+
except RuntimeError:
|
|
100
|
+
self._runner = asyncio.Runner(loop_factory=asyncio.SelectorEventLoop)
|
|
101
|
+
self._loop = self._runner.get_loop()
|
|
84
102
|
|
|
85
103
|
self._exc: BaseException | None = None
|
|
86
104
|
|
|
@@ -89,8 +107,11 @@ class AsyncioEventLoop(EventLoop):
|
|
|
89
107
|
self._idle_callbacks: dict[int, Callable[[], typing.Any]] = {}
|
|
90
108
|
|
|
91
109
|
def __del__(self) -> None:
|
|
92
|
-
if
|
|
93
|
-
|
|
110
|
+
if sys.version_info[:2] < (3, 11):
|
|
111
|
+
if self._event_loop_policy_altered:
|
|
112
|
+
asyncio.set_event_loop_policy(self._original_event_loop_policy) # Restore default event loop policy
|
|
113
|
+
elif self._runner is not None:
|
|
114
|
+
self._runner.close()
|
|
94
115
|
|
|
95
116
|
def _also_call_idle(self, callback: Callable[_Spec, _T]) -> Callable[_Spec, _T]:
|
|
96
117
|
"""
|
|
@@ -205,8 +226,7 @@ class AsyncioEventLoop(EventLoop):
|
|
|
205
226
|
return True
|
|
206
227
|
|
|
207
228
|
def _exception_handler(self, loop: asyncio.AbstractEventLoop, context):
|
|
208
|
-
exc
|
|
209
|
-
if exc:
|
|
229
|
+
if exc := context.get("exception"):
|
|
210
230
|
loop.stop()
|
|
211
231
|
|
|
212
232
|
if self._idle_asyncio_handle:
|
urwid/event_loop/main_loop.py
CHANGED
|
@@ -172,7 +172,8 @@ class MainLoop:
|
|
|
172
172
|
def _set_widget(self, widget: Widget) -> None:
|
|
173
173
|
warnings.warn(
|
|
174
174
|
f"method `{self.__class__.__name__}._set_widget` is deprecated, "
|
|
175
|
-
f"please use `{self.__class__.__name__}.widget` property"
|
|
175
|
+
f"please use `{self.__class__.__name__}.widget` property."
|
|
176
|
+
"API will be removed in version 4.0.",
|
|
176
177
|
DeprecationWarning,
|
|
177
178
|
stacklevel=2,
|
|
178
179
|
)
|
|
@@ -193,7 +194,8 @@ class MainLoop:
|
|
|
193
194
|
def _set_pop_ups(self, pop_ups: bool) -> None:
|
|
194
195
|
warnings.warn(
|
|
195
196
|
f"method `{self.__class__.__name__}._set_pop_ups` is deprecated, "
|
|
196
|
-
f"please use `{self.__class__.__name__}.pop_ups` property"
|
|
197
|
+
f"please use `{self.__class__.__name__}.pop_ups` property."
|
|
198
|
+
"API will be removed in version 4.0.",
|
|
197
199
|
DeprecationWarning,
|
|
198
200
|
stacklevel=2,
|
|
199
201
|
)
|
|
@@ -338,7 +340,7 @@ class MainLoop:
|
|
|
338
340
|
|
|
339
341
|
def _test_run(self):
|
|
340
342
|
"""
|
|
341
|
-
>>> w = _refl("widget")
|
|
343
|
+
>>> w = _refl("widget") # _refl prints out function calls
|
|
342
344
|
>>> w.render_rval = "fake canvas" # *_rval is used for return values
|
|
343
345
|
>>> scr = _refl("screen")
|
|
344
346
|
>>> scr.get_input_descriptors_rval = [42]
|
|
@@ -349,7 +351,7 @@ class MainLoop:
|
|
|
349
351
|
>>> evl.enter_idle_rval = 1
|
|
350
352
|
>>> evl.watch_file_rval = 2
|
|
351
353
|
>>> ml = MainLoop(w, [], scr, event_loop=evl)
|
|
352
|
-
>>> ml.run()
|
|
354
|
+
>>> ml.run() # doctest:+ELLIPSIS
|
|
353
355
|
screen.start()
|
|
354
356
|
screen.set_mouse_tracking()
|
|
355
357
|
screen.unhook_event_loop(...)
|
|
@@ -359,7 +361,7 @@ class MainLoop:
|
|
|
359
361
|
event_loop.remove_enter_idle(1)
|
|
360
362
|
screen.unhook_event_loop(...)
|
|
361
363
|
screen.stop()
|
|
362
|
-
>>> ml.draw_screen()
|
|
364
|
+
>>> ml.draw_screen() # doctest:+ELLIPSIS
|
|
363
365
|
screen.get_cols_rows()
|
|
364
366
|
widget.render((20, 10), focus=True)
|
|
365
367
|
screen.draw_screen((20, 10), 'fake canvas')
|
|
@@ -452,7 +454,7 @@ class MainLoop:
|
|
|
452
454
|
>>> evl = _refl("event_loop")
|
|
453
455
|
>>> ml = MainLoop(w, [], scr, event_loop=evl)
|
|
454
456
|
>>> ml._input_timeout = "old timeout"
|
|
455
|
-
>>> ml._update([
|
|
457
|
+
>>> ml._update(["y"], [121]) # doctest:+ELLIPSIS
|
|
456
458
|
screen.get_cols_rows()
|
|
457
459
|
widget.selectable()
|
|
458
460
|
widget.keypress((15, 5), 'y')
|
|
@@ -460,9 +462,7 @@ class MainLoop:
|
|
|
460
462
|
widget.mouse_event((15, 5), 'mouse press', 1, 5, 4, focus=True)
|
|
461
463
|
>>> ml._update([], [])
|
|
462
464
|
"""
|
|
463
|
-
keys
|
|
464
|
-
|
|
465
|
-
if keys:
|
|
465
|
+
if keys := self.input_filter(keys, raw):
|
|
466
466
|
self.process_input(keys)
|
|
467
467
|
if "window resize" in keys:
|
|
468
468
|
self.screen_size = None
|
|
@@ -493,19 +493,14 @@ class MainLoop:
|
|
|
493
493
|
else:
|
|
494
494
|
self.screen.set_input_timeouts(None)
|
|
495
495
|
keys, raw = self.screen.get_input(True)
|
|
496
|
-
if not keys and next_alarm:
|
|
497
|
-
|
|
498
|
-
if sec <= 0:
|
|
499
|
-
break
|
|
500
|
-
|
|
501
|
-
keys = self.input_filter(keys, raw)
|
|
496
|
+
if not keys and next_alarm and next_alarm[0] - time.time() <= 0:
|
|
497
|
+
break
|
|
502
498
|
|
|
503
|
-
if keys:
|
|
499
|
+
if keys := self.input_filter(keys, raw):
|
|
504
500
|
self.process_input(keys)
|
|
505
501
|
|
|
506
502
|
while next_alarm:
|
|
507
|
-
|
|
508
|
-
if sec > 0:
|
|
503
|
+
if (next_alarm[0] - time.time()) > 0:
|
|
509
504
|
break
|
|
510
505
|
_tm, _tie_break, callback = next_alarm
|
|
511
506
|
callback()
|
|
@@ -563,13 +558,13 @@ class MainLoop:
|
|
|
563
558
|
|
|
564
559
|
if isinstance(key, str):
|
|
565
560
|
if self._topmost_widget.selectable():
|
|
566
|
-
handled_key
|
|
567
|
-
|
|
561
|
+
if handled_key := self._topmost_widget.keypress(self.screen_size, key):
|
|
562
|
+
key = handled_key # noqa: PLW2901
|
|
563
|
+
|
|
564
|
+
else:
|
|
568
565
|
something_handled = True
|
|
569
566
|
continue
|
|
570
567
|
|
|
571
|
-
key = handled_key # noqa: PLW2901
|
|
572
|
-
|
|
573
568
|
elif is_mouse_event(key):
|
|
574
569
|
event, button, col, row = key
|
|
575
570
|
if hasattr(self._topmost_widget, "mouse_event") and self._topmost_widget.mouse_event(
|
|
@@ -604,7 +599,7 @@ class MainLoop:
|
|
|
604
599
|
>>> scr = _refl("screen")
|
|
605
600
|
>>> scr.get_cols_rows_rval = (10, 5)
|
|
606
601
|
>>> ml = MainLoop(w, [], scr)
|
|
607
|
-
>>> ml.process_input([
|
|
602
|
+
>>> ml.process_input(["enter", ("mouse drag", 1, 14, 20)])
|
|
608
603
|
screen.get_cols_rows()
|
|
609
604
|
widget.selectable()
|
|
610
605
|
widget.keypress((10, 5), 'enter')
|
urwid/event_loop/tornado_loop.py
CHANGED
|
@@ -155,12 +155,11 @@ class TornadoEventLoop(EventLoop):
|
|
|
155
155
|
return handle
|
|
156
156
|
|
|
157
157
|
def remove_watch_file(self, handle: int) -> bool:
|
|
158
|
-
fd
|
|
159
|
-
|
|
160
|
-
return
|
|
158
|
+
if (fd := self._watch_handles.pop(handle, None)) is not None:
|
|
159
|
+
self._loop.remove_handler(fd)
|
|
160
|
+
return True
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
return True
|
|
162
|
+
return False
|
|
164
163
|
|
|
165
164
|
def enter_idle(self, callback: Callable[[], typing.Any]) -> int:
|
|
166
165
|
"""
|
urwid/event_loop/trio_loop.py
CHANGED
urwid/font.py
CHANGED
|
@@ -24,12 +24,14 @@ import typing
|
|
|
24
24
|
import warnings
|
|
25
25
|
from pprint import pformat
|
|
26
26
|
|
|
27
|
+
import wcwidth
|
|
28
|
+
|
|
27
29
|
from urwid.canvas import CanvasError, TextCanvas
|
|
28
30
|
from urwid.display.escape import SAFE_ASCII_DEC_SPECIAL_RE
|
|
29
|
-
from urwid.util import apply_target_encoding
|
|
31
|
+
from urwid.util import apply_target_encoding
|
|
30
32
|
|
|
31
33
|
if typing.TYPE_CHECKING:
|
|
32
|
-
from collections.abc import
|
|
34
|
+
from collections.abc import Iterator, Sequence
|
|
33
35
|
|
|
34
36
|
from typing_extensions import Literal
|
|
35
37
|
|
|
@@ -60,7 +62,7 @@ def separate_glyphs(gdata: str, height: int) -> tuple[dict[str, tuple[int, list[
|
|
|
60
62
|
character = key_line[key_index]
|
|
61
63
|
|
|
62
64
|
if key_index < len(key_line) and key_line[key_index] == character:
|
|
63
|
-
end_col +=
|
|
65
|
+
end_col += wcwidth.width(character, control_codes="ignore")
|
|
64
66
|
key_index += 1
|
|
65
67
|
continue
|
|
66
68
|
|
|
@@ -76,7 +78,7 @@ def separate_glyphs(gdata: str, height: int) -> tuple[dict[str, tuple[int, list[
|
|
|
76
78
|
if j >= len(line):
|
|
77
79
|
fill = end_col - start_col - y
|
|
78
80
|
break
|
|
79
|
-
y +=
|
|
81
|
+
y += wcwidth.width(line[j], control_codes="ignore")
|
|
80
82
|
j += 1
|
|
81
83
|
if y + fill != end_col - start_col:
|
|
82
84
|
raise ValueError(repr((y, fill, end_col)))
|
|
@@ -139,7 +141,7 @@ class FontRegistry(type):
|
|
|
139
141
|
3
|
|
140
142
|
>>> with set_temporary_encoding("utf-8"):
|
|
141
143
|
... canvas: TextCanvas = font.render("+")
|
|
142
|
-
>>> b
|
|
144
|
+
>>> b"\\n".join(canvas.text).decode("utf-8")
|
|
143
145
|
' \\n ┼\\n '
|
|
144
146
|
"""
|
|
145
147
|
return mcs.__registered.get(item)
|
|
@@ -192,9 +194,9 @@ class Font(metaclass=FontRegistry):
|
|
|
192
194
|
|
|
193
195
|
__slots__ = ("canvas", "char", "utf8_required")
|
|
194
196
|
|
|
195
|
-
height: int
|
|
196
|
-
data: Sequence[str]
|
|
197
|
-
name: str
|
|
197
|
+
height: int # pylint: disable=declare-non-slot
|
|
198
|
+
data: Sequence[str] # pylint: disable=declare-non-slot
|
|
199
|
+
name: str # pylint: disable=declare-non-slot
|
|
198
200
|
|
|
199
201
|
def __init__(self) -> None:
|
|
200
202
|
if not self.height:
|
|
@@ -206,12 +208,10 @@ class Font(metaclass=FontRegistry):
|
|
|
206
208
|
self.canvas: dict[str, TextCanvas] = {}
|
|
207
209
|
self.utf8_required = False
|
|
208
210
|
if isinstance(self.data, str):
|
|
209
|
-
self.add_glyphs(self.
|
|
211
|
+
self.add_glyphs(self.data)
|
|
210
212
|
|
|
211
213
|
else:
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
for gdata in data:
|
|
214
|
+
for gdata in self.data:
|
|
215
215
|
self.add_glyphs(gdata)
|
|
216
216
|
|
|
217
217
|
def __repr__(self) -> str:
|
|
@@ -223,22 +223,19 @@ class Font(metaclass=FontRegistry):
|
|
|
223
223
|
|
|
224
224
|
@staticmethod
|
|
225
225
|
def _to_text(
|
|
226
|
-
obj: str
|
|
226
|
+
obj: str,
|
|
227
227
|
encoding: str = "utf-8",
|
|
228
228
|
errors: Literal["strict", "ignore", "replace"] = "strict",
|
|
229
229
|
) -> str:
|
|
230
|
+
warnings.warn(
|
|
231
|
+
"_to_text is deprecated: only text fonts are supported. API will be removed in version 4.0.",
|
|
232
|
+
DeprecationWarning,
|
|
233
|
+
stacklevel=3,
|
|
234
|
+
)
|
|
230
235
|
if isinstance(obj, str):
|
|
231
236
|
return obj
|
|
232
237
|
|
|
233
|
-
|
|
234
|
-
warnings.warn(
|
|
235
|
-
"Bytes based fonts are deprecated, please switch to the text one",
|
|
236
|
-
DeprecationWarning,
|
|
237
|
-
stacklevel=3,
|
|
238
|
-
)
|
|
239
|
-
return obj.decode(encoding, errors)
|
|
240
|
-
|
|
241
|
-
raise TypeError(f"{obj!r} is not str|bytes")
|
|
238
|
+
raise TypeError(f"{obj!r} is not str")
|
|
242
239
|
|
|
243
240
|
def add_glyphs(self, gdata: str) -> None:
|
|
244
241
|
d, utf8_required = separate_glyphs(gdata, self.height)
|
urwid/numedit.py
CHANGED
|
@@ -88,28 +88,28 @@ class NumEdit(Edit):
|
|
|
88
88
|
Handle editing keystrokes. Remove leading zeros.
|
|
89
89
|
|
|
90
90
|
>>> e, size = NumEdit("0123456789", "", "5002"), (10,)
|
|
91
|
-
>>> e.keypress(size,
|
|
92
|
-
>>> e.keypress(size,
|
|
91
|
+
>>> e.keypress(size, "home")
|
|
92
|
+
>>> e.keypress(size, "delete")
|
|
93
93
|
>>> assert e.edit_text == "002"
|
|
94
|
-
>>> e.keypress(size,
|
|
94
|
+
>>> e.keypress(size, "end")
|
|
95
95
|
>>> assert e.edit_text == "2"
|
|
96
96
|
>>> # binary only
|
|
97
97
|
>>> e, size = NumEdit("01", "", ""), (10,)
|
|
98
98
|
>>> assert e.edit_text == ""
|
|
99
|
-
>>> e.keypress(size,
|
|
100
|
-
>>> e.keypress(size,
|
|
101
|
-
>>> e.keypress(size,
|
|
99
|
+
>>> e.keypress(size, "1")
|
|
100
|
+
>>> e.keypress(size, "0")
|
|
101
|
+
>>> e.keypress(size, "1")
|
|
102
102
|
>>> assert e.edit_text == "101"
|
|
103
103
|
>>> e, size = NumEdit("0123456789", "", "", allow_negative=True), (10,)
|
|
104
104
|
>>> e.keypress(size, "-")
|
|
105
|
-
>>> e.keypress(size,
|
|
105
|
+
>>> e.keypress(size, "1")
|
|
106
106
|
>>> e.edit_text
|
|
107
107
|
'-1'
|
|
108
|
-
>>> e.keypress(size,
|
|
109
|
-
>>> e.keypress(size,
|
|
108
|
+
>>> e.keypress(size, "home")
|
|
109
|
+
>>> e.keypress(size, "delete")
|
|
110
110
|
>>> e.edit_text
|
|
111
111
|
'1'
|
|
112
|
-
>>> e.keypress(size,
|
|
112
|
+
>>> e.keypress(size, "end")
|
|
113
113
|
>>> e.keypress(size, "-")
|
|
114
114
|
'-'
|
|
115
115
|
>>> e.edit_text
|
|
@@ -141,52 +141,52 @@ class IntegerEdit(NumEdit):
|
|
|
141
141
|
caption -- caption markup
|
|
142
142
|
default -- default edit value
|
|
143
143
|
|
|
144
|
-
>>> IntegerEdit(
|
|
144
|
+
>>> IntegerEdit("", 42)
|
|
145
145
|
<IntegerEdit selectable flow widget '42' edit_pos=2>
|
|
146
|
-
>>> e, size = IntegerEdit(
|
|
147
|
-
>>> e.keypress(size,
|
|
148
|
-
>>> e.keypress(size,
|
|
146
|
+
>>> e, size = IntegerEdit("", "5002"), (10,)
|
|
147
|
+
>>> e.keypress(size, "home")
|
|
148
|
+
>>> e.keypress(size, "delete")
|
|
149
149
|
>>> assert e.edit_text == "002"
|
|
150
|
-
>>> e.keypress(size,
|
|
150
|
+
>>> e.keypress(size, "end")
|
|
151
151
|
>>> assert e.edit_text == "2"
|
|
152
|
-
>>> e.keypress(size,
|
|
153
|
-
>>> e.keypress(size,
|
|
152
|
+
>>> e.keypress(size, "9")
|
|
153
|
+
>>> e.keypress(size, "0")
|
|
154
154
|
>>> assert e.edit_text == "290"
|
|
155
155
|
>>> e, size = IntegerEdit("", ""), (10,)
|
|
156
156
|
>>> assert e.value() is None
|
|
157
157
|
>>> # binary
|
|
158
|
-
>>> e, size = IntegerEdit(
|
|
159
|
-
>>> e.keypress(size,
|
|
160
|
-
>>> e.keypress(size,
|
|
158
|
+
>>> e, size = IntegerEdit("", "1010", base=2), (10,)
|
|
159
|
+
>>> e.keypress(size, "end")
|
|
160
|
+
>>> e.keypress(size, "1")
|
|
161
161
|
>>> assert e.edit_text == "10101"
|
|
162
162
|
>>> assert e.value() == Decimal("21")
|
|
163
163
|
>>> # HEX
|
|
164
|
-
>>> e, size = IntegerEdit(
|
|
165
|
-
>>> e.keypress(size,
|
|
166
|
-
>>> e.keypress(size,
|
|
167
|
-
>>> e.keypress(size,
|
|
164
|
+
>>> e, size = IntegerEdit("", "10", base=16), (10,)
|
|
165
|
+
>>> e.keypress(size, "end")
|
|
166
|
+
>>> e.keypress(size, "F")
|
|
167
|
+
>>> e.keypress(size, "f")
|
|
168
168
|
>>> assert e.edit_text == "10Ff"
|
|
169
|
-
>>> assert e.keypress(size,
|
|
169
|
+
>>> assert e.keypress(size, "G") == "G" # unhandled key
|
|
170
170
|
>>> assert e.edit_text == "10Ff"
|
|
171
171
|
>>> # keep leading 0's when not base 10
|
|
172
|
-
>>> e, size = IntegerEdit(
|
|
172
|
+
>>> e, size = IntegerEdit("", "10FF", base=16), (10,)
|
|
173
173
|
>>> assert e.edit_text == "10FF"
|
|
174
174
|
>>> assert e.value() == Decimal("4351")
|
|
175
|
-
>>> e.keypress(size,
|
|
176
|
-
>>> e.keypress(size,
|
|
177
|
-
>>> e.keypress(size,
|
|
175
|
+
>>> e.keypress(size, "home")
|
|
176
|
+
>>> e.keypress(size, "delete")
|
|
177
|
+
>>> e.keypress(size, "0")
|
|
178
178
|
>>> assert e.edit_text == "00FF"
|
|
179
179
|
>>> # test exception on incompatible value for base
|
|
180
|
-
>>> e, size = IntegerEdit(
|
|
180
|
+
>>> e, size = IntegerEdit("", "10FG", base=16), (10,)
|
|
181
181
|
Traceback (most recent call last):
|
|
182
182
|
...
|
|
183
183
|
ValueError: invalid value: 10FG for base 16
|
|
184
184
|
>>> # test exception on float init value
|
|
185
|
-
>>> e, size = IntegerEdit(
|
|
185
|
+
>>> e, size = IntegerEdit("", 10.0), (10,)
|
|
186
186
|
Traceback (most recent call last):
|
|
187
187
|
...
|
|
188
188
|
ValueError: default: Only 'str', 'int', 'long' or Decimal input allowed
|
|
189
|
-
>>> e, size = IntegerEdit(
|
|
189
|
+
>>> e, size = IntegerEdit("", Decimal("10.0")), (10,)
|
|
190
190
|
Traceback (most recent call last):
|
|
191
191
|
...
|
|
192
192
|
ValueError: not an 'integer Decimal' instance
|
|
@@ -226,8 +226,8 @@ class IntegerEdit(NumEdit):
|
|
|
226
226
|
Return the numeric value of self.edit_text.
|
|
227
227
|
|
|
228
228
|
>>> e, size = IntegerEdit(), (10,)
|
|
229
|
-
>>> e.keypress(size,
|
|
230
|
-
>>> e.keypress(size,
|
|
229
|
+
>>> e.keypress(size, "5")
|
|
230
|
+
>>> e.keypress(size, "1")
|
|
231
231
|
>>> assert e.value() == 51
|
|
232
232
|
"""
|
|
233
233
|
if self.edit_text:
|
|
@@ -240,9 +240,9 @@ class IntegerEdit(NumEdit):
|
|
|
240
240
|
|
|
241
241
|
>>> e, size = IntegerEdit(allow_negative=True), (10,)
|
|
242
242
|
>>> assert int(e) == 0
|
|
243
|
-
>>> e.keypress(size,
|
|
244
|
-
>>> e.keypress(size,
|
|
245
|
-
>>> e.keypress(size,
|
|
243
|
+
>>> e.keypress(size, "-")
|
|
244
|
+
>>> e.keypress(size, "4")
|
|
245
|
+
>>> e.keypress(size, "2")
|
|
246
246
|
>>> assert int(e) == -42
|
|
247
247
|
"""
|
|
248
248
|
if self.edit_text:
|
|
@@ -270,46 +270,46 @@ class FloatEdit(NumEdit):
|
|
|
270
270
|
preserve_significance -- return value has the same signif. as default
|
|
271
271
|
decimal_separator -- use '.' as separator by default, optionally a ','
|
|
272
272
|
|
|
273
|
-
>>> FloatEdit(
|
|
273
|
+
>>> FloatEdit("", "1.065434")
|
|
274
274
|
<FloatEdit selectable flow widget '1.065434' edit_pos=8>
|
|
275
|
-
>>> e, size = FloatEdit(
|
|
276
|
-
>>> e.keypress(size,
|
|
277
|
-
>>> e.keypress(size,
|
|
275
|
+
>>> e, size = FloatEdit("", "1.065434"), (10,)
|
|
276
|
+
>>> e.keypress(size, "home")
|
|
277
|
+
>>> e.keypress(size, "delete")
|
|
278
278
|
>>> assert e.edit_text == ".065434"
|
|
279
|
-
>>> e.keypress(size,
|
|
280
|
-
>>> e.keypress(size,
|
|
279
|
+
>>> e.keypress(size, "end")
|
|
280
|
+
>>> e.keypress(size, "backspace")
|
|
281
281
|
>>> assert e.edit_text == ".06543"
|
|
282
282
|
>>> e, size = FloatEdit(), (10,)
|
|
283
|
-
>>> e.keypress(size,
|
|
284
|
-
>>> e.keypress(size,
|
|
285
|
-
>>> e.keypress(size,
|
|
286
|
-
>>> e.keypress(size,
|
|
287
|
-
>>> e.keypress(size,
|
|
283
|
+
>>> e.keypress(size, "5")
|
|
284
|
+
>>> e.keypress(size, "1")
|
|
285
|
+
>>> e.keypress(size, ".")
|
|
286
|
+
>>> e.keypress(size, "5")
|
|
287
|
+
>>> e.keypress(size, "1")
|
|
288
288
|
>>> assert e.value() == Decimal("51.51"), e.value()
|
|
289
289
|
>>> e, size = FloatEdit(decimal_separator=":"), (10,)
|
|
290
290
|
Traceback (most recent call last):
|
|
291
291
|
...
|
|
292
292
|
ValueError: invalid decimal separator: :
|
|
293
293
|
>>> e, size = FloatEdit(decimal_separator=","), (10,)
|
|
294
|
-
>>> e.keypress(size,
|
|
295
|
-
>>> e.keypress(size,
|
|
296
|
-
>>> e.keypress(size,
|
|
297
|
-
>>> e.keypress(size,
|
|
298
|
-
>>> e.keypress(size,
|
|
294
|
+
>>> e.keypress(size, "5")
|
|
295
|
+
>>> e.keypress(size, "1")
|
|
296
|
+
>>> e.keypress(size, ",")
|
|
297
|
+
>>> e.keypress(size, "5")
|
|
298
|
+
>>> e.keypress(size, "1")
|
|
299
299
|
>>> assert e.edit_text == "51,51"
|
|
300
300
|
>>> e, size = FloatEdit("", "3.1415", preserve_significance=True), (10,)
|
|
301
|
-
>>> e.keypress(size,
|
|
302
|
-
>>> e.keypress(size,
|
|
303
|
-
>>> e.keypress(size,
|
|
301
|
+
>>> e.keypress(size, "end")
|
|
302
|
+
>>> e.keypress(size, "backspace")
|
|
303
|
+
>>> e.keypress(size, "backspace")
|
|
304
304
|
>>> assert e.edit_text == "3.14"
|
|
305
305
|
>>> assert e.value() == Decimal("3.1400")
|
|
306
|
-
>>> e.keypress(size,
|
|
307
|
-
>>> e.keypress(size,
|
|
308
|
-
>>> e.keypress(size,
|
|
306
|
+
>>> e.keypress(size, "1")
|
|
307
|
+
>>> e.keypress(size, "5")
|
|
308
|
+
>>> e.keypress(size, "9")
|
|
309
309
|
>>> assert e.value() == Decimal("3.1416"), e.value()
|
|
310
310
|
>>> e, size = FloatEdit("", ""), (10,)
|
|
311
311
|
>>> assert e.value() is None
|
|
312
|
-
>>> e, size = FloatEdit(
|
|
312
|
+
>>> e, size = FloatEdit("", 10.0), (10,)
|
|
313
313
|
Traceback (most recent call last):
|
|
314
314
|
...
|
|
315
315
|
ValueError: default: Only 'str', 'int', 'long' or Decimal input allowed
|
|
@@ -368,11 +368,11 @@ class FloatEdit(NumEdit):
|
|
|
368
368
|
"""Enforced float value return.
|
|
369
369
|
|
|
370
370
|
>>> e, size = FloatEdit(allow_negative=True), (10,)
|
|
371
|
-
>>> assert float(e) == 0.
|
|
372
|
-
>>> e.keypress(size,
|
|
373
|
-
>>> e.keypress(size,
|
|
374
|
-
>>> e.keypress(size,
|
|
375
|
-
>>> e.keypress(size,
|
|
371
|
+
>>> assert float(e) == 0.0
|
|
372
|
+
>>> e.keypress(size, "-")
|
|
373
|
+
>>> e.keypress(size, "4")
|
|
374
|
+
>>> e.keypress(size, ".")
|
|
375
|
+
>>> e.keypress(size, "2")
|
|
376
376
|
>>> assert float(e) == -4.2
|
|
377
377
|
"""
|
|
378
378
|
if self.edit_text:
|