urwid 2.6.15__py3-none-any.whl → 3.0.0__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.

Files changed (54) hide show
  1. urwid/__init__.py +1 -4
  2. urwid/canvas.py +27 -46
  3. urwid/command_map.py +6 -4
  4. urwid/container.py +1 -1
  5. urwid/decoration.py +1 -1
  6. urwid/display/_posix_raw_display.py +16 -4
  7. urwid/display/_raw_display_base.py +8 -5
  8. urwid/display/_win32_raw_display.py +16 -17
  9. urwid/display/common.py +26 -55
  10. urwid/display/curses.py +1 -1
  11. urwid/display/escape.py +15 -12
  12. urwid/display/lcd.py +4 -6
  13. urwid/display/web.py +7 -12
  14. urwid/event_loop/asyncio_loop.py +1 -2
  15. urwid/event_loop/main_loop.py +13 -18
  16. urwid/event_loop/tornado_loop.py +4 -5
  17. urwid/event_loop/trio_loop.py +1 -1
  18. urwid/font.py +13 -18
  19. urwid/signals.py +2 -1
  20. urwid/str_util.py +15 -18
  21. urwid/text_layout.py +6 -7
  22. urwid/util.py +7 -18
  23. urwid/version.py +9 -4
  24. urwid/vterm.py +18 -45
  25. urwid/widget/__init__.py +0 -6
  26. urwid/widget/attr_wrap.py +8 -10
  27. urwid/widget/bar_graph.py +3 -8
  28. urwid/widget/big_text.py +9 -7
  29. urwid/widget/box_adapter.py +4 -4
  30. urwid/widget/columns.py +52 -83
  31. urwid/widget/container.py +29 -75
  32. urwid/widget/edit.py +8 -8
  33. urwid/widget/filler.py +6 -6
  34. urwid/widget/frame.py +28 -37
  35. urwid/widget/grid_flow.py +25 -110
  36. urwid/widget/line_box.py +13 -0
  37. urwid/widget/listbox.py +12 -50
  38. urwid/widget/monitored_list.py +6 -4
  39. urwid/widget/overlay.py +4 -37
  40. urwid/widget/padding.py +11 -48
  41. urwid/widget/pile.py +179 -158
  42. urwid/widget/popup.py +2 -2
  43. urwid/widget/progress_bar.py +10 -11
  44. urwid/widget/scrollable.py +25 -33
  45. urwid/widget/treetools.py +27 -48
  46. urwid/widget/widget.py +7 -124
  47. urwid/widget/widget_decoration.py +4 -33
  48. urwid/wimp.py +1 -1
  49. {urwid-2.6.15.dist-info → urwid-3.0.0.dist-info}/METADATA +18 -18
  50. urwid-3.0.0.dist-info/RECORD +74 -0
  51. {urwid-2.6.15.dist-info → urwid-3.0.0.dist-info}/WHEEL +1 -1
  52. urwid-2.6.15.dist-info/RECORD +0 -74
  53. {urwid-2.6.15.dist-info → urwid-3.0.0.dist-info/licenses}/COPYING +0 -0
  54. {urwid-2.6.15.dist-info → urwid-3.0.0.dist-info}/top_level.txt +0 -0
urwid/__init__.py CHANGED
@@ -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, TermScroller
242
+ from .vterm import TermCanvas, TermCharset, Terminal, TermModes
246
243
 
247
244
  # ZMQEventLoop cause interpreter crash on windows
248
245
  try:
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
 
@@ -126,7 +125,7 @@ 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, {})[(wcls, size, focus)] = ref
128
+ cls._widgets.setdefault(widget, {})[wcls, size, focus] = ref
130
129
 
131
130
  @classmethod
132
131
  def fetch(cls, widget, wcls, size, focus) -> Canvas | None:
@@ -182,7 +181,7 @@ class CanvasCache:
182
181
  if not sizes:
183
182
  return
184
183
  with suppress(KeyError):
185
- del sizes[(wcls, size, focus)]
184
+ del sizes[wcls, size, focus]
186
185
  if not sizes:
187
186
  with contextlib.suppress(KeyError):
188
187
  del cls._widgets[widget]
@@ -244,15 +243,6 @@ class Canvas:
244
243
  def widget_info(self):
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
- ) -> Iterable[list[tuple[object, Literal["0", "U"] | None, bytes]]]:
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 = self.coords.get("cursor", None)
299
- if not c:
300
- return None
301
- return c[:2] # trim off data part
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 = self.coords.get("pop up", None)
316
- if not c:
317
- return None
318
- return c
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: Sequence[bytes] | None = None,
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
- ) -> Iterable[tuple[object, Literal["0", "U"] | None, bytes]]:
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
- ) -> Iterable[list[tuple[object, Literal["0", "U"] | None, bytes]]]:
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
- ) -> Iterable[list[tuple[object, Literal["0", "U"] | None, bytes]]]:
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
- ) -> Iterable[list[tuple[object, Literal["0", "U"] | None, bytes]]]:
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)] + shards[1:]
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] + (mapping,) + cv[5:])
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] + (combined,) + cv[5:])
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] + (None,) + cv[6:]
986
+ yield (*cv[:5], None, *cv[6:])
1006
987
  else:
1007
988
  yield cv
1008
989
  other_cols += other_cv[2]
@@ -1185,7 +1166,7 @@ def shards_join(shard_lists):
1185
1166
 
1186
1167
 
1187
1168
  def cview_trim_rows(cv, rows: int):
1188
- return cv[:3] + (rows,) + cv[4:]
1169
+ return (*cv[:3], rows, *cv[4:])
1189
1170
 
1190
1171
 
1191
1172
  def cview_trim_top(cv, trim: int):
@@ -1193,11 +1174,11 @@ def cview_trim_top(cv, trim: int):
1193
1174
 
1194
1175
 
1195
1176
  def cview_trim_left(cv, trim: int):
1196
- return (cv[0] + trim, cv[1], cv[2] - trim) + cv[3:]
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] + (cols,) + cv[3:]
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]] + children[:focus_index] + children[focus_index + 1 :]
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]] + children[:focus_item] + children[focus_item + 1 :]
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 typing import Iterator
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(typing.Mapping[str, typing.Union[str, Command, None]]):
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 = dict(self._command_defaults)
110
+ self._command = self._command_defaults.copy()
109
111
 
110
112
  def restore_defaults(self) -> None:
111
- self._command = dict(self._command_defaults)
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 future.",
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 future.",
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
  )
@@ -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:
@@ -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.TCSADRAIN, self._old_termios_settings)
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)
@@ -172,7 +172,7 @@ class Screen(BaseScreen, RealTerminal):
172
172
  After calling this function get_input will include mouse
173
173
  click events along with keystrokes.
174
174
  """
175
- enable = bool(enable) # noqa: FURB123,RUF100
175
+ enable = bool(enable)
176
176
  if enable == self._mouse_tracking_enabled:
177
177
  return
178
178
 
@@ -323,8 +323,8 @@ class Screen(BaseScreen, RealTerminal):
323
323
  return []
324
324
 
325
325
  fd_list: list[socket.socket | typing.IO | int] = [self._resize_pipe_rd]
326
- input_io = self._term_input_io
327
- if input_io is not None:
326
+
327
+ if (input_io := self._term_input_io) is not None:
328
328
  fd_list.append(input_io)
329
329
  return fd_list
330
330
 
@@ -641,7 +641,7 @@ class Screen(BaseScreen, RealTerminal):
641
641
  a, cs, run = row[-1]
642
642
  if run[-1:] == b" " and self.back_color_erase and not using_standout_or_underline(a):
643
643
  whitespace_at_end = True
644
- row = row[:-1] + [(a, cs, run.rstrip(b" "))] # noqa: PLW2901
644
+ row = [*row[:-1], (a, cs, run.rstrip(b" "))] # noqa: PLW2901
645
645
  elif y == maxrow - 1 and maxcol > 1:
646
646
  row, back, ins = self._last_row(row) # noqa: PLW2901
647
647
 
@@ -727,7 +727,10 @@ class Screen(BaseScreen, RealTerminal):
727
727
  self.screen_buf = sb
728
728
  self._screen_buf_canvas = canvas
729
729
 
730
- def _last_row(self, row: list[tuple[object, Literal["0", "U"] | None, bytes]]) -> tuple[
730
+ def _last_row(
731
+ self,
732
+ row: list[tuple[object, Literal["0", "U"] | None, bytes]],
733
+ ) -> tuple[
731
734
  list[tuple[object, Literal["0", "U"] | None, bytes]],
732
735
  int,
733
736
  tuple[object, Literal["0", "U"] | None, bytes],
@@ -94,11 +94,10 @@ class Screen(_raw_display_base.Screen):
94
94
  self._dwOriginalInMode.value | _win32.ENABLE_WINDOW_INPUT | _win32.ENABLE_VIRTUAL_TERMINAL_INPUT
95
95
  )
96
96
 
97
- ok = _win32.SetConsoleMode(handle_out, dword_out_mode)
98
- if not ok:
97
+ if not (ok := _win32.SetConsoleMode(handle_out, dword_out_mode)):
99
98
  raise RuntimeError(f"ConsoleMode set failed for output. Err: {ok!r}")
100
- ok = _win32.SetConsoleMode(handle_in, dword_in_mode)
101
- if not ok:
99
+
100
+ if not (ok := _win32.SetConsoleMode(handle_in, dword_in_mode)):
102
101
  raise RuntimeError(f"ConsoleMode set failed for input. Err: {ok!r}")
103
102
  self._alternate_buffer = alternate_buffer
104
103
  self._next_timeout = self.max_wait
@@ -121,11 +120,11 @@ class Screen(_raw_display_base.Screen):
121
120
 
122
121
  handle_out = _win32.GetStdHandle(_win32.STD_OUTPUT_HANDLE)
123
122
  handle_in = _win32.GetStdHandle(_win32.STD_INPUT_HANDLE)
124
- ok = _win32.SetConsoleMode(handle_out, self._dwOriginalOutMode)
125
- if not ok:
123
+
124
+ if not (ok := _win32.SetConsoleMode(handle_out, self._dwOriginalOutMode)):
126
125
  raise RuntimeError(f"ConsoleMode set failed for output. Err: {ok!r}")
127
- ok = _win32.SetConsoleMode(handle_in, self._dwOriginalInMode)
128
- if not ok:
126
+
127
+ if not (ok := _win32.SetConsoleMode(handle_in, self._dwOriginalInMode)):
129
128
  raise RuntimeError(f"ConsoleMode set failed for input. Err: {ok!r}")
130
129
 
131
130
  super()._stop()
@@ -188,10 +187,9 @@ class Screen(_raw_display_base.Screen):
188
187
 
189
188
  with selectors.DefaultSelector() as selector:
190
189
  selector.register(fd, selectors.EVENT_READ)
191
- input_ready = selector.select(0)
192
- while input_ready:
190
+
191
+ while selector.select(0):
193
192
  chars.extend(self._term_input_file.recv(1024))
194
- input_ready = selector.select(0)
195
193
 
196
194
  return chars
197
195
 
@@ -204,8 +202,8 @@ class Screen(_raw_display_base.Screen):
204
202
  raise RuntimeError("Unexpected terminal output file")
205
203
  handle = _win32.GetStdHandle(_win32.STD_OUTPUT_HANDLE)
206
204
  info = _win32.CONSOLE_SCREEN_BUFFER_INFO()
207
- ok = _win32.GetConsoleScreenBufferInfo(handle, byref(info))
208
- if ok:
205
+
206
+ if _win32.GetConsoleScreenBufferInfo(handle, byref(info)):
209
207
  # Fallback will be used in case of term size could not be determined
210
208
  y, x = info.dwSize.Y, info.dwSize.X
211
209
 
@@ -246,11 +244,12 @@ class ReadInputThread(threading.Thread):
246
244
  if not inp.Event.KeyEvent.bKeyDown:
247
245
  continue
248
246
 
249
- input_data = inp.Event.KeyEvent.uChar.AsciiChar
250
- # On Windows atomic press/release of modifier keys produce phantom input with code NULL
247
+ input_data = inp.Event.KeyEvent.uChar.UnicodeChar
248
+ # On Windows atomic press/release of modifier keys produce phantom input with code NULL.
251
249
  # This input cannot be decoded and should be handled as garbage.
252
- if input_data != b"\x00":
253
- self._input.send(input_data)
250
+ input_bytes = input_data.encode("utf-8")
251
+ if input_bytes != b"\x00":
252
+ self._input.send(input_bytes)
254
253
 
255
254
  elif inp.EventType == _win32.EventType.WINDOW_BUFFER_SIZE_EVENT:
256
255
  self._resize()
urwid/display/common.py CHANGED
@@ -361,19 +361,21 @@ def _color_desc_88(num: int) -> str:
361
361
 
362
362
 
363
363
  def _parse_color_true(desc: str) -> int | None:
364
- c = _parse_color_256(desc)
365
- if c is not None:
364
+ if (c := _parse_color_256(desc)) is not None:
366
365
  (r, g, b) = _COLOR_VALUES_256[c]
367
366
  return (r << 16) + (g << 8) + b
368
367
 
369
368
  if not desc.startswith("#"):
370
369
  return None
370
+
371
371
  if len(desc) == 7:
372
372
  h = desc[1:]
373
373
  return int(h, 16)
374
+
374
375
  if len(desc) == 4:
375
376
  h = f"0x{desc[1]}0{desc[2]}0{desc[3]}"
376
377
  return int(h, 16)
378
+
377
379
  return None
378
380
 
379
381
 
@@ -409,16 +411,16 @@ def _parse_color_256(desc: str) -> int | None:
409
411
 
410
412
  if desc.startswith("#") and len(desc) == 4:
411
413
  # color-cube coordinates
412
- rgb = int(desc[1:], 16)
413
- if rgb < 0:
414
- return None
415
- b, rgb = rgb % 16, rgb // 16
416
- g, r = rgb % 16, rgb // 16
417
- # find the closest rgb values
418
- r = _CUBE_256_LOOKUP_16[r]
419
- g = _CUBE_256_LOOKUP_16[g]
420
- b = _CUBE_256_LOOKUP_16[b]
421
- return _CUBE_START + (r * _CUBE_SIZE_256 + g) * _CUBE_SIZE_256 + b
414
+ if (rgb := int(desc[1:], 16)) >= 0:
415
+ b, rgb = rgb % 16, rgb // 16
416
+ g, r = rgb % 16, rgb // 16
417
+ # find the closest rgb values
418
+ r = _CUBE_256_LOOKUP_16[r]
419
+ g = _CUBE_256_LOOKUP_16[g]
420
+ b = _CUBE_256_LOOKUP_16[b]
421
+ return _CUBE_START + (r * _CUBE_SIZE_256 + g) * _CUBE_SIZE_256 + b
422
+
423
+ return None
422
424
 
423
425
  # Only remaining possibility is gray value
424
426
  if desc.startswith("g#"):
@@ -489,16 +491,16 @@ def _parse_color_88(desc: str) -> int | None:
489
491
 
490
492
  if desc.startswith("#") and len(desc) == 4:
491
493
  # color-cube coordinates
492
- rgb = int(desc[1:], 16)
493
- if rgb < 0:
494
- return None
495
- b, rgb = rgb % 16, rgb // 16
496
- g, r = rgb % 16, rgb // 16
497
- # find the closest rgb values
498
- r = _CUBE_88_LOOKUP_16[r]
499
- g = _CUBE_88_LOOKUP_16[g]
500
- b = _CUBE_88_LOOKUP_16[b]
501
- return _CUBE_START + (r * _CUBE_SIZE_88 + g) * _CUBE_SIZE_88 + b
494
+ if (rgb := int(desc[1:], 16)) >= 0:
495
+ b, rgb = rgb % 16, rgb // 16
496
+ g, r = rgb % 16, rgb // 16
497
+ # find the closest rgb values
498
+ r = _CUBE_88_LOOKUP_16[r]
499
+ g = _CUBE_88_LOOKUP_16[g]
500
+ b = _CUBE_88_LOOKUP_16[b]
501
+ return _CUBE_START + (r * _CUBE_SIZE_88 + g) * _CUBE_SIZE_88 + b
502
+
503
+ return None
502
504
 
503
505
  # Only remaining possibility is gray value
504
506
  if desc.startswith("g#"):
@@ -704,15 +706,6 @@ class AttrSpec:
704
706
  return 16
705
707
  return 1
706
708
 
707
- def _colors(self) -> int:
708
- warnings.warn(
709
- f"Method `{self.__class__.__name__}._colors` is deprecated, "
710
- f"please use property `{self.__class__.__name__}.colors`",
711
- DeprecationWarning,
712
- stacklevel=2,
713
- )
714
- return self.colors
715
-
716
709
  def __repr__(self) -> str:
717
710
  """
718
711
  Return an executable python representation of the AttrSpec
@@ -785,15 +778,6 @@ class AttrSpec:
785
778
  color = 0
786
779
  self.__value = (self.__value & ~_FG_MASK) | color | flags
787
780
 
788
- def _foreground(self) -> str:
789
- warnings.warn(
790
- f"Method `{self.__class__.__name__}._foreground` is deprecated, "
791
- f"please use property `{self.__class__.__name__}.foreground`",
792
- DeprecationWarning,
793
- stacklevel=2,
794
- )
795
- return self.foreground
796
-
797
781
  @property
798
782
  def background(self) -> str:
799
783
  """Return the background color."""
@@ -827,15 +811,6 @@ class AttrSpec:
827
811
  raise AttrSpecError(f"Unrecognised color specification in background ({background!r})")
828
812
  self.__value = (self.__value & ~_BG_MASK) | (color << _BG_SHIFT) | flags
829
813
 
830
- def _background(self) -> str:
831
- warnings.warn(
832
- f"Method `{self.__class__.__name__}._background` is deprecated, "
833
- f"please use property `{self.__class__.__name__}.background`",
834
- DeprecationWarning,
835
- stacklevel=2,
836
- )
837
- return self.background
838
-
839
814
  def get_rgb_values(self) -> tuple[int | None, int | None, int | None, int | None, int | None, int | None]:
840
815
  """
841
816
  Return (fg_red, fg_green, fg_blue, bg_red, bg_green, bg_blue) color
@@ -988,13 +963,9 @@ class ScreenError(Exception):
988
963
  pass
989
964
 
990
965
 
991
- class BaseMeta(signals.MetaSignals, abc.ABCMeta):
992
- """Base metaclass for abstra"""
993
-
994
-
995
- class BaseScreen(metaclass=BaseMeta):
966
+ class BaseScreen(abc.ABC, metaclass=signals.MetaSignals):
996
967
  """
997
- Base class for Screen classes (raw_display.Screen, .. etc)
968
+ Base class for Screen classes (raw_display.Screen, ..., etc.)
998
969
  """
999
970
 
1000
971
  signals: typing.ClassVar[list[str]] = [UPDATE_PALETTE_ENTRY, INPUT_DESCRIPTORS_CHANGED]
urwid/display/curses.py CHANGED
@@ -124,7 +124,7 @@ class Screen(BaseScreen, RealTerminal):
124
124
  After calling this function get_input will include mouse
125
125
  click events along with keystrokes.
126
126
  """
127
- enable = bool(enable) # noqa: FURB123,RUF100
127
+ enable = bool(enable)
128
128
  if enable == self._mouse_tracking_enabled:
129
129
  return
130
130