summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Stepanov <penguinolog@users.noreply.github.com>2023-03-31 17:08:50 +0200
committerGitHub <noreply@github.com>2023-03-31 17:08:50 +0200
commitea7e615ef5ee0897f66210f47ab6390889d7b313 (patch)
treed627570bacd90fb347f684c76c52b6c1065a9c41
parent07621f1c8cc8bd88e55a3c3e48861c65f293b3f1 (diff)
downloadurwid-ea7e615ef5ee0897f66210f47ab6390889d7b313.tar.gz
Python 37+ initial migration (#522)
* Initial migration to the python 3.7: Semi-automatic changes CI related: Update `tox.ini` and `.travis.yml` to run python3 only tests Python 3.11 tests is commented-out on travis until #517 is not merged Manual changes: * `setup.py`: classifiers, remove python2 compatibility code * `docs/manual/wcur2.py`: looks like file was never completed, syntax is invalid * `urwid.compat`: removed `ord2`, `bytes3`, `text_type`, `xrange` and `text_types` Automatic changes (no manual editing, AST validated equality: * removed `u` prefix from all strings: not allowed in modern python code * `bytes()` -> `b''` * `== None` -> `is None` * subclassing of `object` * `super(<Class>`, self>)` ->`super()` * `from __future__ import ...` python3 compatibility imports * `set(<Iterable[Hashable]>)` -> `{<Hashable>}` * partial f-strings conversion * (`IOError`, `select.error`, `socket.error`) -> `OSError` * Switch to f-strings (automatic changes) * Remove `urwid.compat.B` * Remove `urwid.compat.with_metaclass` * use native `super()` instead of `self.__super` * Remove `urwid.compat.chr2` * Remove `urwid.split_repr.python3_repr` * Use native `@classmethod` and `@property` where overload is not possible * Add `from __future__ import annotations` * automatically sort imports * Add DeprecationWarning to the deprecated methods most IDE's will recognize it and annotate during new code usage call with "warnings as errors" mode will help to refactor other users * Address comments * replace homepage address in all files * remove outdated comments in compat.py * make wcur2.py correct python code. For example subclass * replace `self.__super` by `super()` in examples * fix asyncio_socket_server.py: magic with `asyncio` became wrong * Remove `widget.update_wrapper`: this was backport of python `functools.update_wrapper` * display_common.py: fix trivial typo in _colors calculation * use `sorted` method instead of list construction with later sorting * Address comments * `wcur2` include in docs * warning on `signals.Signals.emit` --------- Co-authored-by: Aleksei Stepanov <alekseis@nvidia.com>
-rw-r--r--.travis.yml16
-rw-r--r--docs/conf.py45
-rwxr-xr-xdocs/examples/browse.py6
-rwxr-xr-xdocs/examples/edit.py5
-rw-r--r--docs/examples/subproc2.py6
-rwxr-xr-xdocs/manual/bright_combinations.py11
-rwxr-xr-xdocs/manual/safe_combinations.py11
-rw-r--r--docs/manual/wanat.py3
-rw-r--r--docs/manual/wanat_multi.py3
-rw-r--r--docs/manual/wcur1.py3
-rw-r--r--docs/manual/wcur2.py10
-rw-r--r--docs/manual/widgets.rst1
-rw-r--r--docs/manual/wmod.py9
-rw-r--r--docs/manual/wsel.py3
-rw-r--r--docs/tutorial/adventure.py53
-rw-r--r--docs/tutorial/attr.py5
-rw-r--r--docs/tutorial/cmenu.py31
-rw-r--r--docs/tutorial/highcolors.py5
-rw-r--r--docs/tutorial/hmenu.py41
-rw-r--r--docs/tutorial/input.py5
-rw-r--r--docs/tutorial/minimal.py4
-rw-r--r--docs/tutorial/multiple.py11
-rw-r--r--docs/tutorial/qa.py10
-rw-r--r--docs/tutorial/sig.py10
-rw-r--r--docs/tutorial/smenu.py12
-rw-r--r--examples/asyncio_socket_server.py12
-rwxr-xr-xexamples/bigtext.py8
-rwxr-xr-xexamples/browse.py21
-rwxr-xr-xexamples/calc.py15
-rwxr-xr-xexamples/dialog.py12
-rwxr-xr-xexamples/edit.py6
-rwxr-xr-xexamples/fib.py5
-rwxr-xr-xexamples/graph.py12
-rwxr-xr-xexamples/input_test.py10
-rwxr-xr-xexamples/lcd_cf635.py15
-rwxr-xr-xexamples/palette_test.py9
-rwxr-xr-xexamples/pop_up.py7
-rwxr-xr-xexamples/subproc.py7
-rw-r--r--examples/subproc2.py8
-rwxr-xr-xexamples/terminal.py5
-rwxr-xr-xexamples/tour.py212
-rwxr-xr-xexamples/treesample.py7
-rw-r--r--examples/twisted_serve_ssh.py40
-rw-r--r--setup.py73
-rw-r--r--tox.ini4
-rw-r--r--urwid/__init__.py257
-rw-r--r--urwid/_async_kw_event_loop.py4
-rw-r--r--urwid/canvas.py64
-rw-r--r--urwid/command_map.py5
-rw-r--r--urwid/compat.py87
-rwxr-xr-xurwid/container.py152
-rwxr-xr-xurwid/curses_display.py57
-rwxr-xr-xurwid/decoration.py64
-rwxr-xr-xurwid/display_common.py144
-rw-r--r--urwid/escape.py82
-rwxr-xr-xurwid/font.py77
-rwxr-xr-xurwid/graphics.py87
-rwxr-xr-xurwid/html_fragment.py16
-rw-r--r--urwid/lcd_display.py49
-rw-r--r--urwid/listbox.py84
-rwxr-xr-xurwid/main_loop.py50
-rwxr-xr-xurwid/monitored_list.py58
-rw-r--r--urwid/numedit.py24
-rwxr-xr-xurwid/old_str_util.py46
-rw-r--r--urwid/raw_display.py55
-rw-r--r--urwid/signals.py25
-rwxr-xr-xurwid/split_repr.py47
-rw-r--r--urwid/tests/test_canvas.py168
-rw-r--r--urwid/tests/test_container.py77
-rw-r--r--urwid/tests/test_decoration.py14
-rw-r--r--urwid/tests/test_doctests.py5
-rw-r--r--urwid/tests/test_escapes.py3
-rw-r--r--urwid/tests/test_event_loops.py12
-rw-r--r--urwid/tests/test_floatedit.py3
-rw-r--r--urwid/tests/test_graphics.py23
-rw-r--r--urwid/tests/test_listbox.py37
-rw-r--r--urwid/tests/test_signals.py11
-rw-r--r--urwid/tests/test_str_util.py9
-rw-r--r--urwid/tests/test_text_layout.py52
-rw-r--r--urwid/tests/test_util.py47
-rw-r--r--urwid/tests/test_vterm.py134
-rw-r--r--urwid/tests/test_widget.py53
-rw-r--r--urwid/tests/util.py3
-rw-r--r--urwid/text_layout.py38
-rw-r--r--urwid/treetools.py23
-rw-r--r--urwid/util.py42
-rw-r--r--urwid/version.py2
-rw-r--r--urwid/vterm.py319
-rwxr-xr-xurwid/web_display.py60
-rw-r--r--urwid/widget.py268
-rwxr-xr-xurwid/wimp.py58
91 files changed, 2072 insertions, 1730 deletions
diff --git a/.travis.yml b/.travis.yml
index 74f091f..17e452e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,19 +1,17 @@
language: python
-dist: bionic
+#dist: bionic
matrix:
include:
- - python: 2.7
- env: TOXENV=py27
- - python: 3.5
- env: TOXENV=py35
- - python: 3.6
- env: TOXENV=py36
- python: 3.7
env: TOXENV=py37
- python: 3.8
env: TOXENV=py38
- - python: pypy2
- env: TOXENV=pypy
+ - python: 3.9
+ env: TOXENV=py39
+ - python: 3.10
+ env: TOXENV=py310
+# - python: 3.11
+# env: TOXENV=py311
before_install:
- "sudo apt-get update"
- "sudo apt-get install python-gi python3-gi"
diff --git a/docs/conf.py b/docs/conf.py
index 161a7d3..ba82ef2 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Urwid documentation build configuration file, created by
# sphinx-quickstart on Wed Nov 30 20:10:17 2011.
@@ -11,7 +10,9 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-import sys, os
+from __future__ import annotations
+
+import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -40,24 +41,20 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
-project = u'Urwid'
-copyright = u'2014, Ian Ward et al'
+project = 'Urwid'
+copyright = '2014, Ian Ward et al'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
-if sys.version_info > (3, 0):
- FILE_PATH = os.path.dirname(__file__)
-else:
- FILE_PATH = os.path.dirname(__file__).decode('utf-8')
+FILE_PATH = os.path.dirname(__file__)
VERSION_MODULE = os.path.abspath(os.path.join(FILE_PATH,
'../urwid/version.py'))
VERSION_VARS = {}
-if sys.version_info > (3, 0):
- def execfile(module, global_vars):
- with open(module) as f:
- code = compile(f.read(), "somefile.py", 'exec')
- exec(code, global_vars)
+def execfile(module, global_vars):
+ with open(module) as f:
+ code = compile(f.read(), "somefile.py", 'exec')
+ exec(code, global_vars)
execfile(VERSION_MODULE, VERSION_VARS)
# The short X.Y version.
version = '.'.join([str(x) for x in VERSION_VARS['VERSION'][:2]])
@@ -127,7 +124,7 @@ html_theme_options = {
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
-html_title = "Urwid %s" % (release,)
+html_title = f"Urwid {release}"
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
@@ -211,8 +208,8 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
- ('index', 'Urwid.tex', u'Urwid Documentation',
- u'Ian Ward', 'manual'),
+ ('index', 'Urwid.tex', 'Urwid Documentation',
+ 'Ian Ward', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -241,8 +238,8 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- ('index', 'urwid', u'Urwid Documentation',
- [u'Ian Ward'], 1)
+ ('index', 'urwid', 'Urwid Documentation',
+ ['Ian Ward'], 1)
]
# If true, show URL addresses after external links.
@@ -255,8 +252,8 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- ('index', 'Urwid', u'Urwid Documentation',
- u'Ian Ward', 'Urwid', 'One line description of project.',
+ ('index', 'Urwid', 'Urwid Documentation',
+ 'Ian Ward', 'Urwid', 'One line description of project.',
'Miscellaneous'),
]
@@ -273,10 +270,10 @@ texinfo_documents = [
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
-epub_title = u'Urwid'
-epub_author = u'Ian Ward'
-epub_publisher = u'Ian Ward'
-epub_copyright = u'2014, Ian Ward et al'
+epub_title = 'Urwid'
+epub_author = 'Ian Ward'
+epub_publisher = 'Ian Ward'
+epub_copyright = '2014, Ian Ward et al'
# The language of the text. It defaults to the language option
# or en if the language is not set.
diff --git a/docs/examples/browse.py b/docs/examples/browse.py
index 2bbcc34..6d64cac 100755
--- a/docs/examples/browse.py
+++ b/docs/examples/browse.py
@@ -1,6 +1,10 @@
#!/usr/bin/env python
-import real_browse
+from __future__ import annotations
+
import os
+
+import real_browse
+
os.chdir('/usr/share/doc/python')
real_browse.main()
diff --git a/docs/examples/edit.py b/docs/examples/edit.py
index 7eacc86..18c3066 100755
--- a/docs/examples/edit.py
+++ b/docs/examples/edit.py
@@ -1,7 +1,10 @@
#!/usr/bin/env python
-import sys
+from __future__ import annotations
+
import os
+import sys
+
import real_edit
real_edit.EditDisplay(os.path.join(
diff --git a/docs/examples/subproc2.py b/docs/examples/subproc2.py
index 76c8090..003ffad 100644
--- a/docs/examples/subproc2.py
+++ b/docs/examples/subproc2.py
@@ -1,10 +1,12 @@
+from __future__ import annotations
import time
+
time.sleep(1)
-print """factor: 1
+print("""factor: 1
factor: 1000003
factor: 2000029
factor: 3000073
factor: 4000159
-factor: 5000101"""
+factor: 5000101""")
diff --git a/docs/manual/bright_combinations.py b/docs/manual/bright_combinations.py
index 4ff350c..c566b20 100755
--- a/docs/manual/bright_combinations.py
+++ b/docs/manual/bright_combinations.py
@@ -1,5 +1,7 @@
#!/usr/bin/env python
+from __future__ import annotations
+
import urwid
RED_FGS = ('black', 'light gray', 'white', 'light cyan', 'light red',
@@ -27,8 +29,13 @@ for bg, fgs in BG_FGS:
body.append(s(urwid.Divider()))
body.append(s(
urwid.GridFlow(
- [urwid.AttrMap(urwid.Text("'{0}' on '{1}'".format(fg, bg)),
- urwid.AttrSpec(fg, bg)) for fg in fgs],
+ [
+ urwid.AttrMap(
+ urwid.Text(f"'{fg}' on '{bg}'"),
+ urwid.AttrSpec(fg, bg)
+ )
+ for fg in fgs
+ ],
35, 0, 0, 'left')))
body.append(s(urwid.Divider()))
diff --git a/docs/manual/safe_combinations.py b/docs/manual/safe_combinations.py
index 5a02159..0da3337 100755
--- a/docs/manual/safe_combinations.py
+++ b/docs/manual/safe_combinations.py
@@ -1,5 +1,7 @@
#!/usr/bin/env python
+from __future__ import annotations
+
import urwid
BLACK_FGS = ('light gray', 'dark cyan', 'dark red', 'dark green',
@@ -30,8 +32,13 @@ for bg, fgs in BG_FGS:
body.append(s(urwid.Divider()))
body.append(s(
urwid.GridFlow(
- [urwid.AttrMap(urwid.Text("'{0}' on '{1}'".format(fg, bg)),
- urwid.AttrSpec(fg, bg)) for fg in fgs],
+ [
+ urwid.AttrMap(
+ urwid.Text(f"'{fg}' on '{bg}'"),
+ urwid.AttrSpec(fg, bg)
+ )
+ for fg in fgs
+ ],
35, 0, 0, 'left')))
body.append(s(urwid.Divider()))
diff --git a/docs/manual/wanat.py b/docs/manual/wanat.py
index 723eb90..caf5c21 100644
--- a/docs/manual/wanat.py
+++ b/docs/manual/wanat.py
@@ -1,5 +1,8 @@
+from __future__ import annotations
+
import urwid
+
class Pudding(urwid.Widget):
_sizing = frozenset(['flow'])
diff --git a/docs/manual/wanat_multi.py b/docs/manual/wanat_multi.py
index b6d6943..9a1db87 100644
--- a/docs/manual/wanat_multi.py
+++ b/docs/manual/wanat_multi.py
@@ -1,5 +1,8 @@
+from __future__ import annotations
+
import urwid
+
class MultiPudding(urwid.Widget):
_sizing = frozenset(['flow', 'box'])
diff --git a/docs/manual/wcur1.py b/docs/manual/wcur1.py
index 6bf97f7..0e177be 100644
--- a/docs/manual/wcur1.py
+++ b/docs/manual/wcur1.py
@@ -1,5 +1,8 @@
+from __future__ import annotations
+
import urwid
+
class CursorPudding(urwid.Widget):
_sizing = frozenset(['flow'])
_selectable = True
diff --git a/docs/manual/wcur2.py b/docs/manual/wcur2.py
index 1225419..28ee07a 100644
--- a/docs/manual/wcur2.py
+++ b/docs/manual/wcur2.py
@@ -1,7 +1,13 @@
- def get_pref_col(self, (maxcol,)):
+from __future__ import annotations
+
+import urwid
+
+
+class Widget(urwid.ListBox):
+ def get_pref_col(self, size):
return self.cursor_x
- def move_cursor_to_coords(self, (maxcol,), col, row):
+ def move_cursor_to_coords(self, size, col, row):
assert row == 0
self.cursor_x = col
return True
diff --git a/docs/manual/widgets.rst b/docs/manual/widgets.rst
index 9fabb97..d7da259 100644
--- a/docs/manual/widgets.rst
+++ b/docs/manual/widgets.rst
@@ -749,6 +749,7 @@ specified) then this method must move the cursor to that position and return
.. literalinclude:: wcur2.py
:linenos:
+ :lines: 7-
Widget Metaclass
diff --git a/docs/manual/wmod.py b/docs/manual/wmod.py
index a066a49..d25a070 100644
--- a/docs/manual/wmod.py
+++ b/docs/manual/wmod.py
@@ -1,11 +1,14 @@
+from __future__ import annotations
+
import urwid
+
class QuestionnaireItem(urwid.WidgetWrap):
def __init__(self):
self.options = []
- unsure = urwid.RadioButton(self.options, u"Unsure")
- yes = urwid.RadioButton(self.options, u"Yes")
- no = urwid.RadioButton(self.options, u"No")
+ unsure = urwid.RadioButton(self.options, "Unsure")
+ yes = urwid.RadioButton(self.options, "Yes")
+ no = urwid.RadioButton(self.options, "No")
display_widget = urwid.GridFlow([unsure, yes, no], 15, 3, 1, 'left')
urwid.WidgetWrap.__init__(self, display_widget)
diff --git a/docs/manual/wsel.py b/docs/manual/wsel.py
index 15683d8..2f7c057 100644
--- a/docs/manual/wsel.py
+++ b/docs/manual/wsel.py
@@ -1,5 +1,8 @@
+from __future__ import annotations
+
import urwid
+
class SelectablePudding(urwid.Widget):
_sizing = frozenset(['flow'])
_selectable = True
diff --git a/docs/tutorial/adventure.py b/docs/tutorial/adventure.py
index 129ff50..0733cbe 100644
--- a/docs/tutorial/adventure.py
+++ b/docs/tutorial/adventure.py
@@ -1,17 +1,20 @@
+from __future__ import annotations
+
import urwid
+
class ActionButton(urwid.Button):
def __init__(self, caption, callback):
- super(ActionButton, self).__init__("")
+ super().__init__("")
urwid.connect_signal(self, 'click', callback)
self._w = urwid.AttrMap(urwid.SelectableIcon(caption, 1),
None, focus_map='reversed')
class Place(urwid.WidgetWrap):
def __init__(self, name, choices):
- super(Place, self).__init__(
- ActionButton([u" > go to ", name], self.enter_place))
- self.heading = urwid.Text([u"\nLocation: ", name, "\n"])
+ super().__init__(
+ ActionButton([" > go to ", name], self.enter_place))
+ self.heading = urwid.Text(["\nLocation: ", name, "\n"])
self.choices = choices
# create links back to ourself
for child in choices:
@@ -22,41 +25,41 @@ class Place(urwid.WidgetWrap):
class Thing(urwid.WidgetWrap):
def __init__(self, name):
- super(Thing, self).__init__(
- ActionButton([u" * take ", name], self.take_thing))
+ super().__init__(
+ ActionButton([" * take ", name], self.take_thing))
self.name = name
def take_thing(self, button):
- self._w = urwid.Text(u" - %s (taken)" % self.name)
+ self._w = urwid.Text(f" - {self.name} (taken)")
game.take_thing(self)
def exit_program(button):
raise urwid.ExitMainLoop()
-map_top = Place(u'porch', [
- Place(u'kitchen', [
- Place(u'refrigerator', []),
- Place(u'cupboard', [
- Thing(u'jug'),
+map_top = Place('porch', [
+ Place('kitchen', [
+ Place('refrigerator', []),
+ Place('cupboard', [
+ Thing('jug'),
]),
]),
- Place(u'garden', [
- Place(u'tree', [
- Thing(u'lemon'),
- Thing(u'bird'),
+ Place('garden', [
+ Place('tree', [
+ Thing('lemon'),
+ Thing('bird'),
]),
]),
- Place(u'street', [
- Place(u'store', [
- Thing(u'sugar'),
+ Place('street', [
+ Place('store', [
+ Thing('sugar'),
]),
- Place(u'lake', [
- Place(u'beach', []),
+ Place('lake', [
+ Place('beach', []),
]),
]),
])
-class AdventureGame(object):
+class AdventureGame:
def __init__(self):
self.log = urwid.SimpleFocusListWalker([])
self.top = urwid.ListBox(self.log)
@@ -72,9 +75,9 @@ class AdventureGame(object):
def take_thing(self, thing):
self.inventory.add(thing.name)
- if self.inventory >= set([u'sugar', u'lemon', u'jug']):
- response = urwid.Text(u'You can make lemonade!\n')
- done = ActionButton(u' - Joy', exit_program)
+ if self.inventory >= {'sugar', 'lemon', 'jug'}:
+ response = urwid.Text('You can make lemonade!\n')
+ done = ActionButton(' - Joy', exit_program)
self.log[:] = [response, done]
else:
self.update_place(self.place)
diff --git a/docs/tutorial/attr.py b/docs/tutorial/attr.py
index ce27171..9a6d37a 100644
--- a/docs/tutorial/attr.py
+++ b/docs/tutorial/attr.py
@@ -1,5 +1,8 @@
+from __future__ import annotations
+
import urwid
+
def exit_on_q(key):
if key in ('q', 'Q'):
raise urwid.ExitMainLoop()
@@ -9,7 +12,7 @@ palette = [
('streak', 'black', 'dark red'),
('bg', 'black', 'dark blue'),]
-txt = urwid.Text(('banner', u" Hello World "), align='center')
+txt = urwid.Text(('banner', " Hello World "), align='center')
map1 = urwid.AttrMap(txt, 'streak')
fill = urwid.Filler(map1)
map2 = urwid.AttrMap(fill, 'bg')
diff --git a/docs/tutorial/cmenu.py b/docs/tutorial/cmenu.py
index 65c69c0..22c4072 100644
--- a/docs/tutorial/cmenu.py
+++ b/docs/tutorial/cmenu.py
@@ -1,5 +1,8 @@
+from __future__ import annotations
+
import urwid
+
def menu_button(caption, callback):
button = urwid.Button(caption)
urwid.connect_signal(button, 'click', callback)
@@ -9,7 +12,7 @@ def sub_menu(caption, choices):
contents = menu(caption, choices)
def open_menu(button):
return top.open_box(contents)
- return menu_button([caption, u'...'], open_menu)
+ return menu_button([caption, '...'], open_menu)
def menu(title, choices):
body = [urwid.Text(title), urwid.Divider()]
@@ -17,25 +20,25 @@ def menu(title, choices):
return urwid.ListBox(urwid.SimpleFocusListWalker(body))
def item_chosen(button):
- response = urwid.Text([u'You chose ', button.label, u'\n'])
- done = menu_button(u'Ok', exit_program)
+ response = urwid.Text(['You chose ', button.label, '\n'])
+ done = menu_button('Ok', exit_program)
top.open_box(urwid.Filler(urwid.Pile([response, done])))
def exit_program(button):
raise urwid.ExitMainLoop()
-menu_top = menu(u'Main Menu', [
- sub_menu(u'Applications', [
- sub_menu(u'Accessories', [
- menu_button(u'Text Editor', item_chosen),
- menu_button(u'Terminal', item_chosen),
+menu_top = menu('Main Menu', [
+ sub_menu('Applications', [
+ sub_menu('Accessories', [
+ menu_button('Text Editor', item_chosen),
+ menu_button('Terminal', item_chosen),
]),
]),
- sub_menu(u'System', [
- sub_menu(u'Preferences', [
- menu_button(u'Appearance', item_chosen),
+ sub_menu('System', [
+ sub_menu('Preferences', [
+ menu_button('Appearance', item_chosen),
]),
- menu_button(u'Lock Screen', item_chosen),
+ menu_button('Lock Screen', item_chosen),
]),
])
@@ -43,7 +46,7 @@ class CascadingBoxes(urwid.WidgetPlaceholder):
max_box_levels = 4
def __init__(self, box):
- super(CascadingBoxes, self).__init__(urwid.SolidFill(u'/'))
+ super().__init__(urwid.SolidFill('/'))
self.box_level = 0
self.open_box(box)
@@ -64,7 +67,7 @@ class CascadingBoxes(urwid.WidgetPlaceholder):
self.original_widget = self.original_widget[0]
self.box_level -= 1
else:
- return super(CascadingBoxes, self).keypress(size, key)
+ return super().keypress(size, key)
top = CascadingBoxes(menu_top)
urwid.MainLoop(top, palette=[('reversed', 'standout', '')]).run()
diff --git a/docs/tutorial/highcolors.py b/docs/tutorial/highcolors.py
index fd36395..5b6f4d8 100644
--- a/docs/tutorial/highcolors.py
+++ b/docs/tutorial/highcolors.py
@@ -1,5 +1,8 @@
+from __future__ import annotations
+
import urwid
+
def exit_on_q(key):
if key in ('q', 'Q'):
raise urwid.ExitMainLoop()
@@ -20,7 +23,7 @@ loop.widget.original_widget = urwid.Filler(urwid.Pile([]))
div = urwid.Divider()
outside = urwid.AttrMap(div, 'outside')
inside = urwid.AttrMap(div, 'inside')
-txt = urwid.Text(('banner', u" Hello World "), align='center')
+txt = urwid.Text(('banner', " Hello World "), align='center')
streak = urwid.AttrMap(txt, 'streak')
pile = loop.widget.base_widget # .base_widget skips the decorations
for item in [outside, inside, streak, inside, outside]:
diff --git a/docs/tutorial/hmenu.py b/docs/tutorial/hmenu.py
index c48f42b..6c4ed15 100644
--- a/docs/tutorial/hmenu.py
+++ b/docs/tutorial/hmenu.py
@@ -1,19 +1,22 @@
+from __future__ import annotations
+
import urwid
+
class MenuButton(urwid.Button):
def __init__(self, caption, callback):
- super(MenuButton, self).__init__("")
+ super().__init__("")
urwid.connect_signal(self, 'click', callback)
self._w = urwid.AttrMap(urwid.SelectableIcon(
- [u' \N{BULLET} ', caption], 2), None, 'selected')
+ [' \N{BULLET} ', caption], 2), None, 'selected')
class SubMenu(urwid.WidgetWrap):
def __init__(self, caption, choices):
- super(SubMenu, self).__init__(MenuButton(
- [caption, u"\N{HORIZONTAL ELLIPSIS}"], self.open_menu))
- line = urwid.Divider(u'\N{LOWER ONE QUARTER BLOCK}')
+ super().__init__(MenuButton(
+ [caption, "\N{HORIZONTAL ELLIPSIS}"], self.open_menu))
+ line = urwid.Divider('\N{LOWER ONE QUARTER BLOCK}')
listbox = urwid.ListBox(urwid.SimpleFocusListWalker([
- urwid.AttrMap(urwid.Text([u"\n ", caption]), 'heading'),
+ urwid.AttrMap(urwid.Text(["\n ", caption]), 'heading'),
urwid.AttrMap(line, 'line'),
urwid.Divider()] + choices + [urwid.Divider()]))
self.menu = urwid.AttrMap(listbox, 'options')
@@ -23,31 +26,31 @@ class SubMenu(urwid.WidgetWrap):
class Choice(urwid.WidgetWrap):
def __init__(self, caption):
- super(Choice, self).__init__(
+ super().__init__(
MenuButton(caption, self.item_chosen))
self.caption = caption
def item_chosen(self, button):
- response = urwid.Text([u' You chose ', self.caption, u'\n'])
- done = MenuButton(u'Ok', exit_program)
+ response = urwid.Text([' You chose ', self.caption, '\n'])
+ done = MenuButton('Ok', exit_program)
response_box = urwid.Filler(urwid.Pile([response, done]))
top.open_box(urwid.AttrMap(response_box, 'options'))
def exit_program(key):
raise urwid.ExitMainLoop()
-menu_top = SubMenu(u'Main Menu', [
- SubMenu(u'Applications', [
- SubMenu(u'Accessories', [
- Choice(u'Text Editor'),
- Choice(u'Terminal'),
+menu_top = SubMenu('Main Menu', [
+ SubMenu('Applications', [
+ SubMenu('Accessories', [
+ Choice('Text Editor'),
+ Choice('Terminal'),
]),
]),
- SubMenu(u'System', [
- SubMenu(u'Preferences', [
- Choice(u'Appearance'),
+ SubMenu('System', [
+ SubMenu('Preferences', [
+ Choice('Appearance'),
]),
- Choice(u'Lock Screen'),
+ Choice('Lock Screen'),
]),
])
@@ -67,7 +70,7 @@ focus_map = {
class HorizontalBoxes(urwid.Columns):
def __init__(self):
- super(HorizontalBoxes, self).__init__([], dividechars=1)
+ super().__init__([], dividechars=1)
def open_box(self, box):
if self.contents:
diff --git a/docs/tutorial/input.py b/docs/tutorial/input.py
index 62a61a7..52541d1 100644
--- a/docs/tutorial/input.py
+++ b/docs/tutorial/input.py
@@ -1,11 +1,14 @@
+from __future__ import annotations
+
import urwid
+
def show_or_exit(key):
if key in ('q', 'Q'):
raise urwid.ExitMainLoop()
txt.set_text(repr(key))
-txt = urwid.Text(u"Hello World")
+txt = urwid.Text("Hello World")
fill = urwid.Filler(txt, 'top')
loop = urwid.MainLoop(fill, unhandled_input=show_or_exit)
loop.run()
diff --git a/docs/tutorial/minimal.py b/docs/tutorial/minimal.py
index 98acefa..57bdb5f 100644
--- a/docs/tutorial/minimal.py
+++ b/docs/tutorial/minimal.py
@@ -1,6 +1,8 @@
+from __future__ import annotations
+
import urwid
-txt = urwid.Text(u"Hello World")
+txt = urwid.Text("Hello World")
fill = urwid.Filler(txt, 'top')
loop = urwid.MainLoop(fill)
loop.run()
diff --git a/docs/tutorial/multiple.py b/docs/tutorial/multiple.py
index b2bc8a4..ecca9f0 100644
--- a/docs/tutorial/multiple.py
+++ b/docs/tutorial/multiple.py
@@ -1,18 +1,21 @@
+from __future__ import annotations
+
import urwid
+
def question():
- return urwid.Pile([urwid.Edit(('I say', u"What is your name?\n"))])
+ return urwid.Pile([urwid.Edit(('I say', "What is your name?\n"))])
def answer(name):
- return urwid.Text(('I say', u"Nice to meet you, " + name + "\n"))
+ return urwid.Text(('I say', f"Nice to meet you, {name}\n"))
class ConversationListBox(urwid.ListBox):
def __init__(self):
body = urwid.SimpleFocusListWalker([question()])
- super(ConversationListBox, self).__init__(body)
+ super().__init__(body)
def keypress(self, size, key):
- key = super(ConversationListBox, self).keypress(size, key)
+ key = super().keypress(size, key)
if key != 'enter':
return key
name = self.focus[0].edit_text
diff --git a/docs/tutorial/qa.py b/docs/tutorial/qa.py
index b7d9817..dc9590a 100644
--- a/docs/tutorial/qa.py
+++ b/docs/tutorial/qa.py
@@ -1,5 +1,8 @@
+from __future__ import annotations
+
import urwid
+
def exit_on_q(key):
if key in ('q', 'Q'):
raise urwid.ExitMainLoop()
@@ -7,12 +10,11 @@ def exit_on_q(key):
class QuestionBox(urwid.Filler):
def keypress(self, size, key):
if key != 'enter':
- return super(QuestionBox, self).keypress(size, key)
+ return super().keypress(size, key)
self.original_widget = urwid.Text(
- u"Nice to meet you,\n%s.\n\nPress Q to exit." %
- edit.edit_text)
+ f"Nice to meet you,\n{edit.edit_text}.\n\nPress Q to exit.")
-edit = urwid.Edit(u"What is your name?\n")
+edit = urwid.Edit("What is your name?\n")
fill = QuestionBox(edit)
loop = urwid.MainLoop(fill, unhandled_input=exit_on_q)
loop.run()
diff --git a/docs/tutorial/sig.py b/docs/tutorial/sig.py
index c01c1b2..383553e 100644
--- a/docs/tutorial/sig.py
+++ b/docs/tutorial/sig.py
@@ -1,15 +1,17 @@
+from __future__ import annotations
+
import urwid
palette = [('I say', 'default,bold', 'default', 'bold'),]
-ask = urwid.Edit(('I say', u"What is your name?\n"))
-reply = urwid.Text(u"")
-button = urwid.Button(u'Exit')
+ask = urwid.Edit(('I say', "What is your name?\n"))
+reply = urwid.Text("")
+button = urwid.Button('Exit')
div = urwid.Divider()
pile = urwid.Pile([ask, div, reply, div, button])
top = urwid.Filler(pile, valign='top')
def on_ask_change(edit, new_edit_text):
- reply.set_text(('I say', u"Nice to meet you, %s" % new_edit_text))
+ reply.set_text(('I say', f"Nice to meet you, {new_edit_text}"))
def on_exit_clicked(button):
raise urwid.ExitMainLoop()
diff --git a/docs/tutorial/smenu.py b/docs/tutorial/smenu.py
index dea050e..115e1d3 100644
--- a/docs/tutorial/smenu.py
+++ b/docs/tutorial/smenu.py
@@ -1,6 +1,8 @@
+from __future__ import annotations
+
import urwid
-choices = u'Chapman Cleese Gilliam Idle Jones Palin'.split()
+choices = 'Chapman Cleese Gilliam Idle Jones Palin'.split()
def menu(title, choices):
body = [urwid.Text(title), urwid.Divider()]
@@ -11,8 +13,8 @@ def menu(title, choices):
return urwid.ListBox(urwid.SimpleFocusListWalker(body))
def item_chosen(button, choice):
- response = urwid.Text([u'You chose ', choice, u'\n'])
- done = urwid.Button(u'Ok')
+ response = urwid.Text(['You chose ', choice, '\n'])
+ done = urwid.Button('Ok')
urwid.connect_signal(done, 'click', exit_program)
main.original_widget = urwid.Filler(urwid.Pile([response,
urwid.AttrMap(done, None, focus_map='reversed')]))
@@ -20,8 +22,8 @@ def item_chosen(button, choice):
def exit_program(button):
raise urwid.ExitMainLoop()
-main = urwid.Padding(menu(u'Pythons', choices), left=2, right=2)
-top = urwid.Overlay(main, urwid.SolidFill(u'\N{MEDIUM SHADE}'),
+main = urwid.Padding(menu('Pythons', choices), left=2, right=2)
+top = urwid.Overlay(main, urwid.SolidFill('\N{MEDIUM SHADE}'),
align='center', width=('relative', 60),
valign='middle', height=('relative', 60),
min_width=20, min_height=9)
diff --git a/examples/asyncio_socket_server.py b/examples/asyncio_socket_server.py
index 2833faf..e629d74 100644
--- a/examples/asyncio_socket_server.py
+++ b/examples/asyncio_socket_server.py
@@ -4,22 +4,18 @@
This code works on older Python 3.x if you install `asyncio` from PyPI, and
even Python 2 if you install `trollius`!
"""
-from __future__ import print_function
-try:
- import asyncio
-except ImportError:
- import trollius as asyncio
+from __future__ import annotations
-from datetime import datetime
+import asyncio
+import logging
import sys
import weakref
+from datetime import datetime
import urwid
from urwid.raw_display import Screen
-from urwid.display_common import BaseScreen
-import logging
logging.basicConfig()
loop = asyncio.get_event_loop()
diff --git a/examples/bigtext.py b/examples/bigtext.py
index 6a088ea..2a19fe3 100755
--- a/examples/bigtext.py
+++ b/examples/bigtext.py
@@ -17,12 +17,14 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
"""
Urwid example demonstrating use of the BigText widget.
"""
+from __future__ import annotations
+
import urwid
import urwid.raw_display
@@ -58,7 +60,7 @@ class BigTextDisplay:
return w
def create_disabled_radio_button(self, name):
- w = urwid.Text(" " + name + " (UTF-8 mode required)")
+ w = urwid.Text(f" {name} (UTF-8 mode required)")
w = urwid.AttrWrap(w, 'button disabled')
return w
@@ -110,7 +112,7 @@ class BigTextDisplay:
chosen_font_rb.set_state(True) # causes set_font_event call
# Create Edit widget
- edit = self.create_edit("", "Urwid "+urwid.__version__,
+ edit = self.create_edit("", f"Urwid {urwid.__version__}",
self.edit_change_event)
# ListBox
diff --git a/examples/browse.py b/examples/browse.py
index 39ac27a..37315b1 100755
--- a/examples/browse.py
+++ b/examples/browse.py
@@ -19,7 +19,7 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
"""
Urwid example lazy directory browser / tree view
@@ -31,11 +31,12 @@ Features:
- outputs a quoted list of files and directories "selected" on exit
"""
-from __future__ import print_function
+
+from __future__ import annotations
import itertools
-import re
import os
+import re
import urwid
@@ -48,7 +49,7 @@ class FlagFileWidget(urwid.TreeWidget):
'dirmark')
def __init__(self, node):
- self.__super.__init__(node)
+ super().__init__(node)
# insert an extra AttrWrap for our own use
self._w = urwid.AttrWrap(self._w, None)
self.flagged = False
@@ -59,7 +60,7 @@ class FlagFileWidget(urwid.TreeWidget):
def keypress(self, size, key):
"""allow subclasses to intercept keystrokes"""
- key = self.__super.keypress(size, key)
+ key = super().keypress(size, key)
if key:
key = self.unhandled_keys(size, key)
return key
@@ -89,7 +90,7 @@ class FlagFileWidget(urwid.TreeWidget):
class FileTreeWidget(FlagFileWidget):
"""Widget for individual files."""
def __init__(self, node):
- self.__super.__init__(node)
+ super().__init__(node)
path = node.get_value()
add_widget(path, self)
@@ -114,7 +115,7 @@ class ErrorWidget(urwid.TreeWidget):
class DirectoryWidget(FlagFileWidget):
"""Widget for a directory."""
def __init__(self, node):
- self.__super.__init__(node)
+ super().__init__(node)
path = node.get_value()
add_widget(path, self)
self.expanded = starts_expanded(path)
@@ -350,7 +351,7 @@ def escape_filename_sh(name):
name.replace('"','\\"')
name.replace('`','\\`')
name.replace('$','\\$')
- return '"'+name+'"'
+ return f'"{name}"'
def escape_filename_sh_ansic(name):
@@ -360,14 +361,14 @@ def escape_filename_sh_ansic(name):
# gather the escaped characters into a list
for ch in name:
if ord(ch) < 32:
- out.append("\\x%02x"% ord(ch))
+ out.append(f"\\x{ord(ch):02x}")
elif ch == '\\':
out.append('\\\\')
else:
out.append(ch)
# slap them back together in an ansi-c quote $'...'
- return "$'" + "".join(out) + "'"
+ return f"$'{''.join(out)}'"
SPLIT_RE = re.compile(r'[a-zA-Z]+|\d+')
def alphabetize(s):
diff --git a/examples/calc.py b/examples/calc.py
index 7b47fe9..bf31ba6 100755
--- a/examples/calc.py
+++ b/examples/calc.py
@@ -17,7 +17,7 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
"""
Urwid advanced example column calculator application
@@ -31,7 +31,8 @@ Features:
- outputs commands that may be used to recreate expression on exit
"""
-from __future__ import print_function
+
+from __future__ import annotations
import urwid
import urwid.raw_display
@@ -137,7 +138,7 @@ class Cell:
self.edit = urwid.IntEdit()
if not self.is_top:
- self.edit.set_caption( self.op + " " )
+ self.edit.set_caption( f"{self.op} " )
self.edit.set_layout( None, None, CALC_LAYOUT )
def get_value(self):
@@ -146,7 +147,7 @@ class Cell:
if self.child is not None:
return self.child.get_result()
else:
- return int("0"+self.edit.edit_text)
+ return int(f"0{self.edit.edit_text}")
def get_result(self):
"""Return the numeric result of this cell's operation."""
@@ -203,9 +204,9 @@ class ParentEdit(urwid.Edit):
"""Set the letter of the child column for display."""
self.letter = letter
- caption = "("+letter+")"
+ caption = f"({letter})"
if self.op is not None:
- caption = self.op+" "+caption
+ caption = f"{self.op} {caption}"
self.set_caption(caption)
def keypress(self, size, key):
@@ -478,7 +479,7 @@ class CellColumn( urwid.WidgetWrap ):
if c.op is not None: # only applies to first cell
l.append(c.op)
if c.child is not None:
- l.append("("+c.child.get_expression()+")")
+ l.append(f"({c.child.get_expression()})")
else:
l.append("%d"%c.get_value())
diff --git a/examples/dialog.py b/examples/dialog.py
index b635933..ec8c8e0 100755
--- a/examples/dialog.py
+++ b/examples/dialog.py
@@ -17,13 +17,15 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
"""
Urwid example similar to dialog(1) program
"""
+from __future__ import annotations
+
import sys
import urwid
@@ -215,7 +217,7 @@ class CheckListDialogDisplay(ListDialogDisplay):
for i in self.items:
if i.get_state():
l.append(i.get_label())
- return exitcode, "".join(['"'+tag+'" ' for tag in l])
+ return exitcode, "".join([f'"{tag}" ' for tag in l])
@@ -307,8 +309,8 @@ def show_usage():
"""
Display a helpful usage message.
"""
- modelist = [(mode, help) for (mode, (fn, help)) in MODES.items()]
- modelist.sort()
+ modelist = sorted((mode, help) for (mode, (fn, help)) in MODES.items())
+
sys.stdout.write(
__doc__ +
"\n".join(["%-15s %s"%(mode,help) for (mode,help) in modelist])
@@ -334,7 +336,7 @@ def main():
# Exit
if exitstring:
- sys.stderr.write(exitstring+"\n")
+ sys.stderr.write(f"{exitstring}\n")
sys.exit(exitcode)
diff --git a/examples/edit.py b/examples/edit.py
index e2cc2fa..e3da36b 100755
--- a/examples/edit.py
+++ b/examples/edit.py
@@ -17,7 +17,7 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
"""
Urwid example lazy text editor suitable for tabbed and flowing text
@@ -30,6 +30,8 @@ edit.py <filename>
"""
+from __future__ import annotations
+
import sys
import urwid
@@ -230,7 +232,7 @@ def re_tab(s):
for i in range(8, len(s), 8):
if s[i-2:i] == " ":
# collapse two or more spaces into a tab
- l.append(s[p:i].rstrip() + "\t")
+ l.append(f"{s[p:i].rstrip()}\t")
p = i
if p == 0:
diff --git a/examples/fib.py b/examples/fib.py
index 875c8fc..47fc8c9 100755
--- a/examples/fib.py
+++ b/examples/fib.py
@@ -17,7 +17,7 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
"""
Urwid example fibonacci sequence viewer / unbounded data demo
@@ -27,8 +27,11 @@ Features:
- custom wrap mode "numeric" for wrapping numbers to right and bottom
"""
+from __future__ import annotations
+
import urwid
+
class FibonacciWalker(urwid.ListWalker):
"""ListWalker-compatible class for browsing fibonacci set.
diff --git a/examples/graph.py b/examples/graph.py
index 4a5b5dd..ad2bf15 100755
--- a/examples/graph.py
+++ b/examples/graph.py
@@ -17,7 +17,7 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
"""
Urwid example demonstrating use of the BarGraph widget and creating a
@@ -25,11 +25,13 @@ floating-window appearance. Also shows use of alarms to create timed
animation.
"""
-import urwid
+from __future__ import annotations
import math
import time
+import urwid
+
UPDATE_INTERVAL = 0.2
def sin100( x ):
@@ -203,8 +205,8 @@ class GraphView(urwid.WidgetWrap):
def main_shadow(self, w):
"""Wrap a shadow and background around widget w."""
- bg = urwid.AttrWrap(urwid.SolidFill(u"\u2592"), 'screen edge')
- shadow = urwid.AttrWrap(urwid.SolidFill(u" "), 'main shadow')
+ bg = urwid.AttrWrap(urwid.SolidFill("\u2592"), 'screen edge')
+ shadow = urwid.AttrWrap(urwid.SolidFill(" "), 'main shadow')
bg = urwid.Overlay( shadow, bg,
('fixed left', 3), ('fixed right', 1),
@@ -288,7 +290,7 @@ class GraphView(urwid.WidgetWrap):
def main_window(self):
self.graph = self.bar_graph()
self.graph_wrap = urwid.WidgetWrap( self.graph )
- vline = urwid.AttrWrap( urwid.SolidFill(u'\u2502'), 'line')
+ vline = urwid.AttrWrap( urwid.SolidFill('\u2502'), 'line')
c = self.graph_controls()
w = urwid.Columns([('weight',2,self.graph_wrap),
('fixed',1,vline), c],
diff --git a/examples/input_test.py b/examples/input_test.py
index 0a8eed9..121adc4 100755
--- a/examples/input_test.py
+++ b/examples/input_test.py
@@ -17,18 +17,20 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
"""
Keyboard test application
"""
+from __future__ import annotations
+
+import sys
+
+import urwid
import urwid.curses_display
import urwid.raw_display
import urwid.web_display
-import urwid
-
-import sys
if urwid.web_display.is_web_request():
Screen = urwid.web_display.Screen
diff --git a/examples/lcd_cf635.py b/examples/lcd_cf635.py
index c30a3d2..cd536bf 100755
--- a/examples/lcd_cf635.py
+++ b/examples/lcd_cf635.py
@@ -18,7 +18,10 @@ horizontal slider control, selected check box and selected radio button
respectively.
"""
+from __future__ import annotations
+
import sys
+
import urwid.lcd_display
CGRAM = """
@@ -136,7 +139,7 @@ class LCDHorizontalSlider(urwid.WidgetWrap):
self.bar,
('fixed', 1, urwid.SelectableIcon('\x04')),
])
- self.__super.__init__(cols)
+ super().__init__(cols)
self.callback = callback
def keypress(self, size, key):
@@ -147,7 +150,7 @@ class LCDHorizontalSlider(urwid.WidgetWrap):
self._w.get_focus_column() != 0)
self.callback(self.bar.value)
else:
- return self.__super.keypress(size, key)
+ return super().keypress(size, key)
@@ -156,7 +159,7 @@ class MenuOption(urwid.Button):
A menu option, indicated with a single arrow character
"""
def __init__(self, label, submenu):
- self.__super.__init__("")
+ super().__init__("")
# use a Text widget for label, we want the cursor
# on the arrow not the label
self._label = urwid.Text("")
@@ -172,19 +175,19 @@ class MenuOption(urwid.Button):
def keypress(self, size, key):
if key == 'right':
key = 'enter'
- return self.__super.keypress(size, key)
+ return super().keypress(size, key)
class Menu(urwid.ListBox):
def __init__(self, widgets):
self.menu_parent = None
- self.__super.__init__(urwid.SimpleListWalker(widgets))
+ super().__init__(urwid.SimpleListWalker(widgets))
def keypress(self, size, key):
"""
Go back to the previous menu on cancel button (mapped to esc)
"""
- key = self.__super.keypress(size, key)
+ key = super().keypress(size, key)
if key in ('left', 'esc') and self.menu_parent:
show_menu(self.menu_parent)
else:
diff --git a/examples/palette_test.py b/examples/palette_test.py
index 4fa5d19..d8cbfbc 100755
--- a/examples/palette_test.py
+++ b/examples/palette_test.py
@@ -17,15 +17,16 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
"""
Palette test. Shows the available foreground and background settings
in monochrome, 16 color, 88 color, 256 color, and 24-bit (true) color modes.
"""
+from __future__ import annotations
+
import re
-import sys
import urwid
import urwid.raw_display
@@ -281,12 +282,12 @@ def parse_chart(chart, convert):
entry = entry[elen:].strip()
else: # try the whole thing
attrtext = convert(entry.strip())
- assert attrtext, "Invalid palette entry: %r" % entry
+ assert attrtext, f"Invalid palette entry: {entry!r}"
elen = len(entry)
entry = ""
attr, text = attrtext
if chart == CHART_TRUE:
- out.append((attr, u"\u2584"))
+ out.append((attr, "\u2584"))
else:
out.append((attr, text.ljust(elen)))
return out
diff --git a/examples/pop_up.py b/examples/pop_up.py
index f88c717..a87c7e3 100755
--- a/examples/pop_up.py
+++ b/examples/pop_up.py
@@ -1,7 +1,10 @@
#!/usr/bin/env python
+from __future__ import annotations
+
import urwid
+
class PopUpDialog(urwid.WidgetWrap):
"""A dialog that appears with nothing but a close button """
signals = ['close']
@@ -13,12 +16,12 @@ class PopUpDialog(urwid.WidgetWrap):
"^^ I'm attached to the widget that opened me. "
"Try resizing the window!\n"), close_button])
fill = urwid.Filler(pile)
- self.__super.__init__(urwid.AttrWrap(fill, 'popbg'))
+ super().__init__(urwid.AttrWrap(fill, 'popbg'))
class ThingWithAPopUp(urwid.PopUpLauncher):
def __init__(self):
- self.__super.__init__(urwid.Button("click-me"))
+ super().__init__(urwid.Button("click-me"))
urwid.connect_signal(self.original_widget, 'click',
lambda button: self.open_pop_up())
diff --git a/examples/subproc.py b/examples/subproc.py
index f04e7b0..b078219 100755
--- a/examples/subproc.py
+++ b/examples/subproc.py
@@ -1,10 +1,13 @@
#!/usr/bin/env python
-import subprocess
-import urwid
+from __future__ import annotations
+
import os
+import subprocess
import sys
+import urwid
+
factor_me = 362923067964327863989661926737477737673859044111968554257667
run_me = os.path.join(os.path.dirname(sys.argv[0]), 'subproc2.py')
diff --git a/examples/subproc2.py b/examples/subproc2.py
index a60aa72..39c0819 100644
--- a/examples/subproc2.py
+++ b/examples/subproc2.py
@@ -1,14 +1,10 @@
#!/usr/bin/env python
# this is part of the subproc.py example
-from __future__ import print_function
-import sys
+from __future__ import annotations
-try:
- range = xrange
-except NameError:
- pass
+import sys
num = int(sys.argv[1])
for c in range(1,10000000):
diff --git a/examples/terminal.py b/examples/terminal.py
index c50656a..1fd952f 100755
--- a/examples/terminal.py
+++ b/examples/terminal.py
@@ -17,10 +17,13 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
+
+from __future__ import annotations
import urwid
+
def main():
urwid.set_encoding('utf8')
term = urwid.Terminal(None, encoding='utf-8')
diff --git a/examples/tour.py b/examples/tour.py
index 7e6649f..7bc62c6 100755
--- a/examples/tour.py
+++ b/examples/tour.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
#
# Urwid tour. It slices, it dices..
# Copyright (C) 2004-2011 Ian Ward
@@ -18,125 +17,128 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
"""
Urwid tour. Shows many of the standard widget types and features.
"""
+from __future__ import annotations
+
import urwid
import urwid.raw_display
import urwid.web_display
+
def main():
- text_header = (u"Welcome to the urwid tour! "
- u"UP / DOWN / PAGE UP / PAGE DOWN scroll. F8 exits.")
- text_intro = [('important', u"Text"),
- u" widgets are the most common in "
- u"any urwid program. This Text widget was created "
- u"without setting the wrap or align mode, so it "
- u"defaults to left alignment with wrapping on space "
- u"characters. ",
- ('important', u"Change the window width"),
- u" to see how the widgets on this page react. "
- u"This Text widget is wrapped with a ",
- ('important', u"Padding"),
- u" widget to keep it indented on the left and right."]
- text_right = (u"This Text widget is right aligned. Wrapped "
- u"words stay to the right as well. ")
- text_center = u"This one is center aligned."
- text_clip = (u"Text widgets may be clipped instead of wrapped.\n"
- u"Extra text is discarded instead of wrapped to the next line. "
- u"65-> 70-> 75-> 80-> 85-> 90-> 95-> 100>\n"
- u"Newlines embedded in the string are still respected.")
- text_right_clip = (u"This is a right aligned and clipped Text widget.\n"
- u"<100 <-95 <-90 <-85 <-80 <-75 <-70 <-65 "
- u"Text will be cut off at the left of this widget.")
- text_center_clip = (u"Center aligned and clipped widgets will have "
- u"text cut off both sides.")
- text_ellipsis = (u"Text can be clipped using the ellipsis character (…)\n"
- u"Extra text is discarded and a … mark is shown."
- u"50-> 55-> 60-> 65-> 70-> 75-> 80-> 85-> 90-> 95-> 100>\n"
+ text_header = ("Welcome to the urwid tour! "
+ "UP / DOWN / PAGE UP / PAGE DOWN scroll. F8 exits.")
+ text_intro = [('important', "Text"),
+ " widgets are the most common in "
+ "any urwid program. This Text widget was created "
+ "without setting the wrap or align mode, so it "
+ "defaults to left alignment with wrapping on space "
+ "characters. ",
+ ('important', "Change the window width"),
+ " to see how the widgets on this page react. "
+ "This Text widget is wrapped with a ",
+ ('important', "Padding"),
+ " widget to keep it indented on the left and right."]
+ text_right = ("This Text widget is right aligned. Wrapped "
+ "words stay to the right as well. ")
+ text_center = "This one is center aligned."
+ text_clip = ("Text widgets may be clipped instead of wrapped.\n"
+ "Extra text is discarded instead of wrapped to the next line. "
+ "65-> 70-> 75-> 80-> 85-> 90-> 95-> 100>\n"
+ "Newlines embedded in the string are still respected.")
+ text_right_clip = ("This is a right aligned and clipped Text widget.\n"
+ "<100 <-95 <-90 <-85 <-80 <-75 <-70 <-65 "
+ "Text will be cut off at the left of this widget.")
+ text_center_clip = ("Center aligned and clipped widgets will have "
+ "text cut off both sides.")
+ text_ellipsis = ("Text can be clipped using the ellipsis character (…)\n"
+ "Extra text is discarded and a … mark is shown."
+ "50-> 55-> 60-> 65-> 70-> 75-> 80-> 85-> 90-> 95-> 100>\n"
)
- text_any = (u"The 'any' wrap mode will wrap on any character. This "
- u"mode will not collapse space characters at the end of the "
- u"line but it still honors embedded newline characters.\n"
- u"Like this one.")
- text_padding = (u"Padding widgets have many options. This "
- u"is a standard Text widget wrapped with a Padding widget "
- u"with the alignment set to relative 20% and with its width "
- u"fixed at 40.")
- text_divider = [u"The ", ('important', u"Divider"),
- u" widget repeats the same character across the whole line. "
- u"It can also add blank lines above and below."]
- text_edit = [u"The ", ('important', u"Edit"),
- u" widget is a simple text editing widget. It supports cursor "
- u"movement and tries to maintain the current column when focus "
- u"moves to another edit widget. It wraps and aligns the same "
- u"way as Text widgets." ]
- text_edit_cap1 = ('editcp', u"This is a caption. Edit here: ")
- text_edit_text1 = u"editable stuff"
- text_edit_cap2 = ('editcp', u"This one supports newlines: ")
- text_edit_text2 = (u"line one starts them all\n"
- u"== line 2 == with some more text to edit.. words.. whee..\n"
- u"LINE III, the line to end lines one and two, unless you "
- u"change something.")
- text_edit_cap3 = ('editcp', u"This one is clipped, try "
- u"editing past the edge: ")
- text_edit_text3 = u"add some text here -> -> -> ...."
- text_edit_alignments = u"Different Alignments:"
- text_edit_left = u"left aligned (default)"
- text_edit_center = u"center aligned"
- text_edit_right = u"right aligned"
- text_intedit = ('editcp', [('important', u"IntEdit"),
- u" allows only numbers: "])
- text_edit_padding = ('editcp', u"Edit widget within a Padding widget ")
- text_columns1 = [('important', u"Columns"),
- u" are used to share horizontal screen space. "
- u"This one splits the space into two parts with "
- u"three characters between each column. The "
- u"contents of each column is a single widget."]
- text_columns2 = [u"When you need to put more than one "
- u"widget into a column you can use a ",('important',
- u"Pile"), u" to combine two or more widgets."]
- text_col_columns = u"Columns may be placed inside other columns."
- text_col_21 = u"Col 2.1"
- text_col_22 = u"Col 2.2"
- text_col_23 = u"Col 2.3"
- text_column_widths = (u"Columns may also have uneven relative "
- u"weights or fixed widths. Use a minimum width so that "
- u"columns don't become too small.")
- text_weight = u"Weight %d"
- text_fixed_9 = u"<Fixed 9>" # should be 9 columns wide
- text_fixed_14 = u"<--Fixed 14-->" # should be 14 columns wide
- text_edit_col_cap1 = ('editcp', u"Edit widget within Columns")
- text_edit_col_text1 = u"here's\nsome\ninfo"
- text_edit_col_cap2 = ('editcp', u"and within Pile ")
- text_edit_col_text2 = u"more"
- text_edit_col_cap3 = ('editcp', u"another ")
- text_edit_col_text3 = u"still more"
- text_gridflow = [u"A ",('important', u"GridFlow"), u" widget "
- u"may be used to display a list of flow widgets with equal "
- u"widths. Widgets that don't fit on the first line will "
- u"flow to the next. This is useful for small widgets that "
- u"you want to keep together such as ", ('important', u"Button"),
- u", ",('important', u"CheckBox"), u" and ",
- ('important', u"RadioButton"), u" widgets." ]
- text_button_list = [u"Yes", u"No", u"Perhaps", u"Certainly", u"Partially",
- u"Tuesdays Only", u"Help"]
- text_cb_list = [u"Wax", u"Wash", u"Buff", u"Clear Coat", u"Dry",
- u"Racing Stripe"]
- text_rb_list = [u"Morning", u"Afternoon", u"Evening", u"Weekend"]
- text_listbox = [u"All these widgets have been displayed "
- u"with the help of a ", ('important', u"ListBox"), u" widget. "
- u"ListBox widgets handle scrolling and changing focus. A ",
- ('important', u"Frame"), u" widget is used to keep the "
- u"instructions at the top of the screen."]
+ text_any = ("The 'any' wrap mode will wrap on any character. This "
+ "mode will not collapse space characters at the end of the "
+ "line but it still honors embedded newline characters.\n"
+ "Like this one.")
+ text_padding = ("Padding widgets have many options. This "
+ "is a standard Text widget wrapped with a Padding widget "
+ "with the alignment set to relative 20% and with its width "
+ "fixed at 40.")
+ text_divider = ["The ", ('important', "Divider"),
+ " widget repeats the same character across the whole line. "
+ "It can also add blank lines above and below."]
+ text_edit = ["The ", ('important', "Edit"),
+ " widget is a simple text editing widget. It supports cursor "
+ "movement and tries to maintain the current column when focus "
+ "moves to another edit widget. It wraps and aligns the same "
+ "way as Text widgets." ]
+ text_edit_cap1 = ('editcp', "This is a caption. Edit here: ")
+ text_edit_text1 = "editable stuff"
+ text_edit_cap2 = ('editcp', "This one supports newlines: ")
+ text_edit_text2 = ("line one starts them all\n"
+ "== line 2 == with some more text to edit.. words.. whee..\n"
+ "LINE III, the line to end lines one and two, unless you "
+ "change something.")
+ text_edit_cap3 = ('editcp', "This one is clipped, try "
+ "editing past the edge: ")
+ text_edit_text3 = "add some text here -> -> -> ...."
+ text_edit_alignments = "Different Alignments:"
+ text_edit_left = "left aligned (default)"
+ text_edit_center = "center aligned"
+ text_edit_right = "right aligned"
+ text_intedit = ('editcp', [('important', "IntEdit"),
+ " allows only numbers: "])
+ text_edit_padding = ('editcp', "Edit widget within a Padding widget ")
+ text_columns1 = [('important', "Columns"),
+ " are used to share horizontal screen space. "
+ "This one splits the space into two parts with "
+ "three characters between each column. The "
+ "contents of each column is a single widget."]
+ text_columns2 = ["When you need to put more than one "
+ "widget into a column you can use a ",('important',
+ "Pile"), " to combine two or more widgets."]
+ text_col_columns = "Columns may be placed inside other columns."
+ text_col_21 = "Col 2.1"
+ text_col_22 = "Col 2.2"
+ text_col_23 = "Col 2.3"
+ text_column_widths = ("Columns may also have uneven relative "
+ "weights or fixed widths. Use a minimum width so that "
+ "columns don't become too small.")
+ text_weight = "Weight %d"
+ text_fixed_9 = "<Fixed 9>" # should be 9 columns wide
+ text_fixed_14 = "<--Fixed 14-->" # should be 14 columns wide
+ text_edit_col_cap1 = ('editcp', "Edit widget within Columns")
+ text_edit_col_text1 = "here's\nsome\ninfo"
+ text_edit_col_cap2 = ('editcp', "and within Pile ")
+ text_edit_col_text2 = "more"
+ text_edit_col_cap3 = ('editcp', "another ")
+ text_edit_col_text3 = "still more"
+ text_gridflow = ["A ",('important', "GridFlow"), " widget "
+ "may be used to display a list of flow widgets with equal "
+ "widths. Widgets that don't fit on the first line will "
+ "flow to the next. This is useful for small widgets that "
+ "you want to keep together such as ", ('important', "Button"),
+ ", ",('important', "CheckBox"), " and ",
+ ('important', "RadioButton"), " widgets." ]
+ text_button_list = ["Yes", "No", "Perhaps", "Certainly", "Partially",
+ "Tuesdays Only", "Help"]
+ text_cb_list = ["Wax", "Wash", "Buff", "Clear Coat", "Dry",
+ "Racing Stripe"]
+ text_rb_list = ["Morning", "Afternoon", "Evening", "Weekend"]
+ text_listbox = ["All these widgets have been displayed "
+ "with the help of a ", ('important', "ListBox"), " widget. "
+ "ListBox widgets handle scrolling and changing focus. A ",
+ ('important', "Frame"), " widget is used to keep the "
+ "instructions at the top of the screen."]
def button_press(button):
frame.footer = urwid.AttrWrap(urwid.Text(
- [u"Pressed: ", button.get_label()]), 'header')
+ ["Pressed: ", button.get_label()]), 'header')
radio_button_group = []
diff --git a/examples/treesample.py b/examples/treesample.py
index 7b4f42f..e3e148a 100755
--- a/examples/treesample.py
+++ b/examples/treesample.py
@@ -20,7 +20,7 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
"""
Urwid example lazy directory browser / tree view
@@ -31,8 +31,9 @@ Features:
- custom list walker for displaying widgets in a tree fashion
"""
+from __future__ import annotations
+
import urwid
-import os
class ExampleTreeWidget(urwid.TreeWidget):
@@ -120,7 +121,7 @@ def get_example_tree():
""" generate a quick 100 leaf tree for demo purposes """
retval = {"name":"parent","children":[]}
for i in range(10):
- retval['children'].append({"name":"child " + str(i)})
+ retval['children'].append({"name":f"child {str(i)}"})
retval['children'][i]['children']=[]
for j in range(10):
retval['children'][i]['children'].append({"name":"grandchild " +
diff --git a/examples/twisted_serve_ssh.py b/examples/twisted_serve_ssh.py
index b2e0206..4b8235b 100644
--- a/examples/twisted_serve_ssh.py
+++ b/examples/twisted_serve_ssh.py
@@ -30,24 +30,26 @@ Portions Copyright: 2010, Ian Ward <ian@excess.org>
Licence: LGPL <http://opensource.org/licenses/lgpl-2.1.php>
"""
-from __future__ import print_function
-import os
+from __future__ import annotations
-import urwid
-from urwid.raw_display import Screen
-
-from zope.interface import Interface, Attribute, implements
-from twisted.application.service import Application
from twisted.application.internet import TCPServer
-from twisted.cred.portal import Portal
+from twisted.application.service import Application
+from twisted.conch.insults.insults import ServerProtocol, TerminalProtocol
from twisted.conch.interfaces import IConchUser, ISession
-from twisted.conch.insults.insults import TerminalProtocol, ServerProtocol
-from twisted.conch.manhole_ssh import (ConchFactory, TerminalRealm,
- TerminalUser, TerminalSession, TerminalSessionTransport)
-
-from twisted.python.components import Componentized, Adapter
+from twisted.conch.manhole_ssh import (
+ ConchFactory,
+ TerminalRealm,
+ TerminalSession,
+ TerminalSessionTransport,
+ TerminalUser,
+)
+from twisted.cred.portal import Portal
+from twisted.python.components import Adapter, Componentized
+from zope.interface import Attribute, Interface, implements
+import urwid
+from urwid.raw_display import Screen
class IUrwidUi(Interface):
@@ -84,7 +86,7 @@ class IUrwidMind(Interface):
-class UrwidUi(object):
+class UrwidUi:
def __init__(self, urwid_mind):
self.mind = urwid_mind
@@ -111,7 +113,7 @@ class UrwidUi(object):
-class UnhandledKeyHandler(object):
+class UnhandledKeyHandler:
def __init__(self, mind):
self.mind = mind
@@ -120,7 +122,7 @@ class UnhandledKeyHandler(object):
if isinstance(key, tuple):
pass
else:
- f = getattr(self, 'key_%s' % key.replace(' ', '_'), None)
+ f = getattr(self, f"key_{key.replace(' ', '_')}", None)
if f is None:
return
else:
@@ -214,10 +216,10 @@ class TwistedScreen(Screen):
self.terminal.cursorPosition(0, i)
for (attr, cs, text) in row:
if attr != lasta:
- text = '%s%s' % (self._attr_to_escape(attr), text)
+ text = f'{self._attr_to_escape(attr)}{text}'
lasta = attr
#if cs or attr:
- # print cs, attr
+ # print(cs, attr)
self.write(text)
cursor = r.get_cursor()
if cursor is not None:
@@ -325,7 +327,7 @@ class TwistedScreen(Screen):
bg = "%d" % (a.background_number + 40)
else:
bg = "49"
- return urwid.escape.ESC + "[0;%s;%s%sm" % (fg, st, bg)
+ return f"{urwid.escape.ESC}[0;{fg};{st}{bg}m"
class UrwidTerminalProtocol(TerminalProtocol):
diff --git a/setup.py b/setup.py
index 68f7adb..aad73f2 100644
--- a/setup.py
+++ b/setup.py
@@ -17,41 +17,36 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-try:
- PYTHON3 = not str is bytes
-except NameError:
- PYTHON3 = False
-
-try:
- from setuptools import setup, Extension # distribute required for Python 3
- have_setuptools = True
-except ImportError:
- if PYTHON3:
- raise
- from distutils.core import setup, Extension
- have_setuptools = False
+from __future__ import annotations
import os
-exec(open(os.path.join("urwid","version.py")).read())
-release = __version__
+from setuptools import Extension, setup
+
+with open(os.path.join("urwid", "version.py")) as f:
+ globals_ = {}
+ locals_ = {}
+ # Execute safe way: do not expose anything
+ exec(f.read(), globals_, locals_)
+ release = locals_['__version__']
+
+
+with open("README.rst") as f:
+ long_description = f.read().split('.. content-start\n', 1)[1]
+
setup_d = {
- 'name':"urwid",
- 'version':release,
- 'author':"Ian Ward",
- 'author_email':"ian@excess.org",
- 'ext_modules':[Extension('urwid.str_util', sources=['source/str_util.c'])],
- 'packages':['urwid', 'urwid.tests'],
- 'url':"http://urwid.org/",
- 'license':"LGPL",
- 'keywords':"curses ui widget scroll listbox user interface text layout console ncurses",
- 'platforms':"unix-like",
- 'description': "A full-featured console (xterm et al.) user interface library",
- 'long_description':open("README.rst").read().split('.. content-start\n',1)[1],
- 'classifiers':[
+ 'name': "urwid",
+ 'version': release,
+ 'author': "Ian Ward",
+ 'author_email': "ian@excess.org",
+ 'ext_modules': [Extension('urwid.str_util', sources=['source/str_util.c'])],
+ 'packages': ['urwid', 'urwid.tests'], 'url': "https://urwid.org/", 'license': "LGPL",
+ 'keywords': "curses ui widget scroll listbox user interface text layout console ncurses",
+ 'platforms': "unix-like", 'description': "A full-featured console (xterm et al.) user interface library",
+ 'long_description': long_description, 'classifiers': [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Environment :: Console :: Curses",
@@ -62,25 +57,23 @@ setup_d = {
"Operating System :: MacOS :: MacOS X",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Widget Sets",
- "Programming Language :: Python :: 2",
- "Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.5",
- "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: Implementation :: PyPy",
- ],
- }
-
-if have_setuptools:
- setup_d['zip_safe'] = False
- setup_d['test_suite'] = 'urwid.tests'
+ ],
+ 'zip_safe': False,
+ 'test_suite': 'urwid.tests',
+}
if __name__ == "__main__":
try:
setup(**setup_d)
- except (IOError, SystemExit) as e:
+ except (OSError, SystemExit) as e:
import sys
if "test" in sys.argv:
raise
diff --git a/tox.ini b/tox.ini
index 0142550..96a18a7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py3{6,7,8,9,10,11},pypy3
+envlist = py3{7,8,9,10,11},pypy3
[testenv]
usedevelop = true
@@ -12,3 +12,5 @@ deps =
commands =
coverage run -m unittest discover -s urwid -v
+[isort]
+profile = black \ No newline at end of file
diff --git a/urwid/__init__.py b/urwid/__init__.py
index 3eb4e99..a45e505 100644
--- a/urwid/__init__.py
+++ b/urwid/__init__.py
@@ -18,44 +18,157 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
+from __future__ import annotations
+
+from urwid.canvas import (
+ BlankCanvas,
+ Canvas,
+ CanvasCache,
+ CanvasCombine,
+ CanvasError,
+ CanvasJoin,
+ CanvasOverlay,
+ CompositeCanvas,
+ SolidCanvas,
+ TextCanvas,
+)
+from urwid.command_map import (
+ ACTIVATE,
+ CURSOR_DOWN,
+ CURSOR_LEFT,
+ CURSOR_MAX_LEFT,
+ CURSOR_MAX_RIGHT,
+ CURSOR_PAGE_DOWN,
+ CURSOR_PAGE_UP,
+ CURSOR_RIGHT,
+ CURSOR_UP,
+ REDRAW_SCREEN,
+ CommandMap,
+ command_map,
+)
+from urwid.container import (
+ Columns,
+ ColumnsError,
+ Frame,
+ FrameError,
+ GridFlow,
+ GridFlowError,
+ Overlay,
+ OverlayError,
+ Pile,
+ PileError,
+ WidgetContainerMixin,
+)
+from urwid.decoration import (
+ AttrMap,
+ AttrMapError,
+ AttrWrap,
+ BoxAdapter,
+ BoxAdapterError,
+ Filler,
+ FillerError,
+ Padding,
+ PaddingError,
+ WidgetDecoration,
+ WidgetDisable,
+ WidgetPlaceholder,
+)
+from urwid.font import (
+ Font,
+ HalfBlock5x4Font,
+ HalfBlock6x5Font,
+ HalfBlock7x7Font,
+ HalfBlockHeavy6x5Font,
+ Thin3x3Font,
+ Thin4x3Font,
+ Thin6x6Font,
+ get_all_fonts,
+)
+from urwid.graphics import (
+ BarGraph,
+ BarGraphError,
+ BarGraphMeta,
+ BigText,
+ GraphVScale,
+ LineBox,
+ ProgressBar,
+ scale_bar_values,
+)
+from urwid.listbox import (
+ ListBox,
+ ListBoxError,
+ ListWalker,
+ ListWalkerError,
+ SimpleFocusListWalker,
+ SimpleListWalker,
+)
+from urwid.main_loop import (
+ AsyncioEventLoop,
+ ExitMainLoop,
+ GLibEventLoop,
+ MainLoop,
+ SelectEventLoop,
+ TornadoEventLoop,
+)
+from urwid.monitored_list import MonitoredFocusList, MonitoredList
+from urwid.signals import (
+ MetaSignals,
+ Signals,
+ connect_signal,
+ disconnect_signal,
+ emit_signal,
+ register_signal,
+)
from urwid.version import VERSION, __version__
-from urwid.widget import (FLOW, BOX, FIXED, LEFT, RIGHT, CENTER, TOP, MIDDLE,
- BOTTOM, SPACE, ANY, CLIP, PACK, GIVEN, RELATIVE, RELATIVE_100, WEIGHT,
+from urwid.widget import (
+ ANY,
+ BOTTOM,
+ BOX,
+ CENTER,
+ CLIP,
+ FIXED,
+ FLOW,
+ GIVEN,
+ LEFT,
+ MIDDLE,
+ PACK,
+ RELATIVE,
+ RELATIVE_100,
+ RIGHT,
+ SPACE,
+ TOP,
+ WEIGHT,
+ BoxWidget,
+ Divider,
+ Edit,
+ EditError,
+ FixedWidget,
+ FlowWidget,
+ IntEdit,
+ SolidFill,
+ Text,
+ TextError,
+ Widget,
+ WidgetError,
WidgetMeta,
- WidgetError, Widget, FlowWidget, BoxWidget, fixed_size, FixedWidget,
- Divider, SolidFill, TextError, Text, EditError, Edit, IntEdit,
- delegate_to_widget_mixin, WidgetWrapError, WidgetWrap)
-from urwid.decoration import (WidgetDecoration, WidgetPlaceholder,
- AttrMapError, AttrMap, AttrWrap, BoxAdapterError, BoxAdapter, PaddingError,
- Padding, FillerError, Filler, WidgetDisable)
-from urwid.container import (GridFlowError, GridFlow, OverlayError, Overlay,
- FrameError, Frame, PileError, Pile, ColumnsError, Columns,
- WidgetContainerMixin)
-from urwid.wimp import (SelectableIcon, CheckBoxError, CheckBox, RadioButton,
- Button, PopUpLauncher, PopUpTarget)
-from urwid.listbox import (ListWalkerError, ListWalker, SimpleListWalker,
- SimpleFocusListWalker, ListBoxError, ListBox)
-from urwid.graphics import (BigText, LineBox, BarGraphMeta, BarGraphError,
- BarGraph, GraphVScale, ProgressBar, scale_bar_values)
-from urwid.canvas import (CanvasCache, CanvasError, Canvas, TextCanvas,
- BlankCanvas, SolidCanvas, CompositeCanvas, CanvasCombine, CanvasOverlay,
- CanvasJoin)
-from urwid.font import (get_all_fonts, Font, Thin3x3Font, Thin4x3Font,
- HalfBlock5x4Font, HalfBlock6x5Font, HalfBlockHeavy6x5Font, Thin6x6Font,
- HalfBlock7x7Font)
-from urwid.signals import (MetaSignals, Signals, emit_signal, register_signal,
- connect_signal, disconnect_signal)
-from urwid.monitored_list import MonitoredList, MonitoredFocusList
-from urwid.command_map import (CommandMap, command_map,
- REDRAW_SCREEN, CURSOR_UP, CURSOR_DOWN, CURSOR_LEFT, CURSOR_RIGHT,
- CURSOR_PAGE_UP, CURSOR_PAGE_DOWN, CURSOR_MAX_LEFT, CURSOR_MAX_RIGHT,
- ACTIVATE)
-from urwid.main_loop import (ExitMainLoop, MainLoop, SelectEventLoop,
- GLibEventLoop, TornadoEventLoop, AsyncioEventLoop)
+ WidgetWrap,
+ WidgetWrapError,
+ delegate_to_widget_mixin,
+ fixed_size,
+)
+from urwid.wimp import (
+ Button,
+ CheckBox,
+ CheckBoxError,
+ PopUpLauncher,
+ PopUpTarget,
+ RadioButton,
+ SelectableIcon,
+)
+
try:
from urwid.main_loop import TwistedEventLoop
except ImportError:
@@ -64,21 +177,63 @@ try:
from urwid.main_loop import TrioEventLoop
except ImportError:
pass
-from urwid.text_layout import (TextLayout, StandardTextLayout, default_layout,
- LayoutSegment)
-from urwid.display_common import (UPDATE_PALETTE_ENTRY, DEFAULT, BLACK,
- DARK_RED, DARK_GREEN, BROWN, DARK_BLUE, DARK_MAGENTA, DARK_CYAN,
- LIGHT_GRAY, DARK_GRAY, LIGHT_RED, LIGHT_GREEN, YELLOW, LIGHT_BLUE,
- LIGHT_MAGENTA, LIGHT_CYAN, WHITE, AttrSpecError, AttrSpec, RealTerminal,
- ScreenError, BaseScreen)
-from urwid.util import (calc_text_pos, calc_width, is_wide_char,
- move_next_char, move_prev_char, within_double_byte, detected_encoding,
- set_encoding, get_encoding_mode, apply_target_encoding, supports_unicode,
- calc_trim_text, TagMarkupException, decompose_tagmarkup, MetaSuper,
- int_scale, is_mouse_event)
-from urwid.treetools import (TreeWidgetError, TreeWidget, TreeNode,
- ParentNode, TreeWalker, TreeListBox)
-from urwid.vterm import (TermModes, TermCharset, TermScroller, TermCanvas,
- Terminal)
-
from urwid import raw_display
+from urwid.display_common import (
+ BLACK,
+ BROWN,
+ DARK_BLUE,
+ DARK_CYAN,
+ DARK_GRAY,
+ DARK_GREEN,
+ DARK_MAGENTA,
+ DARK_RED,
+ DEFAULT,
+ LIGHT_BLUE,
+ LIGHT_CYAN,
+ LIGHT_GRAY,
+ LIGHT_GREEN,
+ LIGHT_MAGENTA,
+ LIGHT_RED,
+ UPDATE_PALETTE_ENTRY,
+ WHITE,
+ YELLOW,
+ AttrSpec,
+ AttrSpecError,
+ BaseScreen,
+ RealTerminal,
+ ScreenError,
+)
+from urwid.text_layout import (
+ LayoutSegment,
+ StandardTextLayout,
+ TextLayout,
+ default_layout,
+)
+from urwid.treetools import (
+ ParentNode,
+ TreeListBox,
+ TreeNode,
+ TreeWalker,
+ TreeWidget,
+ TreeWidgetError,
+)
+from urwid.util import (
+ MetaSuper,
+ TagMarkupException,
+ apply_target_encoding,
+ calc_text_pos,
+ calc_trim_text,
+ calc_width,
+ decompose_tagmarkup,
+ detected_encoding,
+ get_encoding_mode,
+ int_scale,
+ is_mouse_event,
+ is_wide_char,
+ move_next_char,
+ move_prev_char,
+ set_encoding,
+ supports_unicode,
+ within_double_byte,
+)
+from urwid.vterm import TermCanvas, TermCharset, Terminal, TermModes, TermScroller
diff --git a/urwid/_async_kw_event_loop.py b/urwid/_async_kw_event_loop.py
index ba8ad9d..bc74b0b 100644
--- a/urwid/_async_kw_event_loop.py
+++ b/urwid/_async_kw_event_loop.py
@@ -18,7 +18,9 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
+
+from __future__ import annotations
from .main_loop import EventLoop, ExitMainLoop
diff --git a/urwid/canvas.py b/urwid/canvas.py
index 479e889..1f4789e 100644
--- a/urwid/canvas.py
+++ b/urwid/canvas.py
@@ -17,18 +17,27 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
+
+from __future__ import annotations
import weakref
-from urwid.util import rle_len, rle_append_modify, rle_join_modify, rle_product, \
- calc_width, calc_text_pos, apply_target_encoding, trim_text_attr_cs
-from urwid.text_layout import trim_line, LayoutSegment
+from urwid.text_layout import LayoutSegment, trim_line
+from urwid.util import (
+ apply_target_encoding,
+ calc_text_pos,
+ calc_width,
+ rle_append_modify,
+ rle_join_modify,
+ rle_len,
+ rle_product,
+ trim_text_attr_cs,
+)
-class CanvasCache(object):
+class CanvasCache:
"""
Cache for rendered canvases. Automatically populated and
accessed by Widget render() MetaClass magic, cleared by
@@ -51,6 +60,7 @@ class CanvasCache(object):
fetches = 0
cleanups = 0
+ @classmethod
def store(cls, wcls, canvas):
"""
Store a weakref to canvas in the cache.
@@ -92,8 +102,8 @@ class CanvasCache(object):
ref = weakref.ref(canvas, cls.cleanup)
cls._refs[ref] = (widget, wcls, size, focus)
cls._widgets.setdefault(widget, {})[(wcls, size, focus)] = ref
- store = classmethod(store)
+ @classmethod
def fetch(cls, widget, wcls, size, focus):
"""
Return the cached canvas or None.
@@ -114,8 +124,8 @@ class CanvasCache(object):
if canv:
cls.hits += 1 # more stats
return canv
- fetch = classmethod(fetch)
+ @classmethod
def invalidate(cls, widget):
"""
Remove all canvases cached for widget.
@@ -138,8 +148,8 @@ class CanvasCache(object):
pass
for w in dependants:
cls.invalidate(w)
- invalidate = classmethod(invalidate)
+ @classmethod
def cleanup(cls, ref):
cls.cleanups += 1 # collect stats
@@ -161,8 +171,8 @@ class CanvasCache(object):
del cls._deps[widget]
except KeyError:
pass
- cleanup = classmethod(cleanup)
+ @classmethod
def clear(cls):
"""
Empty the cache.
@@ -170,14 +180,12 @@ class CanvasCache(object):
cls._widgets = {}
cls._refs = {}
cls._deps = {}
- clear = classmethod(clear)
-
class CanvasError(Exception):
pass
-class Canvas(object):
+class Canvas:
"""
base class for canvases
"""
@@ -233,7 +241,7 @@ class Canvas(object):
Return the text content of the canvas as a list of strings,
one for each row.
"""
- return [bytes().join([text for (attr, cs, text) in row])
+ return [b''.join([text for (attr, cs, text) in row])
for row in self.content()]
text = property(_text_content, _raise_old_repr_error)
@@ -325,7 +333,7 @@ class TextCanvas(Canvas):
check_width -- check and fix width of all lines in text
"""
Canvas.__init__(self)
- if text == None:
+ if text is None:
text = []
if check_width:
@@ -345,27 +353,27 @@ class TextCanvas(Canvas):
else:
maxcol = 0
- if attr == None:
+ if attr is None:
attr = [[] for x in range(len(text))]
- if cs == None:
+ if cs is None:
cs = [[] for x in range(len(text))]
# pad text and attr to maxcol
for i in range(len(text)):
w = widths[i]
if w > maxcol:
- raise CanvasError("Canvas text is wider than the maxcol specified \n%r\n%r\n%r"%(maxcol,widths,text))
+ raise CanvasError(f"Canvas text is wider than the maxcol specified \n{maxcol!r}\n{widths!r}\n{text!r}")
if w < maxcol:
- text[i] = text[i] + bytes().rjust(maxcol-w)
+ text[i] = text[i] + b''.rjust(maxcol-w)
a_gap = len(text[i]) - rle_len( attr[i] )
if a_gap < 0:
- raise CanvasError("Attribute extends beyond text \n%r\n%r" % (text[i],attr[i]) )
+ raise CanvasError(f"Attribute extends beyond text \n{text[i]!r}\n{attr[i]!r}" )
if a_gap:
rle_append_modify( attr[i], (None, a_gap))
cs_gap = len(text[i]) - rle_len( cs[i] )
if cs_gap < 0:
- raise CanvasError("Character Set extends beyond text \n%r\n%r" % (text[i],cs[i]) )
+ raise CanvasError(f"Character Set extends beyond text \n{text[i]!r}\n{cs[i]!r}" )
if cs_gap:
rle_append_modify( cs[i], (None, cs_gap))
@@ -469,7 +477,7 @@ class BlankCanvas(Canvas):
def_attr = None
if attr and None in attr:
def_attr = attr[None]
- line = [(def_attr, None, bytes().rjust(cols))]
+ line = [(def_attr, None, b''.rjust(cols))]
for i in range(rows):
yield line
@@ -492,7 +500,7 @@ class SolidCanvas(Canvas):
def __init__(self, fill_char, cols, rows):
Canvas.__init__(self)
end, col = calc_text_pos(fill_char, 0, len(fill_char), 1)
- assert col == 1, "Invalid fill_char: %r" % fill_char
+ assert col == 1, f"Invalid fill_char: {fill_char!r}"
self._text, cs = apply_target_encoding(fill_char[:end])
self._cs = cs[0][0]
self.size = cols, rows
@@ -740,8 +748,8 @@ class CompositeCanvas(Canvas):
right = self.cols() - left - width
bottom = self.rows() - top - height
- assert right >= 0, "top canvas of overlay not the size expected!" + repr((other.cols(),left,right,width))
- assert bottom >= 0, "top canvas of overlay not the size expected!" + repr((other.rows(),top,bottom,height))
+ assert right >= 0, f"top canvas of overlay not the size expected!{repr((other.cols(), left, right, width))}"
+ assert bottom >= 0, f"top canvas of overlay not the size expected!{repr((other.rows(), top, bottom, height))}"
shards = self.shards
top_shards = []
@@ -1300,14 +1308,14 @@ def apply_text_layout(text, attr, ls, maxcol):
rle_join_modify( linec, cs )
elif s.offs:
if s.sc:
- line.append(bytes().rjust(s.sc))
+ line.append(b''.rjust(s.sc))
attrrange( s.offs, s.offs, s.sc )
else:
- line.append(bytes().rjust(s.sc))
+ line.append(b''.rjust(s.sc))
linea.append((None, s.sc))
linec.append((None, s.sc))
- t.append(bytes().join(line))
+ t.append(b''.join(line))
a.append(linea)
c.append(linec)
diff --git a/urwid/command_map.py b/urwid/command_map.py
index e6965f7..08b0c8d 100644
--- a/urwid/command_map.py
+++ b/urwid/command_map.py
@@ -17,9 +17,8 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
REDRAW_SCREEN = 'redraw screen'
CURSOR_UP = 'cursor up'
@@ -32,7 +31,7 @@ CURSOR_MAX_LEFT = 'cursor max left'
CURSOR_MAX_RIGHT = 'cursor max right'
ACTIVATE = 'activate'
-class CommandMap(object):
+class CommandMap:
"""
dict-like object for looking up commands from keystrokes
diff --git a/urwid/compat.py b/urwid/compat.py
index 5734552..8bcbf05 100644
--- a/urwid/compat.py
+++ b/urwid/compat.py
@@ -1,5 +1,4 @@
#!/usr/bin/python
-# -*- coding: utf-8 -*-
#
# Urwid python compatibility definitions
# Copyright (C) 2011 Ian Ward
@@ -18,90 +17,20 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
-
-import sys
-
-
-PYTHON3 = sys.version_info > (3, 0)
-
-# for iterating over byte strings:
-# ord2 calls ord in python2 only
-# chr2 converts an ordinal value to a length-1 byte string
-# B returns a byte string in all supported python versions
-# bytes3 creates a byte string from a list of ordinal values
-if PYTHON3:
- ord2 = lambda x: x
- chr2 = lambda x: bytes([x])
- B = lambda x: x.encode('iso8859-1')
- bytes3 = bytes
- text_type = str
- xrange = range
- text_types = (str,)
-
- def reraise(tp, value, tb=None):
- """
- Reraise an exception.
- Taken from "six" library (https://pythonhosted.org/six/).
- """
- try:
- if value is None:
- value = tp()
- if value.__traceback__ is not tb:
- raise value.with_traceback(tb)
- raise value
- finally:
- value = None
- tb = None
-else:
- ord2 = ord
- chr2 = chr
- B = lambda x: x
- bytes3 = lambda x: bytes().join([chr(c) for c in x])
- text_type = unicode
- xrange = xrange
- text_types = (str, unicode)
+def reraise(tp, value, tb=None):
"""
Reraise an exception.
Taken from "six" library (https://pythonhosted.org/six/).
"""
- def exec_(_code_, _globs_=None, _locs_=None):
- """Execute code in a namespace."""
- if _globs_ is None:
- frame = sys._getframe(1)
- _globs_ = frame.f_globals
- if _locs_ is None:
- _locs_ = frame.f_locals
- del frame
- elif _locs_ is None:
- _locs_ = _globs_
- exec("""exec _code_ in _globs_, _locs_""")
-
- exec_("""def reraise(tp, value, tb=None):
try:
- raise tp, value, tb
+ if value is None:
+ value = tp()
+ if value.__traceback__ is not tb:
+ raise value.with_traceback(tb)
+ raise value
finally:
+ value = None
tb = None
-""")
-
-def with_metaclass(meta, *bases):
- """
- Create a base class with a metaclass.
- Taken from "six" library (https://pythonhosted.org/six/).
- """
- # This requires a bit of explanation: the basic idea is to make a dummy
- # metaclass for one level of class instantiation that replaces itself with
- # the actual metaclass.
- class metaclass(type):
-
- def __new__(cls, name, this_bases, d):
- return meta(name, bases, d)
-
- @classmethod
- def __prepare__(cls, name, this_bases):
- return meta.__prepare__(name, bases)
- return type.__new__(metaclass, 'temporary_class', (), {})
-
diff --git a/urwid/container.py b/urwid/container.py
index af52a83..889c92e 100755
--- a/urwid/container.py
+++ b/urwid/container.py
@@ -17,26 +17,57 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
+
+from __future__ import annotations
from itertools import chain, repeat
-from urwid.compat import xrange
+from urwid.canvas import (
+ CanvasCombine,
+ CanvasJoin,
+ CanvasOverlay,
+ CompositeCanvas,
+ SolidCanvas,
+)
+from urwid.decoration import (
+ Filler,
+ Padding,
+ calculate_left_right_padding,
+ calculate_top_bottom_filler,
+ normalize_align,
+ normalize_height,
+ normalize_valign,
+ normalize_width,
+ simplify_align,
+ simplify_height,
+ simplify_valign,
+ simplify_width,
+)
+from urwid.monitored_list import MonitoredFocusList, MonitoredList
from urwid.util import is_mouse_press
-from urwid.widget import (Widget, Divider, FLOW, FIXED, PACK, BOX, WidgetWrap,
- GIVEN, WEIGHT, LEFT, RIGHT, RELATIVE, TOP, BOTTOM, CLIP, RELATIVE_100)
-from urwid.decoration import (Padding, Filler, calculate_left_right_padding,
- calculate_top_bottom_filler, normalize_align, normalize_width,
- normalize_valign, normalize_height, simplify_align, simplify_width,
- simplify_valign, simplify_height)
-from urwid.monitored_list import MonitoredList, MonitoredFocusList
-from urwid.canvas import (CompositeCanvas, CanvasOverlay, CanvasCombine,
- SolidCanvas, CanvasJoin)
-
-
-class WidgetContainerMixin(object):
+from urwid.widget import (
+ BOTTOM,
+ BOX,
+ CLIP,
+ FIXED,
+ FLOW,
+ GIVEN,
+ LEFT,
+ PACK,
+ RELATIVE,
+ RELATIVE_100,
+ RIGHT,
+ TOP,
+ WEIGHT,
+ Divider,
+ Widget,
+ WidgetWrap,
+)
+
+
+class WidgetContainerMixin:
"""
Mixin class for widget containers implementing common container methods
"""
@@ -106,7 +137,7 @@ class WidgetContainerMixin(object):
return out
out.append(w)
-class WidgetContainerListContentsMixin(object):
+class WidgetContainerListContentsMixin:
"""
Mixin class for widget containers whose positions are indexes into
a list available as self.contents.
@@ -116,14 +147,14 @@ class WidgetContainerListContentsMixin(object):
Return an iterable of positions for this container from first
to last.
"""
- return iter(xrange(len(self.contents)))
+ return iter(range(len(self.contents)))
def __reversed__(self):
"""
Return an iterable of positions for this container from last
to first.
"""
- return iter(xrange(len(self.contents) - 1, -1, -1))
+ return iter(range(len(self.contents) - 1, -1, -1))
class GridFlowError(Exception):
@@ -158,13 +189,13 @@ class GridFlow(WidgetWrap, WidgetContainerMixin, WidgetContainerListContentsMixi
self.v_sep = v_sep
self.align = align
self._cache_maxcol = None
- self.__super.__init__(None)
+ super().__init__(None)
# set self._w to something other than None
self.get_display_widget(((h_sep+cell_width)*len(cells),))
def _invalidate(self):
self._cache_maxcol = None
- self.__super._invalidate()
+ super()._invalidate()
def _contents_modified(self, slc, new_items):
for item in new_items:
@@ -173,7 +204,7 @@ class GridFlow(WidgetWrap, WidgetContainerMixin, WidgetContainerListContentsMixi
if t != GIVEN:
raise ValueError
except (TypeError, ValueError):
- raise GridFlowError("added content invalid %r" % (item,))
+ raise GridFlowError(f"added content invalid {item!r}")
def _get_cells(self):
ml = MonitoredList(w for w, t in self.contents)
@@ -234,7 +265,7 @@ class GridFlow(WidgetWrap, WidgetContainerMixin, WidgetContainerListContentsMixi
width_amount -- None to use the default cell_width for this GridFlow
"""
if width_type != GIVEN:
- raise GridFlowError("invalid width_type: %r" % (width_type,))
+ raise GridFlowError(f"invalid width_type: {width_type!r}")
if width_amount is None:
width_amount = self._cell_width
return (width_type, width_amount)
@@ -271,7 +302,7 @@ class GridFlow(WidgetWrap, WidgetContainerMixin, WidgetContainerListContentsMixi
if cell == w:
self.focus_position = i
return
- raise ValueError("Widget not found in GridFlow contents: %r" % (cell,))
+ raise ValueError(f"Widget not found in GridFlow contents: {cell!r}")
focus_cell = property(get_focus, _set_focus_cell, doc="""
The widget in focus, for backwards compatibility.
@@ -299,7 +330,7 @@ class GridFlow(WidgetWrap, WidgetContainerMixin, WidgetContainerListContentsMixi
if position < 0 or position >= len(self.contents):
raise IndexError
except (TypeError, IndexError):
- raise IndexError("No GridFlow child widget at position %s" % (position,))
+ raise IndexError(f"No GridFlow child widget at position {position}")
self.contents.focus = position
focus_position = property(_get_focus_position, _set_focus_position, doc="""
index of child widget in focus. Raises :exc:`IndexError` if read when
@@ -407,41 +438,41 @@ class GridFlow(WidgetWrap, WidgetContainerMixin, WidgetContainerListContentsMixi
Captures focus changes.
"""
self.get_display_widget(size)
- key = self.__super.keypress(size, key)
+ key = super().keypress(size, key)
if key is None:
self._set_focus_from_display_widget()
return key
def rows(self, size, focus=False):
self.get_display_widget(size)
- return self.__super.rows(size, focus=focus)
+ return super().rows(size, focus=focus)
def render(self, size, focus=False ):
self.get_display_widget(size)
- return self.__super.render(size, focus)
+ return super().render(size, focus)
def get_cursor_coords(self, size):
"""Get cursor from display widget."""
self.get_display_widget(size)
- return self.__super.get_cursor_coords(size)
+ return super().get_cursor_coords(size)
def move_cursor_to_coords(self, size, col, row):
"""Set the widget in focus based on the col + row."""
self.get_display_widget(size)
- rval = self.__super.move_cursor_to_coords(size, col, row)
+ rval = super().move_cursor_to_coords(size, col, row)
self._set_focus_from_display_widget()
return rval
def mouse_event(self, size, event, button, col, row, focus):
self.get_display_widget(size)
- self.__super.mouse_event(size, event, button, col, row, focus)
+ super().mouse_event(size, event, button, col, row, focus)
self._set_focus_from_display_widget()
return True # at a minimum we adjusted our focus
def get_pref_col(self, size):
"""Return pref col from display widget."""
self.get_display_widget(size)
- return self.__super.get_pref_col(size)
+ return super().get_pref_col(size)
@@ -507,7 +538,7 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
widgets when determining the size and position of *top_w*. *bottom_w* is
always rendered the full size available "below" *top_w*.
"""
- self.__super.__init__()
+ super().__init__()
self.top_w = top_w
self.bottom_w = bottom_w
@@ -624,7 +655,7 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
doc="index of child widget in focus, currently always 1")
def _contents(self):
- class OverlayContents(object):
+ class OverlayContents:
def __len__(inner_self):
return 2
__getitem__ = self._contents__getitem__
@@ -641,13 +672,12 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
self.right, self.valign_type, self.valign_amount,
self.height_type, self.height_amount,
self.min_height, self.top, self.bottom))
- raise IndexError("Overlay.contents has no position %r"
- % (index,))
+ raise IndexError(f"Overlay.contents has no position {index!r}")
def _contents__setitem__(self, index, value):
try:
value_w, value_options = value
except (ValueError, TypeError):
- raise OverlayError("added content invalid: %r" % (value,))
+ raise OverlayError(f"added content invalid: {value!r}")
if index == 0:
if value_options != self._DEFAULT_BOTTOM_OPTIONS:
raise OverlayError("bottom_options must be set to "
@@ -660,8 +690,7 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
height_type, height_amount, min_height, top, bottom,
) = value_options
except (ValueError, TypeError):
- raise OverlayError("top_options is invalid: %r"
- % (value_options,))
+ raise OverlayError(f"top_options is invalid: {value_options!r}")
# normalize first, this is where errors are raised
align_type, align_amount = normalize_align(
simplify_align(align_type, align_amount), OverlayError)
@@ -686,8 +715,7 @@ class Overlay(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
self.min_width = min_width
self.min_height = min_height
else:
- raise IndexError("Overlay.contents has no position %r"
- % (index,))
+ raise IndexError(f"Overlay.contents has no position {index!r}")
self._invalidate()
contents = property(_contents, doc="""
a list-like object similar to::
@@ -839,7 +867,7 @@ class Frame(Widget, WidgetContainerMixin):
:param focus_part: 'header', 'footer' or 'body'
:type focus_part: str
"""
- self.__super.__init__()
+ super().__init__()
self._header = header
self._body = body
@@ -882,10 +910,10 @@ class Frame(Widget, WidgetContainerMixin):
:type part: str
"""
if part not in ('header', 'footer', 'body'):
- raise IndexError('Invalid position for Frame: %s' % (part,))
+ raise IndexError(f'Invalid position for Frame: {part}')
if (part == 'header' and self._header is None) or (
part == 'footer' and self._footer is None):
- raise IndexError('This Frame has no %s' % (part,))
+ raise IndexError(f'This Frame has no {part}')
self.focus_part = part
self._invalidate()
@@ -917,7 +945,7 @@ class Frame(Widget, WidgetContainerMixin):
""")
def _contents(self):
- class FrameContents(object):
+ class FrameContents:
def __len__(inner_self):
return len(inner_self.keys())
def items(inner_self):
@@ -954,16 +982,16 @@ class Frame(Widget, WidgetContainerMixin):
return (self._header, None)
if key == 'footer' and self._footer:
return (self._footer, None)
- raise KeyError("Frame.contents has no key: %r" % (key,))
+ raise KeyError(f"Frame.contents has no key: {key!r}")
def _contents__setitem__(self, key, value):
if key not in ('body', 'header', 'footer'):
- raise KeyError("Frame.contents has no key: %r" % (key,))
+ raise KeyError(f"Frame.contents has no key: {key!r}")
try:
value_w, value_options = value
if value_options is not None:
raise ValueError
except (ValueError, TypeError):
- raise FrameError("added content invalid: %r" % (value,))
+ raise FrameError(f"added content invalid: {value!r}")
if key == 'body':
self.body = value_w
elif key == 'footer':
@@ -972,10 +1000,10 @@ class Frame(Widget, WidgetContainerMixin):
self.header = value_w
def _contents__delitem__(self, key):
if key not in ('header', 'footer'):
- raise KeyError("Frame.contents can't remove key: %r" % (key,))
+ raise KeyError(f"Frame.contents can't remove key: {key!r}")
if (key == 'header' and self._header is None
) or (key == 'footer' and self._footer is None):
- raise KeyError("Frame.contents has no key: %r" % (key,))
+ raise KeyError(f"Frame.contents has no key: {key!r}")
if key == 'header':
self.header = None
else:
@@ -1259,7 +1287,7 @@ class Pile(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
one ``'weight'`` tuple in :attr:`widget_list`.
"""
self._selectable = False
- self.__super.__init__()
+ super().__init__()
self._contents = MonitoredFocusList()
self._contents.set_modified_callback(self._contents_modified)
self._contents.set_focus_changed_callback(lambda f: self._invalidate())
@@ -1284,7 +1312,7 @@ class Pile(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
self.contents.append((w, (f, height)))
else:
raise PileError(
- "initial widget list item invalid %r" % (original,))
+ f"initial widget list item invalid {original!r}")
if focus_item is None and w.selectable():
focus_item = i
@@ -1308,7 +1336,7 @@ class Pile(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
if t not in (PACK, GIVEN, WEIGHT):
raise ValueError
except (TypeError, ValueError):
- raise PileError("added content invalid: %r" % (item,))
+ raise PileError(f"added content invalid: {item!r}")
def _get_widget_list(self):
ml = MonitoredList(w for w, t in self.contents)
@@ -1399,7 +1427,7 @@ class Pile(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
if height_type == PACK:
return (PACK, None)
if height_type not in (GIVEN, WEIGHT):
- raise PileError('invalid height_type: %r' % (height_type,))
+ raise PileError(f'invalid height_type: {height_type!r}')
return (height_type, height_amount)
def set_focus(self, item):
@@ -1419,7 +1447,7 @@ class Pile(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
if item == w:
self.focus_position = i
return
- raise ValueError("Widget not found in Pile contents: %r" % (item,))
+ raise ValueError(f"Widget not found in Pile contents: {item!r}")
def get_focus(self):
"""
@@ -1462,7 +1490,7 @@ class Pile(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
if position < 0 or position >= len(self.contents):
raise IndexError
except (TypeError, IndexError):
- raise IndexError("No Pile child widget at position %s" % (position,))
+ raise IndexError(f"No Pile child widget at position {position}")
self.contents.focus = position
focus_position = property(_get_focus_position, _set_focus_position, doc="""
index of child widget in focus. Raises :exc:`IndexError` if read when
@@ -1773,7 +1801,7 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
filling the full height.
"""
self._selectable = False
- self.__super.__init__()
+ super().__init__()
self._contents = MonitoredFocusList()
self._contents.set_modified_callback(self._contents_modified)
self._contents.set_focus_changed_callback(lambda f: self._invalidate())
@@ -1801,7 +1829,7 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
self.contents.append((w, (f, width, i in box_columns)))
else:
raise ColumnsError(
- "initial widget list item invalid: %r" % (original,))
+ f"initial widget list item invalid: {original!r}")
if focus_column is None and w.selectable():
focus_column = i
@@ -1828,7 +1856,7 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
if t not in (PACK, GIVEN, WEIGHT):
raise ValueError
except (TypeError, ValueError):
- raise ColumnsError("added content invalid %r" % (item,))
+ raise ColumnsError(f"added content invalid {item!r}")
def _get_widget_list(self):
ml = MonitoredList(w for w, t in self.contents)
@@ -1945,12 +1973,12 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
if width_type == PACK:
width_amount = None
if width_type not in (PACK, GIVEN, WEIGHT):
- raise ColumnsError('invalid width_type: %r' % (width_type,))
+ raise ColumnsError(f'invalid width_type: {width_type!r}')
return (width_type, width_amount, box_widget)
def _invalidate(self):
self._cache_maxcol = None
- self.__super._invalidate()
+ super()._invalidate()
def set_focus_column(self, num):
"""
@@ -1987,7 +2015,7 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
if item == w:
self.focus_position = i
return
- raise ValueError("Widget not found in Columns contents: %r" % (item,))
+ raise ValueError(f"Widget not found in Columns contents: {item!r}")
def get_focus(self):
"""
@@ -2019,7 +2047,7 @@ class Columns(Widget, WidgetContainerMixin, WidgetContainerListContentsMixin):
if position < 0 or position >= len(self.contents):
raise IndexError
except (TypeError, IndexError):
- raise IndexError("No Columns child widget at position %s" % (position,))
+ raise IndexError(f"No Columns child widget at position {position}")
self.contents.focus = position
focus_position = property(_get_focus_position, _set_focus_position, doc="""
index of child widget in focus. Raises :exc:`IndexError` if read when
diff --git a/urwid/curses_display.py b/urwid/curses_display.py
index c37507c..3a79ec7 100755
--- a/urwid/curses_display.py
+++ b/urwid/curses_display.py
@@ -17,22 +17,26 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
"""
Curses-based UI implementation
"""
+from __future__ import annotations
+
import curses
+
import _curses
from urwid import escape
-
-from urwid.display_common import BaseScreen, RealTerminal, AttrSpec, \
- UNPRINTABLE_TRANS_TABLE
-from urwid.compat import PYTHON3, text_type, xrange, ord2
+from urwid.display_common import (
+ UNPRINTABLE_TRANS_TABLE,
+ AttrSpec,
+ BaseScreen,
+ RealTerminal,
+)
KEY_RESIZE = 410 # curses.KEY_RESIZE (sometimes not defined)
KEY_MOUSE = 409 # curses.KEY_MOUSE
@@ -60,7 +64,7 @@ _curses_colours = {
class Screen(BaseScreen, RealTerminal):
def __init__(self):
- super(Screen,self).__init__()
+ super().__init__()
self.curses_pairs = [
(None,None), # Can't be sure what pair 0 will default to
]
@@ -130,7 +134,7 @@ class Screen(BaseScreen, RealTerminal):
if not self._signal_keys_set:
self._old_signal_keys = self.tty_signal_keys()
- super(Screen, self)._start()
+ super()._start()
def _stop(self):
"""
@@ -146,7 +150,7 @@ class Screen(BaseScreen, RealTerminal):
if self._old_signal_keys:
self.tty_signal_keys(*self._old_signal_keys)
- super(Screen, self)._stop()
+ super()._stop()
def _setup_colour_pairs(self):
@@ -159,8 +163,8 @@ class Screen(BaseScreen, RealTerminal):
if not self.has_color:
return
- for fg in xrange(8):
- for bg in xrange(8):
+ for fg in range(8):
+ for bg in range(8):
# leave out white on black
if fg == curses.COLOR_WHITE and \
bg == curses.COLOR_BLACK:
@@ -368,7 +372,7 @@ class Screen(BaseScreen, RealTerminal):
l = []
def append_button( b ):
b |= mod
- l.extend([ 27, ord2('['), ord2('M'), b+32, x+33, y+33 ])
+ l.extend([ 27, '[', 'M', b+32, x+33, y+33 ])
if bstate & curses.BUTTON1_PRESSED and last & 1 == 0:
append_button( 0 )
@@ -515,14 +519,11 @@ class Screen(BaseScreen, RealTerminal):
try:
if cs in ("0", "U"):
for i in range(len(seg)):
- self.s.addch( 0x400000 + ord2(seg[i]) )
+ self.s.addch( 0x400000 + seg[i] )
else:
assert cs is None
- if PYTHON3:
- assert isinstance(seg, bytes)
- self.s.addstr(seg.decode('utf-8'))
- else:
- self.s.addstr(seg)
+ assert isinstance(seg, bytes)
+ self.s.addstr(seg.decode('utf-8'))
except _curses.error:
# it's ok to get out of the
# screen on the lower right
@@ -561,20 +562,20 @@ class Screen(BaseScreen, RealTerminal):
class _test:
def __init__(self):
self.ui = Screen()
- self.l = list(_curses_colours.keys())
- self.l.sort()
+ self.l = sorted(_curses_colours)
+
for c in self.l:
self.ui.register_palette( [
- (c+" on black", c, 'black', 'underline'),
- (c+" on dark blue",c, 'dark blue', 'bold'),
- (c+" on light gray",c,'light gray', 'standout'),
+ (f"{c} on black", c, 'black', 'underline'),
+ (f"{c} on dark blue",c, 'dark blue', 'bold'),
+ (f"{c} on light gray",c,'light gray', 'standout'),
])
self.ui.run_wrapper(self.run)
def run(self):
class FakeRender: pass
r = FakeRender()
- text = [" has_color = "+repr(self.ui.has_color),""]
+ text = [f" has_color = {self.ui.has_color!r}",""]
attr = [[],[]]
r.coords = {}
r.cursor = None
@@ -582,7 +583,7 @@ class _test:
for c in self.l:
t = ""
a = []
- for p in c+" on black",c+" on dark blue",c+" on light gray":
+ for p in f"{c} on black",f"{c} on dark blue",f"{c} on light gray":
a.append((p,27))
t=t+ (p+27*" ")[:27]
@@ -605,12 +606,12 @@ class _test:
t = ""
a = []
for k in keys:
- if type(k) == text_type: k = k.encode("utf-8")
- t += "'"+k + "' "
+ if type(k) == str: k = k.encode("utf-8")
+ t += f"'{k}' "
a += [(None,1), ('yellow on dark blue',len(k)),
(None,2)]
- text.append(t + ": "+ repr(raw))
+ text.append(f"{t}: {raw!r}")
attr.append(a)
text = text[-rows:]
attr = attr[-rows:]
diff --git a/urwid/decoration.py b/urwid/decoration.py
index 9c18028..28096ab 100755
--- a/urwid/decoration.py
+++ b/urwid/decoration.py
@@ -17,17 +17,36 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
-from urwid.util import int_scale
-from urwid.widget import (Widget, WidgetError,
- BOX, FLOW, LEFT, CENTER, RIGHT, PACK, CLIP, GIVEN, RELATIVE, RELATIVE_100,
- TOP, MIDDLE, BOTTOM, delegate_to_widget_mixin)
-from urwid.split_repr import remove_defaults
+from __future__ import annotations
+
from urwid.canvas import CompositeCanvas, SolidCanvas
-from urwid.widget import Divider, Edit, Text, SolidFill # doctests
+from urwid.split_repr import remove_defaults
+from urwid.util import int_scale
+from urwid.widget import ( # doctests
+ BOTTOM,
+ BOX,
+ CENTER,
+ CLIP,
+ FLOW,
+ GIVEN,
+ LEFT,
+ MIDDLE,
+ PACK,
+ RELATIVE,
+ RELATIVE_100,
+ RIGHT,
+ TOP,
+ Divider,
+ Edit,
+ SolidFill,
+ Text,
+ Widget,
+ WidgetError,
+ delegate_to_widget_mixin,
+)
class WidgetDecoration(Widget): # "decorator" was already taken
@@ -49,7 +68,7 @@ class WidgetDecoration(Widget): # "decorator" was already taken
def __init__(self, original_widget):
self._original_widget = original_widget
def _repr_words(self):
- return self.__super._repr_words() + [repr(self._original_widget)]
+ return super()._repr_words() + [repr(self._original_widget)]
def _get_original_widget(self):
return self._original_widget
@@ -140,7 +159,7 @@ class AttrMap(delegate_to_widget_mixin('_original_widget'), WidgetDecoration):
>>> next(am2.render(size).content())
[('greeting', None, ...'hi'), ('bg', None, ...' ')]
"""
- self.__super.__init__(w)
+ super().__init__(w)
if type(attr_map) != dict:
self.set_attr_map({None: attr_map})
@@ -154,7 +173,7 @@ class AttrMap(delegate_to_widget_mixin('_original_widget'), WidgetDecoration):
def _repr_attrs(self):
# only include the focus_attr when it takes effect (not None)
- d = dict(self.__super._repr_attrs(), attr_map=self._attr_map)
+ d = dict(super()._repr_attrs(), attr_map=self._attr_map)
if self._focus_map is not None:
d['focus_map'] = self._focus_map
return d
@@ -253,11 +272,11 @@ class AttrWrap(AttrMap):
>>> next(aw.render(size, focus=True).content())
[('fgreet', None, ...'hi ')]
"""
- self.__super.__init__(w, attr, focus_attr)
+ super().__init__(w, attr, focus_attr)
def _repr_attrs(self):
# only include the focus_attr when it takes effect (not None)
- d = dict(self.__super._repr_attrs(), attr=self.attr)
+ d = dict(super()._repr_attrs(), attr=self.attr)
del d['attr_map']
if 'focus_map' in d:
del d['focus_map']
@@ -342,21 +361,20 @@ class BoxAdapter(WidgetDecoration):
<BoxAdapter flow widget <SolidFill box widget 'x'> height=5>
"""
if hasattr(box_widget, 'sizing') and BOX not in box_widget.sizing():
- raise BoxAdapterError("%r is not a box widget" %
- box_widget)
+ raise BoxAdapterError(f"{box_widget!r} is not a box widget")
WidgetDecoration.__init__(self,box_widget)
self.height = height
def _repr_attrs(self):
- return dict(self.__super._repr_attrs(), height=self.height)
+ return dict(super()._repr_attrs(), height=self.height)
# originally stored as box_widget, keep for compatibility
box_widget = property(WidgetDecoration._get_original_widget,
WidgetDecoration._set_original_widget)
def sizing(self):
- return set([FLOW])
+ return {FLOW}
def rows(self, size, focus=False):
"""
@@ -482,7 +500,7 @@ class Padding(WidgetDecoration):
| hi |
| there|
"""
- self.__super.__init__(w)
+ super().__init__(w)
# convert obsolete parameters 'fixed left' and 'fixed right':
if type(align) == tuple and align[0] in ('fixed left',
@@ -515,11 +533,11 @@ class Padding(WidgetDecoration):
def sizing(self):
if self._width_type == CLIP:
- return set([FLOW])
+ return {FLOW}
return self.original_widget.sizing()
def _repr_attrs(self):
- attrs = dict(self.__super._repr_attrs(),
+ attrs = dict(super()._repr_attrs(),
align=self.align,
width=self.width,
left=self.left,
@@ -726,7 +744,7 @@ class Filler(WidgetDecoration):
reducing the valign amount when necessary. If height still
cannot be satisfied it will also be reduced.
"""
- self.__super.__init__(body)
+ super().__init__(body)
# convert old parameters to the new top/bottom values
if isinstance(height, tuple):
@@ -767,10 +785,10 @@ class Filler(WidgetDecoration):
self.min_height = None
def sizing(self):
- return set([BOX]) # always a box widget
+ return {BOX} # always a box widget
def _repr_attrs(self):
- attrs = dict(self.__super._repr_attrs(),
+ attrs = dict(super()._repr_attrs(),
valign=simplify_valign(self.valign_type, self.valign_amount),
height=simplify_height(self.height_type, self.height_amount),
top=self.top,
diff --git a/urwid/display_common.py b/urwid/display_common.py
index 8361b45..b135a5f 100755
--- a/urwid/display_common.py
+++ b/urwid/display_common.py
@@ -16,24 +16,25 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
+
+from __future__ import annotations
import os
import sys
+import warnings
try:
import termios
except ImportError:
pass # windows
-from urwid.util import StoppingContext, int_scale
from urwid import signals
-from urwid.compat import B, bytes3, xrange, with_metaclass
+from urwid.util import StoppingContext, int_scale
# for replacing unprintable bytes with '?'
-UNPRINTABLE_TRANS_TABLE = B("?") * 32 + bytes3(list(xrange(32,256)))
+UNPRINTABLE_TRANS_TABLE = b"?" * 32 + bytes(list(range(32,256)))
# signals sent by BaseScreen
@@ -234,7 +235,7 @@ def _gray_num_88(gnum):
def _color_desc_true(num):
- return "#%06x" %(num)
+ return f"#{num:06x}"
def _color_desc_256(num):
"""
@@ -314,7 +315,7 @@ def _parse_color_true(desc):
h = desc[1:]
return int(h, 16)
elif len(desc) == 4:
- h = "0x%s0%s0%s" %(desc[1], desc[2], desc[3])
+ h = f"0x{desc[1]}0{desc[2]}0{desc[3]}"
return int(h, 16)
return None
@@ -474,7 +475,7 @@ def _parse_color_88(desc):
class AttrSpecError(Exception):
pass
-class AttrSpec(object):
+class AttrSpec:
def __init__(self, fg, bg, colors=256):
"""
fg -- a string containing a comma-separated foreground color
@@ -537,25 +538,66 @@ class AttrSpec(object):
self.foreground = fg
self.background = bg
if self.colors > colors:
- raise AttrSpecError(('foreground/background (%s/%s) require ' +
- 'more colors than have been specified (%d).') %
- (repr(fg), repr(bg), colors))
-
- foreground_basic = property(lambda s: s._value & _FG_BASIC_COLOR != 0)
- foreground_high = property(lambda s: s._value & _FG_HIGH_COLOR != 0)
- foreground_true = property(lambda s: s._value & _FG_TRUE_COLOR != 0)
- foreground_number = property(lambda s: s._value & _FG_COLOR_MASK)
- background_basic = property(lambda s: s._value & _BG_BASIC_COLOR != 0)
- background_high = property(lambda s: s._value & _BG_HIGH_COLOR != 0)
- background_true = property(lambda s: s._value & _BG_TRUE_COLOR != 0)
- background_number = property(lambda s: (s._value & _BG_COLOR_MASK)
- >> _BG_SHIFT)
- italics = property(lambda s: s._value & _ITALICS != 0)
- bold = property(lambda s: s._value & _BOLD != 0)
- underline = property(lambda s: s._value & _UNDERLINE != 0)
- blink = property(lambda s: s._value & _BLINK != 0)
- standout = property(lambda s: s._value & _STANDOUT != 0)
- strikethrough = property(lambda s: s._value & _STRIKETHROUGH != 0)
+ raise AttrSpecError(
+ f'foreground/background ({fg!r}/{bg!r}) '
+ f'require more colors than have been specified ({colors:d}).'
+ )
+
+ @property
+ def foreground_basic(self):
+ return self._value & _FG_BASIC_COLOR != 0
+
+ @property
+ def foreground_high(self):
+ return self._value & _FG_HIGH_COLOR != 0
+
+ @property
+ def foreground_true(self):
+ return self._value & _FG_TRUE_COLOR != 0
+
+ @property
+ def foreground_number(self):
+ return self._value & _FG_COLOR_MASK
+
+ @property
+ def background_basic(self):
+ return self._value & _BG_BASIC_COLOR != 0
+
+ @property
+ def background_high(self):
+ return self._value & _BG_HIGH_COLOR != 0
+
+ @property
+ def background_true(self):
+ return self._value & _BG_TRUE_COLOR != 0
+
+ @property
+ def background_number(self):
+ return (self._value & _BG_COLOR_MASK) >> _BG_SHIFT
+
+ @property
+ def italics(self):
+ return self._value & _ITALICS != 0
+
+ @property
+ def bold(self):
+ return self._value & _BOLD != 0
+
+ @property
+ def underline(self):
+ return self._value & _UNDERLINE != 0
+
+ @property
+ def blink(self):
+ return self._value & _BLINK != 0
+
+ @property
+ def standout(self):
+ return self._value & _STANDOUT != 0
+
+ @property
+ def strikethrough(self):
+ return self._value & _STRIKETHROUGH != 0
def _colors(self):
"""
@@ -569,7 +611,7 @@ class AttrSpec(object):
return 256
if self._value & (_BG_TRUE_COLOR | _FG_TRUE_COLOR):
return 2**24
- if self._value & (_BG_BASIC_COLOR | _BG_BASIC_COLOR):
+ if self._value & (_BG_BASIC_COLOR | _FG_BASIC_COLOR):
return 16
return 1
colors = property(_colors)
@@ -579,11 +621,11 @@ class AttrSpec(object):
Return an executable python representation of the AttrSpec
object.
"""
- args = "%r, %r" % (self.foreground, self.background)
+ args = f"{self.foreground!r}, {self.background!r}"
if self.colors == 88:
# 88-color mode is the only one that is handled differently
- args = args + ", colors=88"
- return "%s(%s)" % (self.__class__.__name__, args)
+ args = f"{args}, colors=88"
+ return f"{self.__class__.__name__}({args})"
def _foreground_color(self):
"""Return only the color component of the foreground."""
@@ -612,9 +654,8 @@ class AttrSpec(object):
if part in _ATTRIBUTES:
# parse and store "settings"/attributes in flags
if flags & _ATTRIBUTES[part]:
- raise AttrSpecError(("Setting %s specified more than" +
- "once in foreground (%s)") % (repr(part),
- repr(foreground)))
+ raise AttrSpecError(
+ f"Setting {part!r} specified more than once in foreground ({foreground!r})")
flags |= _ATTRIBUTES[part]
continue
# past this point we must be specifying a color
@@ -634,11 +675,9 @@ class AttrSpec(object):
flags |= _FG_HIGH_COLOR
# _parse_color_*() return None for unrecognised colors
if scolor is None:
- raise AttrSpecError(("Unrecognised color specification %s " +
- "in foreground (%s)") % (repr(part), repr(foreground)))
+ raise AttrSpecError(f"Unrecognised color specification {part!r} in foreground ({foreground!r})")
if color is not None:
- raise AttrSpecError(("More than one color given for " +
- "foreground (%s)") % (repr(foreground),))
+ raise AttrSpecError(f"More than one color given for foreground ({foreground!r})")
color = scolor
if color is None:
color = 0
@@ -675,8 +714,7 @@ class AttrSpec(object):
color = _parse_color_256(_true_to_256(background) or background)
flags |= _BG_HIGH_COLOR
if color is None:
- raise AttrSpecError(("Unrecognised color specification " +
- "in background (%s)") % (repr(background),))
+ raise AttrSpecError(f"Unrecognised color specification in background ({background!r})")
self._value = (self._value & ~_BG_MASK) | (color << _BG_SHIFT) | flags
background = property(_background, _set_background)
@@ -701,7 +739,7 @@ class AttrSpec(object):
assert self.foreground_number < 88, "Invalid AttrSpec _value"
vals = _COLOR_VALUES_88[self.foreground_number]
elif self.colors == 2**24:
- h = "%06x" %(self.foreground_number)
+ h = f"{self.foreground_number:06x}"
vals = tuple([int(x, 16) for x in [h[0:2], h[2:4], h[4:6]]])
else:
vals = _COLOR_VALUES_256[self.foreground_number]
@@ -712,7 +750,7 @@ class AttrSpec(object):
assert self.background_number < 88, "Invalid AttrSpec _value"
return vals + _COLOR_VALUES_88[self.background_number]
elif self.colors == 2**24:
- h = "%06x" %(self.background_number)
+ h = f"{self.background_number:06x}"
return vals + tuple([int(x, 16) for x in [h[0:2], h[2:4], h[4:6]]])
else:
return vals + _COLOR_VALUES_256[self.background_number]
@@ -726,9 +764,9 @@ class AttrSpec(object):
__hash__ = object.__hash__
-class RealTerminal(object):
+class RealTerminal:
def __init__(self):
- super(RealTerminal,self).__init__()
+ super().__init__()
self._signal_keys_set = False
self._old_signal_keys = None
@@ -782,18 +820,20 @@ class RealTerminal(object):
class ScreenError(Exception):
pass
-class BaseScreen(with_metaclass(signals.MetaSignals, object)):
+class BaseScreen(metaclass=signals.MetaSignals):
"""
Base class for Screen classes (raw_display.Screen, .. etc)
"""
signals = [UPDATE_PALETTE_ENTRY, INPUT_DESCRIPTORS_CHANGED]
def __init__(self):
- super(BaseScreen,self).__init__()
+ super().__init__()
self._palette = {}
self._started = False
- started = property(lambda self: self._started)
+ @property
+ def started(self):
+ return self._started
def start(self, *args, **kwargs):
"""Set up the screen. If the screen has already been started, does
@@ -830,10 +870,13 @@ class BaseScreen(with_metaclass(signals.MetaSignals, object)):
Deprecated in favor of calling `start` as a context manager.
"""
+ warnings.warn(
+ "run_wrapper is deprecated in favor of calling `start` as a context manager.",
+ DeprecationWarning,
+ )
with self.start(*args, **kwargs):
return fn()
-
def register_palette(self, palette):
"""Register a set of palette entries.
@@ -856,11 +899,10 @@ class BaseScreen(with_metaclass(signals.MetaSignals, object)):
self.register_palette_entry(*item)
continue
if len(item) != 2:
- raise ScreenError("Invalid register_palette entry: %s" %
- repr(item))
+ raise ScreenError(f"Invalid register_palette entry: {item!r}")
name, like_name = item
if like_name not in self._palette:
- raise ScreenError("palette entry '%s' doesn't exist"%like_name)
+ raise ScreenError(f"palette entry '{like_name}' doesn't exist")
self._palette[name] = self._palette[like_name]
def register_palette_entry(self, name, foreground, background,
diff --git a/urwid/escape.py b/urwid/escape.py
index 6992834..ec8da4e 100644
--- a/urwid/escape.py
+++ b/urwid/escape.py
@@ -1,5 +1,4 @@
#!/usr/bin/python
-# -*- coding: utf-8 -*-
#
# Urwid escape sequences common to curses_display and raw_display
# Copyright (C) 2004-2011 Ian Ward
@@ -18,14 +17,15 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
"""
Terminal Escape Sequences for input and display
"""
+from __future__ import annotations
+
import re
try:
@@ -33,8 +33,6 @@ try:
except ImportError:
from urwid import old_str_util as str_util
-from urwid.compat import bytes3
-
# NOTE: because of circular imports (urwid.util -> urwid.escape -> urwid.util)
# from urwid.util import is_mouse_event -- will not work here
import urwid.util
@@ -47,16 +45,16 @@ IBMPC_ON = "\x1b[11m"
IBMPC_OFF = "\x1b[10m"
DEC_TAG = "0"
-DEC_SPECIAL_CHARS = u'▮◆▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π≠£·'
-ALT_DEC_SPECIAL_CHARS = u"_`abcdefghijklmnopqrstuvwxyz{|}~"
+DEC_SPECIAL_CHARS = '▮◆▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π≠£·'
+ALT_DEC_SPECIAL_CHARS = "_`abcdefghijklmnopqrstuvwxyz{|}~"
DEC_SPECIAL_CHARMAP = {}
assert len(DEC_SPECIAL_CHARS) == len(ALT_DEC_SPECIAL_CHARS), repr((DEC_SPECIAL_CHARS, ALT_DEC_SPECIAL_CHARS))
for c, alt in zip(DEC_SPECIAL_CHARS, ALT_DEC_SPECIAL_CHARS):
DEC_SPECIAL_CHARMAP[ord(c)] = SO + alt + SI
-SAFE_ASCII_DEC_SPECIAL_RE = re.compile(u"^[ -~%s]*$" % DEC_SPECIAL_CHARS)
-DEC_SPECIAL_RE = re.compile(u"[%s]" % DEC_SPECIAL_CHARS)
+SAFE_ASCII_DEC_SPECIAL_RE = re.compile(f"^[ -~{DEC_SPECIAL_CHARS}]*$")
+DEC_SPECIAL_RE = re.compile(f"[{DEC_SPECIAL_CHARS}]")
###################
@@ -101,12 +99,12 @@ input_sequences = [
for prefix, modifier in zip('O[', ('meta ', 'shift '))
for letter, key in zip('abcd', ('up', 'down', 'right', 'left'))
] + [
- ("[" + digit + symbol, modifier + key)
+ (f"[{digit}{symbol}", modifier + key)
for modifier, symbol in zip(('shift ', 'meta '), '$^')
for digit, key in zip('235678',
('insert', 'delete', 'page up', 'page down', 'home', 'end'))
] + [
- ('O' + chr(ord('p')+n), str(n)) for n in range(10)
+ (f"O{chr(ord('p') + n)}", str(n)) for n in range(10)
] + [
# modified cursor keys + home, end, 5 -- [#X and [1;#X forms
(prefix+digit+letter, escape_modifier(digit) + key)
@@ -116,12 +114,12 @@ input_sequences = [
('up','down','right','left','5','end','5','home'))
] + [
# modified F1-F4 keys -- O#X form
- ("O"+digit+letter, escape_modifier(digit) + key)
+ (f"O{digit}{letter}", escape_modifier(digit) + key)
for digit in "12345678"
for letter,key in zip("PQRS",('f1','f2','f3','f4'))
] + [
# modified F1-F13 keys -- [XX;#~ form
- ("["+str(num)+";"+digit+"~", escape_modifier(digit) + key)
+ (f"[{str(num)};{digit}~", escape_modifier(digit) + key)
for digit in "12345678"
for num,key in zip(
(3,5,6,11,12,13,14,15,17,18,19,20,21,23,24,25,26,28,29,31,32,33,34),
@@ -139,7 +137,7 @@ input_sequences = [
('[0n', 'status ok')
]
-class KeyqueueTrie(object):
+class KeyqueueTrie:
def __init__( self, sequences ):
self.data = {}
for s, result in sequences:
@@ -191,11 +189,11 @@ class KeyqueueTrie(object):
x, y = (keys[1] - 33)%256, (keys[2] - 33)%256 # supports 0-255
prefix = ""
- if b & 4: prefix = prefix + "shift "
- if b & 8: prefix = prefix + "meta "
- if b & 16: prefix = prefix + "ctrl "
- if (b & MOUSE_MULTIPLE_CLICK_MASK)>>9 == 1: prefix = prefix + "double "
- if (b & MOUSE_MULTIPLE_CLICK_MASK)>>9 == 2: prefix = prefix + "triple "
+ if b & 4: prefix = f"{prefix}shift "
+ if b & 8: prefix = f"{prefix}meta "
+ if b & 16: prefix = f"{prefix}ctrl "
+ if (b & MOUSE_MULTIPLE_CLICK_MASK)>>9 == 1: prefix = f"{prefix}double "
+ if (b & MOUSE_MULTIPLE_CLICK_MASK)>>9 == 2: prefix = f"{prefix}triple "
# 0->1, 1->2, 2->3, 64->4, 65->5
button = ((b&64)//64*3) + (b & 3) + 1
@@ -212,7 +210,7 @@ class KeyqueueTrie(object):
else:
action = "press"
- return ( (prefix + "mouse " + action, button, x, y), keys[3:] )
+ return ( (f"{prefix}mouse {action}", button, x, y), keys[3:] )
def read_sgrmouse_info(self, keys, more_available):
# Helpful links:
@@ -228,10 +226,10 @@ class KeyqueueTrie(object):
pos_m = 0
found_m = False
for k in keys:
- value = value + chr(k);
+ value = value + chr(k)
if ((k is ord('M')) or (k is ord('m'))):
found_m = True
- break;
+ break
pos_m += 1
if not found_m:
if more_available:
@@ -261,7 +259,7 @@ class KeyqueueTrie(object):
else:
action = "release"
- return ( ("mouse " + action, button, x, y), keys[pos_m + 1:] )
+ return ( (f"mouse {action}", button, x, y), keys[pos_m + 1:] )
def read_cursor_position(self, keys, more_available):
@@ -385,9 +383,9 @@ def process_keyqueue(codes, more_available):
if code in _keyconv:
return [_keyconv[code]], codes[1:]
if code >0 and code <27:
- return ["ctrl %s" % chr(ord('a')+code-1)], codes[1:]
+ return [f"ctrl {chr(ord('a') + code - 1)}"], codes[1:]
if code >27 and code <32:
- return ["ctrl %s" % chr(ord('A')+code-1)], codes[1:]
+ return [f"ctrl {chr(ord('A') + code - 1)}"], codes[1:]
em = str_util.get_byte_encoding()
@@ -420,7 +418,7 @@ def process_keyqueue(codes, more_available):
if k>256 or k&0xc0 != 0x80:
return ["<%d>"%code], codes[1:]
- s = bytes3(codes[:need_more+1])
+ s = bytes(codes[:need_more+1])
assert isinstance(s, bytes)
try:
@@ -448,7 +446,7 @@ def process_keyqueue(codes, more_available):
return ['esc'] + run, remaining_codes
if run[0] == "esc" or run[0].find("meta ") >= 0:
return ['esc']+run, remaining_codes
- return ['meta '+run[0]]+run[1:], remaining_codes
+ return [f"meta {run[0]}"]+run[1:], remaining_codes
return ['esc'], codes[1:]
@@ -459,23 +457,23 @@ def process_keyqueue(codes, more_available):
ESC = "\x1b"
-CURSOR_HOME = ESC+"[H"
+CURSOR_HOME = f"{ESC}[H"
CURSOR_HOME_COL = "\r"
-APP_KEYPAD_MODE = ESC+"="
-NUM_KEYPAD_MODE = ESC+">"
+APP_KEYPAD_MODE = f"{ESC}="
+NUM_KEYPAD_MODE = f"{ESC}>"
-SWITCH_TO_ALTERNATE_BUFFER = ESC+"7"+ESC+"[?47h"
-RESTORE_NORMAL_BUFFER = ESC+"[?47l"+ESC+"8"
+SWITCH_TO_ALTERNATE_BUFFER = f"{ESC}7{ESC}[?47h"
+RESTORE_NORMAL_BUFFER = f"{ESC}[?47l{ESC}8"
#RESET_SCROLL_REGION = ESC+"[;r"
#RESET = ESC+"c"
-REPORT_STATUS = ESC + "[5n"
-REPORT_CURSOR_POSITION = ESC+"[6n"
+REPORT_STATUS = f"{ESC}[5n"
+REPORT_CURSOR_POSITION = f"{ESC}[6n"
-INSERT_ON = ESC + "[4h"
-INSERT_OFF = ESC + "[4l"
+INSERT_ON = f"{ESC}[4h"
+INSERT_OFF = f"{ESC}[4l"
def set_cursor_position( x, y ):
assert type(x) == int
@@ -494,12 +492,12 @@ def move_cursor_down(x):
if x < 1: return ""
return ESC+"[%dB" % x
-HIDE_CURSOR = ESC+"[?25l"
-SHOW_CURSOR = ESC+"[?25h"
+HIDE_CURSOR = f"{ESC}[?25l"
+SHOW_CURSOR = f"{ESC}[?25h"
-MOUSE_TRACKING_ON = ESC+"[?1000h"+ESC+"[?1002h"+ESC+"[?1006h"
-MOUSE_TRACKING_OFF = ESC+"[?1006l"+ESC+"[?1002l"+ESC+"[?1000l"
+MOUSE_TRACKING_ON = f"{ESC}[?1000h{ESC}[?1002h{ESC}[?1006h"
+MOUSE_TRACKING_OFF = f"{ESC}[?1006l{ESC}[?1002l{ESC}[?1000l"
-DESIGNATE_G1_SPECIAL = ESC+")0"
+DESIGNATE_G1_SPECIAL = f"{ESC})0"
-ERASE_IN_LINE_RIGHT = ESC+"[K"
+ERASE_IN_LINE_RIGHT = f"{ESC}[K"
diff --git a/urwid/font.py b/urwid/font.py
index 30749a6..42dc647 100755
--- a/urwid/font.py
+++ b/urwid/font.py
@@ -1,5 +1,4 @@
#!/usr/bin/python
-# -*- coding: utf-8 -*-
#
# Urwid BigText fonts
# Copyright (C) 2004-2006 Ian Ward
@@ -18,14 +17,14 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
+from __future__ import annotations
+
+from urwid.canvas import TextCanvas
from urwid.escape import SAFE_ASCII_DEC_SPECIAL_RE
from urwid.util import apply_target_encoding, str_util
-from urwid.canvas import TextCanvas
-from urwid.compat import text_type
def separate_glyphs(gdata, height):
@@ -92,7 +91,7 @@ def add_font(name, cls):
_all_fonts.append((name, cls))
-class Font(object):
+class Font:
def __init__(self):
assert self.height
assert self.data
@@ -105,7 +104,7 @@ class Font(object):
@staticmethod
def _to_text(obj, encoding='utf-8', errors='strict'):
- if isinstance(obj, text_type):
+ if isinstance(obj, str):
return obj
elif isinstance(obj, bytes):
return obj.decode(encoding, errors)
@@ -116,8 +115,8 @@ class Font(object):
self.utf8_required |= utf8_required
def characters(self):
- l = list(self.char.keys())
- l.sort()
+ l = sorted(self.char)
+
return "".join(l)
def char_width(self, c):
@@ -152,7 +151,7 @@ class Font(object):
class Thin3x3Font(Font):
height = 3
- data = [u"""
+ data = ["""
000111222333444555666777888999 !
┌─┐ ┐ ┌─┐┌─┐ ┐┌─ ┌─ ┌─┐┌─┐┌─┐ │
│ │ │ ┌─┘ ─┤└─┼└─┐├─┐ ┼├─┤└─┤ │
@@ -167,7 +166,7 @@ add_font("Thin 3x3",Thin3x3Font)
class Thin4x3Font(Font):
height = 3
- data = Thin3x3Font.data + [u"""
+ data = Thin3x3Font.data + ["""
0000111122223333444455556666777788889999 ####$$$$
┌──┐ ┐ ┌──┐┌──┐ ┐┌── ┌── ┌──┐┌──┐┌──┐ ┼─┼┌┼┼┐
│ │ │ ┌──┘ ─┤└──┼└──┐├──┐ ┼├──┤└──┤ ┼─┼└┼┼┐
@@ -177,13 +176,13 @@ add_font("Thin 4x3",Thin4x3Font)
class HalfBlock5x4Font(Font):
height = 4
- data = [u"""
+ data = ["""
00000111112222233333444445555566666777778888899999 !!
▄▀▀▄ ▄█ ▄▀▀▄ ▄▀▀▄ ▄ █ █▀▀▀ ▄▀▀ ▀▀▀█ ▄▀▀▄ ▄▀▀▄ █
█ █ █ ▄▀ ▄▀ █▄▄█ █▄▄ █▄▄ ▐▌ ▀▄▄▀ ▀▄▄█ █
█ █ █ ▄▀ ▄ █ █ █ █ █ █ █ █ █ ▀
▀▀ ▀▀▀ ▀▀▀▀ ▀▀ ▀ ▀▀▀ ▀▀ ▀ ▀▀ ▀▀ ▀
-""", u'''
+""", '''
"""######$$$$$$%%%%%&&&&&((()))******++++++,,,-----..////::;;;
█▐▌ █ █ ▄▀█▀▄ ▐▌▐▌ ▄▀▄ █ █ ▄ ▄ ▄ ▐▌
▀█▀█▀ ▀▄█▄ █ ▀▄▀ ▐▌ ▐▌ ▄▄█▄▄ ▄▄█▄▄ ▄▄▄▄ █ ▀ ▀
@@ -195,37 +194,37 @@ class HalfBlock5x4Font(Font):
▄▀ ▀▀▀▀ ▀▄ ▄▀ █ █▀█ █ █ █ ▄▀ █ ▀▄ ▐▐▌▌
▀▄ ▀▀▀▀ ▄▀ ▀ █ ▀▀▀ █ ▐▌ █ █ █ █ ▀
▀ ▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀▀▀ ▀▀▀▀ ▀ ▀ ▀
-""", u'''
+""", '''
AAAAABBBBBCCCCCDDDDDEEEEEFFFFFGGGGGHHHHHIIJJJJJKKKKK
▄▀▀▄ █▀▀▄ ▄▀▀▄ █▀▀▄ █▀▀▀ █▀▀▀ ▄▀▀▄ █ █ █ █ █ █
█▄▄█ █▄▄▀ █ █ █ █▄▄ █▄▄ █ █▄▄█ █ █ █▄▀
█ █ █ █ █ ▄ █ █ █ █ █ ▀█ █ █ █ ▄ █ █ ▀▄
▀ ▀ ▀▀▀ ▀▀ ▀▀▀ ▀▀▀▀ ▀ ▀▀ ▀ ▀ ▀ ▀▀ ▀ ▀
-''', u'''
+''', '''
LLLLLMMMMMMNNNNNOOOOOPPPPPQQQQQRRRRRSSSSSTTTTT
█ █▄ ▄█ ██ █ ▄▀▀▄ █▀▀▄ ▄▀▀▄ █▀▀▄ ▄▀▀▄ ▀▀█▀▀
█ █ ▀ █ █▐▌█ █ █ █▄▄▀ █ █ █▄▄▀ ▀▄▄ █
█ █ █ █ ██ █ █ █ █ ▌█ █ █ ▄ █ █
▀▀▀▀ ▀ ▀ ▀ ▀ ▀▀ ▀ ▀▀▌ ▀ ▀ ▀▀ ▀
-''', u'''
+''', '''
UUUUUVVVVVVWWWWWWXXXXXXYYYYYYZZZZZ
█ █ █ █ █ █ █ █ █ █ ▀▀▀█
█ █ ▐▌ ▐▌ █ ▄ █ ▀▄▀ ▀▄▀ ▄▀
█ █ █ █ ▐▌█▐▌ ▄▀ ▀▄ █ █
▀▀ ▀ ▀ ▀ ▀ ▀ ▀ ▀▀▀▀
-''', u'''
+''', '''
aaaaabbbbbcccccdddddeeeeeffffggggghhhhhiijjjjkkkkk
█ █ ▄▀▀ █ ▄ ▄ █
▀▀▄ █▀▀▄ ▄▀▀▄ ▄▀▀█ ▄▀▀▄ ▀█▀ ▄▀▀▄ █▀▀▄ ▄ ▄ █ ▄▀
▄▀▀█ █ █ █ ▄ █ █ █▀▀ █ ▀▄▄█ █ █ █ █ █▀▄
▀▀▀ ▀▀▀ ▀▀ ▀▀▀ ▀▀ ▀ ▄▄▀ ▀ ▀ ▀ ▄▄▀ ▀ ▀
-''', u'''
+''', '''
llmmmmmmnnnnnooooopppppqqqqqrrrrssssstttt
█ █
█ █▀▄▀▄ █▀▀▄ ▄▀▀▄ █▀▀▄ ▄▀▀█ █▀▀ ▄▀▀▀ ▀█▀
█ █ █ █ █ █ █ █ █ █ █ █ █ ▀▀▄ █
▀ ▀ ▀ ▀ ▀ ▀▀ █▀▀ ▀▀█ ▀ ▀▀▀ ▀
-''', u'''
+''', '''
uuuuuvvvvvwwwwwwxxxxxxyyyyyzzzzz
█ █ █ █ █ ▄ █ ▀▄ ▄▀ █ █ ▀▀█▀
@@ -236,7 +235,7 @@ add_font("Half Block 5x4",HalfBlock5x4Font)
class HalfBlock6x5Font(Font):
height = 5
- data = [u"""
+ data = ["""
000000111111222222333333444444555555666666777777888888999999 ..::////
▄▀▀▀▄ ▄█ ▄▀▀▀▄ ▄▀▀▀▄ ▄ █ █▀▀▀▀ ▄▀▀▀ ▀▀▀▀█ ▄▀▀▀▄ ▄▀▀▀▄ █
█ █ █ █ █ █ █ █ █ ▐▌ █ █ █ █ ▀ ▐▌
@@ -248,7 +247,7 @@ add_font("Half Block 6x5",HalfBlock6x5Font)
class HalfBlockHeavy6x5Font(Font):
height = 5
- data = [u"""
+ data = ["""
000000111111222222333333444444555555666666777777888888999999 ..::////
▄███▄ ▐█▌ ▄███▄ ▄███▄ █▌ █████ ▄███▄ █████ ▄███▄ ▄███▄ █▌
█▌ ▐█ ▀█▌ ▀ ▐█ ▀ ▐█ █▌ █▌ █▌ █▌ █▌ █▌ ▐█ █▌ ▐█ █▌ ▐█
@@ -260,7 +259,7 @@ add_font("Half Block Heavy 6x5",HalfBlockHeavy6x5Font)
class Thin6x6Font(Font):
height = 6
- data = [u"""
+ data = ["""
000000111111222222333333444444555555666666777777888888999999''
┌───┐ ┐ ┌───┐ ┌───┐ ┐ ┌─── ┌─── ┌───┐ ┌───┐ ┌───┐ │
│ │ │ │ │ ┌ │ │ │ │ │ │ │ │
@@ -292,7 +291,7 @@ class Thin6x6Font(Font):
│ \ │ │ │ │
└ \ ┘ ──── └ │ ┘
-""", u"""
+""", """
AAAAAABBBBBBCCCCCCDDDDDDEEEEEEFFFFFFGGGGGGHHHHHHIIJJJJJJ
┌───┐ ┬───┐ ┌───┐ ┬───┐ ┬───┐ ┬───┐ ┌───┐ ┬ ┬ ┬ ┬
│ │ │ │ │ │ │ │ │ │ │ │ │ │
@@ -300,7 +299,7 @@ AAAAAABBBBBBCCCCCCDDDDDDEEEEEEFFFFFFGGGGGGHHHHHHIIJJJJJJ
│ │ │ │ │ │ │ │ │ │ │ │ │ │ ┬ │
┴ ┴ ┴───┘ └───┘ ┴───┘ ┴───┘ ┴ └───┘ ┴ ┴ ┴ └───┘
-""", u"""
+""", """
KKKKKKLLLLLLMMMMMMNNNNNNOOOOOOPPPPPPQQQQQQRRRRRRSSSSSS
┬ ┬ ┬ ┌─┬─┐ ┬─┐ ┬ ┌───┐ ┬───┐ ┌───┐ ┬───┐ ┌───┐
│ ┌─┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
@@ -308,7 +307,7 @@ KKKKKKLLLLLLMMMMMMNNNNNNOOOOOOPPPPPPQQQQQQRRRRRRSSSSSS
│ └┐ │ │ │ │ │ │ │ │ │ │ ┐│ │ └─┐ │
┴ ┴ ┴───┘ ┴ ┴ ┴ └─┴ └───┘ ┴ └──┼┘ ┴ ┴ └───┘
-""", u"""
+""", """
TTTTTTUUUUUUVVVVVVWWWWWWXXXXXXYYYYYYZZZZZZ
┌─┬─┐ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┌───┐
│ │ │ │ │ │ │ └┐ ┌┘ │ │ ┌─┘
@@ -316,7 +315,7 @@ TTTTTTUUUUUUVVVVVVWWWWWWXXXXXXYYYYYYZZZZZZ
│ │ │ └┐ ┌┘ │ │ │ ┌┘ └┐ │ ┌┘
┴ └───┘ └─┘ └─┴─┘ ┴ ┴ ┴ └───┘
-""", u"""
+""", """
aaaaaabbbbbbccccccddddddeeeeeefffgggggghhhhhhiijjj
┌─┐
│ │ │ │ . .
@@ -324,7 +323,7 @@ aaaaaabbbbbbccccccddddddeeeeeefffgggggghhhhhhiijjj
┌───┤ │ │ │ │ │ ├───┘ │ │ │ │ │ │ │
└───┴ └───┘ └───┘ └───┘ └───┘ ┴ └───┤ ┴ ┴ ┴ │
└───┘ ─┘
-""", u"""
+""", """
kkkkkkllmmmmmmnnnnnnooooooppppppqqqqqqrrrrrssssss
│ │
@@ -332,7 +331,7 @@ kkkkkkllmmmmmmnnnnnnooooooppppppqqqqqqrrrrrssssss
├─┴┐ │ │ │ │ │ │ │ │ │ │ │ │ │ └───┐
┴ └─ └ ┴ ┴ ┴ ┴ └───┘ ├───┘ └───┤ ┴ └───┘
│ │
-""", u"""
+""", """
ttttuuuuuuvvvvvvwwwwwwxxxxxxyyyyyyzzzzzz
@@ -346,7 +345,7 @@ add_font("Thin 6x6",Thin6x6Font)
class HalfBlock7x7Font(Font):
height = 7
- data = [u"""
+ data = ["""
0000000111111122222223333333444444455555556666666777777788888889999999'''
▄███▄ ▐█▌ ▄███▄ ▄███▄ █▌ ▐█████▌ ▄███▄ ▐█████▌ ▄███▄ ▄███▄ ▐█
▐█ █▌ ▀█▌ ▐█ █▌▐█ █▌▐█ █▌ ▐█ ▐█ ▐█ ▐█ █▌▐█ █▌▐█
@@ -355,7 +354,7 @@ class HalfBlock7x7Font(Font):
▐█ █▌ █▌ ▄█▀ ▐█ █▌ █▌ █▌▐█ █▌ █▌ ▐█ █▌ █▌
▀███▀ ███▌ ▐█████▌ ▀███▀ █▌ ▐████▀ ▀███▀ ▐█ ▀███▀ ▀███▀
-""", u'''
+""", '''
!!! """""#######$$$$$$$%%%%%%%&&&&&&&(((())))*******++++++
▐█ ▐█ █▌ ▐█ █▌ █ ▄ █▌ ▄█▄ █▌▐█ ▄▄ ▄▄
▐█ ▐█ █▌▐█████▌ ▄███▄ ▐█▌▐█ ▐█ █▌ ▐█ █▌ ▀█▄█▀ ▐█
@@ -364,7 +363,7 @@ class HalfBlock7x7Font(Font):
▐█ █▌ ▀███▀ █▌▐█▌▐█ █▌ ▐█ █▌ ▀▀ ▀▀
▐█ █ ▐█ ▀ ▀██▀█▌ █▌▐█
-''', u"""
+''', """
,,,------.../////:::;;;<<<<<<<======>>>>>>>???????@@@@@@@
█▌ ▄█▌ ▐█▄ ▄███▄ ▄███▄
▐█ ▐█ ▐█ ▄█▀ ▐████▌ ▀█▄ ▐█ █▌▐█ ▄▄█▌
@@ -382,7 +381,7 @@ class HalfBlock7x7Font(Font):
▐█ ▐█ █▌ █▌ ▐█ ▐█
▐██▌ █▌▐██▌ █████ █▌▐█ ▐█
-""", u"""
+""", """
AAAAAAABBBBBBBCCCCCCCDDDDDDDEEEEEEEFFFFFFFGGGGGGGHHHHHHHIIIIJJJJJJJ
▄███▄ ▐████▄ ▄███▄ ▐████▄ ▐█████▌▐█████▌ ▄███▄ ▐█ █▌ ██▌ █▌
▐█ █▌▐█ █▌▐█ ▐█ █▌▐█ ▐█ ▐█ ▐█ █▌ ▐█ █▌
@@ -391,7 +390,7 @@ AAAAAAABBBBBBBCCCCCCCDDDDDDDEEEEEEEFFFFFFFGGGGGGGHHHHHHHIIIIJJJJJJJ
▐█ █▌▐█ █▌▐█ ▐█ █▌▐█ ▐█ ▐█ █▌▐█ █▌ ▐█ ▐█ █▌
▐█ █▌▐████▀ ▀███▀ ▐████▀ ▐█████▌▐█ ▀███▀ ▐█ █▌ ██▌ ▀███▀
-""", u"""
+""", """
KKKKKKKLLLLLLLMMMMMMMMNNNNNNNOOOOOOOPPPPPPPQQQQQQQRRRRRRRSSSSSSS
▐█ █▌▐█ ▄█▌▐█▄ ▐██ █▌ ▄███▄ ▐████▄ ▄███▄ ▐████▄ ▄███▄
▐█ █▌ ▐█ ▐█ ▐▌ █▌▐██▌ █▌▐█ █▌▐█ █▌▐█ █▌▐█ █▌▐█
@@ -400,7 +399,7 @@ KKKKKKKLLLLLLLMMMMMMMMNNNNNNNOOOOOOOPPPPPPPQQQQQQQRRRRRRRSSSSSSS
▐█ █▌ ▐█ ▐█ █▌▐█ ▐██▌▐█ █▌▐█ ▐█ █▌█▌▐█ █▌ █▌
▐█ █▌▐█████▌▐█ █▌▐█ ██▌ ▀███▀ ▐█ ▀███▀ ▐█ █▌ ▀███▀
▀▀
-""", u"""
+""", """
TTTTTTTUUUUUUUVVVVVVVWWWWWWWWXXXXXXXYYYYYYYZZZZZZZ
█████▌▐█ █▌▐█ █▌▐█ █▌▐█ █▌ █▌ █▌▐█████▌
█▌ ▐█ █▌ █▌ ▐█ ▐█ █▌ ▐█ █▌ ▐█ ▐█ █▌
@@ -409,7 +408,7 @@ TTTTTTTUUUUUUUVVVVVVVWWWWWWWWXXXXXXXYYYYYYYZZZZZZZ
█▌ ▐█ █▌ ▐█▌ ▐█ ▐▌ █▌ █▌ ▐█ █▌ █▌
█▌ ▀███▀ █ ▀█▌▐█▀ ▐█ █▌ █▌ ▐█████▌
-""", u"""
+""", """
aaaaaaabbbbbbbcccccccdddddddeeeeeeefffffggggggghhhhhhhiiijjjj
▐█ █▌ ▄█▌ ▐█ █▌ █▌
▐█ █▌ ▐█ ▐█
@@ -418,7 +417,7 @@ aaaaaaabbbbbbbcccccccdddddddeeeeeeefffffggggggghhhhhhhiiijjjj
▐█▀▀▀█▌▐█ █▌▐█ ▐█ █▌▐█▀▀▀ ▐█ ▐█▄▄▄█▌▐█ █▌ █▌ █▌
▀████▌▐████▀ ▀███▀ ▀████▌ ▀███▀ ▐█ ▀▀▀█▌▐█ █▌ █▌ █▌
▀███▀ ▐██
-""", u"""
+""", """
kkkkkkkllllmmmmmmmmnnnnnnnooooooopppppppqqqqqqqrrrrrrsssssss
▐█ ██
▐█ ▐█
@@ -427,7 +426,7 @@ kkkkkkkllllmmmmmmmmnnnnnnnooooooopppppppqqqqqqqrrrrrrsssssss
▐█▀▀█▄ ▐█ ▐█ ▐▌ █▌▐█ █▌▐█ █▌▐█ █▌▐█ █▌▐█ ▀▀▀█▌
▐█ █▌ ▐█▌▐█ █▌▐█ █▌ ▀███▀ ▐████▀ ▀████▌▐█ ▀███▀
▐█ █▌
-""", u"""
+""", """
tttttuuuuuuuvvvvvvvwwwwwwwwxxxxxxxyyyyyyyzzzzzzz
█▌
█▌
@@ -455,6 +454,6 @@ if __name__ == "__main__":
if c == all_ascii:
print("Full ASCII")
elif c.startswith(all_ascii):
- print("Full ASCII + " + c[len(all_ascii):])
+ print(f"Full ASCII + {c[len(all_ascii):]}")
else:
- print("Characters: " + c)
+ print(f"Characters: {c}")
diff --git a/urwid/graphics.py b/urwid/graphics.py
index f94b2a9..71c0791 100755
--- a/urwid/graphics.py
+++ b/urwid/graphics.py
@@ -1,5 +1,4 @@
#!/usr/bin/python
-# -*- coding: utf-8 -*-
#
# Urwid graphics widgets
# Copyright (C) 2004-2011 Ian Ward
@@ -18,20 +17,38 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
-from urwid.compat import ord2, with_metaclass
-from urwid.util import decompose_tagmarkup, get_encoding_mode
-from urwid.canvas import CompositeCanvas, CanvasJoin, TextCanvas, \
- CanvasCombine, SolidCanvas
-from urwid.widget import WidgetMeta, Widget, BOX, FIXED, FLOW, \
- nocache_widget_render, nocache_widget_render_instance, fixed_size, \
- WidgetWrap, Divider, SolidFill, Text, CENTER, CLIP
-from urwid.container import Pile, Columns
-from urwid.display_common import AttrSpec
+from __future__ import annotations
+
+from urwid.canvas import (
+ CanvasCombine,
+ CanvasJoin,
+ CompositeCanvas,
+ SolidCanvas,
+ TextCanvas,
+)
+from urwid.container import Columns, Pile
from urwid.decoration import WidgetDecoration
+from urwid.display_common import AttrSpec
+from urwid.util import decompose_tagmarkup, get_encoding_mode
+from urwid.widget import (
+ BOX,
+ CENTER,
+ CLIP,
+ FIXED,
+ FLOW,
+ Divider,
+ SolidFill,
+ Text,
+ Widget,
+ WidgetMeta,
+ WidgetWrap,
+ fixed_size,
+ nocache_widget_render,
+ nocache_widget_render_instance,
+)
class BigText(Widget):
@@ -101,9 +118,9 @@ class LineBox(WidgetDecoration, WidgetWrap):
def __init__(self, original_widget, title="",
title_align="center", title_attr=None,
- tlcorner=u'┌', tline=u'─', lline=u'│',
- trcorner=u'┐', blcorner=u'└', rline=u'│',
- bline=u'─', brcorner=u'┘'):
+ tlcorner='┌', tline='─', lline='│',
+ trcorner='┐', blcorner='└', rline='│',
+ bline='─', brcorner='┘'):
"""
Draw a line around original_widget.
@@ -175,7 +192,7 @@ class LineBox(WidgetDecoration, WidgetWrap):
else:
# Note: We need to define a fixed first widget (even if it's 0 width) so that the other
# widgets have something to anchor onto
- middle_widgets.append(('fixed', 0, SolidFill(u"")))
+ middle_widgets.append(('fixed', 0, SolidFill("")))
middle_widgets.append(original_widget)
focus_col = len(middle_widgets) - 1
if rline:
@@ -205,7 +222,7 @@ class LineBox(WidgetDecoration, WidgetWrap):
def format_title(self, text):
if len(text) > 0:
- return " %s " % text
+ return f" {text} "
else:
return ""
@@ -240,7 +257,7 @@ class BarGraphMeta(WidgetMeta):
should use set_data() instead of overriding get_data().
"""
def __init__(cls, name, bases, d):
- super(BarGraphMeta, cls).__init__(name, bases, d)
+ super().__init__(name, bases, d)
if "get_data" in d:
cls.render = nocache_widget_render(cls)
@@ -261,13 +278,13 @@ def nocache_bargraph_get_data(self, get_data_fn):
class BarGraphError(Exception):
pass
-class BarGraph(with_metaclass(BarGraphMeta, Widget)):
+class BarGraph(Widget, metaclass=BarGraphMeta):
_sizing = frozenset([BOX])
ignore_focus = True
- eighths = u' ▁▂▃▄▅▆▇'
- hlines = u'_⎺⎻─⎼⎽'
+ eighths = ' ▁▂▃▄▅▆▇'
+ hlines = '_⎺⎻─⎼⎽'
def __init__(self, attlist, hatt=None, satt=None):
"""
@@ -317,7 +334,7 @@ class BarGraph(with_metaclass(BarGraphMeta, Widget)):
self.attr = []
self.char = []
if len(attlist) < 2:
- raise BarGraphError("attlist must include at least background and seg1: %r" % (attlist,))
+ raise BarGraphError(f"attlist must include at least background and seg1: {attlist!r}")
assert len(attlist) >= 2, 'must at least specify bg and fg!'
for a in attlist:
if type(a) != tuple:
@@ -341,13 +358,13 @@ class BarGraph(with_metaclass(BarGraphMeta, Widget)):
try:
(fg, bg), attr = i
except ValueError:
- raise BarGraphError("satt not in (fg,bg:attr) form: %r" % (i,))
+ raise BarGraphError(f"satt not in (fg,bg:attr) form: {i!r}")
if type(fg) != int or fg >= len(attlist):
- raise BarGraphError("fg not valid integer: %r" % (fg,))
+ raise BarGraphError(f"fg not valid integer: {fg!r}")
if type(bg) != int or bg >= len(attlist):
- raise BarGraphError("bg not valid integer: %r" % (fg,))
+ raise BarGraphError(f"bg not valid integer: {fg!r}")
if fg <= bg:
- raise BarGraphError("fg (%s) not > bg (%s)" % (fg, bg))
+ raise BarGraphError(f"fg ({fg}) not > bg ({bg})")
self.satt = satt
def set_data(self, bardata, top, hlines=None):
@@ -369,9 +386,8 @@ class BarGraph(with_metaclass(BarGraphMeta, Widget)):
and it will have a second segment that starts at 30%.
"""
if hlines is not None:
- hlines = hlines[:] # shallow copy
- hlines.sort()
- hlines.reverse()
+ hlines = sorted(hlines[:], reverse=True) # shallow copy
+
self.data = bardata, top, hlines
self._invalidate()
@@ -713,7 +729,7 @@ def calculate_bargraph_display(bardata, top, bar_widths, maxrow):
col += width
barnum += 1
- #print repr(rows)
+ # print(repr(rows))
# build rowsets data structure
rowsets = []
y_count = 0
@@ -807,9 +823,8 @@ class GraphVScale(Widget):
top -- top y position
"""
- labels = labels[:] # shallow copy
- labels.sort()
- labels.reverse()
+ labels = sorted(labels[:], reverse=True) # shallow copy
+
self.pos = []
self.txt = []
for y, markup in labels:
@@ -865,7 +880,7 @@ def scale_bar_values( bar, top, maxrow ):
class ProgressBar(Widget):
_sizing = frozenset([FLOW])
- eighths = u' ▏▎▍▌▋▊▉'
+ eighths = ' ▏▎▍▌▋▊▉'
text_align = CENTER
@@ -933,7 +948,7 @@ class ProgressBar(Widget):
You can override this method to display custom text.
"""
percent = min(100, max(0, int(self.current * 100 / self.done)))
- return str(percent) + " %"
+ return f"{str(percent)} %"
def render(self, size, focus=False):
"""
@@ -957,7 +972,7 @@ class ProgressBar(Widget):
c._attr = [[(self.normal, maxcol)]]
elif ccol >= maxcol:
c._attr = [[(self.complete, maxcol)]]
- elif cs and ord2(c._text[0][ccol]) == 32:
+ elif cs and c._text[0][ccol] == 32:
t = c._text[0]
cenc = self.eighths[cs].encode("utf-8")
c._text[0] = t[:ccol] + cenc + t[ccol + 1:]
diff --git a/urwid/html_fragment.py b/urwid/html_fragment.py
index 5df9273..b14c57f 100755
--- a/urwid/html_fragment.py
+++ b/urwid/html_fragment.py
@@ -17,18 +17,18 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
"""
HTML PRE-based UI implementation
"""
+from __future__ import annotations
+
from urwid import util
-from urwid.main_loop import ExitMainLoop
from urwid.display_common import AttrSpec, BaseScreen
-
+from urwid.main_loop import ExitMainLoop
# replace control characters with ?'s
_trans_table = "?" * 32 + "".join([chr(x) for x in range(32, 256)])
@@ -47,7 +47,7 @@ class HtmlGenerator(BaseScreen):
started = True
def __init__(self):
- super(HtmlGenerator, self).__init__()
+ super().__init__()
self.colors = 16
self.bright_is_bold = False # ignored
self.has_underline = True # ignored
@@ -124,7 +124,7 @@ class HtmlGenerator(BaseScreen):
l.append("\n")
# add the fragment to the list
- self.fragments.append( "<pre>%s</pre>" % "".join(l) )
+ self.fragments.append( f"<pre>{''.join(l)}</pre>" )
def clear(self):
"""
@@ -160,8 +160,8 @@ def html_span(s, aspec, cursor = -1):
fg_r, fg_g, fg_b = _d_fg_r, _d_fg_g, _d_fg_b
if bg_r is None:
bg_r, bg_g, bg_b = _d_bg_r, _d_bg_g, _d_bg_b
- html_fg = "#%02x%02x%02x" % (fg_r, fg_g, fg_b)
- html_bg = "#%02x%02x%02x" % (bg_r, bg_g, bg_b)
+ html_fg = f"#{fg_r:02x}{fg_g:02x}{fg_b:02x}"
+ html_bg = f"#{bg_r:02x}{bg_g:02x}{bg_b:02x}"
if aspec.standout:
html_fg, html_bg = html_bg, html_fg
extra = (";text-decoration:underline" * aspec.underline +
diff --git a/urwid/lcd_display.py b/urwid/lcd_display.py
index e189d9a..9b0a8e8 100644
--- a/urwid/lcd_display.py
+++ b/urwid/lcd_display.py
@@ -1,5 +1,4 @@
#!/usr/bin/python
-# -*- coding: utf-8 -*-
#
# Urwid LCD display module
# Copyright (C) 2010 Ian Ward
@@ -18,14 +17,16 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
-from .display_common import BaseScreen
+from __future__ import annotations
import time
+from .display_common import BaseScreen
+
+
class LCDScreen(BaseScreen):
def set_terminal_properties(self, colors=None, bright_is_bold=None,
has_underline=None):
@@ -93,7 +94,7 @@ class CFLCDScreen(LCDScreen):
device_path -- eg. '/dev/ttyUSB0'
baud -- baud rate
"""
- super(CFLCDScreen, self).__init__()
+ super().__init__()
self.device_path = device_path
from serial import Serial
self._device = Serial(device_path, baud, timeout=0)
@@ -196,7 +197,7 @@ class CFLCDScreen(LCDScreen):
-class KeyRepeatSimulator(object):
+class KeyRepeatSimulator:
"""
Provide simulated repeat key events when given press and
release events.
@@ -252,7 +253,7 @@ class KeyRepeatSimulator(object):
class CF635Screen(CFLCDScreen):
- u"""
+ """
Crystal Fontz 635 display
20x4 character display + cursor
@@ -274,22 +275,22 @@ class CF635Screen(CFLCDScreen):
# both groups are intended to draw horizontal bars with pixel
# precision, use ▇*[▆▄▃▁]? for a thin bar or ▉*[▋▌▍▏]? for a thick bar
CGROM = (
- u"①②③④⑤⑥⑦⑧①②③④⑤⑥⑦⑧"
- u"►◄⇑⇓«»↖↗↙↘▲▼↲^ˇ█"
- u" !\"#¤%&'()*+,-./"
- u"0123456789:;<=>?"
- u"¡ABCDEFGHIJKLMNO"
- u"PQRSTUVWXYZÄÖÑܧ"
- u"¿abcdefghijklmno"
- u"pqrstuvwxyzäöñüà"
- u"⁰¹²³⁴⁵⁶⁷⁸⁹½¼±≥≤μ"
- u"♪♫⑴♥♦⑵⌜⌟“”()αɛδ∞"
- u"@£$¥èéùìòÇᴾØøʳÅå"
- u"⌂¢ΦτλΩπΨΣθΞ♈ÆæßÉ"
- u"ΓΛΠϒ_ÈÊêçğŞşİι~◊"
- u"▇▆▄▃▁ƒ▉▋▌▍▏⑶◽▪↑→"
- u"↓←ÁÍÓÚÝáíóúýÔôŮů"
- u"ČĔŘŠŽčĕřšž[\]{|}")
+ "①②③④⑤⑥⑦⑧①②③④⑤⑥⑦⑧"
+ "►◄⇑⇓«»↖↗↙↘▲▼↲^ˇ█"
+ " !\"#¤%&'()*+,-./"
+ "0123456789:;<=>?"
+ "¡ABCDEFGHIJKLMNO"
+ "PQRSTUVWXYZÄÖÑܧ"
+ "¿abcdefghijklmno"
+ "pqrstuvwxyzäöñüà"
+ "⁰¹²³⁴⁵⁶⁷⁸⁹½¼±≥≤μ"
+ "♪♫⑴♥♦⑵⌜⌟“”()αɛδ∞"
+ "@£$¥èéùìòÇᴾØøʳÅå"
+ "⌂¢ΦτλΩπΨΣθΞ♈ÆæßÉ"
+ "ΓΛΠϒ_ÈÊêçğŞşİι~◊"
+ "▇▆▄▃▁ƒ▉▋▌▍▏⑶◽▪↑→"
+ "↓←ÁÍÓÚÝáíóúýÔôŮů"
+ r"ČĔŘŠŽčĕřšž[\]{|}")
cursor_style = CFLCDScreen.CURSOR_INVERTING_BLINKING_BLOCK
@@ -303,7 +304,7 @@ class CF635Screen(CFLCDScreen):
repeat_next -- time between each repeated key
key_map -- the keys to send for this device's buttons
"""
- super(CF635Screen, self).__init__(device_path, baud)
+ super().__init__(device_path, baud)
self.repeat_delay = repeat_delay
self.repeat_next = repeat_next
diff --git a/urwid/listbox.py b/urwid/listbox.py
index e49edaa..4a5b5ae 100644
--- a/urwid/listbox.py
+++ b/urwid/listbox.py
@@ -17,26 +17,33 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
-from urwid.compat import xrange, with_metaclass
-from urwid.util import is_mouse_press
-from urwid.canvas import SolidCanvas, CanvasCombine
-from urwid.widget import Widget, nocache_widget_render_instance, BOX, GIVEN
-from urwid.decoration import calculate_top_bottom_filler, normalize_valign
+from __future__ import annotations
+
from urwid import signals
-from urwid.signals import connect_signal, disconnect_signal
-from urwid.monitored_list import MonitoredList, MonitoredFocusList
+from urwid.canvas import CanvasCombine, SolidCanvas
+from urwid.command_map import (
+ CURSOR_DOWN,
+ CURSOR_MAX_LEFT,
+ CURSOR_MAX_RIGHT,
+ CURSOR_PAGE_DOWN,
+ CURSOR_PAGE_UP,
+ CURSOR_UP,
+)
from urwid.container import WidgetContainerMixin
-from urwid.command_map import (CURSOR_UP, CURSOR_DOWN,
- CURSOR_PAGE_UP, CURSOR_PAGE_DOWN, CURSOR_MAX_LEFT, CURSOR_MAX_RIGHT)
+from urwid.decoration import calculate_top_bottom_filler, normalize_valign
+from urwid.monitored_list import MonitoredFocusList, MonitoredList
+from urwid.signals import connect_signal, disconnect_signal
+from urwid.util import is_mouse_press
+from urwid.widget import BOX, GIVEN, Widget, nocache_widget_render_instance
+
class ListWalkerError(Exception):
pass
-class ListWalker(with_metaclass(signals.MetaSignals, object)):
+class ListWalker(metaclass=signals.MetaSignals):
signals = ["modified"]
def _modified(self):
@@ -96,7 +103,7 @@ class SimpleListWalker(MonitoredList, ListWalker):
this list walker to be updated.
"""
if not getattr(contents, '__getitem__', None):
- raise ListWalkerError("SimpleListWalker expecting list like object, got: %r"%(contents,))
+ raise ListWalkerError(f"SimpleListWalker expecting list like object, got: {contents!r}")
MonitoredList.__init__(self, contents)
self.focus = 0
self.wrap_around = wrap_around
@@ -131,7 +138,7 @@ class SimpleListWalker(MonitoredList, ListWalker):
if position < 0 or position >= len(self):
raise ValueError
except (TypeError, ValueError):
- raise IndexError("No widget at position %s" % (position,))
+ raise IndexError(f"No widget at position {position}")
self.focus = position
self._modified()
@@ -160,8 +167,8 @@ class SimpleListWalker(MonitoredList, ListWalker):
Optional method for returning an iterable of positions.
"""
if reverse:
- return xrange(len(self) - 1, -1, -1)
- return xrange(len(self))
+ return range(len(self) - 1, -1, -1)
+ return range(len(self))
class SimpleFocusListWalker(ListWalker, MonitoredFocusList):
@@ -228,8 +235,8 @@ class SimpleFocusListWalker(ListWalker, MonitoredFocusList):
Optional method for returning an iterable of positions.
"""
if reverse:
- return xrange(len(self) - 1, -1, -1)
- return xrange(len(self))
+ return range(len(self) - 1, -1, -1)
+ return range(len(self))
class ListBoxError(Exception):
@@ -466,7 +473,7 @@ class ListBox(Widget, WidgetContainerMixin):
raise ListBoxError("Focus Widget %r at position %r within listbox calculated %d rows but rendered %d!"% (focus_widget,focus_pos,focus_rows, focus_canvas.rows()))
c_cursor = focus_canvas.cursor
if cursor is not None and cursor != c_cursor:
- raise ListBoxError("Focus Widget %r at position %r within listbox calculated cursor coords %r but rendered cursor coords %r!" %(focus_widget,focus_pos,cursor,c_cursor))
+ raise ListBoxError(f"Focus Widget {focus_widget!r} at position {focus_pos!r} within listbox calculated cursor coords {cursor!r} but rendered cursor coords {c_cursor!r}!")
rows += focus_rows
combinelist.append((focus_canvas, focus_pos, True))
@@ -488,13 +495,13 @@ class ListBox(Widget, WidgetContainerMixin):
rows -= trim_bottom
if rows > maxrow:
- raise ListBoxError("Listbox contents too long! Probably urwid's fault (please report): %r" % ((top,middle,bottom),))
+ raise ListBoxError(f"Listbox contents too long! Probably urwid's fault (please report): {top, middle, bottom!r}")
if rows < maxrow:
bottom_pos = focus_pos
if fill_below: bottom_pos = fill_below[-1][1]
if trim_bottom != 0 or self._body.get_next(bottom_pos) != (None,None):
- raise ListBoxError("Listbox contents too short! Probably urwid's fault (please report): %r" % ((top,middle,bottom),))
+ raise ListBoxError(f"Listbox contents too short! Probably urwid's fault (please report): {top, middle, bottom!r}")
final_canvas.pad_trim_top_bottom(0, maxrow - rows)
return final_canvas
@@ -545,8 +552,7 @@ class ListBox(Widget, WidgetContainerMixin):
:type coming_from: str
"""
if coming_from not in ('above', 'below', None):
- raise ListBoxError("coming_from value invalid: %r" %
- (coming_from,))
+ raise ListBoxError(f"coming_from value invalid: {coming_from!r}")
focus_widget, focus_pos = self._body.get_focus()
if focus_widget is None:
raise IndexError("Can't set focus, ListBox is empty")
@@ -588,7 +594,7 @@ class ListBox(Widget, WidgetContainerMixin):
""")
def _contents(self):
- class ListBoxContents(object):
+ class ListBoxContents:
__getitem__ = self._contents__getitem__
return ListBoxContents()
def _contents__getitem__(self, key):
@@ -598,7 +604,7 @@ class ListBox(Widget, WidgetContainerMixin):
try:
return (getitem(key), None)
except (IndexError, KeyError):
- raise KeyError("ListBox.contents key not found: %r" % (key,))
+ raise KeyError(f"ListBox.contents key not found: {key!r}")
# fall back to v1
w, old_focus = self._body.get_focus()
try:
@@ -606,10 +612,13 @@ class ListBox(Widget, WidgetContainerMixin):
self._body.set_focus(key)
return self._body.get_focus()[0]
except (IndexError, KeyError):
- raise KeyError("ListBox.contents key not found: %r" % (key,))
+ raise KeyError(f"ListBox.contents key not found: {key!r}")
finally:
self._body.set_focus(old_focus)
- contents = property(lambda self: self._contents, doc="""
+
+ @property
+ def contents(self):
+ """
An object that allows reading widgets from the ListBox's list
walker as a `(widget, options)` tuple. `None` is currently the only
value for options.
@@ -620,7 +629,8 @@ class ListBox(Widget, WidgetContainerMixin):
You must use the list walker stored as
:attr:`.body` to perform manipulation and iteration, if supported.
- """)
+ """
+ return self._contents
def options(self):
"""
@@ -760,14 +770,14 @@ class ListBox(Widget, WidgetContainerMixin):
if offset_inset >= 0:
if offset_inset >= maxrow:
- raise ListBoxError("Invalid offset_inset: %r, only %r rows in list box"% (offset_inset, maxrow))
+ raise ListBoxError(f"Invalid offset_inset: {offset_inset!r}, only {maxrow!r} rows in list box")
self.offset_rows = offset_inset
self.inset_fraction = (0,1)
else:
target, _ignore = self._body.get_focus()
tgt_rows = target.rows( (maxcol,), True )
if offset_inset + tgt_rows <= 0:
- raise ListBoxError("Invalid offset_inset: %r, only %r rows in target!" %(offset_inset, tgt_rows))
+ raise ListBoxError(f"Invalid offset_inset: {offset_inset!r}, only {tgt_rows!r} rows in target!")
self.offset_rows = 0
self.inset_fraction = (-offset_inset,tgt_rows)
self._invalidate()
@@ -864,7 +874,7 @@ class ListBox(Widget, WidgetContainerMixin):
self.inset_fraction = (0,1)
else:
if offset_inset + tgt_rows <= 0:
- raise ListBoxError("Invalid offset_inset: %s, only %s rows in target!" %(offset_inset, tgt_rows))
+ raise ListBoxError(f"Invalid offset_inset: {offset_inset}, only {tgt_rows} rows in target!")
self.offset_rows = 0
self.inset_fraction = (-offset_inset,tgt_rows)
@@ -892,7 +902,7 @@ class ListBox(Widget, WidgetContainerMixin):
# start from preferred row and move back to closest edge
(pref_col, pref_row) = cursor_coords
if pref_row < 0 or pref_row >= tgt_rows:
- raise ListBoxError("cursor_coords row outside valid range for target. pref_row:%r target_rows:%r"%(pref_row,tgt_rows))
+ raise ListBoxError(f"cursor_coords row outside valid range for target. pref_row:{pref_row!r} target_rows:{tgt_rows!r}")
if coming_from=='above':
attempt_rows = range( pref_row, -1, -1 )
@@ -915,7 +925,7 @@ class ListBox(Widget, WidgetContainerMixin):
if offset_rows == 0:
inum, iden = self.inset_fraction
if inum < 0 or iden < 0 or inum >= iden:
- raise ListBoxError("Invalid inset_fraction: %r"%(self.inset_fraction,))
+ raise ListBoxError(f"Invalid inset_fraction: {self.inset_fraction!r}")
inset_rows = focus_rows * inum // iden
if inset_rows and inset_rows >= focus_rows:
raise ListBoxError("urwid inset_fraction error (please report)")
@@ -1247,8 +1257,8 @@ class ListBox(Widget, WidgetContainerMixin):
# choose the topmost selectable and (newly) visible widget
# search within snap_rows then visible region
- search_order = (list(xrange(snap_region_start, len(t)))
- + list(xrange(snap_region_start-1, -1, -1)))
+ search_order = (list(range(snap_region_start, len(t)))
+ + list(range(snap_region_start-1, -1, -1)))
#assert 0, repr((t, search_order))
bad_choices = []
cut_off_selectable_chosen = 0
@@ -1432,8 +1442,8 @@ class ListBox(Widget, WidgetContainerMixin):
# choose the bottommost selectable and (newly) visible widget
# search within snap_rows then visible region
- search_order = (list(xrange(snap_region_start, len(t)))
- + list(xrange(snap_region_start-1, -1, -1)))
+ search_order = (list(range(snap_region_start, len(t)))
+ + list(range(snap_region_start-1, -1, -1)))
#assert 0, repr((t, search_order))
bad_choices = []
cut_off_selectable_chosen = 0
diff --git a/urwid/main_loop.py b/urwid/main_loop.py
index 5149505..0d91a60 100755
--- a/urwid/main_loop.py
+++ b/urwid/main_loop.py
@@ -19,16 +19,17 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
-import time
+from __future__ import annotations
+
import heapq
-import select
import os
+import select
import signal
import sys
+import time
from functools import wraps
from itertools import count
from weakref import WeakKeyDictionary
@@ -38,12 +39,12 @@ try:
except ImportError:
pass # windows
-from urwid.util import StoppingContext, is_mouse_event
-from urwid.compat import PYTHON3, reraise
-from urwid.command_map import command_map, REDRAW_SCREEN
-from urwid.wimp import PopUpTarget
from urwid import signals
+from urwid.command_map import REDRAW_SCREEN, command_map
+from urwid.compat import reraise
from urwid.display_common import INPUT_DESCRIPTORS_CHANGED
+from urwid.util import StoppingContext, is_mouse_event
+from urwid.wimp import PopUpTarget
PIPE_BUFFER_READ_SIZE = 4096 # can expect this much on Linux, so try for that
@@ -57,7 +58,7 @@ class ExitMainLoop(Exception):
class CantUseExternalLoop(Exception):
pass
-class MainLoop(object):
+class MainLoop:
"""
This is the standard main loop implementation for a single interactive
session.
@@ -589,7 +590,7 @@ class MainLoop(object):
self.screen.draw_screen(self.screen_size, canvas)
-class EventLoop(object):
+class EventLoop:
"""
Abstract class representing an event loop to be used by :class:`MainLoop`.
"""
@@ -788,7 +789,7 @@ class SelectEventLoop(EventLoop):
while True:
try:
self._loop()
- except select.error as e:
+ except OSError as e:
if e.args[0] != 4:
# not just something we need to retry
raise
@@ -1044,7 +1045,7 @@ class TornadoEventLoop(EventLoop):
_ioloop_registry = WeakKeyDictionary() # {<ioloop> : {<handle> : <idle_func>}}
_max_idle_handle = 0
- class PollProxy(object):
+ class PollProxy:
""" A simple proxy for a Python's poll object that wraps the .poll() method
in order to detect idle periods and call Urwid callbacks
"""
@@ -1217,7 +1218,7 @@ class TwistedEventLoop(EventLoop):
instead call start() and stop() before and after starting the
reactor.
- .. _Twisted: http://twistedmatrix.com/trac/
+ .. _Twisted: https://twisted.org/
"""
if reactor is None:
import twisted.internet.reactor
@@ -1251,7 +1252,7 @@ class TwistedEventLoop(EventLoop):
Returns True if the alarm exists, False otherwise
"""
- from twisted.internet.error import AlreadyCancelled, AlreadyCalled
+ from twisted.internet.error import AlreadyCalled, AlreadyCancelled
try:
handle.cancel()
return True
@@ -1496,10 +1497,9 @@ class AsyncioEventLoop(EventLoop):
# Import Trio's event loop only if we are on Python 3.5 or above (async def is
# not supported in earlier versions).
-if sys.version_info >= (3, 5):
- from ._async_kw_event_loop import TrioEventLoop
+from ._async_kw_event_loop import TrioEventLoop
+
-
def _refl(name, rval=None, exit=False):
"""
This function is used to test the main loop classes.
@@ -1516,26 +1516,26 @@ def _refl(name, rval=None, exit=False):
42
"""
- class Reflect(object):
+ class Reflect:
def __init__(self, name, rval=None):
self._name = name
self._rval = rval
def __call__(self, *argl, **argd):
args = ", ".join([repr(a) for a in argl])
if args and argd:
- args = args + ", "
- args = args + ", ".join([k+"="+repr(v) for k,v in argd.items()])
- print(self._name+"("+args+")")
+ args = f"{args}, "
+ args = args + ", ".join([f"{k}={repr(v)}" for k,v in argd.items()])
+ print(f"{self._name}({args})")
if exit:
raise ExitMainLoop()
return self._rval
def __getattr__(self, attr):
if attr.endswith("_rval"):
raise AttributeError()
- #print self._name+"."+attr
- if hasattr(self, attr+"_rval"):
- return Reflect(self._name+"."+attr, getattr(self, attr+"_rval"))
- return Reflect(self._name+"."+attr)
+ #print(self._name+"."+attr)
+ if hasattr(self, f"{attr}_rval"):
+ return Reflect(f"{self._name}.{attr}", getattr(self, f"{attr}_rval"))
+ return Reflect(f"{self._name}.{attr}")
return Reflect(name)
def _test():
diff --git a/urwid/monitored_list.py b/urwid/monitored_list.py
index c159064..8a04615 100755
--- a/urwid/monitored_list.py
+++ b/urwid/monitored_list.py
@@ -17,11 +17,7 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
-
-from __future__ import division, print_function
-
-from urwid.compat import PYTHON3, xrange
+# Urwid web site: https://urwid.org/
def _call_modified(fn):
@@ -63,18 +59,16 @@ class MonitoredList(list):
self._modified = callback
def __repr__(self):
- return "%s(%r)" % (self.__class__.__name__, list(self))
+ return f"{self.__class__.__name__}({list(self)!r})"
__add__ = _call_modified(list.__add__)
__delitem__ = _call_modified(list.__delitem__)
- if not PYTHON3:
- __delslice__ = _call_modified(list.__delslice__)
+
__iadd__ = _call_modified(list.__iadd__)
__imul__ = _call_modified(list.__imul__)
__rmul__ = _call_modified(list.__rmul__)
__setitem__ = _call_modified(list.__setitem__)
- if not PYTHON3:
- __setslice__ = _call_modified(list.__setslice__)
+
append = _call_modified(list.append)
extend = _call_modified(list.extend)
insert = _call_modified(list.insert)
@@ -114,14 +108,13 @@ class MonitoredFocusList(MonitoredList):
"""
focus = argd.pop('focus', 0)
- super(MonitoredFocusList, self).__init__(*argl, **argd)
+ super().__init__(*argl, **argd)
self._focus = focus
self._focus_modified = lambda ml, indices, new_items: None
def __repr__(self):
- return "%s(%r, focus=%r)" % (
- self.__class__.__name__, list(self), self.focus)
+ return f"{self.__class__.__name__}({list(self)!r}, focus={self.focus!r})"
def _get_focus(self):
"""
@@ -162,9 +155,9 @@ class MonitoredFocusList(MonitoredList):
self._focus = 0
return
if index < 0 or index >= len(self):
- raise IndexError('focus index is out of range: %s' % (index,))
+ raise IndexError(f'focus index is out of range: {index}')
if index != int(index):
- raise IndexError('invalid focus index: %s' % (index,))
+ raise IndexError(f'invalid focus index: {index}')
index = int(index)
if index != self._focus:
self._focus_changed(index)
@@ -240,7 +233,7 @@ class MonitoredFocusList(MonitoredList):
"""
num_new_items = len(new_items)
start, stop, step = indices = slc.indices(len(self))
- num_removed = len(list(xrange(*indices)))
+ num_removed = len(list(range(*indices)))
focus = self._validate_contents_modified(indices, new_items)
if focus is not None:
@@ -257,11 +250,11 @@ class MonitoredFocusList(MonitoredList):
else:
if not num_new_items:
# extended slice being removed
- if focus in xrange(start, stop, step):
+ if focus in range(start, stop, step):
focus += 1
# adjust for removed items
- focus -= len(list(xrange(start, min(focus, stop), step)))
+ focus -= len(list(range(start, min(focus, stop), step)))
return min(focus, len(self) + num_new_items - num_removed -1)
@@ -298,7 +291,7 @@ class MonitoredFocusList(MonitoredList):
else:
focus = self._adjust_focus_on_contents_modified(slice(y,
y+1 or None))
- rval = super(MonitoredFocusList, self).__delitem__(y)
+ rval = super().__delitem__(y)
self._set_focus(focus)
return rval
@@ -335,17 +328,10 @@ class MonitoredFocusList(MonitoredList):
focus = self._adjust_focus_on_contents_modified(i, y)
else:
focus = self._adjust_focus_on_contents_modified(slice(i, i+1 or None), [y])
- rval = super(MonitoredFocusList, self).__setitem__(i, y)
+ rval = super().__setitem__(i, y)
self._set_focus(focus)
return rval
- if not PYTHON3:
- def __delslice__(self, i, j):
- return self.__delitem__(slice(i,j))
-
- def __setslice__(self, i, j, y):
- return self.__setitem__(slice(i, j), y)
-
def __imul__(self, n):
"""
>>> def modified(indices, new_items):
@@ -366,7 +352,7 @@ class MonitoredFocusList(MonitoredList):
slice(len(self), len(self)), list(self)*(n-1))
else: # all contents are being removed
focus = self._adjust_focus_on_contents_modified(slice(0, len(self)))
- rval = super(MonitoredFocusList, self).__imul__(n)
+ rval = super().__imul__(n)
self._set_focus(focus)
return rval
@@ -381,7 +367,7 @@ class MonitoredFocusList(MonitoredList):
"""
focus = self._adjust_focus_on_contents_modified(
slice(len(self), len(self)), [item])
- rval = super(MonitoredFocusList, self).append(item)
+ rval = super().append(item)
self._set_focus(focus)
return rval
@@ -396,7 +382,7 @@ class MonitoredFocusList(MonitoredList):
"""
focus = self._adjust_focus_on_contents_modified(
slice(len(self), len(self)), items)
- rval = super(MonitoredFocusList, self).extend(items)
+ rval = super().extend(items)
self._set_focus(focus)
return rval
@@ -412,7 +398,7 @@ class MonitoredFocusList(MonitoredList):
"""
focus = self._adjust_focus_on_contents_modified(slice(index, index),
[item])
- rval = super(MonitoredFocusList, self).insert(index, item)
+ rval = super().insert(index, item)
self._set_focus(focus)
return rval
@@ -434,7 +420,7 @@ class MonitoredFocusList(MonitoredList):
"""
focus = self._adjust_focus_on_contents_modified(slice(index,
index+1 or None))
- rval = super(MonitoredFocusList, self).pop(index)
+ rval = super().pop(index)
self._set_focus(focus)
return rval
@@ -451,7 +437,7 @@ class MonitoredFocusList(MonitoredList):
index = self.index(value)
focus = self._adjust_focus_on_contents_modified(slice(index,
index+1 or None))
- rval = super(MonitoredFocusList, self).remove(value)
+ rval = super().remove(value)
self._set_focus(focus)
return rval
@@ -461,7 +447,7 @@ class MonitoredFocusList(MonitoredList):
>>> ml.reverse(); ml
MonitoredFocusList([4, 3, 2, 1, 0], focus=3)
"""
- rval = super(MonitoredFocusList, self).reverse()
+ rval = super().reverse()
self._set_focus(max(0, len(self) - self._focus - 1))
return rval
@@ -474,14 +460,14 @@ class MonitoredFocusList(MonitoredList):
if not self:
return
value = self[self._focus]
- rval = super(MonitoredFocusList, self).sort(**kwargs)
+ rval = super().sort(**kwargs)
self._set_focus(self.index(value))
return rval
if hasattr(list, 'clear'):
def clear(self):
focus = self._adjust_focus_on_contents_modified(slice(0, 0))
- rval = super(MonitoredFocusList, self).clear()
+ rval = super().clear()
self._set_focus(focus)
return rval
diff --git a/urwid/numedit.py b/urwid/numedit.py
index 3e073cf..ff372d5 100644
--- a/urwid/numedit.py
+++ b/urwid/numedit.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
#
# Urwid basic widget classes
# Copyright (C) 2004-2012 Ian Ward
@@ -17,12 +16,15 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from urwid import Edit
-from decimal import Decimal
+from __future__ import annotations
+
import re
+from decimal import Decimal
+
+from urwid import Edit
class NumEdit(Edit):
@@ -38,7 +40,7 @@ class NumEdit(Edit):
ALLOWED = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def __init__(self, allowed, caption, default, trimLeadingZeros=True):
- super(NumEdit, self).__init__(caption, default)
+ super().__init__(caption, default)
self._allowed = allowed
self.trimLeadingZeros = trimLeadingZeros
@@ -149,10 +151,9 @@ class IntegerEdit(NumEdit):
# in case a float is passed or some other error
if isinstance(default, str) and len(default):
# check if it is a valid initial value
- validation_re = "^[{}]+$".format(allowed_chars)
+ validation_re = f"^[{allowed_chars}]+$"
if not re.match(validation_re, str(default), re.IGNORECASE):
- raise ValueError("invalid value: {} for base {}".format(
- default, base))
+ raise ValueError(f"invalid value: {default} for base {base}")
elif isinstance(default, Decimal):
# a Decimal instance with no fractional part
@@ -162,7 +163,7 @@ class IntegerEdit(NumEdit):
# convert possible int, long or Decimal to str
val = str(default)
- super(IntegerEdit, self).__init__(allowed_chars, caption, val,
+ super().__init__(allowed_chars, caption, val,
trimLeadingZeros=(self.base == 10))
def value(self):
@@ -238,8 +239,7 @@ class FloatEdit(NumEdit):
self.significance = None
self._decimalSeparator = decimalSeparator
if decimalSeparator not in ['.', ',']:
- raise ValueError("invalid decimalSeparator: {}".format(
- decimalSeparator))
+ raise ValueError(f"invalid decimalSeparator: {decimalSeparator}")
val = ""
if default is not None and default != "":
@@ -257,7 +257,7 @@ class FloatEdit(NumEdit):
val = str(default)
- super(FloatEdit, self).__init__(self.ALLOWED[0:10] + decimalSeparator,
+ super().__init__(self.ALLOWED[0:10] + decimalSeparator,
caption, val)
def value(self):
diff --git a/urwid/old_str_util.py b/urwid/old_str_util.py
index 4d3461d..920b4a8 100755
--- a/urwid/old_str_util.py
+++ b/urwid/old_str_util.py
@@ -1,5 +1,4 @@
#!/usr/bin/python
-# -*- coding: utf-8 -*-
#
# Urwid unicode character processing tables
# Copyright (C) 2004-2011 Ian Ward
@@ -18,16 +17,15 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
-import re
+from __future__ import annotations
-from urwid.compat import B, ord2, text_type
+import re
-SAFE_ASCII_RE = re.compile(u"^[ -~]*$")
-SAFE_ASCII_BYTES_RE = re.compile(B("^[ -~]*$"))
+SAFE_ASCII_RE = re.compile("^[ -~]*$")
+SAFE_ASCII_BYTES_RE = re.compile(b"^[ -~]*$")
_byte_encoding = None
@@ -93,7 +91,7 @@ def decode_one( text, pos ):
Return (ordinal at pos, next position) for UTF-8 encoded text.
"""
assert isinstance(text, bytes), text
- b1 = ord2(text[pos])
+ b1 = text[pos]
if not b1 & 0x80:
return b1, pos+1
error = ord("?"), pos+1
@@ -102,7 +100,7 @@ def decode_one( text, pos ):
if lt < 2:
return error
if b1 & 0xe0 == 0xc0:
- b2 = ord2(text[pos+1])
+ b2 = text[pos+1]
if b2 & 0xc0 != 0x80:
return error
o = ((b1&0x1f)<<6)|(b2&0x3f)
@@ -112,10 +110,10 @@ def decode_one( text, pos ):
if lt < 3:
return error
if b1 & 0xf0 == 0xe0:
- b2 = ord2(text[pos+1])
+ b2 = text[pos+1]
if b2 & 0xc0 != 0x80:
return error
- b3 = ord2(text[pos+2])
+ b3 = text[pos+2]
if b3 & 0xc0 != 0x80:
return error
o = ((b1&0x0f)<<12)|((b2&0x3f)<<6)|(b3&0x3f)
@@ -125,13 +123,13 @@ def decode_one( text, pos ):
if lt < 4:
return error
if b1 & 0xf8 == 0xf0:
- b2 = ord2(text[pos+1])
+ b2 = text[pos+1]
if b2 & 0xc0 != 0x80:
return error
- b3 = ord2(text[pos+2])
+ b3 = text[pos+2]
if b3 & 0xc0 != 0x80:
return error
- b4 = ord2(text[pos+2])
+ b4 = text[pos+2]
if b4 & 0xc0 != 0x80:
return error
o = ((b1&0x07)<<18)|((b2&0x3f)<<12)|((b3&0x3f)<<6)|(b4&0x3f)
@@ -155,7 +153,7 @@ def decode_one_right(text, pos):
error = ord("?"), pos-1
p = pos
while p >= 0:
- if ord2(text[p])&0xc0 != 0x80:
+ if text[p]&0xc0 != 0x80:
o, next = decode_one( text, p )
return o, p-1
p -=1
@@ -242,7 +240,7 @@ def is_wide_char(text, offs):
text may be unicode or a byte string in the target _byte_encoding
"""
- if isinstance(text, text_type):
+ if isinstance(text, str):
o = ord(text[offs])
return get_width(o) == 2
assert isinstance(text, bytes)
@@ -258,12 +256,12 @@ def move_prev_char(text, start_offs, end_offs):
Return the position of the character before end_offs.
"""
assert start_offs < end_offs
- if isinstance(text, text_type):
+ if isinstance(text, str):
return end_offs-1
assert isinstance(text, bytes)
if _byte_encoding == "utf8":
o = end_offs-1
- while ord2(text[o])&0xc0 == 0x80:
+ while text[o]&0xc0 == 0x80:
o -= 1
return o
if _byte_encoding == "wide" and within_double_byte(text,
@@ -276,12 +274,12 @@ def move_next_char(text, start_offs, end_offs):
Return the position of the character after start_offs.
"""
assert start_offs < end_offs
- if isinstance(text, text_type):
+ if isinstance(text, str):
return start_offs+1
assert isinstance(text, bytes)
if _byte_encoding == "utf8":
o = start_offs+1
- while o<end_offs and ord2(text[o])&0xc0 == 0x80:
+ while o<end_offs and text[o]&0xc0 == 0x80:
o += 1
return o
if _byte_encoding == "wide" and within_double_byte(text,
@@ -302,13 +300,13 @@ def within_double_byte(text, line_start, pos):
2 -- pos is on the 2nd half of a dbe char
"""
assert isinstance(text, bytes)
- v = ord2(text[pos])
+ v = text[pos]
if v >= 0x40 and v < 0x7f:
# might be second half of big5, uhc or gbk encoding
if pos == line_start: return 0
- if ord2(text[pos-1]) >= 0x81:
+ if text[pos-1] >= 0x81:
if within_double_byte(text, line_start, pos-1) == 1:
return 2
return 0
@@ -317,7 +315,7 @@ def within_double_byte(text, line_start, pos):
i = pos -1
while i >= line_start:
- if ord2(text[i]) < 0x80:
+ if text[i] < 0x80:
break
i -= 1
@@ -361,7 +359,7 @@ def process_east_asian_width():
print("widths = [")
for o in out[1:]: # treat control characters same as ascii
- print("\t%r," % (o,))
+ print(f"\t{o!r},")
print("]")
if __name__ == "__main__":
diff --git a/urwid/raw_display.py b/urwid/raw_display.py
index db470a5..f24e30b 100644
--- a/urwid/raw_display.py
+++ b/urwid/raw_display.py
@@ -17,19 +17,20 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
"""
Direct terminal UI implementation
"""
+from __future__ import annotations
+
import os
import select
+import signal
import struct
import sys
-import signal
try:
import fcntl
@@ -38,15 +39,17 @@ try:
except ImportError:
pass # windows
-from urwid import util
-from urwid import escape
-from urwid.display_common import BaseScreen, RealTerminal, \
- UPDATE_PALETTE_ENTRY, AttrSpec, UNPRINTABLE_TRANS_TABLE, \
- INPUT_DESCRIPTORS_CHANGED
-from urwid import signals
-from urwid.compat import PYTHON3, B
+from subprocess import PIPE, Popen
-from subprocess import Popen, PIPE
+from urwid import escape, signals, util
+from urwid.display_common import (
+ INPUT_DESCRIPTORS_CHANGED,
+ UNPRINTABLE_TRANS_TABLE,
+ UPDATE_PALETTE_ENTRY,
+ AttrSpec,
+ BaseScreen,
+ RealTerminal,
+)
class Screen(BaseScreen, RealTerminal):
@@ -54,7 +57,7 @@ class Screen(BaseScreen, RealTerminal):
"""Initialize a screen that directly prints escape codes to an output
terminal.
"""
- super(Screen, self).__init__()
+ super().__init__()
self._pal_escape = {}
self._pal_attrspec = {}
signals.connect_signal(self, UPDATE_PALETTE_ENTRY,
@@ -136,7 +139,7 @@ class Screen(BaseScreen, RealTerminal):
"""
if not self._resized:
- os.write(self._resize_pipe_wr, B('R'))
+ os.write(self._resize_pipe_wr, b'R')
self._resized = True
self.screen_buf = None
@@ -246,7 +249,7 @@ class Screen(BaseScreen, RealTerminal):
# restore mouse tracking to previous state
self._mouse_tracking(self._mouse_tracking_enabled)
- return super(Screen, self)._start()
+ return super()._start()
def _stop(self):
"""
@@ -280,7 +283,7 @@ class Screen(BaseScreen, RealTerminal):
if self._old_signal_keys:
self.tty_signal_keys(*(self._old_signal_keys + (fd,)))
- super(Screen, self)._stop()
+ super()._stop()
def write(self, data):
@@ -537,7 +540,7 @@ class Screen(BaseScreen, RealTerminal):
try:
while self.gpm_mev is not None and self.gpm_event_pending:
codes.extend(self._encode_gpm_event())
- except IOError as e:
+ except OSError as e:
if e.args[0] != 11:
raise
return codes
@@ -559,7 +562,7 @@ class Screen(BaseScreen, RealTerminal):
ready,w,err = select.select(
fd_list,[],fd_list, timeout)
break
- except select.error as e:
+ except OSError as e:
if e.args[0] != 4:
raise
if self._resized:
@@ -675,7 +678,7 @@ class Screen(BaseScreen, RealTerminal):
buf = fcntl.ioctl(self._term_output_file.fileno(),
termios.TIOCGWINSZ, ' '*4)
y, x = struct.unpack('hh', buf)
- except IOError:
+ except OSError:
# Term size could not be determined
pass
self.maxrow = y
@@ -693,7 +696,7 @@ class Screen(BaseScreen, RealTerminal):
self.write(escape.DESIGNATE_G1_SPECIAL)
self.flush()
break
- except IOError:
+ except OSError:
pass
self._setup_G1_done = True
@@ -808,10 +811,10 @@ class Screen(BaseScreen, RealTerminal):
whitespace_at_end = False
if row:
a, cs, run = row[-1]
- if (run[-1:] == B(' ') and self.back_color_erase
+ if (run[-1:] == b' ' and self.back_color_erase
and not using_standout_or_underline(a)):
whitespace_at_end = True
- row = row[:-1] + [(a, cs, run.rstrip(B(' ')))]
+ row = row[:-1] + [(a, cs, run.rstrip(b' '))]
elif y == maxrow-1 and maxcol > 1:
row, back, ins = self._last_row(row)
@@ -869,11 +872,11 @@ class Screen(BaseScreen, RealTerminal):
return
try:
for l in o:
- if isinstance(l, bytes) and PYTHON3:
+ if isinstance(l, bytes):
l = l.decode('utf-8', 'replace')
self.write(l)
self.flush()
- except IOError as e:
+ except OSError as e:
# ignore interrupted syscall
if e.args[0] != 4:
raise
@@ -987,7 +990,7 @@ class Screen(BaseScreen, RealTerminal):
bg = "%d" % (a.background_number + 40)
else:
bg = "49"
- return escape.ESC + "[0;%s;%s%sm" % (fg, st, bg)
+ return f"{escape.ESC}[0;{fg};{st}{bg}m"
def set_terminal_properties(self, colors=None, bright_is_bold=None,
@@ -1064,11 +1067,11 @@ class Screen(BaseScreen, RealTerminal):
if self.term == 'fbterm':
modify = ["%d;%d;%d;%d" % (index, red, green, blue)
for index, red, green, blue in entries]
- self.write("\x1b[3;"+";".join(modify)+"}")
+ self.write(f"\x1b[3;{';'.join(modify)}}}")
else:
modify = ["%d;rgb:%02x/%02x/%02x" % (index, red, green, blue)
for index, red, green, blue in entries]
- self.write("\x1b]4;"+";".join(modify)+"\x1b\\")
+ self.write(f"\x1b]4;{';'.join(modify)}\x1b\\")
self.flush()
diff --git a/urwid/signals.py b/urwid/signals.py
index 78f1eae..ea40131 100644
--- a/urwid/signals.py
+++ b/urwid/signals.py
@@ -17,11 +17,13 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
+
+from __future__ import annotations
import itertools
+import warnings
import weakref
@@ -34,10 +36,10 @@ class MetaSignals(type):
signals = d.get("signals", [])
for superclass in cls.__bases__:
signals.extend(getattr(superclass, 'signals', []))
- signals = list(dict([(x,None) for x in signals]).keys())
+ signals = list({x:None for x in signals}.keys())
d["signals"] = signals
register_signal(cls, signals)
- super(MetaSignals, cls).__init__(name, bases, d)
+ super().__init__(name, bases, d)
def setdefaultattr(obj, name, value):
# like dict.setdefault() for object attributes
@@ -46,14 +48,14 @@ def setdefaultattr(obj, name, value):
setattr(obj, name, value)
return value
-class Key(object):
+class Key:
"""
Minimal class, whose only purpose is to produce objects with a
unique hash
"""
__slots__ = []
-class Signals(object):
+class Signals:
_signal_attr = '_urwid_signals' # attribute to attach to signal senders
def __init__(self):
@@ -148,11 +150,15 @@ class Signals(object):
handler can also be disconnected by calling
urwid.disconnect_signal, which doesn't need this key.
"""
+ if user_arg is not None:
+ warnings.warn(
+ "Don't use user_arg argument, use user_args instead.",
+ DeprecationWarning,
+ )
sig_cls = obj.__class__
if not name in self._supported.get(sig_cls, []):
- raise NameError("No such signal %r for object %r" %
- (name, obj))
+ raise NameError(f"No such signal {name!r} for object {obj!r}")
# Just generate an arbitrary (but unique) key
key = Key()
@@ -247,8 +253,7 @@ class Signals(object):
:type obj: object
:param name: the signal to send, typically a string
:type name: signal name
- :param \*args: zero or more positional arguments to pass to the signal
- callback functions
+ :param args: zero or more positional arguments to pass to the signal callback functions
This function calls each of the callbacks connected to this signal
with the args arguments as positional parameters.
diff --git a/urwid/split_repr.py b/urwid/split_repr.py
index 5edf4c9..3b27a4a 100755
--- a/urwid/split_repr.py
+++ b/urwid/split_repr.py
@@ -17,15 +17,13 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
-from urwid.compat import PYTHON3
-if not PYTHON3:
- from inspect import getargspec
-else:
- from inspect import getfullargspec
+from __future__ import annotations
+
+from inspect import getfullargspec
+
def split_repr(self):
"""
@@ -50,9 +48,8 @@ def split_repr(self):
>>> Bar()
<Bar words here too attrs='appear too' barttr=42>
"""
- alist = [(str(k), normalize_repr(v))
- for k, v in self._repr_attrs().items()]
- alist.sort()
+ alist = sorted((str(k), normalize_repr(v)) for k, v in self._repr_attrs().items())
+
words = self._repr_words()
if not words and not alist:
# if we're just going to print the classname fall back
@@ -73,30 +70,11 @@ def normalize_repr(v):
"'foo'"
"""
if isinstance(v, dict):
- items = [(repr(k), repr(v)) for k, v in v.items()]
- items.sort()
- return "{" + ", ".join([
- "%s: %s" % itm for itm in items]) + "}"
+ items = sorted((repr(k), repr(v)) for k, v in v.items())
- return repr(v)
-
-def python3_repr(v):
- """
- Return strings and byte strings as they appear in Python 3
-
- >>> python3_repr(u"text")
- "'text'"
- >>> python3_repr(bytes())
- "b''"
- """
- r = repr(v)
- if not PYTHON3:
- if isinstance(v, bytes):
- return 'b' + r
- if r.startswith('u'):
- return r[1:]
- return r
+ return f"{{{', '.join([('%s: %s' % itm) for itm in items])}}}"
+ return repr(v)
def remove_defaults(d, fn):
@@ -125,10 +103,7 @@ def remove_defaults(d, fn):
>>> Foo()
<Foo object>
"""
- if not PYTHON3:
- args, varargs, varkw, defaults = getargspec(fn)
- else:
- args, varargs, varkw, defaults, _, _, _ = getfullargspec(fn)
+ args, varargs, varkw, defaults, _, _, _ = getfullargspec(fn)
# ignore *varargs and **kwargs
if varkw:
diff --git a/urwid/tests/test_canvas.py b/urwid/tests/test_canvas.py
index 0391593..5a1d454 100644
--- a/urwid/tests/test_canvas.py
+++ b/urwid/tests/test_canvas.py
@@ -1,8 +1,9 @@
+from __future__ import annotations
+
import unittest
-from urwid import canvas
-from urwid.compat import B
import urwid
+from urwid import canvas
class CanvasCacheTest(unittest.TestCase):
@@ -12,7 +13,7 @@ class CanvasCacheTest(unittest.TestCase):
def cct(self, widget, size, focus, expected):
got = urwid.CanvasCache.fetch(widget, urwid.Widget, size, focus)
- assert expected==got, "got: %s expected: %s"%(got, expected)
+ assert expected==got, f"got: {got} expected: {expected}"
def test1(self):
a = urwid.Text("")
@@ -42,51 +43,49 @@ class CanvasCacheTest(unittest.TestCase):
class CanvasTest(unittest.TestCase):
def ct(self, text, attr, exp_content):
- c = urwid.TextCanvas([B(t) for t in text], attr)
+ c = urwid.TextCanvas([t.encode('iso8859-1') for t in text], attr)
content = list(c.content())
- assert content == exp_content, "got: %r expected: %r" % (content,
- exp_content)
+ assert content == exp_content, f"got: {content!r} expected: {exp_content!r}"
def ct2(self, text, attr, left, top, cols, rows, def_attr, exp_content):
- c = urwid.TextCanvas([B(t) for t in text], attr)
+ c = urwid.TextCanvas([t.encode('iso8859-1') for t in text], attr)
content = list(c.content(left, top, cols, rows, def_attr))
- assert content == exp_content, "got: %r expected: %r" % (content,
- exp_content)
+ assert content == exp_content, f"got: {content!r} expected: {exp_content!r}"
def test1(self):
- self.ct(["Hello world"], None, [[(None, None, B("Hello world"))]])
+ self.ct(["Hello world"], None, [[(None, None, b"Hello world")]])
self.ct(["Hello world"], [[("a",5)]],
- [[("a", None, B("Hello")), (None, None, B(" world"))]])
+ [[("a", None, b"Hello"), (None, None, b" world")]])
self.ct(["Hi","There"], None,
- [[(None, None, B("Hi "))], [(None, None, B("There"))]])
+ [[(None, None, b"Hi ")], [(None, None, b"There")]])
def test2(self):
self.ct2(["Hello"], None, 0, 0, 5, 1, None,
- [[(None, None, B("Hello"))]])
+ [[(None, None, b"Hello")]])
self.ct2(["Hello"], None, 1, 0, 4, 1, None,
- [[(None, None, B("ello"))]])
+ [[(None, None, b"ello")]])
self.ct2(["Hello"], None, 0, 0, 4, 1, None,
- [[(None, None, B("Hell"))]])
+ [[(None, None, b"Hell")]])
self.ct2(["Hi","There"], None, 1, 0, 3, 2, None,
- [[(None, None, B("i "))], [(None, None, B("her"))]])
+ [[(None, None, b"i ")], [(None, None, b"her")]])
self.ct2(["Hi","There"], None, 0, 0, 5, 1, None,
- [[(None, None, B("Hi "))]])
+ [[(None, None, b"Hi ")]])
self.ct2(["Hi","There"], None, 0, 1, 5, 1, None,
- [[(None, None, B("There"))]])
+ [[(None, None, b"There")]])
class ShardBodyTest(unittest.TestCase):
def sbt(self, shards, shard_tail, expected):
result = canvas.shard_body(shards, shard_tail, False)
- assert result == expected, "got: %r expected: %r" % (result, expected)
+ assert result == expected, f"got: {result!r} expected: {expected!r}"
def sbttail(self, num_rows, sbody, expected):
result = canvas.shard_body_tail(num_rows, sbody)
- assert result == expected, "got: %r expected: %r" % (result, expected)
+ assert result == expected, f"got: {result!r} expected: {expected!r}"
def sbtrow(self, sbody, expected):
result = list(canvas.shard_body_row(sbody))
- assert result == expected, "got: %r expected: %r" % (result, expected)
+ assert result == expected, f"got: {result!r} expected: {expected!r}"
def test1(self):
@@ -138,15 +137,15 @@ class ShardBodyTest(unittest.TestCase):
class ShardsTrimTest(unittest.TestCase):
def sttop(self, shards, top, expected):
result = canvas.shards_trim_top(shards, top)
- assert result == expected, "got: %r expected: %r" % (result, expected)
+ assert result == expected, f"got: {result!r} expected: {expected!r}"
def strows(self, shards, rows, expected):
result = canvas.shards_trim_rows(shards, rows)
- assert result == expected, "got: %r expected: %r" % (result, expected)
+ assert result == expected, f"got: {result!r} expected: {expected!r}"
def stsides(self, shards, left, cols, expected):
result = canvas.shards_trim_sides(shards, left, cols)
- assert result == expected, "got: %r expected: %r" % (result, expected)
+ assert result == expected, f"got: {result!r} expected: {expected!r}"
def test1(self):
@@ -229,7 +228,7 @@ class ShardsTrimTest(unittest.TestCase):
class ShardsJoinTest(unittest.TestCase):
def sjt(self, shard_lists, expected):
result = canvas.shards_join(shard_lists)
- assert result == expected, "got: %r expected: %r" % (result, expected)
+ assert result == expected, f"got: {result!r} expected: {expected!r}"
def test(self):
shards1 = [(5, [(0,0,10,5,None,"foo"), (0,0,5,8,None,"baz")]),
@@ -263,129 +262,126 @@ class CanvasJoinTest(unittest.TestCase):
l = [(c, None, False, n) for c, n in l]
result = list(urwid.CanvasJoin(l).content())
- assert result == expected, "%s expected %r, got %r"%(
- desc, expected, result)
+ assert result == expected, f"{desc} expected {expected!r}, got {result!r}"
def test(self):
C = urwid.TextCanvas
- hello = C([B("hello")])
- there = C([B("there")], [[("a",5)]])
- a = C([B("a")])
- hi = C([B("hi")])
- how = C([B("how")], [[("a",1)]])
- dy = C([B("dy")])
- how_you = C([B("how"), B("you")])
+ hello = C([b"hello"])
+ there = C([b"there"], [[("a",5)]])
+ a = C([b"a"])
+ hi = C([b"hi"])
+ how = C([b"how"], [[("a",1)]])
+ dy = C([b"dy"])
+ how_you = C([b"how", b"you"])
self.cjtest("one", [(hello, 5)],
- [[(None, None, B("hello"))]])
+ [[(None, None, b"hello")]])
self.cjtest("two", [(hello, 5), (there, 5)],
- [[(None, None, B("hello")), ("a", None, B("there"))]])
+ [[(None, None, b"hello"), ("a", None, b"there")]])
self.cjtest("two space", [(hello, 7), (there, 5)],
- [[(None, None, B("hello")),(None,None,B(" ")),
- ("a", None, B("there"))]])
+ [[(None, None, b"hello"),(None,None,b" "),
+ ("a", None, b"there")]])
self.cjtest("three space", [(hi, 4), (how, 3), (dy, 2)],
- [[(None, None, B("hi")),(None,None,B(" ")),("a",None, B("h")),
- (None,None,B("ow")),(None,None,B("dy"))]])
+ [[(None, None, b"hi"),(None,None,b" "),("a",None, b"h"),
+ (None,None,b"ow"),(None,None,b"dy")]])
self.cjtest("four space", [(a, 2), (hi, 3), (dy, 3), (a, 1)],
- [[(None, None, B("a")),(None,None,B(" ")),
- (None, None, B("hi")),(None,None,B(" ")),
- (None, None, B("dy")),(None,None,B(" ")),
- (None, None, B("a"))]])
+ [[(None, None, b"a"),(None,None,b" "),
+ (None, None, b"hi"),(None,None,b" "),
+ (None, None, b"dy"),(None,None,b" "),
+ (None, None, b"a")]])
self.cjtest("pile 2", [(how_you, 4), (hi, 2)],
- [[(None, None, B('how')), (None, None, B(' ')),
- (None, None, B('hi'))],
- [(None, None, B('you')), (None, None, B(' ')),
- (None, None, B(' '))]])
+ [[(None, None, b'how'), (None, None, b' '),
+ (None, None, b'hi')],
+ [(None, None, b'you'), (None, None, b' '),
+ (None, None, b' ')]])
self.cjtest("pile 2r", [(hi, 4), (how_you, 3)],
- [[(None, None, B('hi')), (None, None, B(' ')),
- (None, None, B('how'))],
- [(None, None, B(' ')),
- (None, None, B('you'))]])
+ [[(None, None, b'hi'), (None, None, b' '),
+ (None, None, b'how')],
+ [(None, None, b' '),
+ (None, None, b'you')]])
class CanvasOverlayTest(unittest.TestCase):
def cotest(self, desc, bgt, bga, fgt, fga, l, r, et):
- bgt = B(bgt)
- fgt = B(fgt)
+ bgt = bgt.encode('iso8859-1')
+ fgt = fgt.encode('iso8859-1')
bg = urwid.CompositeCanvas(
urwid.TextCanvas([bgt],[bga]))
fg = urwid.CompositeCanvas(
urwid.TextCanvas([fgt],[fga]))
bg.overlay(fg, l, 0)
result = list(bg.content())
- assert result == et, "%s expected %r, got %r"%(
- desc, et, result)
+ assert result == et, f"{desc} expected {et!r}, got {result!r}"
def test1(self):
self.cotest("left", "qxqxqxqx", [], "HI", [], 0, 6,
- [[(None, None, B("HI")),(None,None,B("qxqxqx"))]])
+ [[(None, None, b"HI"),(None,None,b"qxqxqx")]])
self.cotest("right", "qxqxqxqx", [], "HI", [], 6, 0,
- [[(None, None, B("qxqxqx")),(None,None,B("HI"))]])
+ [[(None, None, b"qxqxqx"),(None,None,b"HI")]])
self.cotest("center", "qxqxqxqx", [], "HI", [], 3, 3,
- [[(None, None, B("qxq")),(None,None,B("HI")),
- (None,None,B("xqx"))]])
+ [[(None, None, b"qxq"),(None,None,b"HI"),
+ (None,None,b"xqx")]])
self.cotest("center2", "qxqxqxqx", [], "HI ", [], 2, 2,
- [[(None, None, B("qx")),(None,None,B("HI ")),
- (None,None,B("qx"))]])
+ [[(None, None, b"qx"),(None,None,b"HI "),
+ (None,None,b"qx")]])
self.cotest("full", "rz", [], "HI", [], 0, 0,
- [[(None, None, B("HI"))]])
+ [[(None, None, b"HI")]])
def test2(self):
self.cotest("same","asdfghjkl",[('a',9)],"HI",[('a',2)],4,3,
- [[('a',None,B("asdf")),('a',None,B("HI")),('a',None,B("jkl"))]])
+ [[('a',None,b"asdf"),('a',None,b"HI"),('a',None,b"jkl")]])
self.cotest("diff","asdfghjkl",[('a',9)],"HI",[('b',2)],4,3,
- [[('a',None,B("asdf")),('b',None,B("HI")),('a',None,B("jkl"))]])
+ [[('a',None,b"asdf"),('b',None,b"HI"),('a',None,b"jkl")]])
self.cotest("None end","asdfghjkl",[('a',9)],"HI ",[('a',2)],
2,3,
- [[('a',None,B("as")),('a',None,B("HI")),
- (None,None,B(" ")),('a',None,B("jkl"))]])
+ [[('a',None,b"as"),('a',None,b"HI"),
+ (None,None,b" "),('a',None,b"jkl")]])
self.cotest("float end","asdfghjkl",[('a',3)],"HI",[('a',2)],
4,3,
- [[('a',None,B("asd")),(None,None,B("f")),
- ('a',None,B("HI")),(None,None,B("jkl"))]])
+ [[('a',None,b"asd"),(None,None,b"f"),
+ ('a',None,b"HI"),(None,None,b"jkl")]])
self.cotest("cover 2","asdfghjkl",[('a',5),('c',4)],"HI",
[('b',2)],4,3,
- [[('a',None,B("asdf")),('b',None,B("HI")),('c',None,B("jkl"))]])
+ [[('a',None,b"asdf"),('b',None,b"HI"),('c',None,b"jkl")]])
self.cotest("cover 2-2","asdfghjkl",
[('a',4),('d',1),('e',1),('c',3)],
"HI",[('b',2)], 4, 3,
- [[('a',None,B("asdf")),('b',None,B("HI")),('c',None,B("jkl"))]])
+ [[('a',None,b"asdf"),('b',None,b"HI"),('c',None,b"jkl")]])
def test3(self):
urwid.set_encoding("euc-jp")
self.cotest("db0","\xA1\xA1\xA1\xA1\xA1\xA1",[],"HI",[],2,2,
- [[(None,None,B("\xA1\xA1")),(None,None,B("HI")),
- (None,None,B("\xA1\xA1"))]])
+ [[(None,None,b"\xA1\xA1"),(None,None,b"HI"),
+ (None,None,b"\xA1\xA1")]])
self.cotest("db1","\xA1\xA1\xA1\xA1\xA1\xA1",[],"OHI",[],1,2,
- [[(None,None,B(" ")),(None,None,B("OHI")),
- (None,None,B("\xA1\xA1"))]])
+ [[(None,None,b" "),(None,None,b"OHI"),
+ (None,None,b"\xA1\xA1")]])
self.cotest("db2","\xA1\xA1\xA1\xA1\xA1\xA1",[],"OHI",[],2,1,
- [[(None,None,B("\xA1\xA1")),(None,None,B("OHI")),
- (None,None,B(" "))]])
+ [[(None,None,b"\xA1\xA1"),(None,None,b"OHI"),
+ (None,None,b" ")]])
self.cotest("db3","\xA1\xA1\xA1\xA1\xA1\xA1",[],"OHIO",[],1,1,
- [[(None,None,B(" ")),(None,None,B("OHIO")),(None,None,B(" "))]])
+ [[(None,None,b" "),(None,None,b"OHIO"),(None,None,b" ")]])
class CanvasPadTrimTest(unittest.TestCase):
def cptest(self, desc, ct, ca, l, r, et):
- ct = B(ct)
+ ct = ct.encode('iso8859-1')
c = urwid.CompositeCanvas(
urwid.TextCanvas([ct], [ca]))
c.pad_trim_left_right(l, r)
result = list(c.content())
- assert result == et, "%s expected %r, got %r"%(
- desc, et, result)
+ assert result == et, f"{desc} expected {et!r}, got {result!r}"
def test1(self):
self.cptest("none", "asdf", [], 0, 0,
- [[(None,None,B("asdf"))]])
+ [[(None,None,b"asdf")]])
self.cptest("left pad", "asdf", [], 2, 0,
- [[(None,None,B(" ")),(None,None,B("asdf"))]])
+ [[(None,None,b" "),(None,None,b"asdf")]])
self.cptest("right pad", "asdf", [], 0, 2,
- [[(None,None,B("asdf")),(None,None,B(" "))]])
+ [[(None,None,b"asdf"),(None,None,b" ")]])
def test2(self):
self.cptest("left trim", "asdf", [], -2, 0,
- [[(None,None,B("df"))]])
+ [[(None,None,b"df")]])
self.cptest("right trim", "asdf", [], 0, -2,
- [[(None,None,B("as"))]])
+ [[(None,None,b"as")]])
diff --git a/urwid/tests/test_container.py b/urwid/tests/test_container.py
index 8b693b7..f5a9182 100644
--- a/urwid/tests/test_container.py
+++ b/urwid/tests/test_container.py
@@ -1,13 +1,10 @@
+from __future__ import annotations
+
import unittest
-try:
- # Python3 version
- import unittest.mock as mock
-except ImportError:
- # Python2, rely on PyPi
- import mock
+from unittest import mock
-from urwid.tests.util import SelectableText
import urwid
+from urwid.tests.util import SelectableText
class FrameTest(unittest.TestCase):
@@ -32,8 +29,7 @@ class FrameTest(unittest.TestCase):
rval = f.frame_top_bottom(size, focus)
exp = (top, bottom), (header_rows, footer_rows)
- assert exp == rval, "%s expected %r but got %r"%(
- desc,exp,rval)
+ assert exp == rval, f"{desc} expected {exp!r} but got {rval!r}"
def test(self):
self.ftbtest("simple", 'body', 0, 0, (9, 10), True, 0, 0)
@@ -71,15 +67,13 @@ class PileTest(unittest.TestCase):
rkey, rfocus, rpref_col):
p = urwid.Pile( l, focus_item )
rval = p.keypress( (20,), key )
- assert rkey == rval, "%s key expected %r but got %r" %(
- desc, rkey, rval)
+ assert rkey == rval, f"{desc} key expected {rkey!r} but got {rval!r}"
new_focus = l.index(p.get_focus())
assert new_focus == rfocus, "%s focus expected %r but got %r" %(
desc, rfocus, new_focus)
new_pref = p.get_pref_col((20,))
assert new_pref == rpref_col, (
- "%s pref_col expected %r but got %r" % (
- desc, rpref_col, new_pref))
+ f"{desc} pref_col expected {rpref_col!r} but got {new_pref!r}")
def test_select_change(self):
T,S,E = urwid.Text, SelectableText, urwid.Edit
@@ -145,7 +139,7 @@ class ColumnsTest(unittest.TestCase):
def cwtest(self, desc, l, divide, size, exp, focus_column=0):
c = urwid.Columns(l, divide, focus_column)
rval = c.column_widths( size )
- assert rval == exp, "%s expected %s, got %s"%(desc,exp,rval)
+ assert rval == exp, f"{desc} expected {exp}, got {rval}"
def test_widths(self):
x = urwid.Text("") # sample "column"
@@ -207,12 +201,11 @@ class ColumnsTest(unittest.TestCase):
def mctest(self, desc, l, divide, size, col, row, exp, f_col, pref_col):
c = urwid.Columns( l, divide )
rval = c.move_cursor_to_coords( size, col, row )
- assert rval == exp, "%s expected %r, got %r"%(desc,exp,rval)
+ assert rval == exp, f"{desc} expected {exp!r}, got {rval!r}"
assert c.focus_col == f_col, "%s expected focus_col %s got %s"%(
desc, f_col, c.focus_col)
pc = c.get_pref_col( size )
- assert pc == pref_col, "%s expected pref_col %s, got %s"%(
- desc, pref_col, pc)
+ assert pc == pref_col, f"{desc} expected pref_col {pref_col}, got {pc}"
def test_move_cursor(self):
e, s, x = urwid.Edit("",""),SelectableText(""), urwid.Text("")
@@ -244,7 +237,7 @@ class ColumnsTest(unittest.TestCase):
urwid.Columns(urwid.Text(c) for c in "ABC")
def test_old_attributes(self):
- c = urwid.Columns([urwid.Text(u'a'), urwid.SolidFill(u'x')],
+ c = urwid.Columns([urwid.Text('a'), urwid.SolidFill('x')],
box_columns=[1])
self.assertEqual(c.box_columns, [1])
c.box_columns=[]
@@ -263,13 +256,13 @@ class ColumnsTest(unittest.TestCase):
class OverlayTest(unittest.TestCase):
def test_old_params(self):
- o1 = urwid.Overlay(urwid.SolidFill(u'X'), urwid.SolidFill(u'O'),
+ o1 = urwid.Overlay(urwid.SolidFill('X'), urwid.SolidFill('O'),
('fixed left', 5), ('fixed right', 4),
('fixed top', 3), ('fixed bottom', 2),)
self.assertEqual(o1.contents[1][1], (
'left', None, 'relative', 100, None, 5, 4,
'top', None, 'relative', 100, None, 3, 2))
- o2 = urwid.Overlay(urwid.SolidFill(u'X'), urwid.SolidFill(u'O'),
+ o2 = urwid.Overlay(urwid.SolidFill('X'), urwid.SolidFill('O'),
('fixed right', 5), ('fixed left', 4),
('fixed bottom', 3), ('fixed top', 2),)
self.assertEqual(o2.contents[1][1], (
@@ -278,7 +271,7 @@ class OverlayTest(unittest.TestCase):
def test_get_cursor_coords(self):
self.assertEqual(urwid.Overlay(urwid.Filler(urwid.Edit()),
- urwid.SolidFill(u'B'),
+ urwid.SolidFill('B'),
'right', 1, 'bottom', 1).get_cursor_coords((2,2)), (1,1))
@@ -374,15 +367,15 @@ class WidgetSquishTest(unittest.TestCase):
self.wstest(urwid.Columns([(4, urwid.SolidFill())]))
def test_buttons(self):
- self.fwstest(urwid.Button(u"hello"))
- self.fwstest(urwid.RadioButton([], u"hello"))
+ self.fwstest(urwid.Button("hello"))
+ self.fwstest(urwid.RadioButton([], "hello"))
class CommonContainerTest(unittest.TestCase):
def test_pile(self):
- t1 = urwid.Text(u'one')
- t2 = urwid.Text(u'two')
- t3 = urwid.Text(u'three')
+ t1 = urwid.Text('one')
+ t2 = urwid.Text('two')
+ t3 = urwid.Text('three')
sf = urwid.SolidFill('x')
p = urwid.Pile([])
self.assertEqual(p.focus, None)
@@ -439,9 +432,9 @@ class CommonContainerTest(unittest.TestCase):
self.assertEqual(len(p.contents), 1)
def test_columns(self):
- t1 = urwid.Text(u'one')
- t2 = urwid.Text(u'two')
- t3 = urwid.Text(u'three')
+ t1 = urwid.Text('one')
+ t2 = urwid.Text('two')
+ t3 = urwid.Text('three')
sf = urwid.SolidFill('x')
c = urwid.Columns([])
self.assertEqual(c.focus, None)
@@ -527,8 +520,8 @@ class CommonContainerTest(unittest.TestCase):
None))
self.assertRaises(IndexError, lambda: setattr(lb, 'focus_position', 0))
- t1 = urwid.Text(u'one')
- t2 = urwid.Text(u'two')
+ t1 = urwid.Text('one')
+ t2 = urwid.Text('two')
lb = urwid.ListBox(urwid.SimpleListWalker([t1, t2]))
self.assertEqual(lb.focus, t1)
self.assertEqual(lb.focus_position, 0)
@@ -552,8 +545,8 @@ class CommonContainerTest(unittest.TestCase):
self.assertRaises(urwid.GridFlowError, lambda: gf.options(
'pack', None))
- t1 = urwid.Text(u'one')
- t2 = urwid.Text(u'two')
+ t1 = urwid.Text('one')
+ t2 = urwid.Text('two')
gf = urwid.GridFlow([t1, t2], 5, 1, 0, 'left')
self.assertEqual(gf.focus, t1)
self.assertEqual(gf.focus_position, 0)
@@ -578,8 +571,8 @@ class CommonContainerTest(unittest.TestCase):
self.assertRaises(ValueError, lambda: gf.set_focus('nonexistant'))
def test_overlay(self):
- s1 = urwid.SolidFill(u'1')
- s2 = urwid.SolidFill(u'2')
+ s1 = urwid.SolidFill('1')
+ s2 = urwid.SolidFill('2')
o = urwid.Overlay(s1, s2,
'center', ('relative', 50), 'middle', ('relative', 50))
self.assertEqual(o.focus, s1)
@@ -595,7 +588,7 @@ class CommonContainerTest(unittest.TestCase):
'middle', None, 'relative', 50, None, 0, 0)))
def test_frame(self):
- s1 = urwid.SolidFill(u'1')
+ s1 = urwid.SolidFill('1')
f = urwid.Frame(s1)
self.assertEqual(f.focus, s1)
@@ -605,9 +598,9 @@ class CommonContainerTest(unittest.TestCase):
self.assertRaises(IndexError, lambda: setattr(f, 'focus_position',
'header'))
- t1 = urwid.Text(u'one')
- t2 = urwid.Text(u'two')
- t3 = urwid.Text(u'three')
+ t1 = urwid.Text('one')
+ t2 = urwid.Text('two')
+ t3 = urwid.Text('three')
f = urwid.Frame(s1, t1, t2, 'header')
self.assertEqual(f.focus, t1)
self.assertEqual(f.focus_position, 'header')
@@ -631,12 +624,12 @@ class CommonContainerTest(unittest.TestCase):
def test_focus_path(self):
# big tree of containers
- t = urwid.Text(u'x')
- e = urwid.Edit(u'?')
+ t = urwid.Text('x')
+ e = urwid.Edit('?')
c = urwid.Columns([t, e, t, t])
p = urwid.Pile([t, t, c, t])
a = urwid.AttrMap(p, 'gets ignored')
- s = urwid.SolidFill(u'/')
+ s = urwid.SolidFill('/')
o = urwid.Overlay(e, s, 'center', 'pack', 'middle', 'pack')
lb = urwid.ListBox(urwid.SimpleFocusListWalker([t, a, o, t]))
lb.focus_position = 1
diff --git a/urwid/tests/test_decoration.py b/urwid/tests/test_decoration.py
index 8ab1acd..7ce235c 100644
--- a/urwid/tests/test_decoration.py
+++ b/urwid/tests/test_decoration.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
import unittest
import urwid
@@ -7,8 +9,7 @@ class PaddingTest(unittest.TestCase):
def ptest(self, desc, align, width, maxcol, left, right,min_width=None):
p = urwid.Padding(None, align, width, min_width)
l, r = p.padding_values((maxcol,),False)
- assert (l,r)==(left,right), "%s expected %s but got %s"%(
- desc, (left,right), (l,r))
+ assert (l,r)==(left,right), f"{desc} expected {left, right} but got {l, r}"
def petest(self, desc, align, width):
self.assertRaises(urwid.PaddingError, lambda:
@@ -77,13 +78,13 @@ class PaddingTest(unittest.TestCase):
# fixing this gets deep into things like Edit._shift_view_to_cursor
# though, so this might not get fixed for a while
- p = urwid.Padding(urwid.Edit(u'',u''), width='pack', left=4)
+ p = urwid.Padding(urwid.Edit('',''), width='pack', left=4)
self.assertEqual(p.render((10,), True).cursor, None)
self.assertEqual(p.get_cursor_coords((10,)), None)
self.assertEqual(p.render((4,), True).cursor, None)
self.assertEqual(p.get_cursor_coords((4,)), None)
- p = urwid.Padding(urwid.Edit(u'',u''), width=('relative', 100), left=4)
+ p = urwid.Padding(urwid.Edit('',''), width=('relative', 100), left=4)
self.assertEqual(p.render((10,), True).cursor, (4, 0))
self.assertEqual(p.get_cursor_coords((10,)), (4, 0))
self.assertEqual(p.render((4,), True).cursor, None)
@@ -95,8 +96,7 @@ class FillerTest(unittest.TestCase):
min_height=None):
f = urwid.Filler(None, valign, height, min_height)
t, b = f.filler_values((20,maxrow), False)
- assert (t,b)==(top,bottom), "%s expected %s but got %s"%(
- desc, (top,bottom), (t,b))
+ assert (t,b)==(top,bottom), f"{desc} expected {top, bottom} but got {t, b}"
def fetest(self, desc, valign, height):
self.assertRaises(urwid.FillerError, lambda:
@@ -146,4 +146,4 @@ class FillerTest(unittest.TestCase):
10,1,1,8)
def test_repr(self):
- repr(urwid.Filler(urwid.Text(u'hai')))
+ repr(urwid.Filler(urwid.Text('hai')))
diff --git a/urwid/tests/test_doctests.py b/urwid/tests/test_doctests.py
index 9b72070..cb0526e 100644
--- a/urwid/tests/test_doctests.py
+++ b/urwid/tests/test_doctests.py
@@ -1,9 +1,12 @@
-import unittest
+from __future__ import annotations
+
import doctest
+import unittest
import urwid
import urwid.numedit
+
def load_tests(loader, tests, ignore):
module_doctests = [
urwid.widget,
diff --git a/urwid/tests/test_escapes.py b/urwid/tests/test_escapes.py
index 7b1fe53..8d11c47 100644
--- a/urwid/tests/test_escapes.py
+++ b/urwid/tests/test_escapes.py
@@ -1,8 +1,9 @@
#!/usr/bin/python
-# -*- coding: utf-8 -*-
""" Tests covering escape sequences processing """
+from __future__ import annotations
+
import unittest
import urwid.escape
diff --git a/urwid/tests/test_event_loops.py b/urwid/tests/test_event_loops.py
index 49d1270..261a614 100644
--- a/urwid/tests/test_event_loops.py
+++ b/urwid/tests/test_event_loops.py
@@ -1,19 +1,19 @@
+from __future__ import annotations
+
import os
import unittest
-import platform
import urwid
-from urwid.compat import PYTHON3
-class EventLoopTestMixin(object):
+class EventLoopTestMixin:
def test_event_loop(self):
rd, wr = os.pipe()
evl = self.evl
out = []
def step1():
out.append("writing")
- os.write(wr, "hi".encode('ascii'))
+ os.write(wr, b"hi")
def step2():
out.append(os.read(rd, 2).decode('ascii'))
raise urwid.ExitMainLoop
@@ -53,7 +53,7 @@ class EventLoopTestMixin(object):
evl = self.evl
out = []
rd, wr = os.pipe()
- self.assertEqual(os.write(wr, "data".encode('ascii')), 4)
+ self.assertEqual(os.write(wr, b"data"), 4)
def say_hello():
out.append("hello")
def say_waiting():
@@ -136,7 +136,7 @@ else:
evl = self.evl
out = []
rd, wr = os.pipe()
- self.assertEqual(os.write(wr, "data".encode('ascii')), 4)
+ self.assertEqual(os.write(wr, b"data"), 4)
def step2():
out.append(os.read(rd, 2).decode('ascii'))
def say_hello():
diff --git a/urwid/tests/test_floatedit.py b/urwid/tests/test_floatedit.py
index 7ac5d27..8db3ba8 100644
--- a/urwid/tests/test_floatedit.py
+++ b/urwid/tests/test_floatedit.py
@@ -1,8 +1,9 @@
import unittest
-from urwid.numedit import FloatEdit
from decimal import Decimal
from itertools import product
+from urwid.numedit import FloatEdit
+
class FloatEditNoPreservePrecicionTest(unittest.TestCase):
def test(self):
diff --git a/urwid/tests/test_graphics.py b/urwid/tests/test_graphics.py
index 05808fc..f731208 100644
--- a/urwid/tests/test_graphics.py
+++ b/urwid/tests/test_graphics.py
@@ -1,15 +1,16 @@
+from __future__ import annotations
+
import unittest
-from urwid import graphics
-from urwid.compat import B
import urwid
+from urwid import graphics
class LineBoxTest(unittest.TestCase):
def border(self, tl, t, tr, l, r, bl, b, br):
- return [bytes().join([tl, t, tr]),
- bytes().join([l, B(" "), r]),
- bytes().join([bl, b, br]),]
+ return [b''.join([tl, t, tr]),
+ b''.join([l, b" ", r]),
+ b''.join([bl, b, br]),]
def test_linebox_pack(self):
# Bug #346 'pack' Padding does not run with LineBox
@@ -29,11 +30,11 @@ class LineBoxTest(unittest.TestCase):
# default
self.assertEqual(l,
- self.border(B("\xe2\x94\x8c"), B("\xe2\x94\x80"),
- B("\xe2\x94\x90"), B("\xe2\x94\x82"), B("\xe2\x94\x82"),
- B("\xe2\x94\x94"), B("\xe2\x94\x80"), B("\xe2\x94\x98")))
+ self.border(b"\xe2\x94\x8c", b"\xe2\x94\x80",
+ b"\xe2\x94\x90", b"\xe2\x94\x82", b"\xe2\x94\x82",
+ b"\xe2\x94\x94", b"\xe2\x94\x80", b"\xe2\x94\x98"))
- nums = [B(str(n)) for n in range(8)]
+ nums = [str(n).encode('iso8859-1') for n in range(8)]
b = dict(zip(["tlcorner", "tline", "trcorner", "lline", "rline",
"blcorner", "bline", "brcorner"], nums))
l = urwid.LineBox(t, **b).render((3,)).text
@@ -44,7 +45,7 @@ class LineBoxTest(unittest.TestCase):
class BarGraphTest(unittest.TestCase):
def bgtest(self, desc, data, top, widths, maxrow, exp ):
rval = graphics.calculate_bargraph_display(data,top,widths,maxrow)
- assert rval == exp, "%s expected %r, got %r"%(desc,exp,rval)
+ assert rval == exp, f"{desc} expected {exp!r}, got {rval!r}"
def test1(self):
self.bgtest('simplest',[[0]],5,[1],1,
@@ -92,7 +93,7 @@ class SmoothBarGraphTest(unittest.TestCase):
None, {(1,0):'red/black', (2,1):'blue/red'})
g.set_data( data, top )
rval = g.calculate_display((5,3))
- assert rval == exp, "%s expected %r, got %r"%(desc,exp,rval)
+ assert rval == exp, f"{desc} expected {exp!r}, got {rval!r}"
def test1(self):
self.sbgtest('simple', [[3]], 5,
diff --git a/urwid/tests/test_listbox.py b/urwid/tests/test_listbox.py
index 9d2eddb..846b17b 100644
--- a/urwid/tests/test_listbox.py
+++ b/urwid/tests/test_listbox.py
@@ -1,8 +1,9 @@
+from __future__ import annotations
+
import unittest
-from urwid.compat import B
-from urwid.tests.util import SelectableText
import urwid
+from urwid.tests.util import SelectableText
class ListBoxCalculateVisibleTest(unittest.TestCase):
@@ -22,8 +23,8 @@ class ListBoxCalculateVisibleTest(unittest.TestCase):
y += offset_inset
cursor = x, y
- assert offset_inset == exp_offset_inset, "%s got: %r expected: %r" %(desc,offset_inset,exp_offset_inset)
- assert cursor == exp_cur, "%s (cursor) got: %r expected: %r" %(desc,cursor,exp_cur)
+ assert offset_inset == exp_offset_inset, f"{desc} got: {offset_inset!r} expected: {exp_offset_inset!r}"
+ assert cursor == exp_cur, f"{desc} (cursor) got: {cursor!r} expected: {exp_cur!r}"
def test1_simple(self):
T = urwid.Text
@@ -113,8 +114,8 @@ class ListBoxChangeFocusTest(unittest.TestCase):
if hasattr(focus_widget,'get_cursor_coords'):
cursor=focus_widget.get_cursor_coords((4,))
- assert act == exp, "%s got: %s expected: %s" %(desc, act, exp)
- assert cursor == exp_cur, "%s (cursor) got: %r expected: %r" %(desc,cursor,exp_cur)
+ assert act == exp, f"{desc} got: {act} expected: {exp}"
+ assert cursor == exp_cur, f"{desc} (cursor) got: {cursor!r} expected: {exp_cur!r}"
def test1unselectable(self):
@@ -206,18 +207,18 @@ class ListBoxChangeFocusTest(unittest.TestCase):
class ListBoxRenderTest(unittest.TestCase):
def ltest(self,desc,body,focus,offset_inset_rows,exp_text,exp_cur):
- exp_text = [B(t) for t in exp_text]
+ exp_text = [t.encode('iso8859-1') for t in exp_text]
lbox = urwid.ListBox(urwid.SimpleListWalker(body))
lbox.body.set_focus( focus )
lbox.shift_focus((4,10), offset_inset_rows )
canvas = lbox.render( (4,5), focus=1 )
- text = [bytes().join([t for at, cs, t in ln]) for ln in canvas.content()]
+ text = [b''.join([t for at, cs, t in ln]) for ln in canvas.content()]
cursor = canvas.cursor
- assert text == exp_text, "%s (text) got: %r expected: %r" %(desc,text,exp_text)
- assert cursor == exp_cur, "%s (cursor) got: %r expected: %r" %(desc,cursor,exp_cur)
+ assert text == exp_text, f"{desc} (text) got: {text!r} expected: {exp_text!r}"
+ assert cursor == exp_cur, f"{desc} (cursor) got: {cursor!r} expected: {exp_cur!r}"
def test1_simple(self):
@@ -292,11 +293,11 @@ class ListBoxRenderTest(unittest.TestCase):
def test4_really_large_contents(self):
T,E = urwid.Text, urwid.Edit
self.ltest("really large edit",
- [T(u"hello"*100)], 0, 0,
+ [T("hello"*100)], 0, 0,
["hell","ohel","lohe","lloh","ello"], None)
self.ltest("really large edit",
- [E(u"", u"hello"*100)], 0, 0,
+ [E("", "hello"*100)], 0, 0,
["hell","ohel","lohe","lloh","llo "], (3,4))
@@ -320,8 +321,8 @@ class ListBoxKeypressTest(unittest.TestCase):
exp = exp_focus, exp_offset_inset
act = focus_pos, offset_inset
- assert act == exp, "%s got: %r expected: %r" %(desc,act,exp)
- assert cursor == exp_cur, "%s (cursor) got: %r expected: %r" %(desc,cursor,exp_cur)
+ assert act == exp, f"{desc} got: {act!r} expected: {exp!r}"
+ assert cursor == exp_cur, f"{desc} (cursor) got: {cursor!r} expected: {exp_cur!r}"
return ret_key,lbox
@@ -768,7 +769,7 @@ class ZeroHeightContentsTest(unittest.TestCase):
def test_listbox_text_pile_page_down(self):
lb = urwid.ListBox(urwid.SimpleListWalker(
- [urwid.Text(u'above'), urwid.Pile([])]))
+ [urwid.Text('above'), urwid.Pile([])]))
lb.keypress((40,10), 'page down')
self.assertEqual(lb.get_focus()[1], 0)
lb.keypress((40,10), 'page down') # second one caused ListBox failure
@@ -776,7 +777,7 @@ class ZeroHeightContentsTest(unittest.TestCase):
def test_listbox_text_pile_page_up(self):
lb = urwid.ListBox(urwid.SimpleListWalker(
- [urwid.Pile([]), urwid.Text(u'below')]))
+ [urwid.Pile([]), urwid.Text('below')]))
lb.set_focus(1)
lb.keypress((40,10), 'page up')
self.assertEqual(lb.get_focus()[1], 1)
@@ -786,7 +787,7 @@ class ZeroHeightContentsTest(unittest.TestCase):
def test_listbox_text_pile_down(self):
sp = urwid.Pile([])
sp.selectable = lambda: True # abuse our Pile
- lb = urwid.ListBox(urwid.SimpleListWalker([urwid.Text(u'above'), sp]))
+ lb = urwid.ListBox(urwid.SimpleListWalker([urwid.Text('above'), sp]))
lb.keypress((40,10), 'down')
self.assertEqual(lb.get_focus()[1], 0)
lb.keypress((40,10), 'down')
@@ -795,7 +796,7 @@ class ZeroHeightContentsTest(unittest.TestCase):
def test_listbox_text_pile_up(self):
sp = urwid.Pile([])
sp.selectable = lambda: True # abuse our Pile
- lb = urwid.ListBox(urwid.SimpleListWalker([sp, urwid.Text(u'below')]))
+ lb = urwid.ListBox(urwid.SimpleListWalker([sp, urwid.Text('below')]))
lb.set_focus(1)
lb.keypress((40,10), 'up')
self.assertEqual(lb.get_focus()[1], 1)
diff --git a/urwid/tests/test_signals.py b/urwid/tests/test_signals.py
index 22db34e..1842bd1 100644
--- a/urwid/tests/test_signals.py
+++ b/urwid/tests/test_signals.py
@@ -1,11 +1,20 @@
import unittest
+
try:
from unittest.mock import Mock
except ImportError:
# Python2, rely on PyPi
from mock import Mock
-from urwid import connect_signal, disconnect_signal, register_signal, emit_signal, Signals, Edit
+from urwid import (
+ Edit,
+ Signals,
+ connect_signal,
+ disconnect_signal,
+ emit_signal,
+ register_signal,
+)
+
class SiglnalsTest(unittest.TestCase):
diff --git a/urwid/tests/test_str_util.py b/urwid/tests/test_str_util.py
index f7ad1d9..2fec29e 100644
--- a/urwid/tests/test_str_util.py
+++ b/urwid/tests/test_str_util.py
@@ -1,15 +1,16 @@
+from __future__ import annotations
+
import unittest
-from urwid.compat import B
from urwid.escape import str_util
class DecodeOneTest(unittest.TestCase):
def gwt(self, ch, exp_ord, exp_pos):
- ch = B(ch)
+ ch = ch.encode('iso8859-1')
o, pos = str_util.decode_one(ch,0)
- assert o==exp_ord, " got:%r expected:%r" % (o, exp_ord)
- assert pos==exp_pos, " got:%r expected:%r" % (pos, exp_pos)
+ assert o==exp_ord, f" got:{o!r} expected:{exp_ord!r}"
+ assert pos==exp_pos, f" got:{pos!r} expected:{exp_pos!r}"
def test1byte(self):
self.gwt("ab", ord("a"), 1)
diff --git a/urwid/tests/test_text_layout.py b/urwid/tests/test_text_layout.py
index b446319..77bd27d 100644
--- a/urwid/tests/test_text_layout.py
+++ b/urwid/tests/test_text_layout.py
@@ -1,14 +1,15 @@
+from __future__ import annotations
+
import unittest
-from urwid import text_layout
-from urwid.compat import B
import urwid
+from urwid import text_layout
-class CalcBreaksTest(object):
+class CalcBreaksTest:
def cbtest(self, width, exp):
result = text_layout.default_layout.calculate_text_segments(
- B(self.text), width, self.mode )
+ self.text.encode('iso8859-1'), width, self.mode )
assert len(result) == len(exp), repr((result, exp))
for l,e in zip(result, exp):
end = l[-1][-1]
@@ -97,11 +98,11 @@ class CalcBreaksCantDisplayTest(unittest.TestCase):
urwid.set_encoding("euc-jp")
self.assertRaises(text_layout.CanNotDisplayText,
text_layout.default_layout.calculate_text_segments,
- B('\xA1\xA1'), 1, 'space' )
+ b'\xA1\xA1', 1, 'space' )
urwid.set_encoding("utf-8")
self.assertRaises(text_layout.CanNotDisplayText,
text_layout.default_layout.calculate_text_segments,
- B('\xe9\xa2\x96'), 1, 'space' )
+ b'\xe9\xa2\x96', 1, 'space' )
class SubsegTest(unittest.TestCase):
@@ -109,10 +110,10 @@ class SubsegTest(unittest.TestCase):
urwid.set_encoding("euc-jp")
def st(self, seg, text, start, end, exp):
- text = B(text)
+ text = text.encode('iso8859-1')
s = urwid.LayoutSegment(seg)
result = s.subseg( text, start, end )
- assert result == exp, "Expected %r, got %r"%(exp,result)
+ assert result == exp, f"Expected {exp!r}, got {result!r}"
def test1_padding(self):
self.st( (10, None), "", 0, 8, [(8, None)] )
@@ -121,18 +122,18 @@ class SubsegTest(unittest.TestCase):
self.st( (10, 0), "", 0, 20, [(10, 0)] )
def test2_text(self):
- self.st( (10, 0, B("1234567890")), "", 0, 8, [(8,0,B("12345678"))] )
- self.st( (10, 0, B("1234567890")), "", 2, 10, [(8,0,B("34567890"))] )
- self.st( (10, 0, B("12\xA1\xA156\xA1\xA190")), "", 2, 8,
- [(6, 0, B("\xA1\xA156\xA1\xA1"))] )
- self.st( (10, 0, B("12\xA1\xA156\xA1\xA190")), "", 3, 8,
- [(5, 0, B(" 56\xA1\xA1"))] )
- self.st( (10, 0, B("12\xA1\xA156\xA1\xA190")), "", 2, 7,
- [(5, 0, B("\xA1\xA156 "))] )
- self.st( (10, 0, B("12\xA1\xA156\xA1\xA190")), "", 3, 7,
- [(4, 0, B(" 56 "))] )
- self.st( (10, 0, B("12\xA1\xA156\xA1\xA190")), "", 0, 20,
- [(10, 0, B("12\xA1\xA156\xA1\xA190"))] )
+ self.st( (10, 0, b"1234567890"), "", 0, 8, [(8,0,b"12345678")] )
+ self.st( (10, 0, b"1234567890"), "", 2, 10, [(8,0,b"34567890")] )
+ self.st( (10, 0, b"12\xA1\xA156\xA1\xA190"), "", 2, 8,
+ [(6, 0, b"\xA1\xA156\xA1\xA1")] )
+ self.st( (10, 0, b"12\xA1\xA156\xA1\xA190"), "", 3, 8,
+ [(5, 0, b" 56\xA1\xA1")] )
+ self.st( (10, 0, b"12\xA1\xA156\xA1\xA190"), "", 2, 7,
+ [(5, 0, b"\xA1\xA156 ")] )
+ self.st( (10, 0, b"12\xA1\xA156\xA1\xA190"), "", 3, 7,
+ [(4, 0, b" 56 ")] )
+ self.st( (10, 0, b"12\xA1\xA156\xA1\xA190"), "", 0, 20,
+ [(10, 0, b"12\xA1\xA156\xA1\xA190")] )
def test3_range(self):
t = "1234567890"
@@ -149,7 +150,7 @@ class SubsegTest(unittest.TestCase):
self.st( (6, 2, 8), t, 1, 5, [(1, 3), (2, 4, 6), (1, 6)] )
-class CalcTranslateTest(object):
+class CalcTranslateTest:
def setUp(self):
urwid.set_encoding("utf-8")
@@ -227,7 +228,7 @@ class CalcTranslateWordTest3(CalcTranslateTest, unittest.TestCase):
def setUp(self):
urwid.set_encoding('utf-8')
- text = B('\xe6\x9b\xbf\xe6\xb4\xbc\n\xe6\xb8\x8e\xe6\xba\x8f\xe6\xbd\xba')
+ text = b'\xe6\x9b\xbf\xe6\xb4\xbc\n\xe6\xb8\x8e\xe6\xba\x8f\xe6\xbd\xba'
width = 10
mode = 'space'
result_left = [
@@ -292,7 +293,7 @@ class CalcTranslateClipTest(CalcTranslateTest, unittest.TestCase):
[(14, 36, 50), (0, 50)]]
class CalcTranslateCantDisplayTest(CalcTranslateTest, unittest.TestCase):
- text = B('Hello\xe9\xa2\x96')
+ text = b'Hello\xe9\xa2\x96'
mode = 'space'
width = 1
result_left = [[]]
@@ -314,8 +315,7 @@ class CalcPosTest(unittest.TestCase):
def tests(self):
for x,y, expected in self.mytests:
got = text_layout.calc_pos( self.text, self.trans, x, y )
- assert got == expected, "%r got:%r expected:%r" % ((x, y), got,
- expected)
+ assert got == expected, f"{x, y!r} got:{got!r} expected:{expected!r}"
class Pos2CoordsTest(unittest.TestCase):
@@ -339,4 +339,4 @@ class Pos2CoordsTest(unittest.TestCase):
for t, answer in self.mytests:
for pos,a in zip(self.pos_list,answer) :
r = text_layout.calc_coords( self.text, t, pos)
- assert r==a, "%r got: %r expected: %r"%(t,r,a)
+ assert r==a, f"{t!r} got: {r!r} expected: {a!r}"
diff --git a/urwid/tests/test_util.py b/urwid/tests/test_util.py
index 2aae075..abe7aa5 100644
--- a/urwid/tests/test_util.py
+++ b/urwid/tests/test_util.py
@@ -1,17 +1,17 @@
-# -*- coding: utf-8 -*-
-import unittest
+from __future__ import annotations
+
import locale
+import unittest
import urwid
from urwid import util
-from urwid.compat import B
class CalcWidthTest(unittest.TestCase):
def wtest(self, desc, s, exp):
- s = B(s)
+ s = s.encode('iso8859-1')
result = util.calc_width( s, 0, len(s))
- assert result==exp, "%s got:%r expected:%r" % (desc, result, exp)
+ assert result==exp, f"{desc} got:{result!r} expected:{exp!r}"
def test1(self):
util.set_encoding("utf-8")
@@ -30,23 +30,22 @@ class CalcWidthTest(unittest.TestCase):
class ConvertDecSpecialTest(unittest.TestCase):
def ctest(self, desc, s, exp, expcs):
- exp = B(exp)
+ exp = exp.encode('iso8859-1')
util.set_encoding('ascii')
c = urwid.Text(s).render((5,))
result = c._text[0]
- assert result==exp, "%s got:%r expected:%r" % (desc, result, exp)
+ assert result==exp, f"{desc} got:{result!r} expected:{exp!r}"
resultcs = c._cs[0]
- assert resultcs==expcs, "%s got:%r expected:%r" % (desc,
- resultcs, expcs)
+ assert resultcs==expcs, f"{desc} got:{resultcs!r} expected:{expcs!r}"
def test1(self):
- self.ctest("no conversion", u"hello", "hello", [(None,5)])
- self.ctest("only special", u"£££££", "}}}}}", [("0",5)])
- self.ctest("mix left", u"££abc", "}}abc", [("0",2),(None,3)])
- self.ctest("mix right", u"abc££", "abc}}", [(None,3),("0",2)])
- self.ctest("mix inner", u"a££bc", "a}}bc",
+ self.ctest("no conversion", "hello", "hello", [(None,5)])
+ self.ctest("only special", "£££££", "}}}}}", [("0",5)])
+ self.ctest("mix left", "££abc", "}}abc", [("0",2),(None,3)])
+ self.ctest("mix right", "abc££", "abc}}", [(None,3),("0",2)])
+ self.ctest("mix inner", "a££bc", "a}}bc",
[(None,1),("0",2),(None,2)] )
- self.ctest("mix well", u"£a£b£", "}a}b}",
+ self.ctest("mix well", "£a£b£", "}a}b}",
[("0",1),(None,1),("0",1),(None,1),("0",1)] )
@@ -55,9 +54,8 @@ class WithinDoubleByteTest(unittest.TestCase):
urwid.set_encoding("euc-jp")
def wtest(self, s, ls, pos, expected, desc):
- result = util.within_double_byte(B(s), ls, pos)
- assert result==expected, "%s got:%r expected: %r" % (desc,
- result, expected)
+ result = util.within_double_byte(s.encode('iso8859-1'), ls, pos)
+ assert result==expected, f"{desc} got:{result!r} expected: {expected!r}"
def test1(self):
self.wtest("mnopqr",0,2,0,'simple no high bytes')
self.wtest("mn\xA1\xA1qr",0,2,1,'simple 1st half')
@@ -91,11 +89,10 @@ class WithinDoubleByteTest(unittest.TestCase):
class CalcTextPosTest(unittest.TestCase):
def ctptest(self, text, tests):
- text = B(text)
+ text = text.encode('iso8859-1')
for s,e,p, expected in tests:
got = util.calc_text_pos( text, s, e, p )
- assert got == expected, "%r got:%r expected:%r" % ((s,e,p),
- got, expected)
+ assert got == expected, f"{s, e, p!r} got:{got!r} expected:{expected!r}"
def test1(self):
text = "hello world out there"
@@ -160,15 +157,15 @@ class TagMarkupTest(unittest.TestCase):
(["mix",[" it",('high',[" up",('ital'," a")])]," little"],
"mix it up a little",
[(None, 6), ('high', 3), ('ital', 2)]),
- ([u"££", u"x££"], u"££x££", []),
- ([B("\xc2\x80"), B("\xc2\x80")], B("\xc2\x80\xc2\x80"), []),
+ (["££", "x££"], "££x££", []),
+ ([b"\xc2\x80", b"\xc2\x80"], b"\xc2\x80\xc2\x80", []),
]
def test(self):
for input, text, attr in self.mytests:
restext,resattr = urwid.decompose_tagmarkup( input )
- assert restext == text, "got: %r expected: %r" % (restext, text)
- assert resattr == attr, "got: %r expected: %r" % (resattr, attr)
+ assert restext == text, f"got: {restext!r} expected: {text!r}"
+ assert resattr == attr, f"got: {resattr!r} expected: {attr!r}"
def test_bad_tuple(self):
self.assertRaises(urwid.TagMarkupException, lambda:
diff --git a/urwid/tests/test_vterm.py b/urwid/tests/test_vterm.py
index cefa089..6ca6fc9 100644
--- a/urwid/tests/test_vterm.py
+++ b/urwid/tests/test_vterm.py
@@ -16,23 +16,23 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
+
+from __future__ import annotations
import errno
import os
import sys
import unittest
-
from itertools import dropwhile
-from urwid.listbox import ListBox
+from urwid import signals, vterm
from urwid.decoration import BoxAdapter
-from urwid import vterm
-from urwid import signals
-from urwid.compat import B
+from urwid.listbox import ListBox
+
-class DummyCommand(object):
- QUITSTRING = B('|||quit|||')
+class DummyCommand:
+ QUITSTRING = b'|||quit|||'
def __init__(self):
self.reader, self.writer = os.pipe()
@@ -40,7 +40,7 @@ class DummyCommand(object):
def __call__(self):
# reset
stdout = getattr(sys.stdout, 'buffer', sys.stdout)
- stdout.write(B('\x1bc'))
+ stdout.write(b'\x1bc')
while True:
data = self.read(1024)
@@ -98,8 +98,8 @@ class TermTest(unittest.TestCase):
self.term.render(self.termsize, focus=False)
def write(self, data):
- data = B(data)
- self.command.write(data.replace(B('\e'), B('\x1b')))
+ data = data.encode('iso8859-1')
+ self.command.write(data.replace(br'\e', b'\x1b'))
def flush(self):
self.write(chr(0x7f))
@@ -108,7 +108,7 @@ class TermTest(unittest.TestCase):
self.term.wait_and_feed()
rendered = self.term.render(self.termsize, focus=focus)
if raw:
- is_empty = lambda c: c == (None, None, B(' '))
+ is_empty = lambda c: c == (None, None, b' ')
content = list(rendered.content())
lines = [list(dropwhile(is_empty, reversed(line)))
for line in content]
@@ -116,11 +116,11 @@ class TermTest(unittest.TestCase):
else:
content = rendered.text
lines = [line.rstrip() for line in content]
- return B('\n').join(lines).rstrip()
+ return b'\n'.join(lines).rstrip()
def expect(self, what, desc=None, raw=False, focus=False):
if not isinstance(what, list):
- what = B(what)
+ what = what.encode('iso8859-1')
got = self.read(raw=raw, focus=focus)
if desc is None:
desc = ''
@@ -138,7 +138,7 @@ class TermTest(unittest.TestCase):
self.expect('hello\nworld')
def test_linefeed2(self):
- self.write('aa\b\b\eDbb')
+ self.write('aa\b\b\\eDbb')
self.expect('aa\nbb')
def test_carriage_return(self):
@@ -146,11 +146,11 @@ class TermTest(unittest.TestCase):
self.expect('world')
def test_insertlines(self):
- self.write('\e[0;0flast\e[0;0f\e[10L\e[0;0ffirst\nsecond\n\e[11D')
+ self.write('\\e[0;0flast\\e[0;0f\\e[10L\\e[0;0ffirst\nsecond\n\\e[11D')
self.expect('first\nsecond\n\n\n\n\n\n\n\n\nlast')
def test_deletelines(self):
- self.write('1\n2\n3\n4\e[2;1f\e[2M')
+ self.write('1\n2\n3\n4\\e[2;1f\\e[2M')
self.expect('1\n4')
def test_nul(self):
@@ -158,21 +158,21 @@ class TermTest(unittest.TestCase):
self.expect('ab')
def test_movement(self):
- self.write('\e[10;20H11\e[10;0f\e[20C\e[K')
+ self.write(r'\e[10;20H11\e[10;0f\e[20C\e[K')
self.expect('\n' * 9 + ' ' * 19 + '1')
- self.write('\e[A\e[B\e[C\e[D\b\e[K')
+ self.write('\\e[A\\e[B\\e[C\\e[D\b\\e[K')
self.expect('')
- self.write('\e[50A2')
+ self.write(r'\e[50A2')
self.expect(' ' * 19 + '2')
- self.write('\b\e[K\e[50B3')
+ self.write('\b\\e[K\\e[50B3')
self.expect('\n' * 23 + ' ' * 19 + '3')
- self.write('\b\e[K' + '\eM' * 30 + '\e[100C4')
+ self.write('\b\\e[K' + r'\eM' * 30 + r'\e[100C4')
self.expect(' ' * 79 + '4')
- self.write('\e[100D\e[K5')
+ self.write(r'\e[100D\e[K5')
self.expect('5')
def edgewall(self):
- edgewall = '1-\e[1;%(x)df-2\e[%(y)d;1f3-\e[%(y)d;%(x)df-4\x0d'
+ edgewall = '1-\\e[1;%(x)df-2\\e[%(y)d;1f3-\\e[%(y)d;%(x)df-4\x0d'
self.write(edgewall % {'x': self.termsize[0] - 1,
'y': self.termsize[1] - 1})
@@ -195,7 +195,7 @@ class TermTest(unittest.TestCase):
+ '3-' + ' ' * 76 + '-4')
for y in range(23, 1, -1):
self.resize(80, y, soft=True)
- self.write('\e[%df\e[J3-\e[%d;%df-4' % (y, y, 79))
+ self.write(r'\e[%df\e[J3-\e[%d;%df-4' % (y, y, 79))
desc = "try to rescale to 80x%d." % y
self.expect('\n' * (y - 2) + '3-' + ' ' * 76 + '-4', desc)
self.resize(80, 24, soft=True)
@@ -204,8 +204,8 @@ class TermTest(unittest.TestCase):
+ '3-' + ' ' * 76 + '-4')
def write_movements(self, arg):
- fmt = 'XXX\n\e[faaa\e[Bccc\e[Addd\e[Bfff\e[Cbbb\e[A\e[Deee'
- self.write(fmt.replace('\e[', '\e['+arg))
+ fmt = 'XXX\n\\e[faaa\\e[Bccc\\e[Addd\\e[Bfff\\e[Cbbb\\e[A\\e[Deee'
+ self.write(fmt.replace(r'\e[', r'\e['+arg))
def test_defargs(self):
self.write_movements('')
@@ -216,82 +216,82 @@ class TermTest(unittest.TestCase):
self.expect('aaa ddd eee\n ccc fff bbb')
def test_erase_line(self):
- self.write('1234567890\e[5D\e[K\n1234567890\e[5D\e[1K\naaaaaaaaaaaaaaa\e[2Ka')
+ self.write('1234567890\\e[5D\\e[K\n1234567890\\e[5D\\e[1K\naaaaaaaaaaaaaaa\\e[2Ka')
self.expect('12345\n 7890\n a')
def test_erase_display(self):
- self.write('1234567890\e[5D\e[Ja')
+ self.write(r'1234567890\e[5D\e[Ja')
self.expect('12345a')
- self.write('98765\e[8D\e[1Jx')
+ self.write(r'98765\e[8D\e[1Jx')
self.expect(' x5a98765')
def test_scrolling_region_simple(self):
- self.write('\e[10;20r\e[10f1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\e[faa')
+ self.write('\\e[10;20r\\e[10f1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\\e[faa')
self.expect('aa' + '\n' * 9 + '2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12')
def test_scrolling_region_reverse(self):
- self.write('\e[2J\e[1;2r\e[5Baaa\r\eM\eM\eMbbb\nXXX')
+ self.write('\\e[2J\\e[1;2r\\e[5Baaa\r\\eM\\eM\\eMbbb\nXXX')
self.expect('\n\nbbb\nXXX\n\naaa')
def test_scrolling_region_move(self):
- self.write('\e[10;20r\e[2J\e[10Bfoo\rbar\rblah\rmooh\r\e[10Aone\r\eM\eMtwo\r\eM\eMthree\r\eM\eMa')
+ self.write('\\e[10;20r\\e[2J\\e[10Bfoo\rbar\rblah\rmooh\r\\e[10Aone\r\\eM\\eMtwo\r\\eM\\eMthree\r\\eM\\eMa')
self.expect('ahree\n\n\n\n\n\n\n\n\n\nmooh')
def test_scrolling_twice(self):
- self.write('\e[?6h\e[10;20r\e[2;5rtest')
+ self.write(r'\e[?6h\e[10;20r\e[2;5rtest')
self.expect('\ntest')
def test_cursor_scrolling_region(self):
- self.write('\e[?6h\e[10;20r\e[10f1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\e[faa')
+ self.write('\\e[?6h\\e[10;20r\\e[10f1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\\e[faa')
self.expect('\n' * 9 + 'aa\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12')
def test_scrolling_region_simple_with_focus(self):
- self.write('\e[10;20r\e[10f1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\e[faa')
+ self.write('\\e[10;20r\\e[10f1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\\e[faa')
self.expect('aa' + '\n' * 9 + '2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12', focus=True)
def test_scrolling_region_reverse_with_focus(self):
- self.write('\e[2J\e[1;2r\e[5Baaa\r\eM\eM\eMbbb\nXXX')
+ self.write('\\e[2J\\e[1;2r\\e[5Baaa\r\\eM\\eM\\eMbbb\nXXX')
self.expect('\n\nbbb\nXXX\n\naaa', focus=True)
def test_scrolling_region_move_with_focus(self):
- self.write('\e[10;20r\e[2J\e[10Bfoo\rbar\rblah\rmooh\r\e[10Aone\r\eM\eMtwo\r\eM\eMthree\r\eM\eMa')
+ self.write('\\e[10;20r\\e[2J\\e[10Bfoo\rbar\rblah\rmooh\r\\e[10Aone\r\\eM\\eMtwo\r\\eM\\eMthree\r\\eM\\eMa')
self.expect('ahree\n\n\n\n\n\n\n\n\n\nmooh', focus=True)
def test_scrolling_twice_with_focus(self):
- self.write('\e[?6h\e[10;20r\e[2;5rtest')
+ self.write(r'\e[?6h\e[10;20r\e[2;5rtest')
self.expect('\ntest', focus=True)
def test_cursor_scrolling_region_with_focus(self):
- self.write('\e[?6h\e[10;20r\e[10f1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\e[faa')
+ self.write('\\e[?6h\\e[10;20r\\e[10f1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\\e[faa')
self.expect('\n' * 9 + 'aa\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12', focus=True)
def test_relative_region_jump(self):
- self.write('\e[21H---\e[10;20r\e[?6h\e[18Htest')
+ self.write(r'\e[21H---\e[10;20r\e[?6h\e[18Htest')
self.expect('\n' * 19 + 'test\n---')
def test_set_multiple_modes(self):
- self.write('\e[?6;5htest')
+ self.write(r'\e[?6;5htest')
self.expect('test')
self.assertTrue(self.term.term_modes.constrain_scrolling)
self.assertTrue(self.term.term_modes.reverse_video)
- self.write('\e[?6;5l')
+ self.write(r'\e[?6;5l')
self.expect('test')
self.assertFalse(self.term.term_modes.constrain_scrolling)
self.assertFalse(self.term.term_modes.reverse_video)
def test_wrap_simple(self):
- self.write('\e[?7h\e[1;%dHtt' % self.term.width)
+ self.write(r'\e[?7h\e[1;%dHtt' % self.term.width)
self.expect(' ' * (self.term.width - 1) + 't\nt')
def test_wrap_backspace_tab(self):
- self.write('\e[?7h\e[1;%dHt\b\b\t\ta' % self.term.width)
+ self.write('\\e[?7h\\e[1;%dHt\b\b\t\ta' % self.term.width)
self.expect(' ' * (self.term.width - 1) + 'a')
def test_cursor_visibility(self):
- self.write('\e[?25linvisible')
+ self.write(r'\e[?25linvisible')
self.expect('invisible', focus=True)
self.assertEqual(self.term.term.cursor, None)
- self.write('\rvisible\e[?25h\e[K')
+ self.write('\rvisible\\e[?25h\\e[K')
self.expect('visible', focus=True)
self.assertNotEqual(self.term.term.cursor, None)
@@ -305,41 +305,41 @@ class TermTest(unittest.TestCase):
def test_encoding_unicode(self):
vterm.util._target_encoding = 'utf-8'
- self.write('\e%G\xe2\x80\x94')
+ self.write('\\e%G\xe2\x80\x94')
self.expect('\xe2\x80\x94')
def test_encoding_unicode_ascii(self):
vterm.util._target_encoding = 'ascii'
- self.write('\e%G\xe2\x80\x94')
+ self.write('\\e%G\xe2\x80\x94')
self.expect('?')
def test_encoding_wrong_unicode(self):
vterm.util._target_encoding = 'utf-8'
- self.write('\e%G\xc0\x99')
+ self.write('\\e%G\xc0\x99')
self.expect('')
def test_encoding_vt100_graphics(self):
vterm.util._target_encoding = 'ascii'
- self.write('\e)0\e(0\x0fg\x0eg\e)Bn\e)0g\e)B\e(B\x0fn')
+ self.write('\\e)0\\e(0\x0fg\x0eg\\e)Bn\\e)0g\\e)B\\e(B\x0fn')
self.expect([[
- (None, '0', B('g')), (None, '0', B('g')),
- (None, None, B('n')), (None, '0', B('g')),
- (None, None, B('n'))
+ (None, '0', b'g'), (None, '0', b'g'),
+ (None, None, b'n'), (None, '0', b'g'),
+ (None, None, b'n')
]], raw=True)
def test_ibmpc_mapping(self):
vterm.util._target_encoding = 'ascii'
- self.write('\e[11m\x18\e[10m\x18')
- self.expect([[(None, 'U', B('\x18'))]], raw=True)
+ self.write('\\e[11m\x18\\e[10m\x18')
+ self.expect([[(None, 'U', b'\x18')]], raw=True)
- self.write('\ec\e)U\x0e\x18\x0f\e[3h\x18\e[3l\x18')
- self.expect([[(None, None, B('\x18'))]], raw=True)
+ self.write('\\ec\\e)U\x0e\x18\x0f\\e[3h\x18\\e[3l\x18')
+ self.expect([[(None, None, b'\x18')]], raw=True)
- self.write('\ec\e[11m\xdb\x18\e[10m\xdb')
+ self.write('\\ec\\e[11m\xdb\x18\\e[10m\xdb')
self.expect([[
- (None, 'U', B('\xdb')), (None, 'U', B('\x18')),
- (None, None, B('\xdb'))
+ (None, 'U', b'\xdb'), (None, 'U', b'\x18'),
+ (None, None, b'\xdb')
]], raw=True)
def test_set_title(self):
@@ -349,20 +349,20 @@ class TermTest(unittest.TestCase):
self._the_title = title
self.connect_signal('title')
- self.write('\e]666parsed right?\e\\te\e]0;test title\007st1')
+ self.write('\\e]666parsed right?\\e\\te\\e]0;test title\007st1')
self.expect('test1')
- self.expect_signal(B('test title'))
- self.write('\e]3;stupid title\e\\\e[0G\e[2Ktest2')
+ self.expect_signal(b'test title')
+ self.write('\\e]3;stupid title\\e\\\\e[0G\\e[2Ktest2')
self.expect('test2')
- self.expect_signal(B('stupid title'))
+ self.expect_signal(b'stupid title')
self.disconnect_signal('title')
def test_set_leds(self):
self.connect_signal('leds')
- self.write('\e[0qtest1')
+ self.write(r'\e[0qtest1')
self.expect('test1')
self.expect_signal('clear')
- self.write('\e[3q\e[H\e[Ktest2')
+ self.write(r'\e[3q\e[H\e[Ktest2')
self.expect('test2')
self.expect_signal('caps_lock')
self.disconnect_signal('leds')
diff --git a/urwid/tests/test_widget.py b/urwid/tests/test_widget.py
index 3f28bc1..4ea2944 100644
--- a/urwid/tests/test_widget.py
+++ b/urwid/tests/test_widget.py
@@ -1,7 +1,7 @@
-# -*- coding: utf-8 -*-
+from __future__ import annotations
+
import unittest
-from urwid.compat import B
import urwid
@@ -10,48 +10,46 @@ class TextTest(unittest.TestCase):
self.t = urwid.Text("I walk the\ncity in the night")
def test1_wrap(self):
- expected = [B(t) for t in ("I walk the","city in ","the night ")]
+ expected = [t.encode('iso8859-1') for t in ("I walk the","city in ","the night ")]
got = self.t.render((10,))._text
- assert got == expected, "got: %r expected: %r" % (got, expected)
+ assert got == expected, f"got: {got!r} expected: {expected!r}"
def test2_left(self):
self.t.set_align_mode('left')
- expected = [B(t) for t in ("I walk the ","city in the night ")]
+ expected = [t.encode('iso8859-1') for t in ("I walk the ","city in the night ")]
got = self.t.render((18,))._text
- assert got == expected, "got: %r expected: %r" % (got, expected)
+ assert got == expected, f"got: {got!r} expected: {expected!r}"
def test3_right(self):
self.t.set_align_mode('right')
- expected = [B(t) for t in (" I walk the"," city in the night")]
+ expected = [t.encode('iso8859-1') for t in (" I walk the"," city in the night")]
got = self.t.render((18,))._text
- assert got == expected, "got: %r expected: %r" % (got, expected)
+ assert got == expected, f"got: {got!r} expected: {expected!r}"
def test4_center(self):
self.t.set_align_mode('center')
- expected = [B(t) for t in (" I walk the "," city in the night")]
+ expected = [t.encode('iso8859-1') for t in (" I walk the "," city in the night")]
got = self.t.render((18,))._text
- assert got == expected, "got: %r expected: %r" % (got, expected)
+ assert got == expected, f"got: {got!r} expected: {expected!r}"
def test5_encode_error(self):
urwid.set_encoding("ascii")
- expected = [B("? ")]
- got = urwid.Text(u'û').render((3,))._text
- assert got == expected, "got: %r expected: %r" % (got, expected)
+ expected = [b"? "]
+ got = urwid.Text('û').render((3,))._text
+ assert got == expected, f"got: {got!r} expected: {expected!r}"
class EditTest(unittest.TestCase):
def setUp(self):
- self.t1 = urwid.Edit(B(""),"blah blah")
- self.t2 = urwid.Edit(B("stuff:"), "blah blah")
- self.t3 = urwid.Edit(B("junk:\n"),"blah blah\n\nbloo",1)
- self.t4 = urwid.Edit(u"better:")
+ self.t1 = urwid.Edit(b"","blah blah")
+ self.t2 = urwid.Edit(b"stuff:", "blah blah")
+ self.t3 = urwid.Edit(b"junk:\n","blah blah\n\nbloo",1)
+ self.t4 = urwid.Edit("better:")
def ktest(self, e, key, expected, pos, desc):
got= e.keypress((12,),key)
- assert got == expected, "%s. got: %r expected:%r" % (desc, got,
- expected)
- assert e.edit_pos == pos, "%s. pos: %r expected pos: %r" % (
- desc, e.edit_pos, pos)
+ assert got == expected, f"{desc}. got: {got!r} expected:{expected!r}"
+ assert e.edit_pos == pos, f"{desc}. pos: {e.edit_pos!r} expected pos: {pos!r}"
def test1_left(self):
self.t1.set_edit_pos(0)
@@ -91,22 +89,21 @@ class EditTest(unittest.TestCase):
def test_utf8_input(self):
urwid.set_encoding("utf-8")
self.t1.set_edit_text('')
- self.t1.keypress((12,), u'û')
- self.assertEqual(self.t1.edit_text, u'û'.encode('utf-8'))
- self.t4.keypress((12,), u'û')
- self.assertEqual(self.t4.edit_text, u'û')
+ self.t1.keypress((12,), 'û')
+ self.assertEqual(self.t1.edit_text, 'û'.encode())
+ self.t4.keypress((12,), 'û')
+ self.assertEqual(self.t4.edit_text, 'û')
class EditRenderTest(unittest.TestCase):
def rtest(self, w, expected_text, expected_cursor):
- expected_text = [B(t) for t in expected_text]
+ expected_text = [t.encode('iso8859-1') for t in expected_text]
get_cursor = w.get_cursor_coords((4,))
assert get_cursor == expected_cursor, "got: %r expected: %r" % (
get_cursor, expected_cursor)
r = w.render((4,), focus = 1)
text = [t for a, cs, t in [ln[0] for ln in r.content()]]
- assert text == expected_text, "got: %r expected: %r" % (text,
- expected_text)
+ assert text == expected_text, f"got: {text!r} expected: {expected_text!r}"
assert r.cursor == expected_cursor, "got: %r expected: %r" % (
r.cursor, expected_cursor)
diff --git a/urwid/tests/util.py b/urwid/tests/util.py
index 0c8c928..4e59339 100644
--- a/urwid/tests/util.py
+++ b/urwid/tests/util.py
@@ -1,5 +1,8 @@
+from __future__ import annotations
+
import urwid
+
class SelectableText(urwid.Text):
def selectable(self):
return True
diff --git a/urwid/text_layout.py b/urwid/text_layout.py
index a1c64f4..5fcb407 100644
--- a/urwid/text_layout.py
+++ b/urwid/text_layout.py
@@ -1,5 +1,4 @@
#!/usr/bin/python
-# -*- coding: utf-8 -*-
#
# Urwid Text Layout classes
# Copyright (C) 2004-2011 Ian Ward
@@ -18,13 +17,20 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
-from urwid.util import calc_width, calc_text_pos, calc_trim_text, is_wide_char, \
- move_prev_char, move_next_char
-from urwid.compat import PYTHON3, B, xrange
+from __future__ import annotations
+
+from urwid.util import (
+ calc_text_pos,
+ calc_trim_text,
+ calc_width,
+ is_wide_char,
+ move_next_char,
+ move_prev_char,
+)
+
class TextLayout:
def supports_align_mode(self, align):
@@ -94,7 +100,7 @@ class StandardTextLayout(TextLayout):
returned by self.layout().
"""
maxwidth = 0
- assert layout, "huh? empty layout?: "+repr(layout)
+ assert layout, f"huh? empty layout?: {layout!r}"
for l in layout:
lw = line_width(l)
if lw >= maxcol:
@@ -131,8 +137,8 @@ class StandardTextLayout(TextLayout):
Returns a layout structure without alignment applied.
"""
nl, nl_o, sp_o = "\n", "\n", " "
- if PYTHON3 and isinstance(text, bytes):
- nl = B(nl) # can only find bytes in python3 bytestrings
+ if isinstance(text, bytes):
+ nl = nl.encode('iso8859-1') # can only find bytes in python3 bytestrings
nl_o = ord(nl_o) # + an item of a bytestring is the ordinal value
sp_o = ord(sp_o)
b = []
@@ -163,7 +169,7 @@ class StandardTextLayout(TextLayout):
if p!=n_end:
l += [(sc, p, n_end)]
if trimmed:
- l += [(1, n_end, u'…'.encode("utf-8"))]
+ l += [(1, n_end, '…'.encode())]
l += [(pad_right,n_end)]
b.append(l)
p = n_cr+1
@@ -318,8 +324,8 @@ class LayoutSegment:
# use text stored in segment (self.text)
spos, epos, pad_left, pad_right = calc_trim_text(
self.text, 0, len(self.text), start, end )
- return [ (end-start, self.offs, bytes().ljust(pad_left) +
- self.text[spos:epos] + bytes().ljust(pad_right)) ]
+ return [ (end-start, self.offs, b''.ljust(pad_left) +
+ self.text[spos:epos] + b''.ljust(pad_right)) ]
elif self.end:
# use text passed as parameter (text)
spos, epos, pad_left, pad_right = calc_trim_text(
@@ -345,7 +351,7 @@ def line_width( segs ):
"""
sc = 0
seglist = segs
- if segs and len(segs[0])==2 and segs[0][1]==None:
+ if segs and len(segs[0])==2 and segs[0][1] is None:
seglist = segs[1:]
for s in seglist:
sc += s[0]
@@ -359,7 +365,7 @@ def shift_line( segs, amount ):
"""
assert type(amount)==int, repr(amount)
- if segs and len(segs[0])==2 and segs[0][1]==None:
+ if segs and len(segs[0])==2 and segs[0][1] is None:
# existing shift
amount += segs[0][0]
if amount:
@@ -477,8 +483,8 @@ def calc_pos( text, layout, pref_col, row ):
if pos is not None:
return pos
- rows_above = list(xrange(row-1,-1,-1))
- rows_below = list(xrange(row+1,len(layout)))
+ rows_above = list(range(row-1,-1,-1))
+ rows_below = list(range(row+1,len(layout)))
while rows_above and rows_below:
if rows_above:
r = rows_above.pop(0)
diff --git a/urwid/treetools.py b/urwid/treetools.py
index ea979c8..769da49 100644
--- a/urwid/treetools.py
+++ b/urwid/treetools.py
@@ -18,9 +18,8 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
"""
Urwid tree view
@@ -31,6 +30,8 @@ Features:
"""
+from __future__ import annotations
+
import urwid
from urwid.wimp import SelectableIcon
@@ -51,7 +52,7 @@ class TreeWidget(urwid.WidgetWrap):
self.is_leaf = not hasattr(node, 'get_first_child')
self.expanded = True
widget = self.get_indented_widget()
- self.__super.__init__(widget)
+ super().__init__(widget)
def selectable(self):
"""
@@ -90,8 +91,7 @@ class TreeWidget(urwid.WidgetWrap):
return self._node
def get_display_text(self):
- return (self.get_node().get_key() + ": " +
- str(self.get_node().get_value()))
+ return (f"{self.get_node().get_key()}: {str(self.get_node().get_value())}")
def next_inorder(self):
"""Return the next TreeWidget depth first from this one."""
@@ -150,7 +150,7 @@ class TreeWidget(urwid.WidgetWrap):
self.expanded = False
self.update_expanded_icon()
elif self._w.selectable():
- return self.__super.keypress(size, key)
+ return super().keypress(size, key)
else:
return key
@@ -193,7 +193,7 @@ class TreeWidget(urwid.WidgetWrap):
return lastdescendant
-class TreeNode(object):
+class TreeNode:
"""
Store tree contents and cache TreeWidget objects.
A TreeNode consists of the following elements:
@@ -243,7 +243,7 @@ class TreeNode(object):
self.get_parent().change_child_key(self._key, key)
def get_parent(self):
- if self._parent == None and self.get_depth() > 0:
+ if self._parent is None and self.get_depth() > 0:
self._parent = self.load_parent()
return self._parent
@@ -320,7 +320,7 @@ class ParentNode(TreeNode):
def change_child_key(self, oldkey, newkey):
if newkey in self._children:
- raise TreeWidgetError("%s is already in use" % newkey)
+ raise TreeWidgetError(f"{newkey} is already in use")
self._children[newkey] = self._children.pop(oldkey)
self._children[newkey].set_key(newkey)
@@ -328,8 +328,7 @@ class ParentNode(TreeNode):
try:
return self.get_child_keys().index(key)
except ValueError:
- errorstring = ("Can't find key %s in ParentNode %s\n" +
- "ParentNode items: %s")
+ errorstring = f"Can't find key %s in ParentNode %s\nParentNode items: %s"
raise TreeWidgetError(errorstring % (key, self.get_key(),
str(self.get_child_keys())))
@@ -418,7 +417,7 @@ class TreeListBox(urwid.ListBox):
collapsing of TreeWidgets"""
def keypress(self, size, key):
- key = self.__super.keypress(size, key)
+ key = super().keypress(size, key)
return self.unhandled_input(size, key)
def unhandled_input(self, size, input):
diff --git a/urwid/util.py b/urwid/util.py
index 6173ca2..3ac4657 100644
--- a/urwid/util.py
+++ b/urwid/util.py
@@ -1,5 +1,4 @@
#!/usr/bin/python
-# -*- coding: utf-8 -*-
#
# Urwid utility functions
# Copyright (C) 2004-2011 Ian Ward
@@ -18,15 +17,15 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
-from urwid import escape
-from urwid.compat import text_type, text_types
+from __future__ import annotations
import codecs
+from urwid import escape
+
str_util = escape.str_util
# bring str_util functions into our namespace
@@ -98,7 +97,7 @@ def set_encoding( encoding ):
_target_encoding = 'ascii'
try:
if encoding:
- u"".encode(encoding)
+ "".encode(encoding)
_target_encoding = encoding
except LookupError: pass
@@ -116,12 +115,12 @@ def apply_target_encoding( s ):
"""
Return (encoded byte string, character set rle).
"""
- if _use_dec_special and type(s) == text_type:
+ if _use_dec_special and type(s) == str:
# first convert drawing characters
s = s.translate(escape.DEC_SPECIAL_CHARMAP)
- if type(s) == text_type:
- s = s.replace(escape.SI+escape.SO, u"") # remove redundant shifts
+ if type(s) == str:
+ s = s.replace(escape.SI+escape.SO, "") # remove redundant shifts
s = codecs.encode(s, _target_encoding, 'replace')
assert isinstance(s, bytes)
@@ -132,7 +131,7 @@ def apply_target_encoding( s ):
assert isinstance(sis[0], bytes)
- sis0 = sis[0].replace(SI, bytes())
+ sis0 = sis[0].replace(SI, b'')
sout = []
cout = []
if sis0:
@@ -153,7 +152,7 @@ def apply_target_encoding( s ):
rle_append_modify(cout, (escape.DEC_TAG.encode('ascii'), len(sin)))
continue
sin, son = sl
- son = son.replace(SI, bytes())
+ son = son.replace(SI, b'')
if sin:
sout.append(sin)
rle_append_modify(cout, (escape.DEC_TAG, len(sin)))
@@ -161,7 +160,7 @@ def apply_target_encoding( s ):
sout.append(son)
rle_append_modify(cout, (None, len(son)))
- outstr = bytes().join(sout)
+ outstr = b''.join(sout)
return outstr, cout
@@ -230,8 +229,8 @@ def trim_text_attr_cs( text, attr, cs, start_col, end_col ):
rle_append_modify( attrtr, (al, 1) )
rle_append_modify( cstr, (None, 1) )
- return (bytes().rjust(pad_left) + text[spos:epos] +
- bytes().rjust(pad_right), attrtr, cstr)
+ return (b''.rjust(pad_left) + text[spos:epos] +
+ b''.rjust(pad_right), attrtr, cstr)
def rle_get_at( rle, pos ):
@@ -409,13 +408,13 @@ def _tagmarkup_recurse( tm, attr ):
if type(tm) == tuple:
# tuples mark a new attribute boundary
if len(tm) != 2:
- raise TagMarkupException("Tuples must be in the form (attribute, tagmarkup): %r" % (tm,))
+ raise TagMarkupException(f"Tuples must be in the form (attribute, tagmarkup): {tm!r}")
attr, element = tm
return _tagmarkup_recurse( element, attr )
- if not isinstance(tm, text_types + (bytes,)):
- raise TagMarkupException("Invalid markup element: %r" % tm)
+ if not isinstance(tm, (str, bytes)):
+ raise TagMarkupException(f"Invalid markup element: {tm!r}")
# text
return [tm], [(attr, len(tm))]
@@ -429,14 +428,13 @@ def is_mouse_press( ev ):
return ev.find("press")>=0
-
class MetaSuper(type):
"""adding .__super"""
def __init__(cls, name, bases, d):
- super(MetaSuper, cls).__init__(name, bases, d)
- if hasattr(cls, "_%s__super" % name):
+ super().__init__(name, bases, d)
+ if hasattr(cls, f"_{name}__super"):
raise AttributeError("Class has same name as one of its super classes")
- setattr(cls, "_%s__super" % name, super(cls))
+ setattr(cls, f"_{name}__super", super(cls))
@@ -461,7 +459,7 @@ def int_scale(val, val_range, out_range):
return num // dem
-class StoppingContext(object):
+class StoppingContext:
"""Context manager that calls ``stop`` on a given object on exit. Used to
make the ``start`` method on `MainLoop` and `BaseScreen` optionally act as
context managers.
diff --git a/urwid/version.py b/urwid/version.py
index f88733b..46fbe75 100644
--- a/urwid/version.py
+++ b/urwid/version.py
@@ -1,4 +1,2 @@
-from __future__ import division, print_function
-
VERSION = (2, 1, 3, 'dev0')
__version__ = '.'.join([str(x) for x in VERSION])
diff --git a/urwid/vterm.py b/urwid/vterm.py
index 761a602..aee0bae 100644
--- a/urwid/vterm.py
+++ b/urwid/vterm.py
@@ -18,36 +18,36 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
-import os
-import sys
-import time
+from __future__ import annotations
+
+import atexit
import copy
import errno
+import os
import select
-import struct
import signal
-import atexit
+import struct
+import sys
+import time
import traceback
try:
- import pty
import fcntl
+ import pty
import termios
except ImportError:
pass # windows
from urwid import util
-from urwid.escape import DEC_SPECIAL_CHARS, ALT_DEC_SPECIAL_CHARS
from urwid.canvas import Canvas
-from urwid.widget import Widget, BOX
-from urwid.display_common import AttrSpec, RealTerminal, _BASIC_COLORS
-from urwid.compat import ord2, chr2, B, PYTHON3, xrange
+from urwid.display_common import _BASIC_COLORS, AttrSpec, RealTerminal
+from urwid.escape import ALT_DEC_SPECIAL_CHARS, DEC_SPECIAL_CHARS
+from urwid.widget import BOX, Widget
-EOF = B('')
+EOF = b''
ESC = chr(27)
KEY_TRANSLATIONS = {
@@ -55,41 +55,41 @@ KEY_TRANSLATIONS = {
'backspace': chr(127),
'tab': chr(9),
'esc': ESC,
- 'up': ESC + '[A',
- 'down': ESC + '[B',
- 'right': ESC + '[C',
- 'left': ESC + '[D',
- 'home': ESC + '[1~',
- 'insert': ESC + '[2~',
- 'delete': ESC + '[3~',
- 'end': ESC + '[4~',
- 'page up': ESC + '[5~',
- 'page down': ESC + '[6~',
-
- 'f1': ESC + '[[A',
- 'f2': ESC + '[[B',
- 'f3': ESC + '[[C',
- 'f4': ESC + '[[D',
- 'f5': ESC + '[[E',
- 'f6': ESC + '[17~',
- 'f7': ESC + '[18~',
- 'f8': ESC + '[19~',
- 'f9': ESC + '[20~',
- 'f10': ESC + '[21~',
- 'f11': ESC + '[23~',
- 'f12': ESC + '[24~',
+ 'up': f"{ESC}[A",
+ 'down': f"{ESC}[B",
+ 'right': f"{ESC}[C",
+ 'left': f"{ESC}[D",
+ 'home': f"{ESC}[1~",
+ 'insert': f"{ESC}[2~",
+ 'delete': f"{ESC}[3~",
+ 'end': f"{ESC}[4~",
+ 'page up': f"{ESC}[5~",
+ 'page down': f"{ESC}[6~",
+
+ 'f1': f"{ESC}[[A",
+ 'f2': f"{ESC}[[B",
+ 'f3': f"{ESC}[[C",
+ 'f4': f"{ESC}[[D",
+ 'f5': f"{ESC}[[E",
+ 'f6': f"{ESC}[17~",
+ 'f7': f"{ESC}[18~",
+ 'f8': f"{ESC}[19~",
+ 'f9': f"{ESC}[20~",
+ 'f10': f"{ESC}[21~",
+ 'f11': f"{ESC}[23~",
+ 'f12': f"{ESC}[24~",
}
KEY_TRANSLATIONS_DECCKM = {
- 'up': ESC + 'OA',
- 'down': ESC + 'OB',
- 'right': ESC + 'OC',
- 'left': ESC + 'OD',
- 'f1': ESC + 'OP',
- 'f2': ESC + 'OQ',
- 'f3': ESC + 'OR',
- 'f4': ESC + 'OS',
- 'f5': ESC + '[15~',
+ 'up': f"{ESC}OA",
+ 'down': f"{ESC}OB",
+ 'right': f"{ESC}OC",
+ 'left': f"{ESC}OD",
+ 'f1': f"{ESC}OP",
+ 'f2': f"{ESC}OQ",
+ 'f3': f"{ESC}OR",
+ 'f4': f"{ESC}OS",
+ 'f5': f"{ESC}[15~",
}
CSI_COMMANDS = {
@@ -101,44 +101,44 @@ CSI_COMMANDS = {
# while callback is executed as:
# callback(<instance of TermCanvas>, arguments, has_question_mark)
- B('@'): (1, 1, lambda s, number, q: s.insert_chars(chars=number[0])),
- B('A'): (1, 1, lambda s, rows, q: s.move_cursor(0, -rows[0], relative=True)),
- B('B'): (1, 1, lambda s, rows, q: s.move_cursor(0, rows[0], relative=True)),
- B('C'): (1, 1, lambda s, cols, q: s.move_cursor(cols[0], 0, relative=True)),
- B('D'): (1, 1, lambda s, cols, q: s.move_cursor(-cols[0], 0, relative=True)),
- B('E'): (1, 1, lambda s, rows, q: s.move_cursor(0, rows[0], relative_y=True)),
- B('F'): (1, 1, lambda s, rows, q: s.move_cursor(0, -rows[0], relative_y=True)),
- B('G'): (1, 1, lambda s, col, q: s.move_cursor(col[0] - 1, 0, relative_y=True)),
- B('H'): (2, 1, lambda s, x_y, q: s.move_cursor(x_y[1] - 1, x_y[0] - 1)),
- B('J'): (1, 0, lambda s, mode, q: s.csi_erase_display(mode[0])),
- B('K'): (1, 0, lambda s, mode, q: s.csi_erase_line(mode[0])),
- B('L'): (1, 1, lambda s, number, q: s.insert_lines(lines=number[0])),
- B('M'): (1, 1, lambda s, number, q: s.remove_lines(lines=number[0])),
- B('P'): (1, 1, lambda s, number, q: s.remove_chars(chars=number[0])),
- B('X'): (1, 1, lambda s, number, q: s.erase(s.term_cursor,
+ b'@': (1, 1, lambda s, number, q: s.insert_chars(chars=number[0])),
+ b'A': (1, 1, lambda s, rows, q: s.move_cursor(0, -rows[0], relative=True)),
+ b'B': (1, 1, lambda s, rows, q: s.move_cursor(0, rows[0], relative=True)),
+ b'C': (1, 1, lambda s, cols, q: s.move_cursor(cols[0], 0, relative=True)),
+ b'D': (1, 1, lambda s, cols, q: s.move_cursor(-cols[0], 0, relative=True)),
+ b'E': (1, 1, lambda s, rows, q: s.move_cursor(0, rows[0], relative_y=True)),
+ b'F': (1, 1, lambda s, rows, q: s.move_cursor(0, -rows[0], relative_y=True)),
+ b'G': (1, 1, lambda s, col, q: s.move_cursor(col[0] - 1, 0, relative_y=True)),
+ b'H': (2, 1, lambda s, x_y, q: s.move_cursor(x_y[1] - 1, x_y[0] - 1)),
+ b'J': (1, 0, lambda s, mode, q: s.csi_erase_display(mode[0])),
+ b'K': (1, 0, lambda s, mode, q: s.csi_erase_line(mode[0])),
+ b'L': (1, 1, lambda s, number, q: s.insert_lines(lines=number[0])),
+ b'M': (1, 1, lambda s, number, q: s.remove_lines(lines=number[0])),
+ b'P': (1, 1, lambda s, number, q: s.remove_chars(chars=number[0])),
+ b'X': (1, 1, lambda s, number, q: s.erase(s.term_cursor,
(s.term_cursor[0]+number[0] - 1,
s.term_cursor[1]))),
- B('a'): ('alias', B('C')),
- B('c'): (0, 0, lambda s, none, q: s.csi_get_device_attributes(q)),
- B('d'): (1, 1, lambda s, row, q: s.move_cursor(0, row[0] - 1, relative_x=True)),
- B('e'): ('alias', B('B')),
- B('f'): ('alias', B('H')),
- B('g'): (1, 0, lambda s, mode, q: s.csi_clear_tabstop(mode[0])),
- B('h'): (1, 0, lambda s, modes, q: s.csi_set_modes(modes, q)),
- B('l'): (1, 0, lambda s, modes, q: s.csi_set_modes(modes, q, reset=True)),
- B('m'): (1, 0, lambda s, attrs, q: s.csi_set_attr(attrs)),
- B('n'): (1, 0, lambda s, mode, q: s.csi_status_report(mode[0])),
- B('q'): (1, 0, lambda s, mode, q: s.csi_set_keyboard_leds(mode[0])),
- B('r'): (2, 0, lambda s, t_b, q: s.csi_set_scroll(t_b[0], t_b[1])),
- B('s'): (0, 0, lambda s, none, q: s.save_cursor()),
- B('u'): (0, 0, lambda s, none, q: s.restore_cursor()),
- B('`'): ('alias', B('G')),
+ b'a': ('alias', b'C'),
+ b'c': (0, 0, lambda s, none, q: s.csi_get_device_attributes(q)),
+ b'd': (1, 1, lambda s, row, q: s.move_cursor(0, row[0] - 1, relative_x=True)),
+ b'e': ('alias', b'B'),
+ b'f': ('alias', b'H'),
+ b'g': (1, 0, lambda s, mode, q: s.csi_clear_tabstop(mode[0])),
+ b'h': (1, 0, lambda s, modes, q: s.csi_set_modes(modes, q)),
+ b'l': (1, 0, lambda s, modes, q: s.csi_set_modes(modes, q, reset=True)),
+ b'm': (1, 0, lambda s, attrs, q: s.csi_set_attr(attrs)),
+ b'n': (1, 0, lambda s, mode, q: s.csi_status_report(mode[0])),
+ b'q': (1, 0, lambda s, mode, q: s.csi_set_keyboard_leds(mode[0])),
+ b'r': (2, 0, lambda s, t_b, q: s.csi_set_scroll(t_b[0], t_b[1])),
+ b's': (0, 0, lambda s, none, q: s.save_cursor()),
+ b'u': (0, 0, lambda s, none, q: s.restore_cursor()),
+ b'`': ('alias', b'G'),
}
CHARSET_DEFAULT = 1
CHARSET_UTF8 = 2
-class TermModes(object):
+class TermModes:
def __init__(self):
self.reset()
@@ -158,7 +158,7 @@ class TermModes(object):
# charset stuff
self.main_charset = CHARSET_DEFAULT
-class TermCharset(object):
+class TermCharset:
MAPPING = {
'default': None,
'vt100': '0',
@@ -228,15 +228,15 @@ class TermScroller(list):
def append(self, obj):
self.trunc()
- super(TermScroller, self).append(obj)
+ super().append(obj)
def insert(self, idx, obj):
self.trunc()
- super(TermScroller, self).insert(idx, obj)
+ super().insert(idx, obj)
def extend(self, seq):
self.trunc()
- super(TermScroller, self).extend(seq)
+ super().extend(seq)
class TermCanvas(Canvas):
cacheable = False
@@ -253,7 +253,7 @@ class TermCanvas(Canvas):
self.scrolling_up = 0
self.utf8_eat_bytes = None
- self.utf8_buffer = bytes()
+ self.utf8_buffer = b''
self.coords["cursor"] = (0, 0, None)
@@ -318,7 +318,7 @@ class TermCanvas(Canvas):
"""
Reset the terminal.
"""
- self.escbuf = bytes()
+ self.escbuf = b''
self.within_escape = False
self.parsestate = 0
@@ -353,7 +353,7 @@ class TermCanvas(Canvas):
def set_tabstop(self, x=None, remove=False, clear=False):
if clear:
- for tab in xrange(len(self.tabstops)):
+ for tab in range(len(self.tabstops)):
self.tabstops[tab] = 0
return
@@ -373,10 +373,10 @@ class TermCanvas(Canvas):
div, mod = divmod(x, 8)
return (self.tabstops[div] & (1 << mod)) > 0
- def empty_line(self, char=B(' ')):
+ def empty_line(self, char=b' '):
return [self.empty_char(char)] * self.width
- def empty_char(self, char=B(' ')):
+ def empty_char(self, char=b' '):
return (self.attrspec, self.charset.current, char)
def addstr(self, data):
@@ -385,7 +385,7 @@ class TermCanvas(Canvas):
return
for byte in data:
- self.addbyte(ord2(byte))
+ self.addbyte(byte)
def resize(self, width, height):
"""
@@ -395,18 +395,18 @@ class TermCanvas(Canvas):
if width > self.width:
# grow
- for y in xrange(self.height):
+ for y in range(self.height):
self.term[y] += [self.empty_char()] * (width - self.width)
elif width < self.width:
# shrink
- for y in xrange(self.height):
+ for y in range(self.height):
self.term[y] = self.term[y][:width]
self.width = width
if height > self.height:
# grow
- for y in xrange(self.height, height):
+ for y in range(self.height, height):
try:
last_line = self.scrollback_buffer.pop()
except IndexError:
@@ -427,7 +427,7 @@ class TermCanvas(Canvas):
self.term.insert(0, last_line)
elif height < self.height:
# shrink
- for y in xrange(height, self.height):
+ for y in range(height, self.height):
self.scrollback_buffer.append(self.term.pop(0))
self.height = height
@@ -447,16 +447,16 @@ class TermCanvas(Canvas):
if self.modes.main_charset != CHARSET_DEFAULT:
return
- if mod == B('('):
+ if mod == b'(':
g = 0
else:
g = 1
- if char == B('0'):
+ if char == b'0':
cset = 'vt100'
- elif char == B('U'):
+ elif char == b'U':
cset = 'ibmpc'
- elif char == B('K'):
+ elif char == b'K':
cset = 'user'
else:
cset = 'default'
@@ -467,10 +467,10 @@ class TermCanvas(Canvas):
"""
Parse ECMA-48 CSI (Control Sequence Introducer) sequences.
"""
- qmark = self.escbuf.startswith(B('?'))
+ qmark = self.escbuf.startswith(b'?')
escbuf = []
- for arg in self.escbuf[qmark and 1 or 0:].split(B(';')):
+ for arg in self.escbuf[qmark and 1 or 0:].split(b';'):
try:
num = int(arg)
except ValueError:
@@ -487,7 +487,7 @@ class TermCanvas(Canvas):
number_of_args, default_value, cmd = csi_cmd
while len(escbuf) < number_of_args:
escbuf.append(default_value)
- for i in xrange(len(escbuf)):
+ for i in range(len(escbuf)):
if escbuf[i] is None or escbuf[i] == 0:
escbuf[i] = default_value
@@ -502,40 +502,40 @@ class TermCanvas(Canvas):
"""
Parse escape sequences which are not CSI.
"""
- if mod == B('#') and char == B('8'):
+ if mod == b'#' and char == b'8':
self.decaln()
- elif mod == B('%'): # select main character set
- if char == B('@'):
+ elif mod == b'%': # select main character set
+ if char == b'@':
self.modes.main_charset = CHARSET_DEFAULT
- elif char in B('G8'):
+ elif char in b'G8':
# 8 is obsolete and only for backwards compatibility
self.modes.main_charset = CHARSET_UTF8
- elif mod == B('(') or mod == B(')'): # define G0/G1
+ elif mod == b'(' or mod == b')': # define G0/G1
self.set_g01(char, mod)
- elif char == B('M'): # reverse line feed
+ elif char == b'M': # reverse line feed
self.linefeed(reverse=True)
- elif char == B('D'): # line feed
+ elif char == b'D': # line feed
self.linefeed()
- elif char == B('c'): # reset terminal
+ elif char == b'c': # reset terminal
self.reset()
- elif char == B('E'): # newline
+ elif char == b'E': # newline
self.newline()
- elif char == B('H'): # set tabstop
+ elif char == b'H': # set tabstop
self.set_tabstop()
- elif char == B('Z'): # DECID
- self.widget.respond(ESC + '[?6c')
- elif char == B('7'): # save current state
+ elif char == b'Z': # DECID
+ self.widget.respond(f"{ESC}[?6c")
+ elif char == b'7': # save current state
self.save_cursor(with_attrs=True)
- elif char == B('8'): # restore current state
+ elif char == b'8': # restore current state
self.restore_cursor(with_attrs=True)
def parse_osc(self, buf):
"""
Parse operating system command.
"""
- if buf.startswith(B(';')): # set window title and icon
+ if buf.startswith(b';'): # set window title and icon
self.widget.set_title(buf[1:])
- elif buf.startswith(B('3;')): # set window title
+ elif buf.startswith(b'3;'): # set window title
self.widget.set_title(buf[2:])
def parse_escape(self, char):
@@ -544,43 +544,43 @@ class TermCanvas(Canvas):
if char in CSI_COMMANDS.keys():
self.parse_csi(char)
self.parsestate = 0
- elif char in B('0123456789;') or (not self.escbuf and char == B('?')):
+ elif char in b'0123456789;' or (not self.escbuf and char == b'?'):
self.escbuf += char
return
- elif self.parsestate == 0 and char == B(']'):
+ elif self.parsestate == 0 and char == b']':
# start of OSC
- self.escbuf = bytes()
+ self.escbuf = b''
self.parsestate = 2
return
- elif self.parsestate == 2 and char == B("\x07"):
+ elif self.parsestate == 2 and char == b"\x07":
# end of OSC
- self.parse_osc(self.escbuf.lstrip(B('0')))
- elif self.parsestate == 2 and self.escbuf[-1:] + char == B(ESC + '\\'):
+ self.parse_osc(self.escbuf.lstrip(b'0'))
+ elif self.parsestate == 2 and self.escbuf[-1:] + char == f"{ESC}\\".encode('iso8859-1'):
# end of OSC
- self.parse_osc(self.escbuf[:-1].lstrip(B('0')))
- elif self.parsestate == 2 and self.escbuf.startswith(B('P')) and \
+ self.parse_osc(self.escbuf[:-1].lstrip(b'0'))
+ elif self.parsestate == 2 and self.escbuf.startswith(b'P') and \
len(self.escbuf) == 8:
# set palette (ESC]Pnrrggbb)
pass
- elif self.parsestate == 2 and not self.escbuf and char == B('R'):
+ elif self.parsestate == 2 and not self.escbuf and char == b'R':
# reset palette
pass
elif self.parsestate == 2:
self.escbuf += char
return
- elif self.parsestate == 0 and char == B('['):
+ elif self.parsestate == 0 and char == b'[':
# start of CSI
- self.escbuf = bytes()
+ self.escbuf = b''
self.parsestate = 1
return
- elif self.parsestate == 0 and char in (B('%'), B('#'), B('('), B(')')):
+ elif self.parsestate == 0 and char in (b'%', b'#', b'(', b')'):
# non-CSI sequence
self.escbuf = char
self.parsestate = 3
return
elif self.parsestate == 3:
self.parse_noncsi(char, self.escbuf)
- elif char in (B('c'), B('D'), B('E'), B('H'), B('M'), B('Z'), B('7'), B('8'), B('>'), B('=')):
+ elif char in (b'c', b'D', b'E', b'H', b'M', b'Z', b'7', b'8', b'>', b'='):
self.parse_noncsi(char)
self.leave_escape()
@@ -588,7 +588,7 @@ class TermCanvas(Canvas):
def leave_escape(self):
self.within_escape = False
self.parsestate = 0
- self.escbuf = bytes()
+ self.escbuf = b''
def get_utf8_len(self, bytenum):
"""
@@ -617,27 +617,27 @@ class TermCanvas(Canvas):
if byte >= 0xc0:
# start multibyte sequence
self.utf8_eat_bytes = self.get_utf8_len(byte)
- self.utf8_buffer = chr2(byte)
+ self.utf8_buffer = bytes([byte])
return
elif 0x80 <= byte < 0xc0 and self.utf8_eat_bytes is not None:
if self.utf8_eat_bytes > 1:
# continue multibyte sequence
self.utf8_eat_bytes -= 1
- self.utf8_buffer += chr2(byte)
+ self.utf8_buffer += bytes([byte])
return
else:
# end multibyte sequence
self.utf8_eat_bytes = None
- sequence = (self.utf8_buffer+chr2(byte)).decode('utf-8', 'ignore')
+ sequence = (self.utf8_buffer+bytes([byte])).decode('utf-8', 'ignore')
if len(sequence) == 0:
# invalid multibyte sequence, stop processing
return
char = sequence.encode(util._target_encoding, 'replace')
else:
self.utf8_eat_bytes = None
- char = chr2(byte)
+ char = bytes([byte])
else:
- char = chr2(byte)
+ char = bytes([byte])
self.process_char(char)
@@ -654,36 +654,36 @@ class TermCanvas(Canvas):
dc = self.modes.display_ctrl
- if char == B("\x1b") and self.parsestate != 2: # escape
+ if char == b"\x1b" and self.parsestate != 2: # escape
self.within_escape = True
- elif not dc and char == B("\x0d"): # carriage return
+ elif not dc and char == b"\x0d": # carriage return
self.carriage_return()
- elif not dc and char == B("\x0f"): # activate G0
+ elif not dc and char == b"\x0f": # activate G0
self.charset.activate(0)
- elif not dc and char == B("\x0e"): # activate G1
+ elif not dc and char == b"\x0e": # activate G1
self.charset.activate(1)
- elif not dc and char in B("\x0a\x0b\x0c"): # line feed
+ elif not dc and char in b"\x0a\x0b\x0c": # line feed
self.linefeed()
if self.modes.lfnl:
self.carriage_return()
- elif not dc and char == B("\x09"): # char tab
+ elif not dc and char == b"\x09": # char tab
self.tab()
- elif not dc and char == B("\x08"): # backspace
+ elif not dc and char == b"\x08": # backspace
if x > 0:
self.set_term_cursor(x - 1, y)
- elif not dc and char == B("\x07") and self.parsestate != 2: # beep
+ elif not dc and char == b"\x07" and self.parsestate != 2: # beep
# we need to check if we're in parsestate 2, as an OSC can be
# terminated by the BEL character!
self.widget.beep()
- elif not dc and char in B("\x18\x1a"): # CAN/SUB
+ elif not dc and char in b"\x18\x1a": # CAN/SUB
self.leave_escape()
- elif not dc and char in B("\x00\x7f"): # NUL/DEL
+ elif not dc and char in b"\x00\x7f": # NUL/DEL
pass # this is ignored
elif self.within_escape:
self.parse_escape(char)
- elif not dc and char == B("\x9b"): # CSI (equivalent to "ESC [")
+ elif not dc and char == b"\x9b": # CSI (equivalent to "ESC [")
self.within_escape = True
- self.escbuf = bytes()
+ self.escbuf = b''
self.parsestate = 1
else:
self.push_cursor(char)
@@ -855,7 +855,7 @@ class TermCanvas(Canvas):
x, y = self.term_cursor
while x < self.width - 1:
- self.set_char(B(" "))
+ self.set_char(b" ")
x += 1
if self.is_tabstop(x):
@@ -884,7 +884,7 @@ class TermCanvas(Canvas):
"""
DEC screen alignment test: Fill screen with E's.
"""
- for row in xrange(self.height):
+ for row in range(self.height):
self.term[row] = self.empty_line('E')
def blank_line(self, row):
@@ -991,7 +991,7 @@ class TermCanvas(Canvas):
# within a single row
if sy == ey:
- for x in xrange(sx, ex + 1):
+ for x in range(sx, ex + 1):
self.term[sy][x] = self.empty_char()
return
@@ -999,10 +999,10 @@ class TermCanvas(Canvas):
y = sy
while y <= ey:
if y == sy:
- for x in xrange(sx, self.width):
+ for x in range(sx, self.width):
self.term[y][x] = self.empty_char()
elif y == ey:
- for x in xrange(ex + 1):
+ for x in range(ex + 1):
self.term[y][x] = self.empty_char()
else:
self.blank_line(y)
@@ -1137,8 +1137,8 @@ class TermCanvas(Canvas):
"""
Reverse video/scanmode (DECSCNM) by swapping fg and bg colors.
"""
- for y in xrange(self.height):
- for x in xrange(self.width):
+ for y in range(self.height):
+ for x in range(self.width):
char = self.term[y][x]
attrs = self.reverse_attrspec(char[0], undo=undo)
self.term[y][x] = (attrs,) + char[1:]
@@ -1222,7 +1222,7 @@ class TermCanvas(Canvas):
ourself as a VT102 terminal.
"""
if not qmark:
- self.widget.respond(ESC + '[?6c')
+ self.widget.respond(f"{ESC}[?6c")
def csi_status_report(self, mode):
"""
@@ -1233,7 +1233,7 @@ class TermCanvas(Canvas):
"""
if mode == 5:
# terminal OK
- self.widget.respond(ESC + '[0n')
+ self.widget.respond(f"{ESC}[0n")
elif mode == 6:
x, y = self.term_cursor
self.widget.respond(ESC + '[%d;%dR' % (y + 1, x + 1))
@@ -1294,7 +1294,7 @@ class TermCanvas(Canvas):
Clears the whole terminal screen and resets the cursor position
to (0, 0) or to the coordinates given by 'cursor'.
"""
- self.term = [self.empty_line() for x in xrange(self.height)]
+ self.term = [self.empty_line() for x in range(self.height)]
if cursor is None:
self.set_term_cursor(0, 0)
@@ -1310,12 +1310,10 @@ class TermCanvas(Canvas):
def content(self, trim_left=0, trim_right=0, cols=None, rows=None,
attr_map=None):
if self.scrolling_up == 0:
- for line in self.term:
- yield line
+ yield from self.term
else:
buf = self.scrollback_buffer + self.term
- for line in buf[-(self.height+self.scrolling_up):-self.scrolling_up]:
- yield line
+ yield from buf[-(self.height+self.scrolling_up):-self.scrolling_up]
def content_delta(self, other):
if other is self:
@@ -1558,7 +1556,7 @@ class Terminal(Widget):
try:
select.select([self.master], [], [], timeout)
break
- except select.error as e:
+ except OSError as e:
if e.args[0] != 4:
raise
self.feed()
@@ -1656,7 +1654,6 @@ class Terminal(Widget):
if self.term_modes.lfnl and key == "\x0d":
key += "\x0a"
- if PYTHON3:
- key = key.encode(self.encoding, 'ignore')
+ key = key.encode(self.encoding, 'ignore')
os.write(self.master, key)
diff --git a/urwid/web_display.py b/urwid/web_display.py
index 2b2de46..37e021a 100755
--- a/urwid/web_display.py
+++ b/urwid/web_display.py
@@ -17,22 +17,24 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
"""
Urwid web application display module
"""
+from __future__ import annotations
+
+import glob
import os
-import sys
-import signal
import random
import select
+import signal
import socket
-import glob
+import sys
from urwid import util
+
_js_code = r"""
// Urwid web (CGI/Asynchronous Javascript) display module
// Copyright (C) 2004-2005 Ian Ward
@@ -51,7 +53,7 @@ _js_code = r"""
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
-// Urwid web site: http://excess.org/urwid/
+// Urwid web site: https://urwid.org/
colours = new Object();
colours = {
@@ -551,7 +553,7 @@ _html_page = [
<head>
<title>Urwid Web Display - ""","""</title>
<style type="text/css">
-""" + _css_style + """
+""" + _css_style + r"""
</style>
</head>
<body id="body" onload="load_web_display()">
@@ -578,7 +580,9 @@ class Screen:
self.has_color = True
self._started = False
- started = property(lambda self: self._started)
+ @property
+ def started(self):
+ return self._started
def register_palette( self, l ):
"""Register a list of palette entries.
@@ -596,7 +600,7 @@ class Screen:
assert len(item) == 2, "Invalid register_palette usage"
name, like_name = item
if like_name not in self.palette:
- raise Exception("palette entry '%s' doesn't exist"%like_name)
+ raise Exception(f"palette entry '{like_name}' doesn't exist")
self.palette[name] = self.palette[like_name]
def register_palette_entry( self, name, foreground, background,
@@ -660,11 +664,11 @@ class Screen:
urwid_id = "%09d%09d"%(random.randrange(10**9),
random.randrange(10**9))
- self.pipe_name = os.path.join(_prefs.pipe_dir,"urwid"+urwid_id)
- os.mkfifo(self.pipe_name+".in",0o600)
+ self.pipe_name = os.path.join(_prefs.pipe_dir,f"urwid{urwid_id}")
+ os.mkfifo(f"{self.pipe_name}.in",0o600)
signal.signal(signal.SIGTERM,self._cleanup_pipe)
- self.input_fd = os.open(self.pipe_name+".in",
+ self.input_fd = os.open(f"{self.pipe_name}.in",
os.O_NONBLOCK | os.O_RDONLY)
self.input_tail = ""
self.content_head = ("Content-type: "
@@ -674,9 +678,7 @@ class Screen:
"--ZZ\r\n")
if self.update_method=="polling":
self.content_head = (
- "Content-type: text/plain\r\n"
- "X-Urwid-ID: "+urwid_id+"\r\n"
- "\r\n\r\n")
+ f'Content-type: text/plain\r\nX-Urwid-ID: {urwid_id}\r\n\r\n\r\n')
signal.signal(signal.SIGALRM,self._handle_alarm)
signal.alarm( ALARM_DELAY )
@@ -731,8 +733,8 @@ class Screen:
if not self.pipe_name: return
# XXX which exceptions does this actually raise? EnvironmentError?
try:
- os.remove(self.pipe_name+".in")
- os.remove(self.pipe_name+".update")
+ os.remove(f"{self.pipe_name}.in")
+ os.remove(f"{self.pipe_name}.update")
except Exception:
pass
@@ -817,7 +819,7 @@ class Screen:
else:
l.append(code_span(run, fg, bg))
- send("".join(l)+"\n")
+ send(f"{''.join(l)}\n")
self.last_screen = new_screen
self.last_screen_width = cols
@@ -851,12 +853,12 @@ class Screen:
Fork a child to run CGI disconnected for polling update method.
Force parent process to exit.
"""
- daemonize( self.pipe_name +".err" )
- self.input_fd = os.open(self.pipe_name+".in",
+ daemonize( f"{self.pipe_name}.err" )
+ self.input_fd = os.open(f"{self.pipe_name}.in",
os.O_NONBLOCK | os.O_RDONLY)
self.update_method = "polling child"
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- s.bind( self.pipe_name+".update" )
+ s.bind( f"{self.pipe_name}.update" )
s.listen(1)
s.settimeout(POLL_CONNECT)
self.server_socket = s
@@ -889,7 +891,7 @@ class Screen:
try:
iready,oready,eready = select.select(
[self.input_fd],[],[],0.5)
- except select.error as e:
+ except OSError as e:
# return on interruptions
if e.args[0] == 4:
if raw_keys:
@@ -904,7 +906,7 @@ class Screen:
keydata = os.read(self.input_fd, MAX_READ)
os.close(self.input_fd)
- self.input_fd = os.open(self.pipe_name+".in",
+ self.input_fd = os.open(f"{self.pipe_name}.in",
os.O_NONBLOCK | os.O_RDONLY)
#sys.stderr.write( repr((keydata,self.input_tail))+"\n" )
keys = keydata.split("\n")
@@ -940,7 +942,7 @@ def code_span( s, fg, bg, cursor = -1):
code_bg + code_fg + s[c_off:c2_off] + "\n" +
code_fg + code_bg + s[c2_off:] + "\n")
else:
- return code_fg + code_bg + s + "\n"
+ return f"{code_fg + code_bg + s}\n"
def html_escape(text):
@@ -1004,20 +1006,20 @@ def handle_short_request():
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
s.connect( os.path.join(_prefs.pipe_dir,
- "urwid"+urwid_id+".update") )
- data = "Content-type: text/plain\r\n\r\n"+s.recv(BUF_SZ)
+ f"urwid{urwid_id}.update") )
+ data = f'Content-type: text/plain\r\n\r\n{s.recv(BUF_SZ)}'
while data:
sys.stdout.write(data)
data = s.recv(BUF_SZ)
return True
- except socket.error:
+ except OSError:
sys.stdout.write("Status: 404 Not Found\r\n\r\n")
return True
# this is a keyboard input request
try:
fd = os.open((os.path.join(_prefs.pipe_dir,
- "urwid"+urwid_id+".in")), os.O_WRONLY)
+ f"urwid{urwid_id}.in")), os.O_WRONLY)
except OSError:
sys.stdout.write("Status: 404 Not Found\r\n\r\n")
return True
@@ -1090,7 +1092,7 @@ def daemonize( errfile ):
except OSError:
pass
- sys.stdin = open("/dev/null","r")
+ sys.stdin = open("/dev/null")
sys.stdout = open("/dev/null","w")
sys.stderr = ErrorLog( errfile )
diff --git a/urwid/widget.py b/urwid/widget.py
index b908d9f..d0ae1fb 100644
--- a/urwid/widget.py
+++ b/urwid/widget.py
@@ -17,24 +17,36 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
-from operator import attrgetter
+from __future__ import annotations
-from urwid.compat import text_type, with_metaclass
-from urwid.util import (MetaSuper, decompose_tagmarkup, calc_width,
- is_wide_char, move_prev_char, move_next_char)
-from urwid.text_layout import calc_pos, calc_coords, shift_line
-from urwid import signals
-from urwid import text_layout
-from urwid.canvas import (CanvasCache, CompositeCanvas, SolidCanvas,
- apply_text_layout)
-from urwid.command_map import (command_map, CURSOR_LEFT, CURSOR_RIGHT,
- CURSOR_UP, CURSOR_DOWN, CURSOR_MAX_LEFT, CURSOR_MAX_RIGHT)
-from urwid.split_repr import split_repr, remove_defaults, python3_repr
+import functools
+import warnings
+from operator import attrgetter
+from urwid import signals, text_layout
+from urwid.canvas import CanvasCache, CompositeCanvas, SolidCanvas, apply_text_layout
+from urwid.command_map import (
+ CURSOR_DOWN,
+ CURSOR_LEFT,
+ CURSOR_MAX_LEFT,
+ CURSOR_MAX_RIGHT,
+ CURSOR_RIGHT,
+ CURSOR_UP,
+ command_map,
+)
+from urwid.split_repr import remove_defaults, split_repr
+from urwid.text_layout import calc_coords, calc_pos, shift_line
+from urwid.util import (
+ MetaSuper,
+ calc_width,
+ decompose_tagmarkup,
+ is_wide_char,
+ move_next_char,
+ move_prev_char,
+)
# define some names for these constants to avoid misspellings in the source
# and to document the constant strings we are using
@@ -86,7 +98,7 @@ class WidgetMeta(MetaSuper, signals.MetaSignals):
def __init__(cls, name, bases, d):
no_cache = d.get("no_cache", [])
- super(WidgetMeta, cls).__init__(name, bases, d)
+ super().__init__(name, bases, d)
if "render" in d:
if "render" not in no_cache:
@@ -105,28 +117,17 @@ class WidgetMeta(MetaSuper, signals.MetaSignals):
class WidgetError(Exception):
pass
+
def validate_size(widget, size, canv):
"""
Raise a WidgetError if a canv does not match size size.
"""
if (size and size[1:] != (0,) and size[0] != canv.cols()) or \
(len(size)>1 and size[1] != canv.rows()):
- raise WidgetError("Widget %r rendered (%d x %d) canvas"
- " when passed size %r!" % (widget, canv.cols(),
- canv.rows(), size))
-
-def update_wrapper(new_fn, fn):
- """
- Copy as much of the function detail from fn to new_fn
- as we can.
- """
- try:
- new_fn.__name__ = fn.__name__
- new_fn.__dict__.update(fn.__dict__)
- new_fn.__doc__ = fn.__doc__
- new_fn.__module__ = fn.__module__
- except TypeError:
- pass # python2.3 ignore read-only attributes
+ raise WidgetError(
+ f"Widget {widget!r} rendered ({canv.cols():d} x {canv.rows():d}) canvas "
+ f"when passed size {size!r}!"
+ )
def cache_widget_render(cls):
@@ -136,6 +137,8 @@ def cache_widget_render(cls):
"""
ignore_focus = bool(getattr(cls, "ignore_focus", False))
fn = cls.render
+
+ @functools.wraps(fn)
def cached_render(self, size, focus=False):
focus = focus and not ignore_focus
canv = CanvasCache.fetch(self, cls, size, focus)
@@ -150,9 +153,9 @@ def cache_widget_render(cls):
CanvasCache.store(cls, canv)
return canv
cached_render.original_fn = fn
- update_wrapper(cached_render, fn)
return cached_render
+
def nocache_widget_render(cls):
"""
Return a function that wraps the cls.render() method
@@ -161,6 +164,8 @@ def nocache_widget_render(cls):
fn = cls.render
if hasattr(fn, "original_fn"):
fn = fn.original_fn
+
+ @functools.wraps(fn)
def finalize_render(self, size, focus=False):
canv = fn(self, size, focus=focus)
if canv.widget_info:
@@ -169,9 +174,9 @@ def nocache_widget_render(cls):
canv.finalize(self, size, focus)
return canv
finalize_render.original_fn = fn
- update_wrapper(finalize_render, fn)
return finalize_render
+
def nocache_widget_render_instance(self):
"""
Return a function that wraps the cls.render() method
@@ -179,6 +184,8 @@ def nocache_widget_render_instance(self):
cache the canvas.
"""
fn = self.render.original_fn
+
+ @functools.wraps(fn)
def finalize_render(size, focus=False):
canv = fn(self, size, focus=focus)
if canv.widget_info:
@@ -186,9 +193,9 @@ def nocache_widget_render_instance(self):
canv.finalize(self, size, focus)
return canv
finalize_render.original_fn = fn
- update_wrapper(finalize_render, fn)
return finalize_render
+
def cache_widget_rows(cls):
"""
Return a function that wraps the cls.rows() method
@@ -196,6 +203,8 @@ def cache_widget_rows(cls):
"""
ignore_focus = bool(getattr(cls, "ignore_focus", False))
fn = cls.rows
+
+ @functools.wraps(fn)
def cached_rows(self, size, focus=False):
focus = focus and not ignore_focus
canv = CanvasCache.fetch(self, cls, size, focus)
@@ -203,11 +212,10 @@ def cache_widget_rows(cls):
return canv.rows()
return fn(self, size, focus)
- update_wrapper(cached_rows, fn)
return cached_rows
-class Widget(with_metaclass(WidgetMeta, object)):
+class Widget(metaclass=WidgetMeta):
"""
Widget base class
@@ -544,30 +552,34 @@ class Widget(with_metaclass(WidgetMeta, object)):
if FIXED in self.sizing():
raise NotImplementedError('Fixed widgets must override'
' Widget.pack()')
- raise WidgetError('Cannot pack () size, this is not a fixed'
- ' widget: %s' % repr(self))
+ raise WidgetError(f'Cannot pack () size, this is not a fixed widget: {self!r}')
elif len(size) == 1:
if FLOW in self.sizing():
return size + (self.rows(size, focus),)
- raise WidgetError('Cannot pack (maxcol,) size, this is not a'
- ' flow widget: %s' % repr(self))
+ raise WidgetError(f'Cannot pack (maxcol,) size, this is not a flow widget: {self!r}')
return size
- base_widget = property(lambda self:self, doc="""
+ @property
+ def base_widget(self):
+ """
Read-only property that steps through decoration widgets
and returns the one at the base. This default implementation
returns self.
- """)
+ """
+ return self
- focus = property(lambda self:None, doc="""
+ @property
+ def focus(self):
+ """
Read-only property returning the child widget in focus for
container widgets. This default implementation
always returns ``None``, indicating that this widget has no children.
- """)
+ """
+ return None
def _not_a_container(self, val=None):
raise IndexError(
- "No focus_position, %r is not a container widget" % self)
+ f"No focus_position, {self!r} is not a container widget")
focus_position = property(_not_a_container, _not_a_container, doc="""
Property for reading and setting the focus position for
container widgets. This default implementation raises
@@ -587,9 +599,7 @@ class Widget(with_metaclass(WidgetMeta, object)):
if self.selectable():
words = ["selectable"] + words
if self.sizing() and self.sizing() != frozenset([FLOW, BOX, FIXED]):
- sizing_modes = list(self.sizing())
- sizing_modes.sort()
- words.append("/".join(sizing_modes))
+ words.append("/".join(sorted(self.sizing())))
return words + ["widget"]
def _repr_attrs(self):
@@ -609,6 +619,17 @@ class FlowWidget(Widget):
"""
_sizing = frozenset([FLOW])
+ def __init__(self, *args, **kwargs):
+ warnings.warn(
+ """
+ FlowWidget is deprecated. Inherit from Widget and add:
+
+ _sizing = frozenset(['flow'])
+
+ at the top of your class definition instead.""",
+ DeprecationWarning,
+ )
+
def rows(self, size, focus=False):
"""
All flow widgets must implement this function.
@@ -637,6 +658,18 @@ class BoxWidget(Widget):
_selectable = True
_sizing = frozenset([BOX])
+ def __init__(self, *args, **kwargs):
+ warnings.warn(
+ """
+ BoxWidget is deprecated. Inherit from Widget and add:
+
+ _sizing = frozenset(['box'])
+ _selectable = True
+
+ at the top of your class definition instead.""",
+ DeprecationWarning,
+ )
+
def render(self, size, focus=False):
"""
All widgets must implement this function.
@@ -651,8 +684,7 @@ def fixed_size(size):
Used by FixedWidgets to test size parameter.
"""
if size != ():
- raise ValueError("FixedWidget takes only () for size." \
- "passed: %r" % (size,))
+ raise ValueError(f"FixedWidget takes only () for size.passed: {size!r}")
class FixedWidget(Widget):
"""
@@ -667,6 +699,17 @@ class FixedWidget(Widget):
"""
_sizing = frozenset([FIXED])
+ def __init__(self, *args, **kwargs):
+ warnings.warn(
+ """
+ FixedWidget is deprecated. Inherit from Widget and add:
+
+ _sizing = frozenset(['fixed'])
+
+ at the top of your class definition instead.""",
+ DeprecationWarning,
+ )
+
def render(self, size, focus=False):
"""
All widgets must implement this function.
@@ -688,7 +731,7 @@ class Divider(Widget):
ignore_focus = True
- def __init__(self,div_char=u" ",top=0,bottom=0):
+ def __init__(self,div_char=" ",top=0,bottom=0):
"""
:param div_char: character to repeat across line
:type div_char: bytes or unicode
@@ -706,17 +749,17 @@ class Divider(Widget):
>>> Divider(u'x', 1, 2)
<Divider flow widget 'x' bottom=2 top=1>
"""
- self.__super.__init__()
+ super().__init__()
self.div_char = div_char
self.top = top
self.bottom = bottom
def _repr_words(self):
- return self.__super._repr_words() + [
- python3_repr(self.div_char)] * (self.div_char != u" ")
+ return super()._repr_words() + [
+ repr(self.div_char)] * (self.div_char != " ")
def _repr_attrs(self):
- attrs = dict(self.__super._repr_attrs())
+ attrs = dict(super()._repr_attrs())
if self.top: attrs['top'] = self.top
if self.bottom: attrs['bottom'] = self.bottom
return attrs
@@ -767,11 +810,11 @@ class SolidFill(BoxWidget):
>>> SolidFill(u'8')
<SolidFill box widget '8'>
"""
- self.__super.__init__()
+ super().__init__()
self.fill_char = fill_char
def _repr_words(self):
- return self.__super._repr_words() + [python3_repr(self.fill_char)]
+ return super()._repr_words() + [repr(self.fill_char)]
def render(self, size, focus=False ):
"""
@@ -829,7 +872,7 @@ class Text(Widget):
>>> t.attrib
[('bold', 5)]
"""
- self.__super.__init__()
+ super().__init__()
self._cache_maxcol = None
self.set_text(markup)
self.set_layout(align, wrap, layout)
@@ -839,23 +882,23 @@ class Text(Widget):
Show the text in the repr in python3 format (b prefix for byte
strings) and truncate if it's too long
"""
- first = self.__super._repr_words()
+ first = super()._repr_words()
text = self.get_text()[0]
- rest = python3_repr(text)
+ rest = repr(text)
if len(rest) > self._repr_content_length_max:
rest = (rest[:self._repr_content_length_max * 2 // 3 - 3] +
'...' + rest[-self._repr_content_length_max // 3:])
return first + [rest]
def _repr_attrs(self):
- attrs = dict(self.__super._repr_attrs(),
+ attrs = dict(super()._repr_attrs(),
align=self._align_mode,
wrap=self._wrap_mode)
return remove_defaults(attrs, Text.__init__)
def _invalidate(self):
self._cache_maxcol = None
- self.__super._invalidate()
+ super()._invalidate()
def set_text(self,markup):
"""
@@ -897,14 +940,21 @@ class Text(Widget):
"""
return self._text, self._attrib
- text = property(lambda self:self.get_text()[0], doc="""
+ @property
+ def text(self):
+ """
Read-only property returning the complete bytes/unicode content
of this widget
- """)
- attrib = property(lambda self:self.get_text()[1], doc="""
+ """
+ return self.get_text()[0]
+
+ @property
+ def attrib(self):
+ """
Read-only property returning the run-length encoded display
attributes of this widget
- """)
+ """
+ return self.get_text()[1]
def set_align_mode(self, mode):
"""
@@ -928,8 +978,7 @@ class Text(Widget):
TextError: Alignment mode 'somewhere' not supported.
"""
if not self.layout.supports_align_mode(mode):
- raise TextError("Alignment mode %r not supported."%
- (mode,))
+ raise TextError(f"Alignment mode {mode!r} not supported.")
self._align_mode = mode
self._invalidate()
@@ -957,7 +1006,7 @@ class Text(Widget):
TextError: Wrap mode 'somehow' not supported.
"""
if not self.layout.supports_wrap_mode(mode):
- raise TextError("Wrap mode %r not supported."%(mode,))
+ raise TextError(f"Wrap mode {mode!r} not supported.")
self._wrap_mode = mode
self._invalidate()
@@ -985,7 +1034,10 @@ class Text(Widget):
align = property(lambda self:self._align_mode, set_align_mode)
wrap = property(lambda self:self._wrap_mode, set_wrap_mode)
- layout = property(lambda self:self._layout)
+
+ @property
+ def layout(self):
+ return self._layout
def render(self, size, focus=False):
"""
@@ -1123,7 +1175,7 @@ class Edit(Text):
"""
return is_wide_char(ch,0) or (len(ch)==1 and ord(ch) >= 32)
- def __init__(self, caption=u"", edit_text=u"", multiline=False,
+ def __init__(self, caption="", edit_text="", multiline=False,
align=LEFT, wrap=SPACE, allow_tab=False,
edit_pos=None, layout=None, mask=None):
"""
@@ -1158,7 +1210,7 @@ class Edit(Text):
<Edit selectable flow widget '3.14' align='right' edit_pos=4>
"""
- self.__super.__init__("", align, wrap, layout)
+ super().__init__("", align, wrap, layout)
self.multiline = multiline
self.allow_tab = allow_tab
self._edit_pos = 0
@@ -1172,13 +1224,13 @@ class Edit(Text):
self._shift_view_to_cursor = False
def _repr_words(self):
- return self.__super._repr_words()[:-1] + [
- python3_repr(self._edit_text)] + [
- 'caption=' + python3_repr(self._caption)] * bool(self._caption) + [
+ return super()._repr_words()[:-1] + [
+ repr(self._edit_text)] + [
+ f"caption={self._caption!r}"] * bool(self._caption) + [
'multiline'] * (self.multiline is True)
def _repr_attrs(self):
- attrs = dict(self.__super._repr_attrs(),
+ attrs = dict(super()._repr_attrs(),
edit_pos=self._edit_pos)
return remove_defaults(attrs, Edit.__init__)
@@ -1293,9 +1345,12 @@ class Edit(Text):
self._caption, self._attrib = decompose_tagmarkup(caption)
self._invalidate()
- caption = property(lambda self:self._caption, doc="""
+ @property
+ def caption(self):
+ """
Read-only property returning the caption for this widget.
- """)
+ """
+ return self._caption
def set_edit_pos(self, pos):
"""
@@ -1416,8 +1471,8 @@ class Edit(Text):
Return text converted to the same type as self.caption
(bytes or unicode)
"""
- tu = isinstance(text, text_type)
- cu = isinstance(self._caption, text_type)
+ tu = isinstance(text, str)
+ cu = isinstance(self._caption, str)
if tu == cu:
return text
if tu:
@@ -1475,8 +1530,8 @@ class Edit(Text):
p = self.edit_pos
if self.valid_char(key):
- if (isinstance(key, text_type) and not
- isinstance(self._caption, text_type)):
+ if (isinstance(key, str) and not
+ isinstance(self._caption, str)):
# screen is sending us unicode input, must be using utf-8
# encoding because that's all we support, so convert it
# to bytes to match our caption's type
@@ -1701,7 +1756,7 @@ class IntEdit(Edit):
"""
if default is not None: val = str(default)
else: val = ""
- self.__super.__init__(caption,val)
+ super().__init__(caption,val)
def keypress(self, size, key):
"""
@@ -1761,17 +1816,42 @@ def delegate_to_widget_mixin(attribute_name):
canv = get_delegate(self).render(size, focus=focus)
return CompositeCanvas(canv)
- selectable = property(lambda self:get_delegate(self).selectable)
- get_cursor_coords = property(
- lambda self:get_delegate(self).get_cursor_coords)
- get_pref_col = property(lambda self:get_delegate(self).get_pref_col)
- keypress = property(lambda self:get_delegate(self).keypress)
- move_cursor_to_coords = property(
- lambda self:get_delegate(self).move_cursor_to_coords)
- rows = property(lambda self:get_delegate(self).rows)
- mouse_event = property(lambda self:get_delegate(self).mouse_event)
- sizing = property(lambda self:get_delegate(self).sizing)
- pack = property(lambda self:get_delegate(self).pack)
+ @property
+ def selectable(self):
+ return get_delegate(self).selectable
+
+ @property
+ def get_cursor_coords(self):
+ return get_delegate(self).get_cursor_coords
+
+ @property
+ def get_pref_col(self):
+ return get_delegate(self).get_pref_col
+
+ @property
+ def keypress(self):
+ return get_delegate(self).keypress
+
+ @property
+ def move_cursor_to_coords(self):
+ return get_delegate(self).move_cursor_to_coords
+
+ @property
+ def rows(self):
+ return get_delegate(self).rows
+
+ @property
+ def mouse_event(self):
+ return get_delegate(self).mouse_event
+
+ @property
+ def sizing(self):
+ return get_delegate(self).sizing
+
+ @property
+ def pack(self):
+ return get_delegate(self).pack
+
return DelegateToWidgetMixin
diff --git a/urwid/wimp.py b/urwid/wimp.py
index e7ac9c9..622af1d 100755
--- a/urwid/wimp.py
+++ b/urwid/wimp.py
@@ -17,21 +17,21 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# Urwid web site: http://excess.org/urwid/
+# Urwid web site: https://urwid.org/
-from __future__ import division, print_function
-from urwid.widget import (Text, WidgetWrap, delegate_to_widget_mixin, BOX,
- FLOW)
+from __future__ import annotations
+
from urwid.canvas import CompositeCanvas
-from urwid.signals import connect_signal
+from urwid.command_map import ACTIVATE
from urwid.container import Columns, Overlay
-from urwid.util import is_mouse_press
-from urwid.text_layout import calc_coords
-from urwid.signals import disconnect_signal # doctests
-from urwid.split_repr import python3_repr
from urwid.decoration import WidgetDecoration
-from urwid.command_map import ACTIVATE
+from urwid.signals import disconnect_signal # doctests
+from urwid.signals import connect_signal
+from urwid.text_layout import calc_coords
+from urwid.util import is_mouse_press
+from urwid.widget import BOX, FLOW, Text, WidgetWrap, delegate_to_widget_mixin
+
class SelectableIcon(Text):
ignore_focus = False
@@ -47,7 +47,7 @@ class SelectableIcon(Text):
displayed at a fixed location in the text when in focus.
This widget has no special handling of keyboard or mouse input.
"""
- self.__super.__init__(text)
+ super().__init__(text)
self._cursor_position = cursor_position
def render(self, size, focus=False):
@@ -66,7 +66,7 @@ class SelectableIcon(Text):
>>> si.render((2,), focus=True).cursor
(0, 1)
"""
- c = self.__super.render(size, focus)
+ c = super().render(size, focus)
if focus:
# create a new canvas so we can add a cursor
c = CompositeCanvas(c)
@@ -145,12 +145,12 @@ class CheckBox(WidgetWrap):
>>> cb.render((20,), focus=True).text # ... = b in Python 3
[...'[X] Extra onions ']
"""
- self.__super.__init__(None) # self.w set by set_state below
+ super().__init__(None) # self.w set by set_state below
self._label = Text("")
self.has_mixed = has_mixed
self._state = None
if checked_symbol:
- self.states[True] = SelectableIcon(u"[%s]" % checked_symbol, 1)
+ self.states[True] = SelectableIcon(f"[{checked_symbol}]", 1)
# The old way of listening for a change was to pass the callback
# in to the constructor. Just convert it to the new way:
if on_state_change:
@@ -159,11 +159,11 @@ class CheckBox(WidgetWrap):
self.set_state(state)
def _repr_words(self):
- return self.__super._repr_words() + [
- python3_repr(self.label)]
+ return super()._repr_words() + [
+ repr(self.label)]
def _repr_attrs(self):
- return dict(self.__super._repr_attrs(),
+ return dict(super()._repr_attrs(),
state=self.state)
def set_label(self, label):
@@ -233,8 +233,7 @@ class CheckBox(WidgetWrap):
return
if state not in self.states:
- raise CheckBoxError("%s Invalid state: %s" % (
- repr(self), repr(state)))
+ raise CheckBoxError(f"{self!r} Invalid state: {state!r}")
# self._state is None is a special case when the CheckBox
# has just been created
@@ -370,7 +369,7 @@ class RadioButton(CheckBox):
state = not group
self.group = group
- self.__super.__init__(label, state, False, on_state_change,
+ super().__init__(label, state, False, on_state_change,
user_data)
group.append(self)
@@ -408,7 +407,7 @@ class RadioButton(CheckBox):
if self._state == state:
return
- self.__super.set_state(state, do_callback)
+ super().set_state(state, do_callback)
# if we're clearing the state we don't have to worry about
# other buttons in the button group
@@ -480,7 +479,7 @@ class Button(WidgetWrap):
self._label,
('fixed', 1, self.button_right)],
dividechars=1)
- self.__super.__init__(cols)
+ super().__init__(cols)
# The old way of listening for a change was to pass the callback
# in to the constructor. Just convert it to the new way:
@@ -491,8 +490,8 @@ class Button(WidgetWrap):
def _repr_words(self):
# include button.label in repr(button)
- return self.__super._repr_words() + [
- python3_repr(self.label)]
+ return super()._repr_words() + [
+ repr(self.label)]
def set_label(self, label):
"""
@@ -566,10 +565,9 @@ class Button(WidgetWrap):
return True
-class PopUpLauncher(delegate_to_widget_mixin('_original_widget'),
- WidgetDecoration):
+class PopUpLauncher(delegate_to_widget_mixin('_original_widget'), WidgetDecoration):
def __init__(self, original_widget):
- self.__super.__init__(original_widget)
+ super().__init__(original_widget)
self._pop_up_widget = None
def create_pop_up(self, *args, **kwargs):
@@ -599,7 +597,7 @@ class PopUpLauncher(delegate_to_widget_mixin('_original_widget'),
self._invalidate()
def render(self, size, focus=False):
- canv = self.__super.render(size, focus)
+ canv = super().render(size, focus)
if self._pop_up_widget:
canv = CompositeCanvas(canv)
canv.set_pop_up(self._pop_up_widget, **self.get_pop_up_parameters(size, focus))
@@ -609,11 +607,11 @@ class PopUpLauncher(delegate_to_widget_mixin('_original_widget'),
class PopUpTarget(WidgetDecoration):
# FIXME: this whole class is a terrible hack and must be fixed
# when layout and rendering are separated
- _sizing = set([BOX])
+ _sizing = {BOX}
_selectable = True
def __init__(self, original_widget):
- self.__super.__init__(original_widget)
+ super().__init__(original_widget)
self._pop_up = None
self._current_widget = self._original_widget