textual-debugger 0.0.3__tar.gz → 0.1.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.
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/PKG-INFO +98 -51
- {textual_debugger-0.0.3/src/tdb → textual_debugger-0.1.0}/README.md +96 -50
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/pyproject.toml +24 -2
- {textual_debugger-0.0.3 → textual_debugger-0.1.0/src/tdb}/README.md +96 -50
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/__init__.py +1 -1
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/app.py +15 -1
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/cli.py +53 -15
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/keybindings/__init__.py +8 -8
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/session/controller.py +123 -76
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/widgets/code_view.py +13 -7
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/textual_debugger.egg-info/PKG-INFO +98 -51
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/textual_debugger.egg-info/requires.txt +1 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/LICENSE +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/setup.cfg +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/__main__.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/app_handlers/__init__.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/app_handlers/dap_events.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/app_handlers/inspection.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/app_helpers.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/breakpoint_hook.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/dap/__init__.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/dap/client.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/dap/messages.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/dap/protocol.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/dap/types.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/inspection.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/persist.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/post_mortem.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/server/__init__.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/server/app.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/server/event_handler.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/server/handlers.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/server/rpc_types.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/server/runner.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/session/__init__.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/session/event_bus.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/session/messages.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/session/state.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/session/textual_handler.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/widgets/__init__.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/widgets/async_tasks_modal.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/widgets/breakpoint_view.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/widgets/console_view.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/widgets/evaluate_console.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/widgets/menu_bar.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/widgets/modals.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/widgets/processes_modal.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/widgets/stack_view.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/widgets/status_bar.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/widgets/threads_modal.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/tdb/widgets/variable_view.py +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/textual_debugger.egg-info/SOURCES.txt +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/textual_debugger.egg-info/dependency_links.txt +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/textual_debugger.egg-info/entry_points.txt +0 -0
- {textual_debugger-0.0.3 → textual_debugger-0.1.0}/src/textual_debugger.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: textual-debugger
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.1.0
|
|
4
4
|
Summary: A TUI Python debugger based on textual and debugpy
|
|
5
5
|
Author-email: Al Danial <al.danial@gmail.com>
|
|
6
6
|
License: textual-debugger is licensed to you under the MIT/X Consortium license:
|
|
@@ -53,6 +53,7 @@ Requires-Dist: fastapi>=0.115.0
|
|
|
53
53
|
Requires-Dist: uvicorn>=0.30.0
|
|
54
54
|
Provides-Extra: dev
|
|
55
55
|
Requires-Dist: pytest; extra == "dev"
|
|
56
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
56
57
|
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
57
58
|
Requires-Dist: httpx; extra == "dev"
|
|
58
59
|
Requires-Dist: matplotlib; extra == "dev"
|
|
@@ -83,7 +84,7 @@ MIT License. Copyright 2026 by Al Danial.
|
|
|
83
84
|
|
|
84
85
|
- supports debugging of synchronous, asynchronous, multi-threaded, and multi-process Python code.
|
|
85
86
|
It specifically supports modules
|
|
86
|
-
- `asyncio` (with a built-in async task inspector)
|
|
87
|
+
- `asyncio` (with a built-in async task inspector and task wait graph)
|
|
87
88
|
- `threading` (with a thread inspector)
|
|
88
89
|
- `multiprocessing` / `concurrent.futures` (with automatic child process attachment and a process inspector)
|
|
89
90
|
|
|
@@ -96,7 +97,7 @@ automated, headless debugging workflows and AI-assisted debugging
|
|
|
96
97
|
built with `textual`, `prompt-toolkit`, `urwid`, `curses`, `rich`, and so on
|
|
97
98
|
|
|
98
99
|
- comes with a post-mortem exception hook that can be installed in Python programs
|
|
99
|
-
to have
|
|
100
|
+
to have `tdb` pop open automatically at the first uncaught exception
|
|
100
101
|
|
|
101
102
|
- can be entirely keyboard-driven
|
|
102
103
|
making it suitable for operation in non-graphical environments (mouse support is
|
|
@@ -118,6 +119,18 @@ as open source.
|
|
|
118
119
|
[Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) program.
|
|
119
120
|
`tdb` was made almost entirely with Claude Code.
|
|
120
121
|
|
|
122
|
+
## Gallery
|
|
123
|
+
<p align="center">
|
|
124
|
+
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
|
|
125
|
+
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_task_graph.png" alt="task graph" width="300">
|
|
126
|
+
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/multiprocessing_process_3.png" alt="multiple processes" width="300">
|
|
127
|
+
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/threading_list.png" alt="thread list" width="300">
|
|
128
|
+
</p>
|
|
129
|
+
|
|
130
|
+
Videos:
|
|
131
|
+
- [tdb basics](https://youtu.be/2_qf2WZDHuA) views, keybindings, breakpoints, stepping, variable modification, call stack
|
|
132
|
+
- [asyncio tasks](https://youtu.be/vM4tODuqMGg) inspect asyncio tasks and their wait graph; code mod to allow pause
|
|
133
|
+
|
|
121
134
|
## Installation
|
|
122
135
|
|
|
123
136
|
```bash
|
|
@@ -130,33 +143,52 @@ or (better):
|
|
|
130
143
|
uv pip install textual-debugger
|
|
131
144
|
```
|
|
132
145
|
|
|
146
|
+
or run it without installing:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
uvx --from textual-debugger tdb my_program.py
|
|
150
|
+
```
|
|
151
|
+
|
|
133
152
|
|
|
134
153
|
## Quick Start
|
|
135
154
|
|
|
136
155
|
```bash
|
|
137
|
-
#
|
|
138
|
-
tdb
|
|
156
|
+
# show comprehensive documentation in a terminal-based Markdown viewer
|
|
157
|
+
tdb --doc
|
|
139
158
|
|
|
140
|
-
#
|
|
141
|
-
tdb
|
|
159
|
+
# debug a script (stops at first line by default)
|
|
160
|
+
tdb my_program.py
|
|
142
161
|
|
|
143
|
-
#
|
|
144
|
-
tdb
|
|
162
|
+
# debug with arguments
|
|
163
|
+
tdb my_program.py arg1 arg2
|
|
145
164
|
|
|
146
|
-
#
|
|
147
|
-
tdb
|
|
165
|
+
# add breakpoints at lines 20 and 35 of `my_program.py` and line 14 of `module.py`
|
|
166
|
+
tdb -k 20 -k 35 -k module.py:14 my_program.py arg1 arg2
|
|
148
167
|
|
|
149
|
-
|
|
150
|
-
|
|
168
|
+
# use a specific virtualenv
|
|
169
|
+
tdb --python /path/to/venv/bin/python my_program.py
|
|
151
170
|
|
|
152
|
-
#
|
|
153
|
-
tdb --
|
|
171
|
+
# step into, or stop at tracebacks in library code
|
|
172
|
+
tdb --no-just-my-code /path/to/venv/bin/python my_program.py
|
|
173
|
+
|
|
174
|
+
# run until first breakpoint or exit
|
|
175
|
+
tdb --no-stop-on-entry my_program.py
|
|
176
|
+
|
|
177
|
+
# run the debuggee in an external terminal
|
|
178
|
+
tdb --terminal xterm my_program.py
|
|
179
|
+
|
|
180
|
+
# attach to a remote Python program that has a debugpy server on port 5678
|
|
181
|
+
tdb -r remotehost:5678 my_program.py
|
|
182
|
+
|
|
183
|
+
# prevent `argparse` confusion by separating `tdb` switches from
|
|
184
|
+
# the debuggee switches by prefixing the debuggee with `--`
|
|
185
|
+
tdb --python /path/to/venv/bin/python -- my_program.py -k 17 --max 23.3
|
|
154
186
|
```
|
|
155
187
|
|
|
156
|
-
|
|
188
|
+
Alternatively, use the module entry point:
|
|
157
189
|
|
|
158
190
|
```bash
|
|
159
|
-
python -m tdb
|
|
191
|
+
python -m tdb my_program.py
|
|
160
192
|
```
|
|
161
193
|
|
|
162
194
|
## Layout
|
|
@@ -178,29 +210,18 @@ python -m tdb my_script.py
|
|
|
178
210
|
└───────────────────────────────────────────────────────┘
|
|
179
211
|
```
|
|
180
212
|
|
|
213
|
+
The status bar shows the current execution state (running, paused,
|
|
214
|
+
breakpoint hit) and location.
|
|
215
|
+
The footer shows the most relevant keybindings for the current mode.
|
|
216
|
+
|
|
181
217
|
## Features
|
|
182
218
|
|
|
183
219
|
### Navigation and Keybindings
|
|
184
220
|
|
|
221
|
+
|
|
185
222
|
The Code View shows syntax-highlighted Python source with line numbers.
|
|
186
223
|
A cursor line (blue) tracks your position; the current execution line is highlighted in gold.
|
|
187
224
|
|
|
188
|
-
**Navigation (vim-style by default):**
|
|
189
|
-
|
|
190
|
-
| Key | Action |
|
|
191
|
-
|-----|--------|
|
|
192
|
-
| `j` / `k` | Move cursor down / up |
|
|
193
|
-
| `5j`, `10k` | Move N lines with count prefix |
|
|
194
|
-
| `g` | Go to line (with count: `42g` jumps to line 42) |
|
|
195
|
-
| `G` | Go to end of file |
|
|
196
|
-
| `[` / `]` | Jump to previous / next paragraph boundary |
|
|
197
|
-
| `/` | Search forward |
|
|
198
|
-
| `?` | Search backward |
|
|
199
|
-
| `n` / `N` | Next / previous search result |
|
|
200
|
-
| `PageUp` / `PageDown` | Scroll by page |
|
|
201
|
-
|
|
202
|
-
Switch between Navigation and Debug modes with `Escape`.
|
|
203
|
-
|
|
204
225
|
**View focus shortcuts (global):**
|
|
205
226
|
|
|
206
227
|
| Key | View |
|
|
@@ -225,6 +246,24 @@ Switch between Navigation and Debug modes with `Escape`.
|
|
|
225
246
|
| `Alt+A` | Async Tasks |
|
|
226
247
|
| `Alt+H` | Help (Documentation, About) |
|
|
227
248
|
|
|
249
|
+
**Navigation (vim-style by default):**
|
|
250
|
+
|
|
251
|
+
By default the Code View is in Debug mode. Hit `Escape` to switch to Navigate mode
|
|
252
|
+
In Navigate mode, you can move around the file with the following keys:
|
|
253
|
+
|
|
254
|
+
| Key | Action |
|
|
255
|
+
|-----|--------|
|
|
256
|
+
| `j` / `k` | Move cursor down / up |
|
|
257
|
+
| `5j`, `10k` | Move N lines down / up with count prefix |
|
|
258
|
+
| `G` | Go to end of file (with count: `42G` jumps to line 42)|
|
|
259
|
+
| `[` / `]` | Jump to previous / next paragraph boundary |
|
|
260
|
+
| `/` | Search forward |
|
|
261
|
+
| `?` | Search backward |
|
|
262
|
+
| `n` / `N` | Next / previous search result |
|
|
263
|
+
| `PageUp` / `PageDown` | Scroll by page |
|
|
264
|
+
|
|
265
|
+
Switch from Navigate back to Debug mode with `Escape`.
|
|
266
|
+
|
|
228
267
|
> **Note:** Many terminals send the byte sequence `ESC+f` for `Alt+F`, which Textual's
|
|
229
268
|
ANSI parser rewrites to `Ctrl+Right` (the readline "forward-word" convention).
|
|
230
269
|
`tdb` binds both so `Alt+F` works as expected regardless.
|
|
@@ -273,8 +312,7 @@ Breakpoints persist across session restarts.
|
|
|
273
312
|
|
|
274
313
|
The Variable View shows a tree of scopes (Locals, Globals) with all variables in the current
|
|
275
314
|
frame. Expand nodes to drill into complex objects. Children are loaded lazily on demand.
|
|
276
|
-
|
|
277
|
-
Format: `name (type) = value`
|
|
315
|
+
Variable values can be changed in the Evaluate Console.
|
|
278
316
|
|
|
279
317
|
### Call Stack
|
|
280
318
|
|
|
@@ -301,11 +339,17 @@ A REPL at the bottom-left evaluates expressions in the current scope:
|
|
|
301
339
|
(a, *p) : Join two or more pathname components...
|
|
302
340
|
```
|
|
303
341
|
|
|
342
|
+
Variable values set here are reflected in the running code.
|
|
343
|
+
|
|
304
344
|
### Console Output
|
|
305
345
|
|
|
306
346
|
The Console View captures stdout (normal text) and stderr (red text) from the debuggee
|
|
307
347
|
in real time.
|
|
308
348
|
|
|
349
|
+
If your program prints a lot, or prompts for input, or uses colors or
|
|
350
|
+
terminal control codes, consider running it in an external terminal
|
|
351
|
+
with `--terminal` for the best experience.
|
|
352
|
+
|
|
309
353
|
### Crash Detection
|
|
310
354
|
|
|
311
355
|
When the debuggee raises an unhandled exception, `tdb`:
|
|
@@ -353,7 +397,9 @@ memoization.
|
|
|
353
397
|
|
|
354
398
|
### Live Breakpoint Hook
|
|
355
399
|
|
|
356
|
-
|
|
400
|
+
`tdb` has an improved implemenation of the standard `breakpoint()` function (or equivalently,
|
|
401
|
+
`pdb.set_trace()`) used to pause at a specific line to inspect, then
|
|
402
|
+
here=
|
|
357
403
|
continuing--use `tdb.breakpoint()`:
|
|
358
404
|
|
|
359
405
|
```python
|
|
@@ -499,17 +545,17 @@ print("tdb is attached!")
|
|
|
499
545
|
|
|
500
546
|
When the debuggee runs and hits the `debugpy.wait_for_client()` line, it starts a
|
|
501
547
|
debugpy server listening on port 5678.
|
|
502
|
-
Attach `tdb` to it with the `-r`
|
|
548
|
+
Attach `tdb` to it with the `-r` switch, specifying the host and port.
|
|
503
549
|
If the debuggee is on the same machine, you can omit the host or use `localhost`.
|
|
504
550
|
This example assumes the debuggee runs on 192.168.1.10 and listens on port 5678:
|
|
505
551
|
|
|
506
552
|
```bash
|
|
507
553
|
# Attach from tdb:
|
|
508
|
-
tdb -r 5678
|
|
554
|
+
tdb -r 5678 # to localhost
|
|
509
555
|
tdb -r 192.168.1.10:5678
|
|
510
556
|
|
|
511
557
|
# With breakpoints:
|
|
512
|
-
tdb -r 5678 -k
|
|
558
|
+
tdb -r 5678 -k my_program.py:42
|
|
513
559
|
```
|
|
514
560
|
|
|
515
561
|
All debugging features (breakpoints, stepping, variable inspection, threads, processes,
|
|
@@ -518,7 +564,7 @@ source file when the program stops.
|
|
|
518
564
|
|
|
519
565
|
### External Terminal Support
|
|
520
566
|
|
|
521
|
-
Some Python programs, notably text user interfaces,
|
|
567
|
+
Some Python programs, notably text user interfaces, use terminal control
|
|
522
568
|
codes and require direct access to the terminal to function properly.
|
|
523
569
|
Such programs can be debugged with `tdb` by having it launch the debuggee in
|
|
524
570
|
a separate terminal:
|
|
@@ -537,9 +583,9 @@ This feature only works in graphical environments where external terminals are a
|
|
|
537
583
|
### Keybinding Schemes
|
|
538
584
|
|
|
539
585
|
```bash
|
|
540
|
-
tdb --keybindings vim
|
|
541
|
-
tdb --keybindings emacs
|
|
542
|
-
tdb --keybindings default
|
|
586
|
+
tdb --keybindings vim my_program.py # default
|
|
587
|
+
tdb --keybindings emacs my_program.py
|
|
588
|
+
tdb --keybindings default my_program.py
|
|
543
589
|
```
|
|
544
590
|
|
|
545
591
|
The keybinding choice is saved to `~/.config/tdb/config.json` and remembered for subsequent
|
|
@@ -553,7 +599,7 @@ debugging, CI pipelines, or AI-assisted debugging workflows.
|
|
|
553
599
|
### Headless Mode (no TUI)
|
|
554
600
|
|
|
555
601
|
```bash
|
|
556
|
-
python -m tdb --headless
|
|
602
|
+
python -m tdb --headless my_program.py &
|
|
557
603
|
```
|
|
558
604
|
|
|
559
605
|
The server listens on `http://127.0.0.1:8150/rpc` (change with `--server-port`).
|
|
@@ -561,7 +607,7 @@ The server listens on `http://127.0.0.1:8150/rpc` (change with `--server-port`).
|
|
|
561
607
|
### Dual Mode (TUI + server)
|
|
562
608
|
|
|
563
609
|
```bash
|
|
564
|
-
tdb --server
|
|
610
|
+
tdb --server my_program.py
|
|
565
611
|
```
|
|
566
612
|
|
|
567
613
|
Both the interactive TUI and the JSON-RPC server run simultaneously.
|
|
@@ -643,18 +689,19 @@ Each is JSON with `event`, `data`, and `timestamp` fields.
|
|
|
643
689
|
## CLI Reference
|
|
644
690
|
|
|
645
691
|
```
|
|
646
|
-
usage: tdb [-h] [-r [HOST:]PORT] [
|
|
647
|
-
[--no-
|
|
648
|
-
[--
|
|
692
|
+
usage: tdb [-h] [-v/--version] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
|
|
693
|
+
[--no-just-my-code] [--no-subprocess] [--python PYTHON]
|
|
694
|
+
[-d/--doc] [--doc-text] [--keybindings {default,vim,emacs}]
|
|
649
695
|
[--terminal {xterm,konsole,gnome-terminal,ghostty,kitty,iterm2,warp,wezterm,terminator}]
|
|
650
|
-
[--server] [--headless] [--server-port
|
|
696
|
+
[--server] [--headless] [-k FILE:LINE|LINE] [--server-port SERVER_PORT]
|
|
651
697
|
[program] [args ...]
|
|
698
|
+
|
|
652
699
|
```
|
|
653
700
|
|
|
654
701
|
| Flag | Description |
|
|
655
702
|
|------|-------------|
|
|
656
|
-
| `-r
|
|
657
|
-
| `-k`, `--breakpoint FILE:LINE` | Set a breakpoint (may be repeated) |
|
|
703
|
+
| `-r HOST:PORT` | Attach to a remote debugpy server |
|
|
704
|
+
| `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated) |
|
|
658
705
|
| `--no-stop-on-entry` | Do not pause at the first line (default: stop on entry) |
|
|
659
706
|
| `--cwd DIR` | Working directory for the debuggee |
|
|
660
707
|
| `--python PATH` | Python interpreter for the debuggee |
|
|
@@ -19,7 +19,7 @@ MIT License. Copyright 2026 by Al Danial.
|
|
|
19
19
|
|
|
20
20
|
- supports debugging of synchronous, asynchronous, multi-threaded, and multi-process Python code.
|
|
21
21
|
It specifically supports modules
|
|
22
|
-
- `asyncio` (with a built-in async task inspector)
|
|
22
|
+
- `asyncio` (with a built-in async task inspector and task wait graph)
|
|
23
23
|
- `threading` (with a thread inspector)
|
|
24
24
|
- `multiprocessing` / `concurrent.futures` (with automatic child process attachment and a process inspector)
|
|
25
25
|
|
|
@@ -32,7 +32,7 @@ automated, headless debugging workflows and AI-assisted debugging
|
|
|
32
32
|
built with `textual`, `prompt-toolkit`, `urwid`, `curses`, `rich`, and so on
|
|
33
33
|
|
|
34
34
|
- comes with a post-mortem exception hook that can be installed in Python programs
|
|
35
|
-
to have
|
|
35
|
+
to have `tdb` pop open automatically at the first uncaught exception
|
|
36
36
|
|
|
37
37
|
- can be entirely keyboard-driven
|
|
38
38
|
making it suitable for operation in non-graphical environments (mouse support is
|
|
@@ -54,6 +54,18 @@ as open source.
|
|
|
54
54
|
[Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) program.
|
|
55
55
|
`tdb` was made almost entirely with Claude Code.
|
|
56
56
|
|
|
57
|
+
## Gallery
|
|
58
|
+
<p align="center">
|
|
59
|
+
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
|
|
60
|
+
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_task_graph.png" alt="task graph" width="300">
|
|
61
|
+
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/multiprocessing_process_3.png" alt="multiple processes" width="300">
|
|
62
|
+
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/threading_list.png" alt="thread list" width="300">
|
|
63
|
+
</p>
|
|
64
|
+
|
|
65
|
+
Videos:
|
|
66
|
+
- [tdb basics](https://youtu.be/2_qf2WZDHuA) views, keybindings, breakpoints, stepping, variable modification, call stack
|
|
67
|
+
- [asyncio tasks](https://youtu.be/vM4tODuqMGg) inspect asyncio tasks and their wait graph; code mod to allow pause
|
|
68
|
+
|
|
57
69
|
## Installation
|
|
58
70
|
|
|
59
71
|
```bash
|
|
@@ -66,33 +78,52 @@ or (better):
|
|
|
66
78
|
uv pip install textual-debugger
|
|
67
79
|
```
|
|
68
80
|
|
|
81
|
+
or run it without installing:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
uvx --from textual-debugger tdb my_program.py
|
|
85
|
+
```
|
|
86
|
+
|
|
69
87
|
|
|
70
88
|
## Quick Start
|
|
71
89
|
|
|
72
90
|
```bash
|
|
73
|
-
#
|
|
74
|
-
tdb
|
|
91
|
+
# show comprehensive documentation in a terminal-based Markdown viewer
|
|
92
|
+
tdb --doc
|
|
75
93
|
|
|
76
|
-
#
|
|
77
|
-
tdb
|
|
94
|
+
# debug a script (stops at first line by default)
|
|
95
|
+
tdb my_program.py
|
|
78
96
|
|
|
79
|
-
#
|
|
80
|
-
tdb
|
|
97
|
+
# debug with arguments
|
|
98
|
+
tdb my_program.py arg1 arg2
|
|
81
99
|
|
|
82
|
-
#
|
|
83
|
-
tdb
|
|
100
|
+
# add breakpoints at lines 20 and 35 of `my_program.py` and line 14 of `module.py`
|
|
101
|
+
tdb -k 20 -k 35 -k module.py:14 my_program.py arg1 arg2
|
|
84
102
|
|
|
85
|
-
|
|
86
|
-
|
|
103
|
+
# use a specific virtualenv
|
|
104
|
+
tdb --python /path/to/venv/bin/python my_program.py
|
|
87
105
|
|
|
88
|
-
#
|
|
89
|
-
tdb --
|
|
106
|
+
# step into, or stop at tracebacks in library code
|
|
107
|
+
tdb --no-just-my-code /path/to/venv/bin/python my_program.py
|
|
108
|
+
|
|
109
|
+
# run until first breakpoint or exit
|
|
110
|
+
tdb --no-stop-on-entry my_program.py
|
|
111
|
+
|
|
112
|
+
# run the debuggee in an external terminal
|
|
113
|
+
tdb --terminal xterm my_program.py
|
|
114
|
+
|
|
115
|
+
# attach to a remote Python program that has a debugpy server on port 5678
|
|
116
|
+
tdb -r remotehost:5678 my_program.py
|
|
117
|
+
|
|
118
|
+
# prevent `argparse` confusion by separating `tdb` switches from
|
|
119
|
+
# the debuggee switches by prefixing the debuggee with `--`
|
|
120
|
+
tdb --python /path/to/venv/bin/python -- my_program.py -k 17 --max 23.3
|
|
90
121
|
```
|
|
91
122
|
|
|
92
|
-
|
|
123
|
+
Alternatively, use the module entry point:
|
|
93
124
|
|
|
94
125
|
```bash
|
|
95
|
-
python -m tdb
|
|
126
|
+
python -m tdb my_program.py
|
|
96
127
|
```
|
|
97
128
|
|
|
98
129
|
## Layout
|
|
@@ -114,29 +145,18 @@ python -m tdb my_script.py
|
|
|
114
145
|
└───────────────────────────────────────────────────────┘
|
|
115
146
|
```
|
|
116
147
|
|
|
148
|
+
The status bar shows the current execution state (running, paused,
|
|
149
|
+
breakpoint hit) and location.
|
|
150
|
+
The footer shows the most relevant keybindings for the current mode.
|
|
151
|
+
|
|
117
152
|
## Features
|
|
118
153
|
|
|
119
154
|
### Navigation and Keybindings
|
|
120
155
|
|
|
156
|
+
|
|
121
157
|
The Code View shows syntax-highlighted Python source with line numbers.
|
|
122
158
|
A cursor line (blue) tracks your position; the current execution line is highlighted in gold.
|
|
123
159
|
|
|
124
|
-
**Navigation (vim-style by default):**
|
|
125
|
-
|
|
126
|
-
| Key | Action |
|
|
127
|
-
|-----|--------|
|
|
128
|
-
| `j` / `k` | Move cursor down / up |
|
|
129
|
-
| `5j`, `10k` | Move N lines with count prefix |
|
|
130
|
-
| `g` | Go to line (with count: `42g` jumps to line 42) |
|
|
131
|
-
| `G` | Go to end of file |
|
|
132
|
-
| `[` / `]` | Jump to previous / next paragraph boundary |
|
|
133
|
-
| `/` | Search forward |
|
|
134
|
-
| `?` | Search backward |
|
|
135
|
-
| `n` / `N` | Next / previous search result |
|
|
136
|
-
| `PageUp` / `PageDown` | Scroll by page |
|
|
137
|
-
|
|
138
|
-
Switch between Navigation and Debug modes with `Escape`.
|
|
139
|
-
|
|
140
160
|
**View focus shortcuts (global):**
|
|
141
161
|
|
|
142
162
|
| Key | View |
|
|
@@ -161,6 +181,24 @@ Switch between Navigation and Debug modes with `Escape`.
|
|
|
161
181
|
| `Alt+A` | Async Tasks |
|
|
162
182
|
| `Alt+H` | Help (Documentation, About) |
|
|
163
183
|
|
|
184
|
+
**Navigation (vim-style by default):**
|
|
185
|
+
|
|
186
|
+
By default the Code View is in Debug mode. Hit `Escape` to switch to Navigate mode
|
|
187
|
+
In Navigate mode, you can move around the file with the following keys:
|
|
188
|
+
|
|
189
|
+
| Key | Action |
|
|
190
|
+
|-----|--------|
|
|
191
|
+
| `j` / `k` | Move cursor down / up |
|
|
192
|
+
| `5j`, `10k` | Move N lines down / up with count prefix |
|
|
193
|
+
| `G` | Go to end of file (with count: `42G` jumps to line 42)|
|
|
194
|
+
| `[` / `]` | Jump to previous / next paragraph boundary |
|
|
195
|
+
| `/` | Search forward |
|
|
196
|
+
| `?` | Search backward |
|
|
197
|
+
| `n` / `N` | Next / previous search result |
|
|
198
|
+
| `PageUp` / `PageDown` | Scroll by page |
|
|
199
|
+
|
|
200
|
+
Switch from Navigate back to Debug mode with `Escape`.
|
|
201
|
+
|
|
164
202
|
> **Note:** Many terminals send the byte sequence `ESC+f` for `Alt+F`, which Textual's
|
|
165
203
|
ANSI parser rewrites to `Ctrl+Right` (the readline "forward-word" convention).
|
|
166
204
|
`tdb` binds both so `Alt+F` works as expected regardless.
|
|
@@ -209,8 +247,7 @@ Breakpoints persist across session restarts.
|
|
|
209
247
|
|
|
210
248
|
The Variable View shows a tree of scopes (Locals, Globals) with all variables in the current
|
|
211
249
|
frame. Expand nodes to drill into complex objects. Children are loaded lazily on demand.
|
|
212
|
-
|
|
213
|
-
Format: `name (type) = value`
|
|
250
|
+
Variable values can be changed in the Evaluate Console.
|
|
214
251
|
|
|
215
252
|
### Call Stack
|
|
216
253
|
|
|
@@ -237,11 +274,17 @@ A REPL at the bottom-left evaluates expressions in the current scope:
|
|
|
237
274
|
(a, *p) : Join two or more pathname components...
|
|
238
275
|
```
|
|
239
276
|
|
|
277
|
+
Variable values set here are reflected in the running code.
|
|
278
|
+
|
|
240
279
|
### Console Output
|
|
241
280
|
|
|
242
281
|
The Console View captures stdout (normal text) and stderr (red text) from the debuggee
|
|
243
282
|
in real time.
|
|
244
283
|
|
|
284
|
+
If your program prints a lot, or prompts for input, or uses colors or
|
|
285
|
+
terminal control codes, consider running it in an external terminal
|
|
286
|
+
with `--terminal` for the best experience.
|
|
287
|
+
|
|
245
288
|
### Crash Detection
|
|
246
289
|
|
|
247
290
|
When the debuggee raises an unhandled exception, `tdb`:
|
|
@@ -289,7 +332,9 @@ memoization.
|
|
|
289
332
|
|
|
290
333
|
### Live Breakpoint Hook
|
|
291
334
|
|
|
292
|
-
|
|
335
|
+
`tdb` has an improved implemenation of the standard `breakpoint()` function (or equivalently,
|
|
336
|
+
`pdb.set_trace()`) used to pause at a specific line to inspect, then
|
|
337
|
+
here=
|
|
293
338
|
continuing--use `tdb.breakpoint()`:
|
|
294
339
|
|
|
295
340
|
```python
|
|
@@ -435,17 +480,17 @@ print("tdb is attached!")
|
|
|
435
480
|
|
|
436
481
|
When the debuggee runs and hits the `debugpy.wait_for_client()` line, it starts a
|
|
437
482
|
debugpy server listening on port 5678.
|
|
438
|
-
Attach `tdb` to it with the `-r`
|
|
483
|
+
Attach `tdb` to it with the `-r` switch, specifying the host and port.
|
|
439
484
|
If the debuggee is on the same machine, you can omit the host or use `localhost`.
|
|
440
485
|
This example assumes the debuggee runs on 192.168.1.10 and listens on port 5678:
|
|
441
486
|
|
|
442
487
|
```bash
|
|
443
488
|
# Attach from tdb:
|
|
444
|
-
tdb -r 5678
|
|
489
|
+
tdb -r 5678 # to localhost
|
|
445
490
|
tdb -r 192.168.1.10:5678
|
|
446
491
|
|
|
447
492
|
# With breakpoints:
|
|
448
|
-
tdb -r 5678 -k
|
|
493
|
+
tdb -r 5678 -k my_program.py:42
|
|
449
494
|
```
|
|
450
495
|
|
|
451
496
|
All debugging features (breakpoints, stepping, variable inspection, threads, processes,
|
|
@@ -454,7 +499,7 @@ source file when the program stops.
|
|
|
454
499
|
|
|
455
500
|
### External Terminal Support
|
|
456
501
|
|
|
457
|
-
Some Python programs, notably text user interfaces,
|
|
502
|
+
Some Python programs, notably text user interfaces, use terminal control
|
|
458
503
|
codes and require direct access to the terminal to function properly.
|
|
459
504
|
Such programs can be debugged with `tdb` by having it launch the debuggee in
|
|
460
505
|
a separate terminal:
|
|
@@ -473,9 +518,9 @@ This feature only works in graphical environments where external terminals are a
|
|
|
473
518
|
### Keybinding Schemes
|
|
474
519
|
|
|
475
520
|
```bash
|
|
476
|
-
tdb --keybindings vim
|
|
477
|
-
tdb --keybindings emacs
|
|
478
|
-
tdb --keybindings default
|
|
521
|
+
tdb --keybindings vim my_program.py # default
|
|
522
|
+
tdb --keybindings emacs my_program.py
|
|
523
|
+
tdb --keybindings default my_program.py
|
|
479
524
|
```
|
|
480
525
|
|
|
481
526
|
The keybinding choice is saved to `~/.config/tdb/config.json` and remembered for subsequent
|
|
@@ -489,7 +534,7 @@ debugging, CI pipelines, or AI-assisted debugging workflows.
|
|
|
489
534
|
### Headless Mode (no TUI)
|
|
490
535
|
|
|
491
536
|
```bash
|
|
492
|
-
python -m tdb --headless
|
|
537
|
+
python -m tdb --headless my_program.py &
|
|
493
538
|
```
|
|
494
539
|
|
|
495
540
|
The server listens on `http://127.0.0.1:8150/rpc` (change with `--server-port`).
|
|
@@ -497,7 +542,7 @@ The server listens on `http://127.0.0.1:8150/rpc` (change with `--server-port`).
|
|
|
497
542
|
### Dual Mode (TUI + server)
|
|
498
543
|
|
|
499
544
|
```bash
|
|
500
|
-
tdb --server
|
|
545
|
+
tdb --server my_program.py
|
|
501
546
|
```
|
|
502
547
|
|
|
503
548
|
Both the interactive TUI and the JSON-RPC server run simultaneously.
|
|
@@ -579,18 +624,19 @@ Each is JSON with `event`, `data`, and `timestamp` fields.
|
|
|
579
624
|
## CLI Reference
|
|
580
625
|
|
|
581
626
|
```
|
|
582
|
-
usage: tdb [-h] [-r [HOST:]PORT] [
|
|
583
|
-
[--no-
|
|
584
|
-
[--
|
|
627
|
+
usage: tdb [-h] [-v/--version] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
|
|
628
|
+
[--no-just-my-code] [--no-subprocess] [--python PYTHON]
|
|
629
|
+
[-d/--doc] [--doc-text] [--keybindings {default,vim,emacs}]
|
|
585
630
|
[--terminal {xterm,konsole,gnome-terminal,ghostty,kitty,iterm2,warp,wezterm,terminator}]
|
|
586
|
-
[--server] [--headless] [--server-port
|
|
631
|
+
[--server] [--headless] [-k FILE:LINE|LINE] [--server-port SERVER_PORT]
|
|
587
632
|
[program] [args ...]
|
|
633
|
+
|
|
588
634
|
```
|
|
589
635
|
|
|
590
636
|
| Flag | Description |
|
|
591
637
|
|------|-------------|
|
|
592
|
-
| `-r
|
|
593
|
-
| `-k`, `--breakpoint FILE:LINE` | Set a breakpoint (may be repeated) |
|
|
638
|
+
| `-r HOST:PORT` | Attach to a remote debugpy server |
|
|
639
|
+
| `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated) |
|
|
594
640
|
| `--no-stop-on-entry` | Do not pause at the first line (default: stop on entry) |
|
|
595
641
|
| `--cwd DIR` | Working directory for the debuggee |
|
|
596
642
|
| `--python PATH` | Python interpreter for the debuggee |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "textual-debugger"
|
|
3
|
-
|
|
3
|
+
dynamic = ["version"]
|
|
4
4
|
description = "A TUI Python debugger based on textual and debugpy"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
|
|
30
30
|
# extras needed to run the examples and tests
|
|
31
31
|
[project.optional-dependencies]
|
|
32
|
-
dev = ["pytest", "pytest-asyncio", "httpx",
|
|
32
|
+
dev = ["pytest", "pytest-cov", "pytest-asyncio", "httpx",
|
|
33
33
|
"matplotlib",
|
|
34
34
|
"pyyaml",
|
|
35
35
|
"toml",
|
|
@@ -48,6 +48,9 @@
|
|
|
48
48
|
requires = ["setuptools>=68.0"]
|
|
49
49
|
build-backend = "setuptools.build_meta"
|
|
50
50
|
|
|
51
|
+
[tool.setuptools.dynamic]
|
|
52
|
+
version = {attr = "tdb.__version__"}
|
|
53
|
+
|
|
51
54
|
[tool.setuptools.packages.find]
|
|
52
55
|
where = ["src"]
|
|
53
56
|
|
|
@@ -57,6 +60,25 @@
|
|
|
57
60
|
[tool.pytest.ini_options]
|
|
58
61
|
testpaths = ["tests"]
|
|
59
62
|
asyncio_mode = "auto"
|
|
63
|
+
addopts = "--cov=tdb --cov-report=term-missing"
|
|
60
64
|
filterwarnings = [
|
|
61
65
|
"ignore::DeprecationWarning",
|
|
62
66
|
]
|
|
67
|
+
|
|
68
|
+
[tool.coverage.run]
|
|
69
|
+
source = ["src/tdb"]
|
|
70
|
+
branch = true
|
|
71
|
+
omit = [
|
|
72
|
+
"*/tests/*",
|
|
73
|
+
"src/tdb/__main__.py",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
[tool.coverage.report]
|
|
77
|
+
exclude_lines = [
|
|
78
|
+
"pragma: no cover",
|
|
79
|
+
"raise NotImplementedError",
|
|
80
|
+
"if TYPE_CHECKING:",
|
|
81
|
+
"if __name__ == .__main__.:",
|
|
82
|
+
]
|
|
83
|
+
show_missing = true
|
|
84
|
+
skip_covered = false
|