urwid 2.6.0.post0__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 (75) hide show
  1. urwid/__init__.py +333 -0
  2. urwid/canvas.py +1413 -0
  3. urwid/command_map.py +137 -0
  4. urwid/container.py +59 -0
  5. urwid/decoration.py +65 -0
  6. urwid/display/__init__.py +97 -0
  7. urwid/display/_posix_raw_display.py +413 -0
  8. urwid/display/_raw_display_base.py +914 -0
  9. urwid/display/_web.css +12 -0
  10. urwid/display/_web.js +462 -0
  11. urwid/display/_win32.py +171 -0
  12. urwid/display/_win32_raw_display.py +269 -0
  13. urwid/display/common.py +1219 -0
  14. urwid/display/curses.py +690 -0
  15. urwid/display/escape.py +624 -0
  16. urwid/display/html_fragment.py +251 -0
  17. urwid/display/lcd.py +518 -0
  18. urwid/display/raw.py +37 -0
  19. urwid/display/web.py +636 -0
  20. urwid/event_loop/__init__.py +55 -0
  21. urwid/event_loop/abstract_loop.py +175 -0
  22. urwid/event_loop/asyncio_loop.py +231 -0
  23. urwid/event_loop/glib_loop.py +294 -0
  24. urwid/event_loop/main_loop.py +721 -0
  25. urwid/event_loop/select_loop.py +230 -0
  26. urwid/event_loop/tornado_loop.py +206 -0
  27. urwid/event_loop/trio_loop.py +302 -0
  28. urwid/event_loop/twisted_loop.py +269 -0
  29. urwid/event_loop/zmq_loop.py +275 -0
  30. urwid/font.py +695 -0
  31. urwid/graphics.py +96 -0
  32. urwid/highlight.css +19 -0
  33. urwid/listbox.py +1899 -0
  34. urwid/monitored_list.py +522 -0
  35. urwid/numedit.py +376 -0
  36. urwid/signals.py +330 -0
  37. urwid/split_repr.py +130 -0
  38. urwid/str_util.py +358 -0
  39. urwid/text_layout.py +632 -0
  40. urwid/treetools.py +515 -0
  41. urwid/util.py +557 -0
  42. urwid/version.py +16 -0
  43. urwid/vterm.py +1806 -0
  44. urwid/widget/__init__.py +181 -0
  45. urwid/widget/attr_map.py +161 -0
  46. urwid/widget/attr_wrap.py +140 -0
  47. urwid/widget/bar_graph.py +649 -0
  48. urwid/widget/big_text.py +77 -0
  49. urwid/widget/box_adapter.py +126 -0
  50. urwid/widget/columns.py +1145 -0
  51. urwid/widget/constants.py +574 -0
  52. urwid/widget/container.py +227 -0
  53. urwid/widget/divider.py +110 -0
  54. urwid/widget/edit.py +718 -0
  55. urwid/widget/filler.py +403 -0
  56. urwid/widget/frame.py +539 -0
  57. urwid/widget/grid_flow.py +539 -0
  58. urwid/widget/line_box.py +194 -0
  59. urwid/widget/overlay.py +829 -0
  60. urwid/widget/padding.py +597 -0
  61. urwid/widget/pile.py +971 -0
  62. urwid/widget/popup.py +170 -0
  63. urwid/widget/progress_bar.py +141 -0
  64. urwid/widget/scrollable.py +597 -0
  65. urwid/widget/solid_fill.py +44 -0
  66. urwid/widget/text.py +354 -0
  67. urwid/widget/widget.py +852 -0
  68. urwid/widget/widget_decoration.py +166 -0
  69. urwid/widget/wimp.py +792 -0
  70. urwid/wimp.py +23 -0
  71. urwid-2.6.0.post0.dist-info/COPYING +504 -0
  72. urwid-2.6.0.post0.dist-info/METADATA +332 -0
  73. urwid-2.6.0.post0.dist-info/RECORD +75 -0
  74. urwid-2.6.0.post0.dist-info/WHEEL +5 -0
  75. urwid-2.6.0.post0.dist-info/top_level.txt +1 -0
urwid/numedit.py ADDED
@@ -0,0 +1,376 @@
1
+ #
2
+ # Urwid basic widget classes
3
+ # Copyright (C) 2004-2012 Ian Ward
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ #
19
+ # Urwid web site: https://urwid.org/
20
+
21
+
22
+ from __future__ import annotations
23
+
24
+ import re
25
+ import warnings
26
+ from decimal import Decimal
27
+ from typing import TYPE_CHECKING
28
+
29
+ from urwid import Edit
30
+
31
+ if TYPE_CHECKING:
32
+ from collections.abc import Container
33
+
34
+
35
+ class NumEdit(Edit):
36
+ """NumEdit - edit numerical types
37
+
38
+ based on the characters in 'allowed' different numerical types
39
+ can be edited:
40
+ + regular int: 0123456789
41
+ + regular float: 0123456789.
42
+ + regular oct: 01234567
43
+ + regular hex: 0123456789abcdef
44
+ """
45
+
46
+ ALLOWED = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
47
+
48
+ def __init__(
49
+ self,
50
+ allowed: Container[str],
51
+ caption,
52
+ default: str | bytes,
53
+ trimLeadingZeros: bool | None = None,
54
+ *,
55
+ trim_leading_zeros: bool = True,
56
+ allow_negative: bool = False,
57
+ ):
58
+ super().__init__(caption, default)
59
+ self._allowed = allowed
60
+ self._trim_leading_zeros = trim_leading_zeros
61
+ self._allow_negative = allow_negative
62
+
63
+ if trimLeadingZeros is not None:
64
+ warnings.warn(
65
+ "'trimLeadingZeros' argument is deprecated. Use 'trim_leading_zeros' keyword argument",
66
+ DeprecationWarning,
67
+ stacklevel=3,
68
+ )
69
+ self._trim_leading_zeros = trimLeadingZeros
70
+
71
+ def valid_char(self, ch: str) -> bool:
72
+ """
73
+ Return true for allowed characters.
74
+ """
75
+ if len(ch) == 1:
76
+ if ch.upper() in self._allowed:
77
+ return True
78
+
79
+ return self._allow_negative and ch == "-" and self.edit_pos == 0 and "-" not in self.edit_text
80
+ return False
81
+
82
+ def keypress(self, size: tuple[int], key: str) -> str | None:
83
+ """
84
+ Handle editing keystrokes. Remove leading zeros.
85
+
86
+ >>> e, size = NumEdit("0123456789", "", "5002"), (10,)
87
+ >>> e.keypress(size, 'home')
88
+ >>> e.keypress(size, 'delete')
89
+ >>> assert e.edit_text == "002"
90
+ >>> e.keypress(size, 'end')
91
+ >>> assert e.edit_text == "2"
92
+ >>> # binary only
93
+ >>> e, size = NumEdit("01", "", ""), (10,)
94
+ >>> assert e.edit_text == ""
95
+ >>> e.keypress(size, '1')
96
+ >>> e.keypress(size, '0')
97
+ >>> e.keypress(size, '1')
98
+ >>> assert e.edit_text == "101"
99
+ >>> e, size = NumEdit("0123456789", "", "", allow_negative=True), (10,)
100
+ >>> e.keypress(size, "-")
101
+ >>> e.keypress(size, '1')
102
+ >>> e.edit_text
103
+ '-1'
104
+ >>> e.keypress(size, 'home')
105
+ >>> e.keypress(size, 'delete')
106
+ >>> e.edit_text
107
+ '1'
108
+ >>> e.keypress(size, 'end')
109
+ >>> e.keypress(size, "-")
110
+ '-'
111
+ >>> e.edit_text
112
+ '1'
113
+ """
114
+ unhandled = super().keypress(size, key)
115
+
116
+ if not unhandled and self._trim_leading_zeros:
117
+ # trim leading zeros
118
+ while self.edit_pos > 0 and self.edit_text[:1] == "0":
119
+ self.set_edit_pos(self.edit_pos - 1)
120
+ self.set_edit_text(self.edit_text[1:])
121
+
122
+ return unhandled
123
+
124
+
125
+ class IntegerEdit(NumEdit):
126
+ """Edit widget for integer values"""
127
+
128
+ def __init__(
129
+ self,
130
+ caption="",
131
+ default: int | str | Decimal | None = None,
132
+ base: int = 10,
133
+ *,
134
+ allow_negative: bool = False,
135
+ ) -> None:
136
+ """
137
+ caption -- caption markup
138
+ default -- default edit value
139
+
140
+ >>> IntegerEdit(u"", 42)
141
+ <IntegerEdit selectable flow widget '42' edit_pos=2>
142
+ >>> e, size = IntegerEdit(u"", "5002"), (10,)
143
+ >>> e.keypress(size, 'home')
144
+ >>> e.keypress(size, 'delete')
145
+ >>> assert e.edit_text == "002"
146
+ >>> e.keypress(size, 'end')
147
+ >>> assert e.edit_text == "2"
148
+ >>> e.keypress(size, '9')
149
+ >>> e.keypress(size, '0')
150
+ >>> assert e.edit_text == "290"
151
+ >>> e, size = IntegerEdit("", ""), (10,)
152
+ >>> assert e.value() is None
153
+ >>> # binary
154
+ >>> e, size = IntegerEdit(u"", "1010", base=2), (10,)
155
+ >>> e.keypress(size, 'end')
156
+ >>> e.keypress(size, '1')
157
+ >>> assert e.edit_text == "10101"
158
+ >>> assert e.value() == Decimal("21")
159
+ >>> # HEX
160
+ >>> e, size = IntegerEdit(u"", "10", base=16), (10,)
161
+ >>> e.keypress(size, 'end')
162
+ >>> e.keypress(size, 'F')
163
+ >>> e.keypress(size, 'f')
164
+ >>> assert e.edit_text == "10Ff"
165
+ >>> assert e.keypress(size, 'G') == 'G' # unhandled key
166
+ >>> assert e.edit_text == "10Ff"
167
+ >>> # keep leading 0's when not base 10
168
+ >>> e, size = IntegerEdit(u"", "10FF", base=16), (10,)
169
+ >>> assert e.edit_text == "10FF"
170
+ >>> assert e.value() == Decimal("4351")
171
+ >>> e.keypress(size, 'home')
172
+ >>> e.keypress(size, 'delete')
173
+ >>> e.keypress(size, '0')
174
+ >>> assert e.edit_text == "00FF"
175
+ >>> # test exception on incompatible value for base
176
+ >>> e, size = IntegerEdit(u"", "10FG", base=16), (10,)
177
+ Traceback (most recent call last):
178
+ ...
179
+ ValueError: invalid value: 10FG for base 16
180
+ >>> # test exception on float init value
181
+ >>> e, size = IntegerEdit(u"", 10.0), (10,)
182
+ Traceback (most recent call last):
183
+ ...
184
+ ValueError: default: Only 'str', 'int', 'long' or Decimal input allowed
185
+ >>> e, size = IntegerEdit(u"", Decimal("10.0")), (10,)
186
+ Traceback (most recent call last):
187
+ ...
188
+ ValueError: not an 'integer Decimal' instance
189
+ """
190
+ self.base = base
191
+ val = ""
192
+ allowed_chars = self.ALLOWED[: self.base]
193
+ if default is not None:
194
+ if not isinstance(default, (int, str, Decimal)):
195
+ raise ValueError("default: Only 'str', 'int' or Decimal input allowed")
196
+
197
+ # convert to a long first, this will raise a ValueError
198
+ # in case a float is passed or some other error
199
+ if isinstance(default, str) and len(default):
200
+ # check if it is a valid initial value
201
+ validation_re = f"^[{allowed_chars}]+$"
202
+ if not re.match(validation_re, str(default), re.IGNORECASE):
203
+ raise ValueError(f"invalid value: {default} for base {base}")
204
+
205
+ elif isinstance(default, Decimal) and default.as_tuple()[2] != 0:
206
+ # a Decimal instance with no fractional part
207
+ raise ValueError("not an 'integer Decimal' instance")
208
+
209
+ # convert possible int, long or Decimal to str
210
+ val = str(default)
211
+
212
+ super().__init__(
213
+ allowed_chars,
214
+ caption,
215
+ val,
216
+ trim_leading_zeros=(self.base == 10),
217
+ allow_negative=allow_negative,
218
+ )
219
+
220
+ def value(self) -> Decimal | None:
221
+ """
222
+ Return the numeric value of self.edit_text.
223
+
224
+ >>> e, size = IntegerEdit(), (10,)
225
+ >>> e.keypress(size, '5')
226
+ >>> e.keypress(size, '1')
227
+ >>> assert e.value() == 51
228
+ """
229
+ if self.edit_text:
230
+ return Decimal(int(self.edit_text, self.base))
231
+
232
+ return None
233
+
234
+ def __int__(self) -> int:
235
+ """Enforced int value return.
236
+
237
+ >>> e, size = IntegerEdit(allow_negative=True), (10,)
238
+ >>> assert int(e) == 0
239
+ >>> e.keypress(size, '-')
240
+ >>> e.keypress(size, '4')
241
+ >>> e.keypress(size, '2')
242
+ >>> assert int(e) == -42
243
+ """
244
+ if self.edit_text:
245
+ return int(self.edit_text, self.base)
246
+ return 0
247
+
248
+
249
+ class FloatEdit(NumEdit):
250
+ """Edit widget for float values."""
251
+
252
+ def __init__(
253
+ self,
254
+ caption="",
255
+ default: str | int | Decimal | None = None,
256
+ preserveSignificance: bool | None = None,
257
+ decimalSeparator: str | None = None,
258
+ *,
259
+ preserve_significance: bool = True,
260
+ decimal_separator: str = ".",
261
+ allow_negative: bool = False,
262
+ ) -> None:
263
+ """
264
+ caption -- caption markup
265
+ default -- default edit value
266
+ preserve_significance -- return value has the same signif. as default
267
+ decimal_separator -- use '.' as separator by default, optionally a ','
268
+
269
+ >>> FloatEdit(u"", "1.065434")
270
+ <FloatEdit selectable flow widget '1.065434' edit_pos=8>
271
+ >>> e, size = FloatEdit(u"", "1.065434"), (10,)
272
+ >>> e.keypress(size, 'home')
273
+ >>> e.keypress(size, 'delete')
274
+ >>> assert e.edit_text == ".065434"
275
+ >>> e.keypress(size, 'end')
276
+ >>> e.keypress(size, 'backspace')
277
+ >>> assert e.edit_text == ".06543"
278
+ >>> e, size = FloatEdit(), (10,)
279
+ >>> e.keypress(size, '5')
280
+ >>> e.keypress(size, '1')
281
+ >>> e.keypress(size, '.')
282
+ >>> e.keypress(size, '5')
283
+ >>> e.keypress(size, '1')
284
+ >>> assert e.value() == Decimal("51.51"), e.value()
285
+ >>> e, size = FloatEdit(decimal_separator=":"), (10,)
286
+ Traceback (most recent call last):
287
+ ...
288
+ ValueError: invalid decimal separator: :
289
+ >>> e, size = FloatEdit(decimal_separator=","), (10,)
290
+ >>> e.keypress(size, '5')
291
+ >>> e.keypress(size, '1')
292
+ >>> e.keypress(size, ',')
293
+ >>> e.keypress(size, '5')
294
+ >>> e.keypress(size, '1')
295
+ >>> assert e.edit_text == "51,51"
296
+ >>> e, size = FloatEdit("", "3.1415", preserve_significance=True), (10,)
297
+ >>> e.keypress(size, 'end')
298
+ >>> e.keypress(size, 'backspace')
299
+ >>> e.keypress(size, 'backspace')
300
+ >>> assert e.edit_text == "3.14"
301
+ >>> assert e.value() == Decimal("3.1400")
302
+ >>> e.keypress(size, '1')
303
+ >>> e.keypress(size, '5')
304
+ >>> e.keypress(size, '9')
305
+ >>> assert e.value() == Decimal("3.1416"), e.value()
306
+ >>> e, size = FloatEdit("", ""), (10,)
307
+ >>> assert e.value() is None
308
+ >>> e, size = FloatEdit(u"", 10.0), (10,)
309
+ Traceback (most recent call last):
310
+ ...
311
+ ValueError: default: Only 'str', 'int', 'long' or Decimal input allowed
312
+ """
313
+ self.significance = None
314
+ self._decimal_separator = decimal_separator
315
+ if decimalSeparator is not None:
316
+ warnings.warn(
317
+ "'decimalSeparator' argument is deprecated. Use 'decimal_separator' keyword argument",
318
+ DeprecationWarning,
319
+ stacklevel=3,
320
+ )
321
+ self._decimal_separator = decimalSeparator
322
+
323
+ if self._decimal_separator not in {".", ","}:
324
+ raise ValueError(f"invalid decimal separator: {self._decimal_separator}")
325
+
326
+ if preserveSignificance is not None:
327
+ warnings.warn(
328
+ "'preserveSignificance' argument is deprecated. Use 'preserve_significance' keyword argument",
329
+ DeprecationWarning,
330
+ stacklevel=3,
331
+ )
332
+ preserve_significance = preserveSignificance
333
+
334
+ val = ""
335
+ if default is not None and default != "": # noqa: PLC1901,RUF100
336
+ if not isinstance(default, (int, str, Decimal)):
337
+ raise ValueError("default: Only 'str', 'int' or Decimal input allowed")
338
+
339
+ if isinstance(default, str) and default:
340
+ # check if it is a float, raises a ValueError otherwise
341
+ float(default)
342
+ default = Decimal(default)
343
+
344
+ if preserve_significance and isinstance(default, Decimal):
345
+ self.significance = default
346
+
347
+ val = str(default)
348
+
349
+ super().__init__(self.ALLOWED[0:10] + self._decimal_separator, caption, val, allow_negative=allow_negative)
350
+
351
+ def value(self) -> Decimal | None:
352
+ """
353
+ Return the numeric value of self.edit_text.
354
+ """
355
+ if self.edit_text:
356
+ normalized = Decimal(self.edit_text.replace(self._decimal_separator, "."))
357
+ if self.significance is not None:
358
+ return normalized.quantize(self.significance)
359
+ return normalized
360
+
361
+ return None
362
+
363
+ def __float__(self) -> float:
364
+ """Enforced float value return.
365
+
366
+ >>> e, size = FloatEdit(allow_negative=True), (10,)
367
+ >>> assert float(e) == 0.
368
+ >>> e.keypress(size, '-')
369
+ >>> e.keypress(size, '4')
370
+ >>> e.keypress(size, '.')
371
+ >>> e.keypress(size, '2')
372
+ >>> assert float(e) == -4.2
373
+ """
374
+ if self.edit_text:
375
+ return float(self.edit_text.replace(self._decimal_separator, "."))
376
+ return 0.0