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.
Files changed (62) hide show
  1. urwid/__init__.py +30 -20
  2. urwid/canvas.py +34 -53
  3. urwid/command_map.py +6 -4
  4. urwid/container.py +1 -1
  5. urwid/decoration.py +1 -1
  6. urwid/display/__init__.py +53 -48
  7. urwid/display/_posix_raw_display.py +20 -8
  8. urwid/display/_raw_display_base.py +21 -16
  9. urwid/display/_win32_raw_display.py +16 -17
  10. urwid/display/common.py +45 -74
  11. urwid/display/curses.py +3 -5
  12. urwid/display/escape.py +28 -13
  13. urwid/display/lcd.py +8 -10
  14. urwid/display/web.py +11 -16
  15. urwid/event_loop/asyncio_loop.py +35 -15
  16. urwid/event_loop/main_loop.py +18 -23
  17. urwid/event_loop/tornado_loop.py +4 -5
  18. urwid/event_loop/trio_loop.py +1 -1
  19. urwid/font.py +19 -22
  20. urwid/numedit.py +65 -65
  21. urwid/signals.py +19 -27
  22. urwid/split_repr.py +9 -3
  23. urwid/str_util.py +105 -60
  24. urwid/text_layout.py +14 -13
  25. urwid/util.py +8 -19
  26. urwid/version.py +22 -4
  27. urwid/vterm.py +20 -47
  28. urwid/widget/__init__.py +0 -6
  29. urwid/widget/attr_map.py +10 -10
  30. urwid/widget/attr_wrap.py +11 -13
  31. urwid/widget/bar_graph.py +3 -8
  32. urwid/widget/big_text.py +8 -9
  33. urwid/widget/box_adapter.py +6 -6
  34. urwid/widget/columns.py +52 -83
  35. urwid/widget/container.py +29 -75
  36. urwid/widget/divider.py +6 -6
  37. urwid/widget/edit.py +50 -50
  38. urwid/widget/filler.py +14 -14
  39. urwid/widget/frame.py +31 -40
  40. urwid/widget/grid_flow.py +25 -110
  41. urwid/widget/line_box.py +31 -18
  42. urwid/widget/listbox.py +16 -51
  43. urwid/widget/monitored_list.py +75 -49
  44. urwid/widget/overlay.py +4 -37
  45. urwid/widget/padding.py +31 -68
  46. urwid/widget/pile.py +179 -158
  47. urwid/widget/popup.py +2 -2
  48. urwid/widget/progress_bar.py +17 -18
  49. urwid/widget/scrollable.py +26 -34
  50. urwid/widget/solid_fill.py +3 -3
  51. urwid/widget/text.py +44 -30
  52. urwid/widget/treetools.py +27 -48
  53. urwid/widget/widget.py +13 -130
  54. urwid/widget/widget_decoration.py +6 -35
  55. urwid/widget/wimp.py +61 -61
  56. urwid/wimp.py +1 -1
  57. {urwid-2.6.15.dist-info → urwid-3.0.5.dist-info}/METADATA +24 -24
  58. urwid-3.0.5.dist-info/RECORD +74 -0
  59. {urwid-2.6.15.dist-info → urwid-3.0.5.dist-info}/WHEEL +1 -1
  60. urwid-2.6.15.dist-info/RECORD +0 -74
  61. {urwid-2.6.15.dist-info → urwid-3.0.5.dist-info/licenses}/COPYING +0 -0
  62. {urwid-2.6.15.dist-info → urwid-3.0.5.dist-info}/top_level.txt +0 -0
urwid/vterm.py CHANGED
@@ -35,7 +35,6 @@ import termios
35
35
  import time
36
36
  import traceback
37
37
  import typing
38
- import warnings
39
38
  from collections import deque
40
39
  from contextlib import suppress
41
40
  from dataclasses import dataclass
@@ -248,8 +247,7 @@ class TermCharset:
248
247
 
249
248
  def apply_mapping(self, char: bytes) -> bytes:
250
249
  if self._sgr_mapping or self._g[self.active] == "ibmpc":
251
- dec_pos = DEC_SPECIAL_CHARS.find(char.decode("cp437"))
252
- if dec_pos >= 0:
250
+ if (dec_pos := DEC_SPECIAL_CHARS.find(char.decode("cp437"))) >= 0:
253
251
  self.current = "0"
254
252
  return ALT_DEC_SPECIAL_CHARS[dec_pos].encode("cp437")
255
253
 
@@ -259,39 +257,6 @@ class TermCharset:
259
257
  return char
260
258
 
261
259
 
262
- class TermScroller(list):
263
- """
264
- List subclass that handles the terminal scrollback buffer,
265
- truncating it as necessary.
266
- """
267
-
268
- SCROLLBACK_LINES = 10000
269
-
270
- def __init__(self, iterable: Iterable[typing.Any]) -> None:
271
- warnings.warn(
272
- "`TermScroller` is deprecated. Please use `collections.deque` with non-zero `maxlen` instead.",
273
- DeprecationWarning,
274
- stacklevel=3,
275
- )
276
- super().__init__(iterable)
277
-
278
- def trunc(self) -> None:
279
- if len(self) >= self.SCROLLBACK_LINES:
280
- self.pop(0)
281
-
282
- def append(self, obj) -> None:
283
- self.trunc()
284
- super().append(obj)
285
-
286
- def insert(self, idx: typing.SupportsIndex, obj) -> None:
287
- self.trunc()
288
- super().insert(idx, obj)
289
-
290
- def extend(self, seq) -> None:
291
- self.trunc()
292
- super().extend(seq)
293
-
294
-
295
260
  class TermCanvas(Canvas):
296
261
  cacheable = False
297
262
 
@@ -375,7 +340,7 @@ class TermCanvas(Canvas):
375
340
  lines = self.height // 2
376
341
 
377
342
  if not up:
378
- lines = -lines # pylint: disable=invalid-unary-operand-type # type already narrowed
343
+ lines = -lines
379
344
 
380
345
  maxscroll = len(self.scrollback_buffer)
381
346
  self.scrolling_up += lines
@@ -489,8 +454,7 @@ class TermCanvas(Canvas):
489
454
  continue
490
455
 
491
456
  # adjust x axis of scrollback buffer to the current width
492
- padding = self.width - len(last_line)
493
- if padding > 0:
457
+ if (padding := self.width - len(last_line)) > 0:
494
458
  last_line += [self.empty_char()] * padding
495
459
  else:
496
460
  last_line = last_line[: self.width]
@@ -549,8 +513,7 @@ class TermCanvas(Canvas):
549
513
 
550
514
  escbuf.append(num)
551
515
 
552
- cmd_ = CSI_COMMANDS[char]
553
- if cmd_ is not None:
516
+ if (cmd_ := CSI_COMMANDS[char]) is not None:
554
517
  if isinstance(cmd_, CSIAlias):
555
518
  csi_cmd: CSICommand = CSI_COMMANDS[cmd_.alias] # type: ignore[assignment]
556
519
  elif isinstance(cmd_, CSICommand):
@@ -699,11 +662,13 @@ class TermCanvas(Canvas):
699
662
 
700
663
  # end multibyte sequence
701
664
  self.utf8_eat_bytes = None
702
- sequence = (self.utf8_buffer + bytes([byte])).decode("utf-8", "ignore")
703
- if not sequence:
665
+
666
+ if sequence := (self.utf8_buffer + bytes([byte])).decode("utf-8", "ignore"):
667
+ char = sequence.encode(util.get_encoding(), "replace")
668
+
669
+ else:
704
670
  # invalid multibyte sequence, stop processing
705
671
  return
706
- char = sequence.encode(util.get_encoding(), "replace")
707
672
  else:
708
673
  self.utf8_eat_bytes = None
709
674
  char = bytes([byte])
@@ -1118,6 +1083,14 @@ class TermCanvas(Canvas):
1118
1083
  elif 40 <= attr <= 47:
1119
1084
  bg = attr - 40
1120
1085
  colors = max(16, colors)
1086
+ # AIXTERM bright color spec
1087
+ # https://en.wikipedia.org/wiki/ANSI_escape_code
1088
+ elif 90 <= attr <= 97:
1089
+ fg = attr - 90 + 8
1090
+ colors = max(16, colors)
1091
+ elif 100 <= attr <= 107:
1092
+ bg = attr - 100 + 8
1093
+ colors = max(16, colors)
1121
1094
  elif attr in {38, 48}:
1122
1095
  if idx + 2 < len(attrs) and attrs[idx + 1] == 5:
1123
1096
  # 8 bit color specification
@@ -1264,7 +1237,7 @@ class TermCanvas(Canvas):
1264
1237
  for x in range(self.width):
1265
1238
  char = self.term[y][x]
1266
1239
  attrs = self.reverse_attrspec(char[0], undo=undo)
1267
- self.term[y][x] = (attrs,) + char[1:]
1240
+ self.term[y][x] = (attrs, *char[1:])
1268
1241
 
1269
1242
  def set_mode(
1270
1243
  self,
@@ -1445,8 +1418,8 @@ class TermCanvas(Canvas):
1445
1418
  if self.scrolling_up == 0:
1446
1419
  yield from self.term
1447
1420
  else:
1448
- buf = self.scrollback_buffer + self.term
1449
- yield from buf[-(self.height + self.scrolling_up) : -self.scrolling_up]
1421
+ viewport_range = slice(-(self.height + self.scrolling_up), -self.scrolling_up)
1422
+ yield from (*self.scrollback_buffer, *self.term)[viewport_range]
1450
1423
 
1451
1424
  def content_delta(self, other: Canvas):
1452
1425
  if other is self:
urwid/widget/__init__.py CHANGED
@@ -41,9 +41,6 @@ from .solid_fill import SolidFill
41
41
  from .text import Text, TextError
42
42
  from .treetools import ParentNode, TreeListBox, TreeNode, TreeWalker, TreeWidget, TreeWidgetError
43
43
  from .widget import (
44
- BoxWidget,
45
- FixedWidget,
46
- FlowWidget,
47
44
  Widget,
48
45
  WidgetError,
49
46
  WidgetMeta,
@@ -87,7 +84,6 @@ __all__ = (
87
84
  "BigText",
88
85
  "BoxAdapter",
89
86
  "BoxAdapterError",
90
- "BoxWidget",
91
87
  "Button",
92
88
  "CheckBox",
93
89
  "CheckBoxError",
@@ -99,8 +95,6 @@ __all__ = (
99
95
  "EditError",
100
96
  "Filler",
101
97
  "FillerError",
102
- "FixedWidget",
103
- "FlowWidget",
104
98
  "Frame",
105
99
  "FrameError",
106
100
  "GraphVScale",
urwid/widget/attr_map.py CHANGED
@@ -42,19 +42,19 @@ class AttrMap(delegate_to_widget_mixin("_original_widget"), WidgetDecoration[Wra
42
42
  :type focus_map: display attribute or dict
43
43
 
44
44
  >>> from urwid import Divider, Edit, Text
45
- >>> AttrMap(Divider(u"!"), 'bright')
45
+ >>> AttrMap(Divider("!"), "bright")
46
46
  <AttrMap flow widget <Divider flow widget '!'> attr_map={None: 'bright'}>
47
- >>> AttrMap(Edit(), 'notfocus', 'focus').attr_map
47
+ >>> AttrMap(Edit(), "notfocus", "focus").attr_map
48
48
  {None: 'notfocus'}
49
- >>> AttrMap(Edit(), 'notfocus', 'focus').focus_map
49
+ >>> AttrMap(Edit(), "notfocus", "focus").focus_map
50
50
  {None: 'focus'}
51
51
  >>> size = (5,)
52
- >>> am = AttrMap(Text(u"hi"), 'greeting', 'fgreet')
53
- >>> next(am.render(size, focus=False).content()) # ... = b in Python 3
52
+ >>> am = AttrMap(Text("hi"), "greeting", "fgreet")
53
+ >>> next(am.render(size, focus=False).content()) # ... = b in Python 3
54
54
  [('greeting', None, ...'hi ')]
55
55
  >>> next(am.render(size, focus=True).content())
56
56
  [('fgreet', None, ...'hi ')]
57
- >>> am2 = AttrMap(Text(('word', u"hi")), {'word':'greeting', None:'bg'})
57
+ >>> am2 = AttrMap(Text(("word", "hi")), {"word": "greeting", None: "bg"})
58
58
  >>> am2
59
59
  <AttrMap fixed/flow widget <Text fixed/flow widget 'hi'> attr_map={'word': 'greeting', None: 'bg'}>
60
60
  >>> next(am2.render(size).content())
@@ -94,8 +94,8 @@ class AttrMap(delegate_to_widget_mixin("_original_widget"), WidgetDecoration[Wra
94
94
  constructor does. You must specify {None: attribute} instead.
95
95
 
96
96
  >>> from urwid import Text
97
- >>> w = AttrMap(Text(u"hi"), None)
98
- >>> w.set_attr_map({'a':'b'})
97
+ >>> w = AttrMap(Text("hi"), None)
98
+ >>> w.set_attr_map({"a": "b"})
99
99
  >>> w
100
100
  <AttrMap fixed/flow widget <Text fixed/flow widget 'hi'> attr_map={'a': 'b'}>
101
101
  """
@@ -129,8 +129,8 @@ class AttrMap(delegate_to_widget_mixin("_original_widget"), WidgetDecoration[Wra
129
129
  constructor does. You must specify {None: attribute} instead.
130
130
 
131
131
  >>> from urwid import Text
132
- >>> w = AttrMap(Text(u"hi"), {})
133
- >>> w.set_focus_map({'a':'b'})
132
+ >>> w = AttrMap(Text("hi"), {})
133
+ >>> w.set_focus_map({"a": "b"})
134
134
  >>> w
135
135
  <AttrMap fixed/flow widget <Text fixed/flow widget 'hi'> attr_map={} focus_map={'a': 'b'}>
136
136
  >>> w.set_focus_map(None)
urwid/widget/attr_wrap.py CHANGED
@@ -25,12 +25,12 @@ class AttrWrap(AttrMap):
25
25
  new code should use AttrMap instead.
26
26
 
27
27
  >>> from urwid import Divider, Edit, Text
28
- >>> AttrWrap(Divider(u"!"), 'bright')
28
+ >>> AttrWrap(Divider("!"), "bright")
29
29
  <AttrWrap flow widget <Divider flow widget '!'> attr='bright'>
30
- >>> AttrWrap(Edit(), 'notfocus', 'focus')
30
+ >>> AttrWrap(Edit(), "notfocus", "focus")
31
31
  <AttrWrap selectable flow widget <Edit selectable flow widget '' edit_pos=0> attr='notfocus' focus_attr='focus'>
32
32
  >>> size = (5,)
33
- >>> aw = AttrWrap(Text(u"hi"), 'greeting', 'fgreet')
33
+ >>> aw = AttrWrap(Text("hi"), "greeting", "fgreet")
34
34
  >>> next(aw.render(size, focus=False).content())
35
35
  [('greeting', None, ...'hi ')]
36
36
  >>> next(aw.render(size, focus=True).content())
@@ -47,8 +47,7 @@ class AttrWrap(AttrMap):
47
47
  # only include the focus_attr when it takes effect (not None)
48
48
  d = {**super()._repr_attrs(), "attr": self.attr}
49
49
  del d["attr_map"]
50
- if "focus_map" in d:
51
- del d["focus_map"]
50
+ d.pop("focus_map", None)
52
51
  if self.focus_attr is not None:
53
52
  d["focus_attr"] = self.focus_attr
54
53
  return d
@@ -57,8 +56,8 @@ class AttrWrap(AttrMap):
57
56
  def w(self) -> Widget:
58
57
  """backwards compatibility, widget used to be stored as w"""
59
58
  warnings.warn(
60
- "backwards compatibility, widget used to be stored as original_widget",
61
- PendingDeprecationWarning,
59
+ "backwards compatibility, widget used to be stored as original_widget. API will be removed in version 5.0.",
60
+ DeprecationWarning,
62
61
  stacklevel=2,
63
62
  )
64
63
  return self.original_widget
@@ -66,15 +65,15 @@ class AttrWrap(AttrMap):
66
65
  @w.setter
67
66
  def w(self, new_widget: Widget) -> None:
68
67
  warnings.warn(
69
- "backwards compatibility, widget used to be stored as original_widget",
70
- PendingDeprecationWarning,
68
+ "backwards compatibility, widget used to be stored as original_widget. API will be removed in version 5.0.",
69
+ DeprecationWarning,
71
70
  stacklevel=2,
72
71
  )
73
72
  self.original_widget = new_widget
74
73
 
75
74
  def get_w(self):
76
75
  warnings.warn(
77
- "backwards compatibility, widget used to be stored as original_widget",
76
+ "backwards compatibility, widget used to be stored as original_widget. API will be removed in version 4.0.",
78
77
  DeprecationWarning,
79
78
  stacklevel=2,
80
79
  )
@@ -82,7 +81,7 @@ class AttrWrap(AttrMap):
82
81
 
83
82
  def set_w(self, new_widget: Widget) -> None:
84
83
  warnings.warn(
85
- "backwards compatibility, widget used to be stored as original_widget",
84
+ "backwards compatibility, widget used to be stored as original_widget. API will be removed in version 4.0.",
86
85
  DeprecationWarning,
87
86
  stacklevel=2,
88
87
  )
@@ -105,8 +104,7 @@ class AttrWrap(AttrMap):
105
104
  attr = property(get_attr, set_attr)
106
105
 
107
106
  def get_focus_attr(self) -> Hashable | None:
108
- focus_map = self.focus_map
109
- if focus_map:
107
+ if focus_map := self.focus_map:
110
108
  return focus_map[None]
111
109
  return None
112
110
 
urwid/widget/bar_graph.py CHANGED
@@ -269,12 +269,7 @@ class BarGraph(Widget, metaclass=BarGraphMeta):
269
269
  ]
270
270
 
271
271
  # reverse the hlines to match screen ordering
272
- rhl = []
273
- for h in hlines:
274
- rh = float(top - h) * maxrow / top - shiftr
275
- if rh < 0:
276
- continue
277
- rhl.append(rh)
272
+ rhl = [rh for h in hlines if (rh := float(top - h) * maxrow / top - shiftr) >= 0]
278
273
 
279
274
  # build a list of rows that will have hlines
280
275
  hrows = []
@@ -410,7 +405,7 @@ class BarGraph(Widget, metaclass=BarGraphMeta):
410
405
  if len(bar_type) == 3:
411
406
  # vertical eighths
412
407
  fg, bg, k = bar_type
413
- a = self.satt[(fg, bg)]
408
+ a = self.satt[fg, bg]
414
409
  t = self.eighths[k] * width
415
410
  else:
416
411
  # horizontal lines
@@ -562,7 +557,7 @@ def calculate_bargraph_display(bardata, top: float, bar_widths: list[int], maxro
562
557
  la, ln = last[i]
563
558
 
564
559
  if i < len(last):
565
- o += [(la, ln)] + last[i + 1 :]
560
+ o += [(la, ln), *last[i + 1 :]]
566
561
  last = o
567
562
  y_count += 1
568
563
 
urwid/widget/big_text.py CHANGED
@@ -70,15 +70,14 @@ class BigText(Widget):
70
70
  a, ak = attrib[ai]
71
71
  ai += 1
72
72
  ak -= 1
73
- width = self.font.char_width(ch)
74
- if not width:
75
- # ignore invalid characters
76
- continue
77
- c: TextCanvas | CompositeCanvas = self.font.render(ch)
78
- if a is not None:
79
- c = CompositeCanvas(c)
80
- c.fill_attr(a)
81
- o.append((c, None, False, width))
73
+
74
+ if width := self.font.char_width(ch):
75
+ c: TextCanvas | CompositeCanvas = self.font.render(ch)
76
+ if a is not None:
77
+ c = CompositeCanvas(c)
78
+ c.fill_attr(a)
79
+ o.append((c, None, False, width))
80
+
82
81
  if o:
83
82
  canv = CanvasJoin(o)
84
83
  else:
@@ -32,7 +32,7 @@ class BoxAdapter(WidgetDecoration[WrappedWidget]):
32
32
  :type height: int
33
33
 
34
34
  >>> from urwid import SolidFill
35
- >>> BoxAdapter(SolidFill(u"x"), 5) # 5-rows of x's
35
+ >>> BoxAdapter(SolidFill("x"), 5) # 5-rows of x's
36
36
  <BoxAdapter flow widget <SolidFill box widget 'x'> height=5>
37
37
  """
38
38
  if hasattr(box_widget, "sizing") and Sizing.BOX not in box_widget.sizing():
@@ -48,8 +48,8 @@ class BoxAdapter(WidgetDecoration[WrappedWidget]):
48
48
  @property
49
49
  def box_widget(self) -> WrappedWidget:
50
50
  warnings.warn(
51
- "original stored as original_widget, keep for compatibility",
52
- PendingDeprecationWarning,
51
+ "original stored as original_widget, keep for compatibility. API will be removed in version 5.0.",
52
+ DeprecationWarning,
53
53
  stacklevel=2,
54
54
  )
55
55
  return self.original_widget
@@ -57,8 +57,8 @@ class BoxAdapter(WidgetDecoration[WrappedWidget]):
57
57
  @box_widget.setter
58
58
  def box_widget(self, widget: WrappedWidget) -> None:
59
59
  warnings.warn(
60
- "original stored as original_widget, keep for compatibility",
61
- PendingDeprecationWarning,
60
+ "original stored as original_widget, keep for compatibility. API will be removed in version 5.0.",
61
+ DeprecationWarning,
62
62
  stacklevel=2,
63
63
  )
64
64
  self.original_widget = widget
@@ -71,7 +71,7 @@ class BoxAdapter(WidgetDecoration[WrappedWidget]):
71
71
  Return the predetermined height (behave like a flow widget)
72
72
 
73
73
  >>> from urwid import SolidFill
74
- >>> BoxAdapter(SolidFill(u"x"), 5).rows((20,))
74
+ >>> BoxAdapter(SolidFill("x"), 5).rows((20,))
75
75
  5
76
76
  """
77
77
  return self.height