urwid 2.6.16__py3-none-any.whl → 3.0.1__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 (53) hide show
  1. urwid/__init__.py +1 -4
  2. urwid/canvas.py +19 -38
  3. urwid/command_map.py +4 -3
  4. urwid/container.py +1 -1
  5. urwid/decoration.py +1 -1
  6. urwid/display/_raw_display_base.py +8 -5
  7. urwid/display/_win32_raw_display.py +11 -13
  8. urwid/display/common.py +26 -55
  9. urwid/display/curses.py +1 -1
  10. urwid/display/escape.py +6 -8
  11. urwid/display/lcd.py +4 -6
  12. urwid/display/web.py +7 -12
  13. urwid/event_loop/asyncio_loop.py +33 -15
  14. urwid/event_loop/main_loop.py +13 -18
  15. urwid/event_loop/tornado_loop.py +4 -5
  16. urwid/event_loop/trio_loop.py +1 -1
  17. urwid/font.py +10 -15
  18. urwid/signals.py +2 -1
  19. urwid/str_util.py +15 -18
  20. urwid/text_layout.py +6 -7
  21. urwid/util.py +6 -17
  22. urwid/version.py +9 -4
  23. urwid/vterm.py +9 -44
  24. urwid/widget/__init__.py +0 -6
  25. urwid/widget/attr_wrap.py +8 -10
  26. urwid/widget/bar_graph.py +2 -7
  27. urwid/widget/big_text.py +9 -7
  28. urwid/widget/box_adapter.py +4 -4
  29. urwid/widget/columns.py +50 -81
  30. urwid/widget/container.py +29 -75
  31. urwid/widget/edit.py +8 -8
  32. urwid/widget/filler.py +6 -6
  33. urwid/widget/frame.py +28 -37
  34. urwid/widget/grid_flow.py +24 -109
  35. urwid/widget/line_box.py +13 -0
  36. urwid/widget/listbox.py +12 -50
  37. urwid/widget/monitored_list.py +6 -4
  38. urwid/widget/overlay.py +4 -37
  39. urwid/widget/padding.py +11 -48
  40. urwid/widget/pile.py +177 -156
  41. urwid/widget/popup.py +2 -2
  42. urwid/widget/progress_bar.py +0 -10
  43. urwid/widget/scrollable.py +24 -32
  44. urwid/widget/treetools.py +27 -48
  45. urwid/widget/widget.py +7 -124
  46. urwid/widget/widget_decoration.py +4 -33
  47. urwid/wimp.py +1 -1
  48. {urwid-2.6.16.dist-info → urwid-3.0.1.dist-info}/METADATA +10 -15
  49. urwid-3.0.1.dist-info/RECORD +74 -0
  50. {urwid-2.6.16.dist-info → urwid-3.0.1.dist-info}/WHEEL +1 -1
  51. urwid-2.6.16.dist-info/RECORD +0 -74
  52. {urwid-2.6.16.dist-info → urwid-3.0.1.dist-info/licenses}/COPYING +0 -0
  53. {urwid-2.6.16.dist-info → urwid-3.0.1.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
 
@@ -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,
@@ -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
  """
@@ -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,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] + (rows,) + cv[4:]
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) + cv[4:]
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) + 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,6 +21,7 @@ from __future__ import annotations
21
21
 
22
22
  import enum
23
23
  import typing
24
+ from collections.abc import MutableMapping
24
25
 
25
26
  if typing.TYPE_CHECKING:
26
27
  from collections.abc import Iterator
@@ -56,7 +57,7 @@ CURSOR_MAX_RIGHT = Command.MAX_RIGHT
56
57
  ACTIVATE = Command.ACTIVATE
57
58
 
58
59
 
59
- class CommandMap(typing.Mapping[str, typing.Union[str, Command, None]]):
60
+ class CommandMap(MutableMapping[str, typing.Union[str, Command, None]]):
60
61
  """
61
62
  dict-like object for looking up commands from keystrokes
62
63
 
@@ -106,10 +107,10 @@ class CommandMap(typing.Mapping[str, typing.Union[str, Command, None]]):
106
107
  }
107
108
 
108
109
  def __init__(self) -> None:
109
- self._command = dict(self._command_defaults)
110
+ self._command = self._command_defaults.copy()
110
111
 
111
112
  def restore_defaults(self) -> None:
112
- self._command = dict(self._command_defaults)
113
+ self._command = self._command_defaults.copy()
113
114
 
114
115
  def __getitem__(self, key: str) -> str | Command | None:
115
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
  )
@@ -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
 
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
 
urwid/display/escape.py CHANGED
@@ -216,10 +216,10 @@ class KeyqueueTrie:
216
216
  root[ord(s)] = result
217
217
 
218
218
  def get(self, keys, more_available: bool):
219
- result = self.get_recurse(self.data, keys, more_available)
220
- if not result:
221
- result = self.read_cursor_position(keys, more_available)
222
- return result
219
+ if result := self.get_recurse(self.data, keys, more_available):
220
+ return result
221
+
222
+ return self.read_cursor_position(keys, more_available)
223
223
 
224
224
  def get_recurse(
225
225
  self,
@@ -540,9 +540,7 @@ def process_keyqueue(codes: Sequence[int], more_available: bool) -> tuple[list[s
540
540
  if code != 27:
541
541
  return [f"<{code:d}>"], codes[1:]
542
542
 
543
- result = input_trie.get(codes[1:], more_available)
544
-
545
- if result is not None:
543
+ if (result := input_trie.get(codes[1:], more_available)) is not None:
546
544
  result, remaining_codes = result
547
545
  return [result], remaining_codes
548
546
 
@@ -553,7 +551,7 @@ def process_keyqueue(codes: Sequence[int], more_available: bool) -> tuple[list[s
553
551
  return ["esc", *run], remaining_codes
554
552
  if run[0] == "esc" or run[0].find("meta ") >= 0:
555
553
  return ["esc", *run], remaining_codes
556
- return [f"meta {run[0]}"] + run[1:], remaining_codes
554
+ return [f"meta {run[0]}", *run[1:]], remaining_codes
557
555
 
558
556
  return ["esc"], codes[1:]
559
557
 
urwid/display/lcd.py CHANGED
@@ -218,10 +218,9 @@ class CFLCDScreen(LCDScreen, abc.ABC):
218
218
  raise cls.MoreDataRequired
219
219
 
220
220
  data_end = 2 + packet_len
221
- crc = cls.get_crc(data[:data_end])
222
- pcrc = data[data_end : data_end + 2]
223
- if crc != pcrc:
224
- raise cls.InvalidPacket("CRC doesn't match")
221
+ if (crc := cls.get_crc(data[:data_end])) != (packet_crc := data[data_end : data_end + 2]):
222
+ raise cls.InvalidPacket(f"CRC doesn't match ({crc=}, {packet_crc=})")
223
+
225
224
  return command, data[2:data_end], data[data_end + 2 :]
226
225
 
227
226
 
@@ -401,8 +400,7 @@ class CF635Screen(CFLCDScreen):
401
400
 
402
401
  packet = self._read_packet()
403
402
 
404
- next_repeat = self.key_repeat.next_event()
405
- if next_repeat:
403
+ if next_repeat := self.key_repeat.next_event():
406
404
  timeout, key = next_repeat
407
405
  if not timeout:
408
406
  data_input.append(key)
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 ** 9):09d}{random.randrange(10 ** 9):09d}" # noqa: S311
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
- old_line_numbers = self.last_screen.get(sig, None)
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:
@@ -609,16 +606,14 @@ def daemonize(errfile: str) -> None:
609
606
  """
610
607
  Detach process and become a daemon.
611
608
  """
612
- pid = os.fork()
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
- pid = os.fork()
621
- if pid:
616
+ if os.fork():
622
617
  os._exit(0)
623
618
 
624
619
  os.chdir("/")
@@ -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,37 @@ 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
- if loop:
71
- self._loop: asyncio.AbstractEventLoop = loop
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
- else:
75
- self._original_event_loop_policy = asyncio.get_event_loop_policy()
76
- if IS_WINDOWS and not isinstance(self._original_event_loop_policy, asyncio.WindowsSelectorEventLoopPolicy):
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._event_loop_policy_altered = False
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(asyncio.WindowsSelectorEventLoopPolicy())
83
+ self._event_loop_policy_altered = True
84
+ else:
85
+ self._event_loop_policy_altered = False
86
+
87
+ self._loop = asyncio.get_event_loop()
82
88
 
83
- self._loop = asyncio.get_event_loop()
89
+ else:
90
+ self._runner: asyncio.Runner | None = None
91
+
92
+ if loop:
93
+ self._loop: asyncio.AbstractEventLoop = loop
94
+ else:
95
+ try:
96
+ self._loop = asyncio.get_running_loop()
97
+ except RuntimeError:
98
+ self._runner = asyncio.Runner(loop_factory=asyncio.SelectorEventLoop)
99
+ self._loop = self._runner.get_loop()
84
100
 
85
101
  self._exc: BaseException | None = None
86
102
 
@@ -89,8 +105,11 @@ class AsyncioEventLoop(EventLoop):
89
105
  self._idle_callbacks: dict[int, Callable[[], typing.Any]] = {}
90
106
 
91
107
  def __del__(self) -> None:
92
- if self._event_loop_policy_altered:
93
- asyncio.set_event_loop_policy(self._original_event_loop_policy) # Restore default event loop policy
108
+ if sys.version_info[:2] < (3, 11):
109
+ if self._event_loop_policy_altered:
110
+ asyncio.set_event_loop_policy(self._original_event_loop_policy) # Restore default event loop policy
111
+ elif self._runner is not None:
112
+ self._runner.close()
94
113
 
95
114
  def _also_call_idle(self, callback: Callable[_Spec, _T]) -> Callable[_Spec, _T]:
96
115
  """
@@ -205,8 +224,7 @@ class AsyncioEventLoop(EventLoop):
205
224
  return True
206
225
 
207
226
  def _exception_handler(self, loop: asyncio.AbstractEventLoop, context):
208
- exc = context.get("exception")
209
- if exc:
227
+ if exc := context.get("exception"):
210
228
  loop.stop()
211
229
 
212
230
  if self._idle_asyncio_handle: