urwid 4.1.6__tar.gz → 4.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {urwid-4.1.6 → urwid-4.2.0}/.github/workflows/documentation.yml +3 -12
- {urwid-4.1.6 → urwid-4.2.0}/.github/workflows/isolated_mypy.yml +2 -6
- {urwid-4.1.6 → urwid-4.2.0}/.github/workflows/isolated_pylint.yml +3 -12
- {urwid-4.1.6 → urwid-4.2.0}/.github/workflows/pythonpackage.yml +10 -54
- {urwid-4.1.6 → urwid-4.2.0}/.github/workflows/yamllint.yml +1 -1
- {urwid-4.1.6 → urwid-4.2.0}/PKG-INFO +7 -2
- {urwid-4.1.6 → urwid-4.2.0}/README.rst +1 -1
- {urwid-4.1.6 → urwid-4.2.0}/classifiers.txt +5 -0
- {urwid-4.1.6 → urwid-4.2.0}/conftest.py +3 -0
- urwid-4.2.0/pylint-requirements.txt +1 -0
- {urwid-4.1.6 → urwid-4.2.0}/pyproject.toml +17 -12
- {urwid-4.1.6 → urwid-4.2.0}/urwid/__init__.py +4 -1
- {urwid-4.1.6 → urwid-4.2.0}/urwid/ansi_parser.py +27 -26
- {urwid-4.1.6 → urwid-4.2.0}/urwid/canvas.py +57 -73
- {urwid-4.1.6 → urwid-4.2.0}/urwid/command_map.py +13 -6
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/_posix_raw_display.py +15 -31
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/_raw_display_base.py +39 -54
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/_win32.py +17 -13
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/_win32_raw_display.py +10 -12
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/common.py +98 -33
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/curses.py +19 -25
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/escape.py +11 -6
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/html_fragment.py +32 -29
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/lcd.py +46 -46
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/raw.py +1 -3
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/web.py +24 -65
- {urwid-4.1.6 → urwid-4.2.0}/urwid/event_loop/abstract_loop.py +14 -23
- {urwid-4.1.6 → urwid-4.2.0}/urwid/event_loop/asyncio_loop.py +8 -14
- {urwid-4.1.6 → urwid-4.2.0}/urwid/event_loop/glib_loop.py +21 -18
- {urwid-4.1.6 → urwid-4.2.0}/urwid/event_loop/main_loop.py +59 -69
- {urwid-4.1.6 → urwid-4.2.0}/urwid/event_loop/select_loop.py +11 -20
- {urwid-4.1.6 → urwid-4.2.0}/urwid/event_loop/tornado_loop.py +13 -8
- {urwid-4.1.6 → urwid-4.2.0}/urwid/event_loop/trio_loop.py +27 -31
- {urwid-4.1.6 → urwid-4.2.0}/urwid/event_loop/twisted_loop.py +14 -24
- {urwid-4.1.6 → urwid-4.2.0}/urwid/event_loop/zmq_loop.py +35 -37
- {urwid-4.1.6 → urwid-4.2.0}/urwid/font.py +30 -4
- {urwid-4.1.6 → urwid-4.2.0}/urwid/graphics.py +7 -10
- {urwid-4.1.6 → urwid-4.2.0}/urwid/numedit.py +12 -11
- {urwid-4.1.6 → urwid-4.2.0}/urwid/signals.py +32 -36
- {urwid-4.1.6 → urwid-4.2.0}/urwid/split_repr.py +8 -8
- {urwid-4.1.6 → urwid-4.2.0}/urwid/str_util.py +12 -10
- {urwid-4.1.6 → urwid-4.2.0}/urwid/text_layout.py +220 -26
- {urwid-4.1.6 → urwid-4.2.0}/urwid/util.py +38 -42
- {urwid-4.1.6 → urwid-4.2.0}/urwid/version.py +3 -3
- {urwid-4.1.6 → urwid-4.2.0}/urwid/vterm.py +122 -146
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/__init__.py +2 -0
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/ansi_text.py +9 -10
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/attr_map.py +10 -10
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/attr_wrap.py +12 -8
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/bar_graph.py +30 -41
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/big_text.py +11 -4
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/box_adapter.py +14 -9
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/columns.py +20 -26
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/constants.py +26 -24
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/container.py +37 -48
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/deque_walker.py +10 -9
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/divider.py +3 -3
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/edit.py +71 -68
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/filler.py +6 -4
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/frame.py +21 -26
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/grid_flow.py +27 -25
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/line_box.py +6 -1
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/list_walker.py +31 -16
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/listbox.py +36 -36
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/monitored_deque.py +36 -20
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/monitored_list.py +63 -38
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/overlay.py +20 -11
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/padding.py +13 -17
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/pile.py +16 -14
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/popup.py +19 -8
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/progress_bar.py +13 -12
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/scrollable.py +36 -15
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/solid_fill.py +3 -3
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/text.py +49 -54
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/treetools.py +43 -22
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/widget.py +73 -52
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/widget_decoration.py +17 -7
- {urwid-4.1.6 → urwid-4.2.0}/urwid/widget/wimp.py +31 -20
- {urwid-4.1.6 → urwid-4.2.0}/urwid.egg-info/PKG-INFO +7 -2
- {urwid-4.1.6 → urwid-4.2.0}/urwid.egg-info/scm_file_list.json +2 -0
- urwid-4.2.0/urwid.egg-info/scm_version.json +8 -0
- urwid-4.1.6/pylint-requirements.txt +0 -1
- urwid-4.1.6/urwid.egg-info/scm_version.json +0 -8
- {urwid-4.1.6 → urwid-4.2.0}/.devcontainer/dev.dockerfile +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.devcontainer/devcontainer.json +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.dockerignore +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.editorconfig +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.github/CODE_OF_CONDUCT.md +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.github/ISSUE_TEMPLATE/docs.md +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.github/dependabot.yml +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.github/labeler.yml +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.github/release.yml +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.github/workflows/labels.yml +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.gitignore +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.pre-commit-config.yaml +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/.yamllint.yml +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/COPYING +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/MANIFEST.in +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/SECURITY.md +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/mypy-requirements.txt +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/pytest_requirements.txt +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/requirements.txt +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/setup.cfg +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/tox.ini +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/__init__.py +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/_web.css +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/urwid/display/_web.js +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/urwid/event_loop/__init__.py +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/urwid/py.typed +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/urwid.egg-info/SOURCES.txt +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/urwid.egg-info/dependency_links.txt +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/urwid.egg-info/not-zip-safe +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/urwid.egg-info/requires.txt +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/urwid.egg-info/top_level.txt +0 -0
- {urwid-4.1.6 → urwid-4.2.0}/yamllint-requirements.txt +0 -0
|
@@ -26,23 +26,14 @@ jobs:
|
|
|
26
26
|
- uses: actions/checkout@v7
|
|
27
27
|
with:
|
|
28
28
|
fetch-depth: 0 # need for setuptools_scm
|
|
29
|
-
- name: Set up Python
|
|
30
|
-
uses: actions/setup-python@v7
|
|
31
|
-
with:
|
|
32
|
-
python-version: '3.x'
|
|
33
29
|
- name: Install uv
|
|
34
|
-
uses: astral-sh/setup-uv@v10.
|
|
35
|
-
- name: Install Ubuntu dependencies
|
|
36
|
-
run: |
|
|
37
|
-
sudo apt-get -qq update
|
|
38
|
-
sudo apt-get install -y python3-dev python3-gi python3-gi-cairo
|
|
39
|
-
sudo apt-get install -y gobject-introspection libgirepository-2.0-dev libcairo2-dev
|
|
30
|
+
uses: astral-sh/setup-uv@v10.2.0
|
|
40
31
|
- name: Create virtual environment
|
|
41
|
-
run: uv venv
|
|
32
|
+
run: uv venv
|
|
42
33
|
- name: Install python dependencies
|
|
43
34
|
run: uv pip install -U --group pyproject.toml:documentation
|
|
44
35
|
- name: Install main package with all extras
|
|
45
|
-
run: uv pip install -U -e ".[curses,
|
|
36
|
+
run: uv pip install -U -e ".[curses,serial,tornado,trio,twisted,zmq]"
|
|
46
37
|
- name: Build documentation
|
|
47
38
|
run: uv run --no-project sphinx-build docs build/documentation
|
|
48
39
|
- uses: actions/upload-artifact@v7
|
|
@@ -16,14 +16,10 @@ jobs:
|
|
|
16
16
|
runs-on: ubuntu-latest
|
|
17
17
|
steps:
|
|
18
18
|
- uses: actions/checkout@v7
|
|
19
|
-
- name: Set up Python
|
|
20
|
-
uses: actions/setup-python@v7
|
|
21
|
-
with:
|
|
22
|
-
python-version: '3.x'
|
|
23
19
|
- name: Install uv
|
|
24
|
-
uses: astral-sh/setup-uv@v10.
|
|
20
|
+
uses: astral-sh/setup-uv@v10.2.0
|
|
25
21
|
- name: Create virtual environment
|
|
26
|
-
run: uv venv
|
|
22
|
+
run: uv venv
|
|
27
23
|
- name: Install package with all extras
|
|
28
24
|
run: uv pip install -e ".[curses,serial,tornado,trio,twisted,zmq]"
|
|
29
25
|
- name: Install dependencies
|
|
@@ -16,21 +16,12 @@ jobs:
|
|
|
16
16
|
runs-on: ubuntu-latest
|
|
17
17
|
steps:
|
|
18
18
|
- uses: actions/checkout@v7
|
|
19
|
-
- name: Install Ubuntu dependencies
|
|
20
|
-
run: |
|
|
21
|
-
sudo apt-get -qq update
|
|
22
|
-
sudo apt-get install -y python3-dev python3-gi python3-gi-cairo
|
|
23
|
-
sudo apt-get install -y gobject-introspection libgirepository-2.0-dev libcairo2-dev
|
|
24
|
-
- name: Set up Python
|
|
25
|
-
uses: actions/setup-python@v7
|
|
26
|
-
with:
|
|
27
|
-
python-version: '3.x'
|
|
28
19
|
- name: Install uv
|
|
29
|
-
uses: astral-sh/setup-uv@v10.
|
|
20
|
+
uses: astral-sh/setup-uv@v10.2.0
|
|
30
21
|
- name: Create virtual environment
|
|
31
|
-
run: uv venv
|
|
22
|
+
run: uv venv
|
|
32
23
|
- name: Install package with all extras
|
|
33
|
-
run: uv pip install -e ".[curses,
|
|
24
|
+
run: uv pip install -e ".[curses,serial,tornado,trio,twisted,zmq]"
|
|
34
25
|
- name: Install dependencies
|
|
35
26
|
run: uv pip install --upgrade -r pylint-requirements.txt
|
|
36
27
|
- name: Lint with pylint
|
|
@@ -52,21 +52,12 @@ jobs:
|
|
|
52
52
|
runs-on: ubuntu-latest
|
|
53
53
|
steps:
|
|
54
54
|
- uses: actions/checkout@v7
|
|
55
|
-
- name: Install Ubuntu dependencies
|
|
56
|
-
run: |
|
|
57
|
-
sudo apt-get -qq update
|
|
58
|
-
sudo apt-get install -y python3-dev python3-gi python3-gi-cairo
|
|
59
|
-
sudo apt-get install -y gobject-introspection libgirepository-2.0-dev libcairo2-dev
|
|
60
|
-
- name: Set up Python
|
|
61
|
-
uses: actions/setup-python@v7
|
|
62
|
-
with:
|
|
63
|
-
python-version: '3.x'
|
|
64
55
|
- name: Install uv
|
|
65
|
-
uses: astral-sh/setup-uv@v10.
|
|
56
|
+
uses: astral-sh/setup-uv@v10.2.0
|
|
66
57
|
- name: Create virtual environment
|
|
67
|
-
run: uv venv
|
|
58
|
+
run: uv venv
|
|
68
59
|
- name: Install package with all extras
|
|
69
|
-
run: uv pip install -e ".[curses,
|
|
60
|
+
run: uv pip install -e ".[curses,serial,tornado,trio,twisted,zmq]"
|
|
70
61
|
- name: Install dependencies
|
|
71
62
|
run: uv pip install --upgrade -r pylint-requirements.txt
|
|
72
63
|
- name: Lint with pylint
|
|
@@ -77,14 +68,10 @@ jobs:
|
|
|
77
68
|
runs-on: ubuntu-latest
|
|
78
69
|
steps:
|
|
79
70
|
- uses: actions/checkout@v7
|
|
80
|
-
- name: Set up Python
|
|
81
|
-
uses: actions/setup-python@v7
|
|
82
|
-
with:
|
|
83
|
-
python-version: '3.x'
|
|
84
71
|
- name: Install uv
|
|
85
|
-
uses: astral-sh/setup-uv@v10.
|
|
72
|
+
uses: astral-sh/setup-uv@v10.2.0
|
|
86
73
|
- name: Create virtual environment
|
|
87
|
-
run: uv venv
|
|
74
|
+
run: uv venv
|
|
88
75
|
- name: Install package with all extras
|
|
89
76
|
run: uv pip install -e ".[curses,serial,tornado,trio,twisted,zmq]"
|
|
90
77
|
- name: Install dependencies
|
|
@@ -102,9 +89,6 @@ jobs:
|
|
|
102
89
|
# , "macos-latest"
|
|
103
90
|
# enable macOS only if OS specific branch will be added
|
|
104
91
|
python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13", "3.14" ]
|
|
105
|
-
exclude:
|
|
106
|
-
- os: "windows-latest"
|
|
107
|
-
python-version: "3.14"
|
|
108
92
|
|
|
109
93
|
steps:
|
|
110
94
|
- uses: actions/checkout@v7
|
|
@@ -116,24 +100,11 @@ jobs:
|
|
|
116
100
|
python-version: ${{ matrix.python-version }}
|
|
117
101
|
allow-prereleases: true
|
|
118
102
|
- name: Install uv
|
|
119
|
-
uses: astral-sh/setup-uv@v10.
|
|
120
|
-
- name: Install Ubuntu dependencies
|
|
121
|
-
if: runner.os == 'Linux'
|
|
122
|
-
run: |
|
|
123
|
-
sudo apt-get -qq update
|
|
124
|
-
sudo apt-get install -y python3-dev python3-gi python3-gi-cairo
|
|
125
|
-
sudo apt-get install -y gobject-introspection libgirepository-2.0-dev libcairo2-dev
|
|
126
|
-
- name: Install Mac OS X dependencies
|
|
127
|
-
if: runner.os == 'macOS'
|
|
128
|
-
run: |
|
|
129
|
-
brew install gtk+3 gobject-introspection gtk-mac-integration
|
|
103
|
+
uses: astral-sh/setup-uv@v10.2.0
|
|
130
104
|
- name: Create virtual environment
|
|
131
105
|
run: uv venv --python-preference only-system --python ${{ matrix.python-version }}
|
|
132
106
|
- name: Install dependencies
|
|
133
107
|
run: uv pip install -r pytest_requirements.txt
|
|
134
|
-
- name: Install PyGObject
|
|
135
|
-
if: runner.os == 'Linux' || runner.os == 'macOS'
|
|
136
|
-
run: uv pip install PyGObject
|
|
137
108
|
- name: Install package with all extras
|
|
138
109
|
run: uv pip install -e ".[curses,serial,tornado,trio,twisted,zmq]"
|
|
139
110
|
- name: Test
|
|
@@ -165,18 +136,11 @@ jobs:
|
|
|
165
136
|
with:
|
|
166
137
|
fetch-depth: 0 # need for setuptools_scm
|
|
167
138
|
|
|
168
|
-
- uses: actions/setup-python@v7
|
|
169
|
-
with:
|
|
170
|
-
python-version: "3.x"
|
|
171
|
-
|
|
172
139
|
- name: Install uv
|
|
173
|
-
uses: astral-sh/setup-uv@v10.
|
|
174
|
-
|
|
175
|
-
- name: Install dependencies
|
|
176
|
-
run: uv pip install --system -U build
|
|
140
|
+
uses: astral-sh/setup-uv@v10.2.0
|
|
177
141
|
|
|
178
142
|
- name: Build dist
|
|
179
|
-
run:
|
|
143
|
+
run: uv build
|
|
180
144
|
|
|
181
145
|
- uses: actions/upload-artifact@v7
|
|
182
146
|
with:
|
|
@@ -196,15 +160,8 @@ jobs:
|
|
|
196
160
|
needs: [ build ]
|
|
197
161
|
steps:
|
|
198
162
|
- uses: actions/checkout@v7
|
|
199
|
-
- name: Set up Python
|
|
200
|
-
uses: actions/setup-python@v7
|
|
201
|
-
with:
|
|
202
|
-
python-version: '3.x'
|
|
203
163
|
- name: Install uv
|
|
204
|
-
uses: astral-sh/setup-uv@v10.
|
|
205
|
-
- name: Install dependencies
|
|
206
|
-
run: |
|
|
207
|
-
uv pip install --system --upgrade twine
|
|
164
|
+
uses: astral-sh/setup-uv@v10.2.0
|
|
208
165
|
- uses: actions/download-artifact@v8
|
|
209
166
|
with:
|
|
210
167
|
# unpacks default artifact into dist/
|
|
@@ -214,8 +171,7 @@ jobs:
|
|
|
214
171
|
path: dist
|
|
215
172
|
|
|
216
173
|
- name: Validate metadata
|
|
217
|
-
run:
|
|
218
|
-
twine check dist/*
|
|
174
|
+
run: uvx twine check dist/*
|
|
219
175
|
|
|
220
176
|
upload_pypi:
|
|
221
177
|
needs: [ build, Metadata ]
|
|
@@ -17,7 +17,7 @@ jobs:
|
|
|
17
17
|
steps:
|
|
18
18
|
- uses: actions/checkout@v7
|
|
19
19
|
- name: Install uv
|
|
20
|
-
uses: astral-sh/setup-uv@v10.
|
|
20
|
+
uses: astral-sh/setup-uv@v10.2.0
|
|
21
21
|
- name: Lint YAML files
|
|
22
22
|
run: uv run --no-project --with-requirements yamllint-requirements.txt yamllint . -f github
|
|
23
23
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: urwid
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.2.0
|
|
4
4
|
Summary: A full-featured console (xterm et al.) user interface library
|
|
5
5
|
Author-email: Ian Ward <ian@excess.org>
|
|
6
6
|
License-Expression: LGPL-2.1-only
|
|
@@ -19,6 +19,7 @@ Classifier: Operating System :: Unix
|
|
|
19
19
|
Classifier: Operating System :: MacOS :: MacOS X
|
|
20
20
|
Classifier: Operating System :: Microsoft :: Windows
|
|
21
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
22
23
|
Classifier: Topic :: Software Development :: Widget Sets
|
|
23
24
|
Classifier: Programming Language :: Python :: 3
|
|
24
25
|
Classifier: Programming Language :: Python :: 3.9
|
|
@@ -27,9 +28,13 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
27
28
|
Classifier: Programming Language :: Python :: 3.12
|
|
28
29
|
Classifier: Programming Language :: Python :: 3.13
|
|
29
30
|
Classifier: Programming Language :: Python :: 3.14
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
30
32
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
31
33
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
32
34
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
35
|
+
Classifier: Programming Language :: Python :: Implementation :: GraalPy
|
|
36
|
+
Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
|
|
37
|
+
Classifier: Typing :: Typed
|
|
33
38
|
Requires-Python: >=3.9.0
|
|
34
39
|
Description-Content-Type: text/x-rst
|
|
35
40
|
License-File: COPYING
|
|
@@ -71,7 +76,7 @@ It includes many features useful for text console application developers includi
|
|
|
71
76
|
- Automatic, programmable text alignment and wrapping
|
|
72
77
|
- Simple markup for setting text attributes within blocks of text
|
|
73
78
|
- Powerful list box with programmable content for scrolling all widget types
|
|
74
|
-
- Your choice of event loops: Twisted, Glib, Tornado, asyncio, trio, ZeroMQ or select-based loop
|
|
79
|
+
- Your choice of event loops: Twisted, Glib (deprecated), Tornado, asyncio, trio, ZeroMQ or select-based loop
|
|
75
80
|
- Pre-built widgets include edit boxes, buttons, check boxes and radio buttons
|
|
76
81
|
- Display modules include raw, curses, and experimental LCD and web displays
|
|
77
82
|
- Support for UTF-8, simple 8-bit and CJK encodings
|
|
@@ -14,7 +14,7 @@ It includes many features useful for text console application developers includi
|
|
|
14
14
|
- Automatic, programmable text alignment and wrapping
|
|
15
15
|
- Simple markup for setting text attributes within blocks of text
|
|
16
16
|
- Powerful list box with programmable content for scrolling all widget types
|
|
17
|
-
- Your choice of event loops: Twisted, Glib, Tornado, asyncio, trio, ZeroMQ or select-based loop
|
|
17
|
+
- Your choice of event loops: Twisted, Glib (deprecated), Tornado, asyncio, trio, ZeroMQ or select-based loop
|
|
18
18
|
- Pre-built widgets include edit boxes, buttons, check boxes and radio buttons
|
|
19
19
|
- Display modules include raw, curses, and experimental LCD and web displays
|
|
20
20
|
- Support for UTF-8, simple 8-bit and CJK encodings
|
|
@@ -7,6 +7,7 @@ Operating System :: Unix
|
|
|
7
7
|
Operating System :: MacOS :: MacOS X
|
|
8
8
|
Operating System :: Microsoft :: Windows
|
|
9
9
|
Topic :: Software Development :: Libraries :: Python Modules
|
|
10
|
+
Topic :: Software Development :: User Interfaces
|
|
10
11
|
Topic :: Software Development :: Widget Sets
|
|
11
12
|
Programming Language :: Python :: 3
|
|
12
13
|
Programming Language :: Python :: 3.9
|
|
@@ -15,6 +16,10 @@ Programming Language :: Python :: 3.11
|
|
|
15
16
|
Programming Language :: Python :: 3.12
|
|
16
17
|
Programming Language :: Python :: 3.13
|
|
17
18
|
Programming Language :: Python :: 3.14
|
|
19
|
+
Programming Language :: Python :: 3.15
|
|
18
20
|
Programming Language :: Python :: 3 :: Only
|
|
19
21
|
Programming Language :: Python :: Implementation :: CPython
|
|
20
22
|
Programming Language :: Python :: Implementation :: PyPy
|
|
23
|
+
Programming Language :: Python :: Implementation :: GraalPy
|
|
24
|
+
Programming Language :: Python :: Free Threading :: 2 - Beta
|
|
25
|
+
Typing :: Typed
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Pytest configuration: skip collecting modules that cannot be imported on this platform."""
|
|
2
|
+
|
|
1
3
|
from __future__ import annotations
|
|
2
4
|
|
|
3
5
|
import importlib.util
|
|
@@ -52,6 +54,7 @@ def _backend_available(name: str) -> bool:
|
|
|
52
54
|
|
|
53
55
|
|
|
54
56
|
def pytest_ignore_collect(collection_path: Path) -> bool | None:
|
|
57
|
+
"""Skip collecting a platform-only or optional-backend test module that cannot run here."""
|
|
55
58
|
name = collection_path.name
|
|
56
59
|
|
|
57
60
|
if not _PLATFORM_ONLY.get(name, True):
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pylint==4.0.9
|
|
@@ -144,6 +144,7 @@ extend-select = [
|
|
|
144
144
|
"PL",
|
|
145
145
|
"PERF",
|
|
146
146
|
"RUF", # tryceratops, pyupgrade, isort, pylint + perflint, Ruff-specific
|
|
147
|
+
"D", # pydocstyle
|
|
147
148
|
]
|
|
148
149
|
extend-ignore = [
|
|
149
150
|
"E221",
|
|
@@ -164,12 +165,24 @@ extend-ignore = [
|
|
|
164
165
|
"SIM108", # Use ternary operator,
|
|
165
166
|
"TRY003", # long messages prepare outside, ...
|
|
166
167
|
"PYI063", # MonitoredList is a list subclass, this expected to change
|
|
168
|
+
"ISC001", # conflicts with the formatter, which can produce single-line implicit concatenation
|
|
169
|
+
# suspicious-xml-*-usage: recommend defusedxml, which is not a dependency
|
|
170
|
+
"S313",
|
|
171
|
+
"S314",
|
|
172
|
+
"S315",
|
|
173
|
+
"S316",
|
|
174
|
+
"S317",
|
|
175
|
+
"S318",
|
|
176
|
+
"S319",
|
|
167
177
|
]
|
|
168
178
|
[tool.ruff.lint.isort]
|
|
169
179
|
known-third-party = []
|
|
170
180
|
[tool.ruff.lint.per-file-ignores]
|
|
171
181
|
"__init__.py" = [ "F401", "SIM105" ]
|
|
172
182
|
"urwid/display/curses.py" = [ "A005" ]
|
|
183
|
+
# Shown verbatim in the docs as numbered listings, wcur2.py from a fixed line (`:lines: 7-`).
|
|
184
|
+
"docs/tutorial/*.py" = [ "D100", "D101", "D102", "D103", "D107" ]
|
|
185
|
+
"docs/manual/*.py" = [ "D100", "D101", "D102", "D103", "D107" ]
|
|
173
186
|
[tool.ruff.lint.pydocstyle]
|
|
174
187
|
convention = "pep257"
|
|
175
188
|
[tool.ruff.lint.pylint]
|
|
@@ -182,27 +195,22 @@ allow-dunder-method-names = [
|
|
|
182
195
|
]
|
|
183
196
|
|
|
184
197
|
[tool.pylint]
|
|
185
|
-
reports = false
|
|
186
198
|
disable = [
|
|
187
199
|
"arguments-differ",
|
|
188
200
|
"attribute-defined-outside-init", # old bad practice, fixed step-by-step
|
|
189
|
-
"broad-
|
|
201
|
+
"broad-exception-caught",
|
|
190
202
|
"broad-exception-raised",
|
|
191
203
|
"consider-using-assignment-expr",
|
|
192
|
-
# iterable modification during iteration
|
|
193
|
-
"consider-using-enumerate",
|
|
204
|
+
"consider-using-enumerate", # iterable modification during iteration
|
|
194
205
|
"cyclic-import",
|
|
195
206
|
"file-ignored",
|
|
196
207
|
"fixme",
|
|
197
208
|
"import-outside-toplevel",
|
|
198
209
|
"invalid-name",
|
|
199
|
-
# Magic part
|
|
200
|
-
"invalid-overridden-method",
|
|
210
|
+
"invalid-overridden-method", # Magic part
|
|
201
211
|
"locally-disabled",
|
|
202
212
|
"logging-format-interpolation",
|
|
203
213
|
"logging-fstring-interpolation",
|
|
204
|
-
"missing-docstring",
|
|
205
|
-
"redefined-variable-type", # makes not happy typechecking, but super common
|
|
206
214
|
"similarities",
|
|
207
215
|
"superfluous-parens",
|
|
208
216
|
"suppressed-message",
|
|
@@ -220,12 +228,10 @@ disable = [
|
|
|
220
228
|
"too-many-statements",
|
|
221
229
|
"unused-argument",
|
|
222
230
|
"unused-private-member",
|
|
223
|
-
# too complex union
|
|
224
|
-
"use-implicit-booleaness-not-comparison-to-string",
|
|
231
|
+
"use-implicit-booleaness-not-comparison-to-string", # too complex union
|
|
225
232
|
"use-implicit-booleaness-not-comparison-to-zero",
|
|
226
233
|
]
|
|
227
234
|
enable = "all"
|
|
228
|
-
extension-pkg-whitelist = []
|
|
229
235
|
ignore = [ "CVS", "version.py" ]
|
|
230
236
|
# gi (PyGObject) is only installed with the "glib" extra; static checks also run without it.
|
|
231
237
|
ignored-modules = [ "gi" ]
|
|
@@ -237,7 +243,6 @@ load-plugins = [
|
|
|
237
243
|
"pylint.extensions.dunder",
|
|
238
244
|
"pylint.extensions.for_any_all",
|
|
239
245
|
"pylint.extensions.overlapping_exceptions",
|
|
240
|
-
"pylint.extensions.redefined_variable_type",
|
|
241
246
|
"pylint.extensions.typing",
|
|
242
247
|
]
|
|
243
248
|
max-line-length = 120
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
# Urwid web site: https://urwid.org/
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
"""Urwid, a console user interface library: the public API re-exported from its submodules."""
|
|
23
|
+
|
|
22
24
|
from __future__ import annotations
|
|
23
25
|
|
|
24
26
|
import importlib.util
|
|
@@ -517,7 +519,8 @@ class _MovedModule(types.ModuleType):
|
|
|
517
519
|
self._moved_to = moved_to
|
|
518
520
|
|
|
519
521
|
def __getattr__(self, name: str) -> typing.Any:
|
|
520
|
-
"""
|
|
522
|
+
"""Resolve `name` by importing the module this one moved to.
|
|
523
|
+
|
|
521
524
|
:raises AttributeError: *name* is not found, including when *self._moved_to* itself cannot be imported.
|
|
522
525
|
"""
|
|
523
526
|
try:
|
|
@@ -109,20 +109,20 @@ class SkippedOp:
|
|
|
109
109
|
|
|
110
110
|
@dataclasses.dataclass(frozen=True)
|
|
111
111
|
class ParsedLine:
|
|
112
|
-
"""The result of parsing one physical line of ANSI-coloured text.
|
|
112
|
+
r"""The result of parsing one physical line of ANSI-coloured text.
|
|
113
113
|
|
|
114
114
|
:ivar text: the resolved, escape-free text of this line. When produced by :class:`AnsiParser` constructed with
|
|
115
|
-
``one_line=True`` (or :func:`parse_ansi_line`), this is guaranteed never to contain a
|
|
115
|
+
``one_line=True`` (or :func:`parse_ansi_line`), this is guaranteed never to contain a ``\n`` -- line ends
|
|
116
116
|
are structural split points that produce *separate* :class:`ParsedLine` instances. That invariant is
|
|
117
117
|
deliberately **not** upheld when :class:`AnsiParser` is constructed with ``one_line=False`` (or by
|
|
118
118
|
:func:`parse_ansi_text`): since that mode resolves a whole multi-row block into a single :class:`ParsedLine`,
|
|
119
|
-
its ``text`` legitimately contains embedded
|
|
119
|
+
its ``text`` legitimately contains embedded ``\n`` characters, one per row boundary, by design.
|
|
120
120
|
:ivar attrib: run-length encoded display attributes for :attr:`text`, the same shape as
|
|
121
121
|
:attr:`urwid.Text.attrib`. ``None`` is a valid RLE tag (meaning "no attribute"), matching
|
|
122
122
|
:func:`urwid.util.decompose_tagmarkup`'s convention.
|
|
123
123
|
:ivar last_attr: the SGR/colour state in effect at the end of this line; feed this back in as ``previous_attr``
|
|
124
124
|
to continue the same colour state into a subsequently parsed line.
|
|
125
|
-
:ivar bel: the number of BEL (
|
|
125
|
+
:ivar bel: the number of BEL (``\a``) characters seen on this line (``0`` if none were seen).
|
|
126
126
|
:ivar title: the last OSC window-title string seen on this line, or ``None`` if none was seen.
|
|
127
127
|
:ivar leds: the last keyboard-LED state requested on this line (``CSI n q``), or ``None`` if none was seen.
|
|
128
128
|
:ivar skipped: every :class:`SkippedOp` stripped while parsing this line, in encounter order, for programmatic
|
|
@@ -139,7 +139,7 @@ class ParsedLine:
|
|
|
139
139
|
|
|
140
140
|
|
|
141
141
|
class AnsiParser:
|
|
142
|
-
"""Incremental ANSI parser that resolves escape sequences against a 2-D cell grid, in one of two modes.
|
|
142
|
+
r"""Incremental ANSI parser that resolves escape sequences against a 2-D cell grid, in one of two modes.
|
|
143
143
|
|
|
144
144
|
Feed text in with :meth:`feed` -- any number of times, e.g. as chunks arrive from a subprocess -- and call
|
|
145
145
|
:meth:`finalize` once to retrieve the result. :meth:`finalize` is a one-shot, terminal operation: construct a
|
|
@@ -154,18 +154,18 @@ class AnsiParser:
|
|
|
154
154
|
treated, and in whether the side channels (``bel``, ``title``, ``leds``, ``skipped``) are reset per line or
|
|
155
155
|
aggregated over the whole input:
|
|
156
156
|
|
|
157
|
-
* ``one_line=True``: a line-end sequence (
|
|
157
|
+
* ``one_line=True``: a line-end sequence (``\n``, ``\r``, ``\r\n`` or ``\n\r``, the latter two pairs
|
|
158
158
|
collapsing into a single event) is a **structural split point**, not discarded data -- the moment one is
|
|
159
159
|
seen, the current row is completed into a :class:`ParsedLine` and pushed onto an internal completed-lines
|
|
160
160
|
queue, and a fresh row is started for what follows, carrying the SGR/colour state
|
|
161
161
|
(:attr:`ParsedLine.last_attr`) forward but resetting the per-line side channels. Vertical cursor movement
|
|
162
162
|
(``CSI n A/B/E/F/d`` and ``CSI r;c H``/``f``) is meaningless outside a single line, so it is stripped and
|
|
163
163
|
logged as a :class:`SkippedOp` rather than reinterpreted.
|
|
164
|
-
* ``one_line=False`` (the default): a full newline (
|
|
164
|
+
* ``one_line=False`` (the default): a full newline (``\n``, or a collapsed ``\r\n``/``\n\r`` pair) genuinely
|
|
165
165
|
moves to a fresh row, resetting the column, while everything keeps accumulating in the same persistent grid
|
|
166
|
-
-- nothing is split off. A **bare**
|
|
166
|
+
-- nothing is split off. A **bare** ``\r`` (not part of a ``\r\n``/``\n\r`` pair) is carriage-return-only:
|
|
167
167
|
it resets the column without changing row, letting progress-bar-style in-place redraw sequences
|
|
168
|
-
(``"progress: 10
|
|
168
|
+
(``"progress: 10%\rprogress: 20%"``) resolve correctly onto a single row, mirroring real terminal behaviour.
|
|
169
169
|
Vertical cursor movement genuinely repositions the cursor within the grid instead of being stripped. The side
|
|
170
170
|
channels are aggregated over the *entire* input rather than reset per row: ``bel`` is the total BEL count,
|
|
171
171
|
``title``/``leds`` are the last value seen anywhere, and ``skipped`` lists every :class:`SkippedOp` in
|
|
@@ -180,7 +180,7 @@ class AnsiParser:
|
|
|
180
180
|
:meth:`finalize` returns a uniform ``tuple[ParsedLine, ...]`` in both modes: in ``one_line=True`` mode, every
|
|
181
181
|
line completed since construction (queue plus a final flush of whatever remains, possibly empty or
|
|
182
182
|
unterminated); in ``one_line=False`` mode, always exactly one :class:`ParsedLine` -- built from the whole grid,
|
|
183
|
-
rows joined with ``"
|
|
183
|
+
rows joined with ``"\n"`` -- wrapped in a 1-tuple.
|
|
184
184
|
|
|
185
185
|
:param previous_attr: SGR/colour state to seed the parser with, as if it were carried over from previously
|
|
186
186
|
parsed text.
|
|
@@ -188,17 +188,18 @@ class AnsiParser:
|
|
|
188
188
|
results; if false (the default), resolve the whole input into a single multi-row :class:`ParsedLine`.
|
|
189
189
|
|
|
190
190
|
>>> parser = AnsiParser(one_line=True)
|
|
191
|
-
>>> parser.feed("hello
|
|
191
|
+
>>> parser.feed("hello\nworld")
|
|
192
192
|
>>> [line.text for line in parser.finalize()]
|
|
193
193
|
['hello', 'world']
|
|
194
194
|
|
|
195
195
|
>>> parser = AnsiParser()
|
|
196
|
-
>>> parser.feed("hello
|
|
196
|
+
>>> parser.feed("hello\nworld")
|
|
197
197
|
>>> parser.finalize()[0].text
|
|
198
|
-
'hello
|
|
198
|
+
'hello\nworld'
|
|
199
199
|
"""
|
|
200
200
|
|
|
201
201
|
def __init__(self, previous_attr: AttrSpec | None = None, *, one_line: bool = False) -> None:
|
|
202
|
+
"""Create a parser, as described in the class docstring."""
|
|
202
203
|
self._one_line = one_line
|
|
203
204
|
self._attrspec: AttrSpec | None = previous_attr
|
|
204
205
|
|
|
@@ -285,12 +286,12 @@ class AnsiParser:
|
|
|
285
286
|
i += 1
|
|
286
287
|
|
|
287
288
|
def finalize(self) -> tuple[ParsedLine, ...]:
|
|
288
|
-
"""Retrieve the parsed result, in a uniform ``tuple[ParsedLine, ...]`` shape regardless of mode.
|
|
289
|
+
r"""Retrieve the parsed result, in a uniform ``tuple[ParsedLine, ...]`` shape regardless of mode.
|
|
289
290
|
|
|
290
291
|
In ``one_line=True`` mode, whatever remains in the current (possibly empty, possibly unterminated) row is
|
|
291
292
|
flushed as one final :class:`ParsedLine`, and every line completed since construction is returned, in
|
|
292
293
|
order. In ``one_line=False`` mode, the whole accumulated grid is resolved into a single :class:`ParsedLine`
|
|
293
|
-
(its ``text`` joining every resolved row with ``"
|
|
294
|
+
(its ``text`` joining every resolved row with ``"\n"``) and returned as a 1-tuple.
|
|
294
295
|
|
|
295
296
|
This is a terminal operation: construct a new :class:`AnsiParser` to parse a fresh "session" of input.
|
|
296
297
|
|
|
@@ -324,10 +325,10 @@ class AnsiParser:
|
|
|
324
325
|
# -- internal: line/row completion and cursor management -------------
|
|
325
326
|
|
|
326
327
|
def _handle_line_end(self) -> None:
|
|
327
|
-
"""Complete the current line, aborting any in-flight escape sequence.
|
|
328
|
+
r"""Complete the current line, aborting any in-flight escape sequence.
|
|
328
329
|
|
|
329
330
|
Only called in one_line mode: a line end always aborts any in-flight escape sequence, since real escape
|
|
330
|
-
sequences never legitimately contain a raw
|
|
331
|
+
sequences never legitimately contain a raw \r or \n. In screen mode a newline is instead handled directly
|
|
331
332
|
in :meth:`feed` without touching escape-framing state.
|
|
332
333
|
"""
|
|
333
334
|
self._within_escape = False
|
|
@@ -587,9 +588,9 @@ class AnsiParser:
|
|
|
587
588
|
|
|
588
589
|
|
|
589
590
|
def parse_ansi_line(text: str, previous_attr: AttrSpec | None = None) -> tuple[ParsedLine, ...]:
|
|
590
|
-
"""Feed ``text`` through a fresh :class:`AnsiParser` (``one_line=True``) and return its finalised result.
|
|
591
|
+
r"""Feed ``text`` through a fresh :class:`AnsiParser` (``one_line=True``) and return its finalised result.
|
|
591
592
|
|
|
592
|
-
Returns one :class:`ParsedLine` per line found in ``text`` (split on
|
|
593
|
+
Returns one :class:`ParsedLine` per line found in ``text`` (split on ``\n``/``\r``/``\r\n``/``\n\r``), plus
|
|
593
594
|
a trailing entry for a final unterminated fragment, if any -- never fewer than one element, even for empty
|
|
594
595
|
input, and every character of the original data is preserved across the returned lines (line-end characters are
|
|
595
596
|
structural split points, not stripped content).
|
|
@@ -599,11 +600,11 @@ def parse_ansi_line(text: str, previous_attr: AttrSpec | None = None) -> tuple[P
|
|
|
599
600
|
previously parsed line.
|
|
600
601
|
:returns: every :class:`ParsedLine` found in ``text``, in order.
|
|
601
602
|
|
|
602
|
-
>>> [line.text for line in parse_ansi_line("one
|
|
603
|
+
>>> [line.text for line in parse_ansi_line("one\ntwo\nthree")]
|
|
603
604
|
['one', 'two', 'three']
|
|
604
605
|
>>> [line.text for line in parse_ansi_line("")]
|
|
605
606
|
['']
|
|
606
|
-
>>> [line.text for line in parse_ansi_line("a
|
|
607
|
+
>>> [line.text for line in parse_ansi_line("a\nb")]
|
|
607
608
|
['a', 'b']
|
|
608
609
|
"""
|
|
609
610
|
parser = AnsiParser(previous_attr, one_line=True)
|
|
@@ -612,22 +613,22 @@ def parse_ansi_line(text: str, previous_attr: AttrSpec | None = None) -> tuple[P
|
|
|
612
613
|
|
|
613
614
|
|
|
614
615
|
def parse_ansi_text(text: str, previous_attr: AttrSpec | None = None) -> ParsedLine:
|
|
615
|
-
"""Feed ``text`` through a fresh :class:`AnsiParser` (``one_line=False``) and return its sole resolved line.
|
|
616
|
+
r"""Feed ``text`` through a fresh :class:`AnsiParser` (``one_line=False``) and return its sole resolved line.
|
|
616
617
|
|
|
617
618
|
Unlike :func:`parse_ansi_line`, vertical cursor movement and newlines are resolved into real multi-row output
|
|
618
619
|
rather than stripped/split: the returned single :class:`ParsedLine`'s ``text`` joins every resolved row with
|
|
619
|
-
``"
|
|
620
|
+
``"\n"``.
|
|
620
621
|
|
|
621
622
|
:param text: the text to parse.
|
|
622
623
|
:param previous_attr: SGR/colour state to seed the parser with, as if it were carried over from previously
|
|
623
624
|
parsed text.
|
|
624
625
|
:returns: a single :class:`ParsedLine` representing the whole resolved block.
|
|
625
626
|
|
|
626
|
-
>>> parse_ansi_text("one
|
|
627
|
-
'one
|
|
627
|
+
>>> parse_ansi_text("one\ntwo\nthree").text
|
|
628
|
+
'one\ntwo\nthree'
|
|
628
629
|
>>> parse_ansi_text("").text
|
|
629
630
|
''
|
|
630
|
-
>>> parse_ansi_text("progress: 10
|
|
631
|
+
>>> parse_ansi_text("progress: 10%\rprogress: 20%").text
|
|
631
632
|
'progress: 20%'
|
|
632
633
|
"""
|
|
633
634
|
parser = AnsiParser(previous_attr, one_line=False)
|