textual-debugger 0.1.0__tar.gz → 0.1.2__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.
Files changed (68) hide show
  1. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/PKG-INFO +44 -8
  2. {textual_debugger-0.1.0/src/tdb → textual_debugger-0.1.2}/README.md +43 -7
  3. {textual_debugger-0.1.0 → textual_debugger-0.1.2/src/tdb}/README.md +43 -7
  4. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/__init__.py +1 -1
  5. textual_debugger-0.1.2/src/tdb/_timeouts.py +59 -0
  6. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/app.py +189 -146
  7. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/app_handlers/dap_events.py +40 -23
  8. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/app_handlers/inspection.py +152 -45
  9. textual_debugger-0.1.2/src/tdb/app_handlers/routing.py +194 -0
  10. textual_debugger-0.1.2/src/tdb/app_handlers/ui_panels.py +53 -0
  11. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/app_helpers.py +2 -0
  12. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/breakpoint_hook.py +2 -1
  13. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/cli.py +176 -61
  14. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/dap/client.py +61 -27
  15. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/dap/types.py +12 -4
  16. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/inspection.py +39 -30
  17. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/keybindings/__init__.py +20 -11
  18. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/persist.py +73 -39
  19. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/post_mortem.py +39 -28
  20. textual_debugger-0.1.2/src/tdb/processes_cache.py +239 -0
  21. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/server/event_handler.py +9 -6
  22. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/server/handlers.py +84 -26
  23. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/server/runner.py +8 -2
  24. textual_debugger-0.1.2/src/tdb/session/child_processes.py +248 -0
  25. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/session/controller.py +220 -297
  26. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/session/messages.py +9 -2
  27. textual_debugger-0.1.2/src/tdb/session/post_mortem_loader.py +78 -0
  28. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/session/state.py +66 -0
  29. textual_debugger-0.1.2/src/tdb/session/statement_stepper.py +196 -0
  30. textual_debugger-0.1.2/src/tdb/session/terminal.py +121 -0
  31. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/session/textual_handler.py +6 -2
  32. textual_debugger-0.1.2/src/tdb/source_analysis.py +162 -0
  33. textual_debugger-0.1.2/src/tdb/widgets/_inspection_modal.py +319 -0
  34. textual_debugger-0.1.2/src/tdb/widgets/async_tasks_modal.py +318 -0
  35. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/widgets/breakpoint_view.py +5 -2
  36. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/widgets/code_view.py +164 -56
  37. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/widgets/evaluate_console.py +6 -2
  38. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/widgets/menu_bar.py +14 -5
  39. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/widgets/modals.py +112 -6
  40. textual_debugger-0.1.2/src/tdb/widgets/processes_modal.py +276 -0
  41. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/widgets/stack_view.py +3 -1
  42. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/widgets/status_bar.py +3 -1
  43. textual_debugger-0.1.2/src/tdb/widgets/threads_modal.py +154 -0
  44. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/widgets/variable_view.py +3 -6
  45. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/textual_debugger.egg-info/PKG-INFO +44 -8
  46. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/textual_debugger.egg-info/SOURCES.txt +10 -0
  47. textual_debugger-0.1.0/src/tdb/widgets/async_tasks_modal.py +0 -340
  48. textual_debugger-0.1.0/src/tdb/widgets/processes_modal.py +0 -279
  49. textual_debugger-0.1.0/src/tdb/widgets/threads_modal.py +0 -244
  50. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/LICENSE +0 -0
  51. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/pyproject.toml +0 -0
  52. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/setup.cfg +0 -0
  53. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/__main__.py +0 -0
  54. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/app_handlers/__init__.py +0 -0
  55. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/dap/__init__.py +0 -0
  56. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/dap/messages.py +0 -0
  57. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/dap/protocol.py +0 -0
  58. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/server/__init__.py +0 -0
  59. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/server/app.py +0 -0
  60. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/server/rpc_types.py +0 -0
  61. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/session/__init__.py +0 -0
  62. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/session/event_bus.py +0 -0
  63. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/widgets/__init__.py +0 -0
  64. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/tdb/widgets/console_view.py +0 -0
  65. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/textual_debugger.egg-info/dependency_links.txt +0 -0
  66. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/textual_debugger.egg-info/entry_points.txt +0 -0
  67. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/src/textual_debugger.egg-info/requires.txt +0 -0
  68. {textual_debugger-0.1.0 → textual_debugger-0.1.2}/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.1.0
3
+ Version: 0.1.2
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:
@@ -130,6 +130,8 @@ as open source.
130
130
  Videos:
131
131
  - [tdb basics](https://youtu.be/2_qf2WZDHuA) views, keybindings, breakpoints, stepping, variable modification, call stack
132
132
  - [asyncio tasks](https://youtu.be/vM4tODuqMGg) inspect asyncio tasks and their wait graph; code mod to allow pause
133
+ - [threads and processes](https://youtu.be/J8LOARLs2oQ) inspect variables and call stacks in multiple threads and processes
134
+ - [external terminal](https://youtu.be/121aihjAQ8g) run the debuggee in a separate terminal--ideal for debugging TUI applications
133
135
 
134
136
  ## Installation
135
137
 
@@ -162,14 +164,16 @@ tdb my_program.py
162
164
  # debug with arguments
163
165
  tdb my_program.py arg1 arg2
164
166
 
165
- # add breakpoints at lines 20 and 35 of `my_program.py` and line 14 of `module.py`
167
+ # add breakpoints at lines 20 and 35 of `my_program.py` and line 14
168
+ # of `module.py` (when -k is given, --no-stop-on-entry is set and the
169
+ # program runs to the first breakpoint)
166
170
  tdb -k 20 -k 35 -k module.py:14 my_program.py arg1 arg2
167
171
 
168
172
  # use a specific virtualenv
169
173
  tdb --python /path/to/venv/bin/python my_program.py
170
174
 
171
175
  # step into, or stop at tracebacks in library code
172
- tdb --no-just-my-code /path/to/venv/bin/python my_program.py
176
+ tdb --no-just-my-code --python /path/to/venv/bin/python my_program.py
173
177
 
174
178
  # run until first breakpoint or exit
175
179
  tdb --no-stop-on-entry my_program.py
@@ -240,7 +244,7 @@ A cursor line (blue) tracks your position; the current execution line is highlig
240
244
  | Key | Menu |
241
245
  |-----|------|
242
246
  | `Alt+F` | File (open a different script to debug) |
243
- | `Alt+C` | Configure (Color Theme, Keybindings) |
247
+ | `Alt+C` | Configure (Color Theme, Keybindings, Step Mode) |
244
248
  | `Alt+T` | Threads |
245
249
  | `Alt+P` | Processes |
246
250
  | `Alt+A` | Async Tasks |
@@ -275,13 +279,15 @@ those for gdb/pdb, with some aliases and extras thrown in for convenience.
275
279
 
276
280
  | Key | Action |
277
281
  |-----|--------|
278
- | `n` | Step over (next line) |
282
+ | `n` | Step over (next statement) |
279
283
  | `s` | Step into function call |
280
284
  | `o` / `f` / `r` | Step out of current function (also aliased as "finish" and "return") |
281
285
  | `c` | Continue execution |
282
286
  | `p` | Pause a running program |
283
287
  | `t` | Run to cursor position |
284
288
  | `u` / `d` | Navigate stack up (caller) / down (callee) |
289
+ | `j` / `k` | Move cursor down / up (with count: `5j`, `10k`) |
290
+ | `G` | Go to last line (with count: `42G` jumps to line 42) |
285
291
  | `R` | Restart the debug session |
286
292
  | `Ctrl+Q` | Quit |
287
293
 
@@ -290,6 +296,23 @@ those for gdb/pdb, with some aliases and extras thrown in for convenience.
290
296
  normally and stops at the return point. A true gdb-style immediate-return (skipping
291
297
  remaining code in the function without executing side effects) is not supported by DAP/debugpy.
292
298
 
299
+ **Step granularity (statement vs. line):** by default, `n` (step over) and `s` (step into)
300
+ treat a multi-line source statement as a single step. For example, stepping over
301
+
302
+ ```python
303
+ results = await asyncio.gather(
304
+ fetch(1, 2),
305
+ fetch(2, 1),
306
+ fetch(3, 3),
307
+ )
308
+ print(results) # next stop lands here, not on each sub-line above
309
+ ```
310
+
311
+ lands on `print(results)`, not on each interior sub-line of the `gather` call. Switch to
312
+ **Line** mode (Configure > Step Mode) to get debugpy's native per-line behavior, which
313
+ stops on each physical line — useful for inspecting how a complex expression is built up.
314
+ The choice is saved to `~/.config/tdb/config.json`.
315
+
293
316
  ### Breakpoints
294
317
 
295
318
  Click the gutter in the Code View to toggle a breakpoint, or press `b` in Debug mode.
@@ -701,8 +724,8 @@ usage: tdb [-h] [-v/--version] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
701
724
  | Flag | Description |
702
725
  |------|-------------|
703
726
  | `-r HOST:PORT` | Attach to a remote debugpy server |
704
- | `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated) |
705
- | `--no-stop-on-entry` | Do not pause at the first line (default: stop on entry) |
727
+ | `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated). Passing `-k` implies `--no-stop-on-entry` so the program runs straight to the first breakpoint. |
728
+ | `--no-stop-on-entry` | Do not pause at the first line (default: stop on entry; automatic when `-k` is given) |
706
729
  | `--cwd DIR` | Working directory for the debuggee |
707
730
  | `--python PATH` | Python interpreter for the debuggee |
708
731
  | `--no-just-my-code` | Step into stdlib/site-packages code instead of skipping it
@@ -724,12 +747,25 @@ On Windows, it uses `%APPDATA%\tdb\`.
724
747
 
725
748
  | File | Contents |
726
749
  |------|----------|
727
- | `config.json` | User preferences (keybinding scheme) |
750
+ | `config.json` | User preferences (keybinding scheme, color theme, step mode) |
728
751
  | `breakpoints.json` | Breakpoints from previous sessions, keyed by project directory |
729
752
 
730
753
  Breakpoints are saved on exit and restored when debugging a program in the same
731
754
  directory. Each project's breakpoints are independent.
732
755
 
756
+ **Step mode** (`step_mode` in `config.json`) controls how `n` (step over) and `s` (step
757
+ into) handle multi-line source statements:
758
+
759
+ | Value | Behavior |
760
+ |-------|----------|
761
+ | `"statement"` (default) | A multi-line statement (e.g. a `gather(...)` call spanning five lines) is one step. The debugger keeps issuing DAP steps until execution leaves the statement, then stops on the next logical line. |
762
+ | `"line"` | debugpy's native per-line behavior — stops on every physical line, including each interior sub-line of a multi-line expression. |
763
+
764
+ Change it from the menu (**Configure > Step Mode**); the choice is saved immediately and
765
+ applies to all future sessions. Breakpoint hits, exceptions, and pauses always interrupt
766
+ a statement step, so a breakpoint set on a sub-line of a multi-line expression still
767
+ fires as expected.
768
+
733
769
  ## Tech Stack
734
770
 
735
771
  - [textual](https://github.com/Textualize/textual) : TUI framework
@@ -65,6 +65,8 @@ as open source.
65
65
  Videos:
66
66
  - [tdb basics](https://youtu.be/2_qf2WZDHuA) views, keybindings, breakpoints, stepping, variable modification, call stack
67
67
  - [asyncio tasks](https://youtu.be/vM4tODuqMGg) inspect asyncio tasks and their wait graph; code mod to allow pause
68
+ - [threads and processes](https://youtu.be/J8LOARLs2oQ) inspect variables and call stacks in multiple threads and processes
69
+ - [external terminal](https://youtu.be/121aihjAQ8g) run the debuggee in a separate terminal--ideal for debugging TUI applications
68
70
 
69
71
  ## Installation
70
72
 
@@ -97,14 +99,16 @@ tdb my_program.py
97
99
  # debug with arguments
98
100
  tdb my_program.py arg1 arg2
99
101
 
100
- # add breakpoints at lines 20 and 35 of `my_program.py` and line 14 of `module.py`
102
+ # add breakpoints at lines 20 and 35 of `my_program.py` and line 14
103
+ # of `module.py` (when -k is given, --no-stop-on-entry is set and the
104
+ # program runs to the first breakpoint)
101
105
  tdb -k 20 -k 35 -k module.py:14 my_program.py arg1 arg2
102
106
 
103
107
  # use a specific virtualenv
104
108
  tdb --python /path/to/venv/bin/python my_program.py
105
109
 
106
110
  # step into, or stop at tracebacks in library code
107
- tdb --no-just-my-code /path/to/venv/bin/python my_program.py
111
+ tdb --no-just-my-code --python /path/to/venv/bin/python my_program.py
108
112
 
109
113
  # run until first breakpoint or exit
110
114
  tdb --no-stop-on-entry my_program.py
@@ -175,7 +179,7 @@ A cursor line (blue) tracks your position; the current execution line is highlig
175
179
  | Key | Menu |
176
180
  |-----|------|
177
181
  | `Alt+F` | File (open a different script to debug) |
178
- | `Alt+C` | Configure (Color Theme, Keybindings) |
182
+ | `Alt+C` | Configure (Color Theme, Keybindings, Step Mode) |
179
183
  | `Alt+T` | Threads |
180
184
  | `Alt+P` | Processes |
181
185
  | `Alt+A` | Async Tasks |
@@ -210,13 +214,15 @@ those for gdb/pdb, with some aliases and extras thrown in for convenience.
210
214
 
211
215
  | Key | Action |
212
216
  |-----|--------|
213
- | `n` | Step over (next line) |
217
+ | `n` | Step over (next statement) |
214
218
  | `s` | Step into function call |
215
219
  | `o` / `f` / `r` | Step out of current function (also aliased as "finish" and "return") |
216
220
  | `c` | Continue execution |
217
221
  | `p` | Pause a running program |
218
222
  | `t` | Run to cursor position |
219
223
  | `u` / `d` | Navigate stack up (caller) / down (callee) |
224
+ | `j` / `k` | Move cursor down / up (with count: `5j`, `10k`) |
225
+ | `G` | Go to last line (with count: `42G` jumps to line 42) |
220
226
  | `R` | Restart the debug session |
221
227
  | `Ctrl+Q` | Quit |
222
228
 
@@ -225,6 +231,23 @@ those for gdb/pdb, with some aliases and extras thrown in for convenience.
225
231
  normally and stops at the return point. A true gdb-style immediate-return (skipping
226
232
  remaining code in the function without executing side effects) is not supported by DAP/debugpy.
227
233
 
234
+ **Step granularity (statement vs. line):** by default, `n` (step over) and `s` (step into)
235
+ treat a multi-line source statement as a single step. For example, stepping over
236
+
237
+ ```python
238
+ results = await asyncio.gather(
239
+ fetch(1, 2),
240
+ fetch(2, 1),
241
+ fetch(3, 3),
242
+ )
243
+ print(results) # next stop lands here, not on each sub-line above
244
+ ```
245
+
246
+ lands on `print(results)`, not on each interior sub-line of the `gather` call. Switch to
247
+ **Line** mode (Configure > Step Mode) to get debugpy's native per-line behavior, which
248
+ stops on each physical line — useful for inspecting how a complex expression is built up.
249
+ The choice is saved to `~/.config/tdb/config.json`.
250
+
228
251
  ### Breakpoints
229
252
 
230
253
  Click the gutter in the Code View to toggle a breakpoint, or press `b` in Debug mode.
@@ -636,8 +659,8 @@ usage: tdb [-h] [-v/--version] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
636
659
  | Flag | Description |
637
660
  |------|-------------|
638
661
  | `-r HOST:PORT` | Attach to a remote debugpy server |
639
- | `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated) |
640
- | `--no-stop-on-entry` | Do not pause at the first line (default: stop on entry) |
662
+ | `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated). Passing `-k` implies `--no-stop-on-entry` so the program runs straight to the first breakpoint. |
663
+ | `--no-stop-on-entry` | Do not pause at the first line (default: stop on entry; automatic when `-k` is given) |
641
664
  | `--cwd DIR` | Working directory for the debuggee |
642
665
  | `--python PATH` | Python interpreter for the debuggee |
643
666
  | `--no-just-my-code` | Step into stdlib/site-packages code instead of skipping it
@@ -659,12 +682,25 @@ On Windows, it uses `%APPDATA%\tdb\`.
659
682
 
660
683
  | File | Contents |
661
684
  |------|----------|
662
- | `config.json` | User preferences (keybinding scheme) |
685
+ | `config.json` | User preferences (keybinding scheme, color theme, step mode) |
663
686
  | `breakpoints.json` | Breakpoints from previous sessions, keyed by project directory |
664
687
 
665
688
  Breakpoints are saved on exit and restored when debugging a program in the same
666
689
  directory. Each project's breakpoints are independent.
667
690
 
691
+ **Step mode** (`step_mode` in `config.json`) controls how `n` (step over) and `s` (step
692
+ into) handle multi-line source statements:
693
+
694
+ | Value | Behavior |
695
+ |-------|----------|
696
+ | `"statement"` (default) | A multi-line statement (e.g. a `gather(...)` call spanning five lines) is one step. The debugger keeps issuing DAP steps until execution leaves the statement, then stops on the next logical line. |
697
+ | `"line"` | debugpy's native per-line behavior — stops on every physical line, including each interior sub-line of a multi-line expression. |
698
+
699
+ Change it from the menu (**Configure > Step Mode**); the choice is saved immediately and
700
+ applies to all future sessions. Breakpoint hits, exceptions, and pauses always interrupt
701
+ a statement step, so a breakpoint set on a sub-line of a multi-line expression still
702
+ fires as expected.
703
+
668
704
  ## Tech Stack
669
705
 
670
706
  - [textual](https://github.com/Textualize/textual) : TUI framework
@@ -65,6 +65,8 @@ as open source.
65
65
  Videos:
66
66
  - [tdb basics](https://youtu.be/2_qf2WZDHuA) views, keybindings, breakpoints, stepping, variable modification, call stack
67
67
  - [asyncio tasks](https://youtu.be/vM4tODuqMGg) inspect asyncio tasks and their wait graph; code mod to allow pause
68
+ - [threads and processes](https://youtu.be/J8LOARLs2oQ) inspect variables and call stacks in multiple threads and processes
69
+ - [external terminal](https://youtu.be/121aihjAQ8g) run the debuggee in a separate terminal--ideal for debugging TUI applications
68
70
 
69
71
  ## Installation
70
72
 
@@ -97,14 +99,16 @@ tdb my_program.py
97
99
  # debug with arguments
98
100
  tdb my_program.py arg1 arg2
99
101
 
100
- # add breakpoints at lines 20 and 35 of `my_program.py` and line 14 of `module.py`
102
+ # add breakpoints at lines 20 and 35 of `my_program.py` and line 14
103
+ # of `module.py` (when -k is given, --no-stop-on-entry is set and the
104
+ # program runs to the first breakpoint)
101
105
  tdb -k 20 -k 35 -k module.py:14 my_program.py arg1 arg2
102
106
 
103
107
  # use a specific virtualenv
104
108
  tdb --python /path/to/venv/bin/python my_program.py
105
109
 
106
110
  # step into, or stop at tracebacks in library code
107
- tdb --no-just-my-code /path/to/venv/bin/python my_program.py
111
+ tdb --no-just-my-code --python /path/to/venv/bin/python my_program.py
108
112
 
109
113
  # run until first breakpoint or exit
110
114
  tdb --no-stop-on-entry my_program.py
@@ -175,7 +179,7 @@ A cursor line (blue) tracks your position; the current execution line is highlig
175
179
  | Key | Menu |
176
180
  |-----|------|
177
181
  | `Alt+F` | File (open a different script to debug) |
178
- | `Alt+C` | Configure (Color Theme, Keybindings) |
182
+ | `Alt+C` | Configure (Color Theme, Keybindings, Step Mode) |
179
183
  | `Alt+T` | Threads |
180
184
  | `Alt+P` | Processes |
181
185
  | `Alt+A` | Async Tasks |
@@ -210,13 +214,15 @@ those for gdb/pdb, with some aliases and extras thrown in for convenience.
210
214
 
211
215
  | Key | Action |
212
216
  |-----|--------|
213
- | `n` | Step over (next line) |
217
+ | `n` | Step over (next statement) |
214
218
  | `s` | Step into function call |
215
219
  | `o` / `f` / `r` | Step out of current function (also aliased as "finish" and "return") |
216
220
  | `c` | Continue execution |
217
221
  | `p` | Pause a running program |
218
222
  | `t` | Run to cursor position |
219
223
  | `u` / `d` | Navigate stack up (caller) / down (callee) |
224
+ | `j` / `k` | Move cursor down / up (with count: `5j`, `10k`) |
225
+ | `G` | Go to last line (with count: `42G` jumps to line 42) |
220
226
  | `R` | Restart the debug session |
221
227
  | `Ctrl+Q` | Quit |
222
228
 
@@ -225,6 +231,23 @@ those for gdb/pdb, with some aliases and extras thrown in for convenience.
225
231
  normally and stops at the return point. A true gdb-style immediate-return (skipping
226
232
  remaining code in the function without executing side effects) is not supported by DAP/debugpy.
227
233
 
234
+ **Step granularity (statement vs. line):** by default, `n` (step over) and `s` (step into)
235
+ treat a multi-line source statement as a single step. For example, stepping over
236
+
237
+ ```python
238
+ results = await asyncio.gather(
239
+ fetch(1, 2),
240
+ fetch(2, 1),
241
+ fetch(3, 3),
242
+ )
243
+ print(results) # next stop lands here, not on each sub-line above
244
+ ```
245
+
246
+ lands on `print(results)`, not on each interior sub-line of the `gather` call. Switch to
247
+ **Line** mode (Configure > Step Mode) to get debugpy's native per-line behavior, which
248
+ stops on each physical line — useful for inspecting how a complex expression is built up.
249
+ The choice is saved to `~/.config/tdb/config.json`.
250
+
228
251
  ### Breakpoints
229
252
 
230
253
  Click the gutter in the Code View to toggle a breakpoint, or press `b` in Debug mode.
@@ -636,8 +659,8 @@ usage: tdb [-h] [-v/--version] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
636
659
  | Flag | Description |
637
660
  |------|-------------|
638
661
  | `-r HOST:PORT` | Attach to a remote debugpy server |
639
- | `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated) |
640
- | `--no-stop-on-entry` | Do not pause at the first line (default: stop on entry) |
662
+ | `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated). Passing `-k` implies `--no-stop-on-entry` so the program runs straight to the first breakpoint. |
663
+ | `--no-stop-on-entry` | Do not pause at the first line (default: stop on entry; automatic when `-k` is given) |
641
664
  | `--cwd DIR` | Working directory for the debuggee |
642
665
  | `--python PATH` | Python interpreter for the debuggee |
643
666
  | `--no-just-my-code` | Step into stdlib/site-packages code instead of skipping it
@@ -659,12 +682,25 @@ On Windows, it uses `%APPDATA%\tdb\`.
659
682
 
660
683
  | File | Contents |
661
684
  |------|----------|
662
- | `config.json` | User preferences (keybinding scheme) |
685
+ | `config.json` | User preferences (keybinding scheme, color theme, step mode) |
663
686
  | `breakpoints.json` | Breakpoints from previous sessions, keyed by project directory |
664
687
 
665
688
  Breakpoints are saved on exit and restored when debugging a program in the same
666
689
  directory. Each project's breakpoints are independent.
667
690
 
691
+ **Step mode** (`step_mode` in `config.json`) controls how `n` (step over) and `s` (step
692
+ into) handle multi-line source statements:
693
+
694
+ | Value | Behavior |
695
+ |-------|----------|
696
+ | `"statement"` (default) | A multi-line statement (e.g. a `gather(...)` call spanning five lines) is one step. The debugger keeps issuing DAP steps until execution leaves the statement, then stops on the next logical line. |
697
+ | `"line"` | debugpy's native per-line behavior — stops on every physical line, including each interior sub-line of a multi-line expression. |
698
+
699
+ Change it from the menu (**Configure > Step Mode**); the choice is saved immediately and
700
+ applies to all future sessions. Breakpoint hits, exceptions, and pauses always interrupt
701
+ a statement step, so a breakpoint set on a sub-line of a multi-line expression still
702
+ fires as expected.
703
+
668
704
  ## Tech Stack
669
705
 
670
706
  - [textual](https://github.com/Textualize/textual) : TUI framework
@@ -1,6 +1,6 @@
1
1
  from tdb.breakpoint_hook import breakpoint
2
2
  from tdb.post_mortem import exception_hook
3
3
 
4
- __version__ = "0.1.0"
4
+ __version__ = "0.1.2"
5
5
 
6
6
  __all__ = ["breakpoint", "exception_hook"]
@@ -0,0 +1,59 @@
1
+ """Centralized timeout constants (seconds).
2
+
3
+ Reasoning per constant lives beside its value so the "why this number?"
4
+ question doesn't require reading commit history or guessing. Group by
5
+ subsystem; alphabetize within group.
6
+
7
+ Bare integers/floats for wall-clock seconds. Always pass to
8
+ `asyncio.wait_for(..., timeout=X)` (or equivalent). Don't multiply
9
+ these together to derive new timeouts — add a new named constant.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+
15
+ # --- DAP protocol round-trips --------------------------------------------
16
+ # debugpy is normally fast (single-digit ms) but some requests block on the
17
+ # debuggee's main thread (e.g. evaluate during a long sync call). 30s gives
18
+ # headroom for those without hanging a user-typed step forever.
19
+ DAP_REQUEST = 30.0
20
+ """Default per-request timeout for `DAPClient._send`."""
21
+
22
+ # Child-process DAPClient disconnect runs in parallel with the parent's
23
+ # `disconnect(terminate=True)`. debugpy cascades termination from the
24
+ # parent, so the per-child handshake is cleanup, not load-bearing — keep
25
+ # it short so multi-process quit doesn't pile up 8 × 30s waits.
26
+ DAP_DISCONNECT_CHILD = 1.0
27
+ """Wait for one child DAP session to acknowledge disconnect."""
28
+
29
+ # Parent disconnect is also bounded. The parent's `disconnect` is what
30
+ # actually kills the debuggee in non-remote-attach mode, so we wait a
31
+ # bit longer than children — but still capped to keep quit snappy.
32
+ DAP_DISCONNECT_PARENT = 2.0
33
+ """Wait for the parent DAP session to acknowledge disconnect."""
34
+
35
+ # Time to wait for debugpy to emit `initialized` after launch/attach.
36
+ # Cold debugpy start (no compiled cache, large dependency graph) can
37
+ # take ~1-3s on modest machines; 10s is comfortable padding.
38
+ DAP_INITIALIZED = 10.0
39
+ """Wait for `initialized` event after launch / attach."""
40
+
41
+ # Time to wait for a `stopped` event after launch with `stop_on_entry=True`.
42
+ # Same shape as DAP_INITIALIZED — we just need debugpy to reach line 1.
43
+ DAP_STOP_ON_ENTRY = 10.0
44
+ """Wait for the first `stopped` event when stop_on_entry is set."""
45
+
46
+ # Child-process attach handshake: after a `debugpyAttach` reverse event,
47
+ # the child opens its DAP socket and we connect + initialize. 30s allows
48
+ # for slow forkserver starts on Linux (Python 3.14 default).
49
+ DAP_CHILD_ATTACH = 30.0
50
+ """End-to-end timeout for connecting + initializing a child DAP session."""
51
+
52
+
53
+ # --- RPC handler waits ---------------------------------------------------
54
+ # Step / continue can run a long tail in multi-process programs: after
55
+ # releasing all breakpoint hits, the remainder of the script may run for
56
+ # many seconds before `terminated` arrives. 30s wasn't enough; 10 min is
57
+ # arbitrary-but-comfortable for typical interactive sessions.
58
+ RPC_STEP_WAIT = 600.0
59
+ """How long the RPC `_step_action` blocks awaiting the next stop."""