urwid 3.0.3__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/signals.py CHANGED
@@ -23,7 +23,6 @@ from __future__ import annotations
23
23
  import abc
24
24
  import itertools
25
25
  import typing
26
- import warnings
27
26
  import weakref
28
27
 
29
28
  if typing.TYPE_CHECKING:
@@ -99,9 +98,9 @@ class Signals:
99
98
  :type name: signal name
100
99
  :param callback: the function to call when that signal is sent
101
100
  :type callback: function
102
- :param user_arg: deprecated additional argument to callback (appended
103
- after the arguments passed when the signal is
104
- emitted). If None no arguments will be added.
101
+ :param user_arg: additional argument to callback
102
+ (appended after the arguments passed when the signal is emitted).
103
+ If None no arguments will be added.
105
104
  Don't use this argument, use user_args instead.
106
105
  :param weak_args: additional arguments passed to the callback
107
106
  (before any arguments passed when the signal
@@ -133,11 +132,11 @@ class Signals:
133
132
  As an example of using weak_args, consider the following snippet:
134
133
 
135
134
  >>> import urwid
136
- >>> debug = urwid.Text('')
135
+ >>> debug = urwid.Text("")
137
136
  >>> def handler(widget, newtext):
138
- ... debug.set_text("Edit widget changed to %s" % newtext)
139
- >>> edit = urwid.Edit('')
140
- >>> key = urwid.connect_signal(edit, 'change', handler)
137
+ ... debug.set_text("Edit widget changed to %s" % newtext)
138
+ >>> edit = urwid.Edit("")
139
+ >>> key = urwid.connect_signal(edit, "change", handler)
141
140
 
142
141
  If you now build some interface using "edit" and "debug", the
143
142
  "debug" widget will show whatever you type in the "edit" widget.
@@ -149,11 +148,11 @@ class Signals:
149
148
  (it's not really a closure, since it doesn't reference any
150
149
  outside variables, so it's just a dynamic function):
151
150
 
152
- >>> debug = urwid.Text('')
151
+ >>> debug = urwid.Text("")
153
152
  >>> def handler(weak_debug, widget, newtext):
154
- ... weak_debug.set_text("Edit widget changed to %s" % newtext)
155
- >>> edit = urwid.Edit('')
156
- >>> key = urwid.connect_signal(edit, 'change', handler, weak_args=[debug])
153
+ ... weak_debug.set_text("Edit widget changed to %s" % newtext)
154
+ >>> edit = urwid.Edit("")
155
+ >>> key = urwid.connect_signal(edit, "change", handler, weak_args=[debug])
157
156
 
158
157
  Here the weak_debug parameter in print_debug is the value passed
159
158
  in the weak_args list to connect_signal. Note that the
@@ -167,12 +166,6 @@ class Signals:
167
166
  handler can also be disconnected by calling
168
167
  urwid.disconnect_signal, which doesn't need this key.
169
168
  """
170
- if user_arg is not None:
171
- warnings.warn(
172
- "Don't use user_arg argument, use user_args instead.",
173
- DeprecationWarning,
174
- stacklevel=2,
175
- )
176
169
 
177
170
  sig_cls = obj.__class__
178
171
  if name not in self._supported.get(sig_cls, ()):
@@ -195,9 +188,8 @@ class Signals:
195
188
  # their callbacks) from existing.
196
189
  obj_weak = weakref.ref(obj)
197
190
 
198
- def weakref_callback(weakref): # pylint: disable=redefined-outer-name # bad, but not changing API
199
- o = obj_weak()
200
- if o:
191
+ def weakref_callback(_ref: weakref.ReferenceType[typing.Any]) -> None:
192
+ if o := obj_weak():
201
193
  self.disconnect_by_key(o, name, key)
202
194
 
203
195
  user_args = self._prepare_user_args(weak_args, user_args, weakref_callback)
@@ -209,7 +201,7 @@ class Signals:
209
201
  self,
210
202
  weak_args: Iterable[typing.Any] = (),
211
203
  user_args: Iterable[typing.Any] = (),
212
- callback: Callable[..., typing.Any] | None = None,
204
+ callback: Callable[[weakref.ReferenceType[typing.Any]], typing.Any] | None = None,
213
205
  ) -> tuple[Collection[weakref.ReferenceType], Collection[typing.Any]]:
214
206
  # Turn weak_args into weakrefs and prepend them to user_args
215
207
  w_args = tuple(weakref.ref(w_arg, callback) for w_arg in weak_args)
@@ -303,14 +295,13 @@ class Signals:
303
295
  self,
304
296
  callback,
305
297
  user_arg: typing.Any,
306
- weak_args: Collection[weakref.ReferenceType],
307
- user_args: Collection[typing.Any],
298
+ weak_args: Iterable[weakref.ReferenceType],
299
+ user_args: Iterable[typing.Any],
308
300
  emit_args: Iterable[typing.Any],
309
301
  ) -> bool:
310
302
  args_to_pass = []
311
303
  for w_arg in weak_args:
312
- real_arg = w_arg()
313
- if real_arg is not None:
304
+ if (real_arg := w_arg()) is not None:
314
305
  args_to_pass.append(real_arg)
315
306
  else:
316
307
  # de-referenced
urwid/split_repr.py CHANGED
@@ -32,15 +32,18 @@ def split_repr(self):
32
32
 
33
33
  >>> class Foo(object):
34
34
  ... __repr__ = split_repr
35
+ ...
35
36
  ... def _repr_words(self):
36
37
  ... return ["words", "here"]
38
+ ...
37
39
  ... def _repr_attrs(self):
38
- ... return {'attrs': "appear too"}
40
+ ... return {"attrs": "appear too"}
39
41
  >>> Foo()
40
42
  <Foo words here attrs='appear too'>
41
43
  >>> class Bar(Foo):
42
44
  ... def _repr_words(self):
43
45
  ... return Foo._repr_words(self) + ["too"]
46
+ ...
44
47
  ... def _repr_attrs(self):
45
48
  ... return dict(Foo._repr_attrs(self), barttr=42)
46
49
  >>> Bar()
@@ -62,9 +65,9 @@ def normalize_repr(v):
62
65
  """
63
66
  Return dictionary repr sorted by keys, leave others unchanged
64
67
 
65
- >>> normalize_repr({1:2,3:4,5:6,7:8})
68
+ >>> normalize_repr({1: 2, 3: 4, 5: 6, 7: 8})
66
69
  '{1: 2, 3: 4, 5: 6, 7: 8}'
67
- >>> normalize_repr('foo')
70
+ >>> normalize_repr("foo")
68
71
  "'foo'"
69
72
  """
70
73
  if isinstance(v, dict):
@@ -88,9 +91,12 @@ def remove_defaults(d, fn):
88
91
  >>> class Foo(object):
89
92
  ... def __init__(self, a=1, b=2):
90
93
  ... self.values = a, b
94
+ ...
91
95
  ... __repr__ = split_repr
96
+ ...
92
97
  ... def _repr_words(self):
93
98
  ... return ["object"]
99
+ ...
94
100
  ... def _repr_attrs(self):
95
101
  ... d = dict(a=self.values[0], b=self.values[1])
96
102
  ... return remove_defaults(d, Foo.__init__)
urwid/str_util.py CHANGED
@@ -36,6 +36,16 @@ _byte_encoding: Literal["utf8", "narrow", "wide"] = "narrow"
36
36
 
37
37
 
38
38
  def get_char_width(char: str) -> Literal[0, 1, 2]:
39
+ """
40
+ Return the screen column width for a single character.
41
+
42
+ .. deprecated:: 3.0.4
43
+ """
44
+ warnings.warn(
45
+ "get_char_width is deprecated in favor of wcwidth.width",
46
+ DeprecationWarning,
47
+ stacklevel=2,
48
+ )
39
49
  if (width := wcwidth.wcwidth(char)) >= 0:
40
50
  return width
41
51
 
@@ -43,8 +53,37 @@ def get_char_width(char: str) -> Literal[0, 1, 2]:
43
53
 
44
54
 
45
55
  def get_width(o: int) -> Literal[0, 1, 2]:
46
- """Return the screen column width for unicode ordinal o."""
47
- return get_char_width(chr(o))
56
+ """
57
+ Return the screen column width for unicode ordinal o.
58
+
59
+ .. deprecated:: 3.0.4
60
+ """
61
+ warnings.warn(
62
+ "get_width is deprecated in favor of wcwidth.width",
63
+ DeprecationWarning,
64
+ stacklevel=2,
65
+ )
66
+ if (width := wcwidth.wcwidth(chr(o))) >= 0:
67
+ return width
68
+
69
+ return 0
70
+
71
+
72
+ def _decode_grapheme_at(text: bytes, start: int, end: int) -> tuple[str, int]:
73
+ """
74
+ Decode bytes starting at `start` to get the first grapheme cluster.
75
+
76
+ :param text: UTF-8 encoded bytes
77
+ :param start: starting byte position
78
+ :param end: ending byte position
79
+ :returns: (grapheme_string, next_byte_position)
80
+
81
+ Assumes caller provides valid UTF-8 byte boundaries.
82
+ """
83
+ decoded = text[start:end].decode("utf-8")
84
+ grapheme = next(wcwidth.iter_graphemes(decoded), "")
85
+ grapheme_bytes = grapheme.encode("utf-8")
86
+ return grapheme, start + len(grapheme_bytes)
48
87
 
49
88
 
50
89
  def decode_one(text: bytes | str, pos: int) -> tuple[int, int]:
@@ -157,23 +196,26 @@ def calc_string_text_pos(text: str, start_offs: int, end_offs: int, pref_col: in
157
196
  where start_offs is the offset into text assumed to be screen column 0
158
197
  and end_offs is the end of the range to search.
159
198
 
199
+ Iterates by grapheme clusters for emoji ZWJ sequences, flags,
200
+ combining characters, and other multi-codepoint unicode sequences.
201
+
160
202
  :param text: string
161
203
  :param start_offs: starting text position
162
204
  :param end_offs: ending text position
163
205
  :param pref_col: target column
164
206
  :returns: (position, actual_col)
165
-
166
- ..note:: this method is a simplified version of `wcwidth.wcswidth` and ideally should be in wcwidth package.
167
207
  """
168
208
  if start_offs > end_offs:
169
209
  raise ValueError((start_offs, end_offs))
170
210
 
171
211
  cols = 0
172
- for idx in range(start_offs, end_offs):
173
- width = get_char_width(text[idx])
174
- if width + cols > pref_col:
175
- return idx, cols
176
- cols += width
212
+ pos = start_offs
213
+ for grapheme in wcwidth.iter_graphemes(text[start_offs:end_offs]):
214
+ grapheme_width = wcwidth.width(grapheme, control_codes="ignore")
215
+ if grapheme_width + cols > pref_col:
216
+ return pos, cols
217
+ cols += grapheme_width
218
+ pos += len(grapheme)
177
219
 
178
220
  return end_offs, cols
179
221
 
@@ -198,16 +240,10 @@ def calc_text_pos(text: str | bytes, start_offs: int, end_offs: int, pref_col: i
198
240
  raise TypeError(text)
199
241
 
200
242
  if _byte_encoding == "utf8":
201
- i = start_offs
202
- sc = 0
203
- while i < end_offs:
204
- o, n = decode_one(text, i)
205
- w = get_width(o)
206
- if w + sc > pref_col:
207
- return i, sc
208
- i = n
209
- sc += w
210
- return i, sc
243
+ decoded = text[start_offs:end_offs].decode("utf-8")
244
+ str_pos, cols = calc_string_text_pos(decoded, 0, len(decoded), pref_col)
245
+ byte_offset = len(decoded[:str_pos].encode("utf-8"))
246
+ return start_offs + byte_offset, cols
211
247
 
212
248
  # "wide" and "narrow"
213
249
  i = start_offs + pref_col
@@ -225,19 +261,20 @@ def calc_width(text: str | bytes, start_offs: int, end_offs: int) -> int:
225
261
  text may be unicode or a byte string in the target _byte_encoding
226
262
 
227
263
  Some characters are wide (take two columns) and others affect the
228
- previous character (take zero columns). Use the widths table above
229
- to calculate the screen column width of text[start_offs:end_offs]
264
+ previous character (take zero columns), while others are grouped
265
+ in sequence by "grapheme boundaries" (Emoji, Skin tones, flags, etc).
230
266
  """
231
267
 
232
268
  if start_offs > end_offs:
233
- raise ValueError((start_offs, end_offs))
269
+ msg = f"{start_offs=} > {end_offs=}"
270
+ raise ValueError(msg)
234
271
 
235
272
  if isinstance(text, str):
236
- return sum(get_char_width(char) for char in text[start_offs:end_offs])
273
+ return wcwidth.width(text[start_offs:end_offs], control_codes="ignore")
237
274
 
238
275
  if _byte_encoding == "utf8":
239
276
  try:
240
- return sum(get_char_width(char) for char in text[start_offs:end_offs].decode("utf-8"))
277
+ return wcwidth.width(text[start_offs:end_offs].decode("utf-8"), control_codes="ignore")
241
278
  except UnicodeDecodeError as exc:
242
279
  warnings.warn(
243
280
  "`calc_width` with text encoded to bytes can produce incorrect results"
@@ -250,8 +287,8 @@ def calc_width(text: str | bytes, start_offs: int, end_offs: int) -> int:
250
287
  sc = 0
251
288
  while i < end_offs:
252
289
  o, i = decode_one(text, i)
253
- w = get_width(o)
254
- sc += w
290
+ if (w := wcwidth.wcwidth(chr(o))) > 0:
291
+ sc += w
255
292
  return sc
256
293
  # "wide", "narrow" or all printable ASCII, just return the character count
257
294
  return end_offs - start_offs
@@ -259,17 +296,22 @@ def calc_width(text: str | bytes, start_offs: int, end_offs: int) -> int:
259
296
 
260
297
  def is_wide_char(text: str | bytes, offs: int) -> bool:
261
298
  """
262
- Test if the character at offs within text is wide.
299
+ Test if the grapheme cluster at offs within text is wide (2 columns).
300
+
301
+ For Unicode strings, extracts the full grapheme cluster starting at offs
302
+ and checks if it renders as wide. This correctly handles multi-codepoint
303
+ graphemes like emoji ZWJ sequences and flags.
263
304
 
264
305
  text may be unicode or a byte string in the target _byte_encoding
265
306
  """
266
307
  if isinstance(text, str):
267
- return get_char_width(text[offs]) == 2
308
+ grapheme = next(wcwidth.iter_graphemes(text[offs:]))
309
+ return wcwidth.width(grapheme, control_codes="ignore") == 2
268
310
  if not isinstance(text, bytes):
269
311
  raise TypeError(text)
270
312
  if _byte_encoding == "utf8":
271
- o, _n = decode_one(text, offs)
272
- return get_width(o) == 2
313
+ grapheme, _ = _decode_grapheme_at(text, offs, len(text))
314
+ return wcwidth.width(grapheme, control_codes="ignore") == 2
273
315
  if _byte_encoding == "wide":
274
316
  return within_double_byte(text, offs, offs) == 1
275
317
  return False
@@ -277,19 +319,23 @@ def is_wide_char(text: str | bytes, offs: int) -> bool:
277
319
 
278
320
  def move_prev_char(text: str | bytes, start_offs: int, end_offs: int) -> int:
279
321
  """
280
- Return the position of the character before end_offs.
322
+ Return the position of the grapheme cluster before end_offs.
323
+
324
+ For Unicode strings, handle multi-codepoint, "grapheme clusters",
325
+ to better measure emoji ZWJ, flags, combining characters, skin tones.
281
326
  """
282
327
  if start_offs >= end_offs:
283
328
  raise ValueError((start_offs, end_offs))
284
329
  if isinstance(text, str):
285
- return end_offs - 1
330
+ return wcwidth.grapheme_boundary_before(text, end_offs)
286
331
  if not isinstance(text, bytes):
287
332
  raise TypeError(text)
288
333
  if _byte_encoding == "utf8":
289
- o = end_offs - 1
290
- while text[o] & 0xC0 == 0x80:
291
- o -= 1
292
- return o
334
+ decoded = text[start_offs:end_offs].decode("utf-8")
335
+ str_pos = len(decoded)
336
+ prev_str_pos = wcwidth.grapheme_boundary_before(decoded, str_pos)
337
+ prefix = decoded[:prev_str_pos]
338
+ return start_offs + len(prefix.encode("utf-8"))
293
339
  if _byte_encoding == "wide" and within_double_byte(text, start_offs, end_offs - 1) == 2:
294
340
  return end_offs - 2
295
341
  return end_offs - 1
@@ -297,19 +343,21 @@ def move_prev_char(text: str | bytes, start_offs: int, end_offs: int) -> int:
297
343
 
298
344
  def move_next_char(text: str | bytes, start_offs: int, end_offs: int) -> int:
299
345
  """
300
- Return the position of the character after start_offs.
346
+ Return the position of the next grapheme cluster after start_offs.
347
+
348
+ For Unicode strings, handle multi-codepoint, "grapheme clusters",
349
+ to better measure emoji ZWJ, flags, combining characters, skin tones.
301
350
  """
302
351
  if start_offs >= end_offs:
303
352
  raise ValueError((start_offs, end_offs))
304
353
  if isinstance(text, str):
305
- return start_offs + 1
354
+ grapheme = next(wcwidth.iter_graphemes(text[start_offs:end_offs]))
355
+ return start_offs + len(grapheme)
306
356
  if not isinstance(text, bytes):
307
357
  raise TypeError(text)
308
358
  if _byte_encoding == "utf8":
309
- o = start_offs + 1
310
- while o < end_offs and text[o] & 0xC0 == 0x80:
311
- o += 1
312
- return o
359
+ _, next_pos = _decode_grapheme_at(text, start_offs, end_offs)
360
+ return next_pos
313
361
  if _byte_encoding == "wide" and within_double_byte(text, start_offs, start_offs) == 1:
314
362
  return start_offs + 2
315
363
  return start_offs + 1
urwid/text_layout.py CHANGED
@@ -23,7 +23,9 @@ from __future__ import annotations
23
23
  import functools
24
24
  import typing
25
25
 
26
- from urwid.str_util import calc_text_pos, calc_width, get_char_width, is_wide_char, move_next_char, move_prev_char
26
+ import wcwidth
27
+
28
+ from urwid.str_util import calc_text_pos, calc_width, is_wide_char, move_next_char, move_prev_char
27
29
  from urwid.util import calc_trim_text, get_encoding
28
30
 
29
31
  if typing.TYPE_CHECKING:
@@ -44,7 +46,7 @@ def get_ellipsis_string(encoding: str) -> str:
44
46
  @functools.lru_cache(maxsize=4)
45
47
  def _get_width(string) -> int:
46
48
  """Get ellipsis character width for given encoding."""
47
- return sum(get_char_width(char) for char in string)
49
+ return wcwidth.width(string, control_codes="ignore")
48
50
 
49
51
 
50
52
  class TextLayout:
@@ -176,7 +178,7 @@ class StandardTextLayout(TextLayout):
176
178
  self,
177
179
  text: str | bytes,
178
180
  width: int,
179
- wrap: Literal["any", "space", "clip", "ellipsis"] | WrapMode,
181
+ wrap: Literal["clip", "ellipsis", WrapMode.CLIP, WrapMode.ELLIPSIS],
180
182
  ) -> list[list[tuple[int, int, int | bytes] | tuple[int, int | None]]]:
181
183
  """Calculate text segments for cases of a text trimmed (wrap is clip or ellipsis)."""
182
184
  segments = []
@@ -185,8 +187,8 @@ class StandardTextLayout(TextLayout):
185
187
  encoding = get_encoding()
186
188
  ellipsis_string = get_ellipsis_string(encoding)
187
189
  ellipsis_width = _get_width(ellipsis_string)
188
- while width - 1 < ellipsis_width and ellipsis_string:
189
- ellipsis_string = ellipsis_string[:-1]
190
+ if (extra := width - ellipsis_width - 1) < 0:
191
+ ellipsis_string = ellipsis_string[:extra]
190
192
  ellipsis_width = _get_width(ellipsis_string)
191
193
 
192
194
  ellipsis_char = ellipsis_string.encode(encoding)
@@ -236,7 +238,7 @@ class StandardTextLayout(TextLayout):
236
238
  """
237
239
  Calculate the segments of text to display given width screen columns to display them.
238
240
 
239
- text - unicode text or byte string to display
241
+ text - Unicode text or byte string to display
240
242
  width - number of available screen columns
241
243
  wrap - wrapping mode used
242
244
 
urwid/util.py CHANGED
@@ -510,7 +510,7 @@ def int_scale(val: int, val_range: int, out_range: int) -> int:
510
510
 
511
511
  >>> "%x" % int_scale(0x7, 0x10, 0x10000)
512
512
  '7777'
513
- >>> "%x" % int_scale(0x5f, 0x100, 0x10)
513
+ >>> "%x" % int_scale(0x5F, 0x100, 0x10)
514
514
  '6'
515
515
  >>> int_scale(2, 6, 101)
516
516
  40
urwid/version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '3.0.3'
32
- __version_tuple__ = version_tuple = (3, 0, 3)
31
+ __version__ = version = '3.0.5'
32
+ __version_tuple__ = version_tuple = (3, 0, 5)
33
33
 
34
34
  __commit_id__ = commit_id = None
urwid/vterm.py CHANGED
@@ -1418,8 +1418,8 @@ class TermCanvas(Canvas):
1418
1418
  if self.scrolling_up == 0:
1419
1419
  yield from self.term
1420
1420
  else:
1421
- buf = self.scrollback_buffer + self.term
1422
- 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]
1423
1423
 
1424
1424
  def content_delta(self, other: Canvas):
1425
1425
  if other is self:
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())
urwid/widget/big_text.py CHANGED
@@ -78,9 +78,6 @@ class BigText(Widget):
78
78
  c.fill_attr(a)
79
79
  o.append((c, None, False, width))
80
80
 
81
- else:
82
- # ignore invalid characters
83
- continue
84
81
  if o:
85
82
  canv = CanvasJoin(o)
86
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():
@@ -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
urwid/widget/divider.py CHANGED
@@ -59,9 +59,9 @@ class Divider(Widget):
59
59
 
60
60
  >>> Divider()
61
61
  <Divider flow widget>
62
- >>> Divider(u'-')
62
+ >>> Divider("-")
63
63
  <Divider flow widget '-'>
64
- >>> Divider(u'x', 1, 2)
64
+ >>> Divider("x", 1, 2)
65
65
  <Divider flow widget 'x' bottom=2 top=1>
66
66
  """
67
67
  super().__init__()
@@ -86,7 +86,7 @@ class Divider(Widget):
86
86
 
87
87
  >>> Divider().rows((10,))
88
88
  1
89
- >>> Divider(u'x', 1, 2).rows((10,))
89
+ >>> Divider("x", 1, 2).rows((10,))
90
90
  4
91
91
  """
92
92
  (_maxcol,) = size
@@ -100,11 +100,11 @@ class Divider(Widget):
100
100
  """
101
101
  Render the divider as a canvas and return it.
102
102
 
103
- >>> Divider().render((10,)).text # ... = b in Python 3
103
+ >>> Divider().render((10,)).text # ... = b in Python 3
104
104
  [...' ']
105
- >>> Divider(u'-', top=1).render((10,)).text
105
+ >>> Divider("-", top=1).render((10,)).text
106
106
  [...' ', ...'----------']
107
- >>> Divider(u'x', bottom=2).render((5,)).text
107
+ >>> Divider("x", bottom=2).render((5,)).text
108
108
  [...'xxxxx', ...' ', ...' ']
109
109
  """
110
110
  (maxcol,) = size