textual-debugger 0.1.1__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.1 → textual_debugger-0.1.2}/PKG-INFO +42 -8
  2. {textual_debugger-0.1.1/src/tdb → textual_debugger-0.1.2}/README.md +41 -7
  3. {textual_debugger-0.1.1 → textual_debugger-0.1.2/src/tdb}/README.md +41 -7
  4. {textual_debugger-0.1.1 → 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.1 → textual_debugger-0.1.2}/src/tdb/app.py +189 -146
  7. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/app_handlers/dap_events.py +40 -23
  8. {textual_debugger-0.1.1 → 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.1 → textual_debugger-0.1.2}/src/tdb/app_helpers.py +2 -0
  12. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/breakpoint_hook.py +2 -1
  13. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/cli.py +176 -61
  14. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/dap/client.py +61 -27
  15. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/dap/types.py +12 -4
  16. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/inspection.py +39 -30
  17. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/keybindings/__init__.py +20 -11
  18. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/persist.py +73 -39
  19. {textual_debugger-0.1.1 → 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.1 → textual_debugger-0.1.2}/src/tdb/server/event_handler.py +9 -6
  22. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/server/handlers.py +84 -26
  23. {textual_debugger-0.1.1 → 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.1 → textual_debugger-0.1.2}/src/tdb/session/controller.py +220 -297
  26. {textual_debugger-0.1.1 → 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.1 → 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.1 → 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.1 → textual_debugger-0.1.2}/src/tdb/widgets/breakpoint_view.py +5 -2
  36. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/widgets/code_view.py +164 -56
  37. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/widgets/evaluate_console.py +6 -2
  38. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/widgets/menu_bar.py +14 -5
  39. {textual_debugger-0.1.1 → 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.1 → textual_debugger-0.1.2}/src/tdb/widgets/stack_view.py +3 -1
  42. {textual_debugger-0.1.1 → 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.1 → textual_debugger-0.1.2}/src/tdb/widgets/variable_view.py +3 -6
  45. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/textual_debugger.egg-info/PKG-INFO +42 -8
  46. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/textual_debugger.egg-info/SOURCES.txt +10 -0
  47. textual_debugger-0.1.1/src/tdb/widgets/async_tasks_modal.py +0 -340
  48. textual_debugger-0.1.1/src/tdb/widgets/processes_modal.py +0 -279
  49. textual_debugger-0.1.1/src/tdb/widgets/threads_modal.py +0 -244
  50. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/LICENSE +0 -0
  51. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/pyproject.toml +0 -0
  52. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/setup.cfg +0 -0
  53. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/__main__.py +0 -0
  54. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/app_handlers/__init__.py +0 -0
  55. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/dap/__init__.py +0 -0
  56. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/dap/messages.py +0 -0
  57. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/dap/protocol.py +0 -0
  58. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/server/__init__.py +0 -0
  59. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/server/app.py +0 -0
  60. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/server/rpc_types.py +0 -0
  61. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/session/__init__.py +0 -0
  62. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/session/event_bus.py +0 -0
  63. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/widgets/__init__.py +0 -0
  64. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/tdb/widgets/console_view.py +0 -0
  65. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/textual_debugger.egg-info/dependency_links.txt +0 -0
  66. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/textual_debugger.egg-info/entry_points.txt +0 -0
  67. {textual_debugger-0.1.1 → textual_debugger-0.1.2}/src/textual_debugger.egg-info/requires.txt +0 -0
  68. {textual_debugger-0.1.1 → 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.1
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:
@@ -164,14 +164,16 @@ tdb my_program.py
164
164
  # debug with arguments
165
165
  tdb my_program.py arg1 arg2
166
166
 
167
- # 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)
168
170
  tdb -k 20 -k 35 -k module.py:14 my_program.py arg1 arg2
169
171
 
170
172
  # use a specific virtualenv
171
173
  tdb --python /path/to/venv/bin/python my_program.py
172
174
 
173
175
  # step into, or stop at tracebacks in library code
174
- 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
175
177
 
176
178
  # run until first breakpoint or exit
177
179
  tdb --no-stop-on-entry my_program.py
@@ -242,7 +244,7 @@ A cursor line (blue) tracks your position; the current execution line is highlig
242
244
  | Key | Menu |
243
245
  |-----|------|
244
246
  | `Alt+F` | File (open a different script to debug) |
245
- | `Alt+C` | Configure (Color Theme, Keybindings) |
247
+ | `Alt+C` | Configure (Color Theme, Keybindings, Step Mode) |
246
248
  | `Alt+T` | Threads |
247
249
  | `Alt+P` | Processes |
248
250
  | `Alt+A` | Async Tasks |
@@ -277,13 +279,15 @@ those for gdb/pdb, with some aliases and extras thrown in for convenience.
277
279
 
278
280
  | Key | Action |
279
281
  |-----|--------|
280
- | `n` | Step over (next line) |
282
+ | `n` | Step over (next statement) |
281
283
  | `s` | Step into function call |
282
284
  | `o` / `f` / `r` | Step out of current function (also aliased as "finish" and "return") |
283
285
  | `c` | Continue execution |
284
286
  | `p` | Pause a running program |
285
287
  | `t` | Run to cursor position |
286
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) |
287
291
  | `R` | Restart the debug session |
288
292
  | `Ctrl+Q` | Quit |
289
293
 
@@ -292,6 +296,23 @@ those for gdb/pdb, with some aliases and extras thrown in for convenience.
292
296
  normally and stops at the return point. A true gdb-style immediate-return (skipping
293
297
  remaining code in the function without executing side effects) is not supported by DAP/debugpy.
294
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
+
295
316
  ### Breakpoints
296
317
 
297
318
  Click the gutter in the Code View to toggle a breakpoint, or press `b` in Debug mode.
@@ -703,8 +724,8 @@ usage: tdb [-h] [-v/--version] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
703
724
  | Flag | Description |
704
725
  |------|-------------|
705
726
  | `-r HOST:PORT` | Attach to a remote debugpy server |
706
- | `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated) |
707
- | `--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) |
708
729
  | `--cwd DIR` | Working directory for the debuggee |
709
730
  | `--python PATH` | Python interpreter for the debuggee |
710
731
  | `--no-just-my-code` | Step into stdlib/site-packages code instead of skipping it
@@ -726,12 +747,25 @@ On Windows, it uses `%APPDATA%\tdb\`.
726
747
 
727
748
  | File | Contents |
728
749
  |------|----------|
729
- | `config.json` | User preferences (keybinding scheme) |
750
+ | `config.json` | User preferences (keybinding scheme, color theme, step mode) |
730
751
  | `breakpoints.json` | Breakpoints from previous sessions, keyed by project directory |
731
752
 
732
753
  Breakpoints are saved on exit and restored when debugging a program in the same
733
754
  directory. Each project's breakpoints are independent.
734
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
+
735
769
  ## Tech Stack
736
770
 
737
771
  - [textual](https://github.com/Textualize/textual) : TUI framework
@@ -99,14 +99,16 @@ tdb my_program.py
99
99
  # debug with arguments
100
100
  tdb my_program.py arg1 arg2
101
101
 
102
- # 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)
103
105
  tdb -k 20 -k 35 -k module.py:14 my_program.py arg1 arg2
104
106
 
105
107
  # use a specific virtualenv
106
108
  tdb --python /path/to/venv/bin/python my_program.py
107
109
 
108
110
  # step into, or stop at tracebacks in library code
109
- 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
110
112
 
111
113
  # run until first breakpoint or exit
112
114
  tdb --no-stop-on-entry my_program.py
@@ -177,7 +179,7 @@ A cursor line (blue) tracks your position; the current execution line is highlig
177
179
  | Key | Menu |
178
180
  |-----|------|
179
181
  | `Alt+F` | File (open a different script to debug) |
180
- | `Alt+C` | Configure (Color Theme, Keybindings) |
182
+ | `Alt+C` | Configure (Color Theme, Keybindings, Step Mode) |
181
183
  | `Alt+T` | Threads |
182
184
  | `Alt+P` | Processes |
183
185
  | `Alt+A` | Async Tasks |
@@ -212,13 +214,15 @@ those for gdb/pdb, with some aliases and extras thrown in for convenience.
212
214
 
213
215
  | Key | Action |
214
216
  |-----|--------|
215
- | `n` | Step over (next line) |
217
+ | `n` | Step over (next statement) |
216
218
  | `s` | Step into function call |
217
219
  | `o` / `f` / `r` | Step out of current function (also aliased as "finish" and "return") |
218
220
  | `c` | Continue execution |
219
221
  | `p` | Pause a running program |
220
222
  | `t` | Run to cursor position |
221
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) |
222
226
  | `R` | Restart the debug session |
223
227
  | `Ctrl+Q` | Quit |
224
228
 
@@ -227,6 +231,23 @@ those for gdb/pdb, with some aliases and extras thrown in for convenience.
227
231
  normally and stops at the return point. A true gdb-style immediate-return (skipping
228
232
  remaining code in the function without executing side effects) is not supported by DAP/debugpy.
229
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
+
230
251
  ### Breakpoints
231
252
 
232
253
  Click the gutter in the Code View to toggle a breakpoint, or press `b` in Debug mode.
@@ -638,8 +659,8 @@ usage: tdb [-h] [-v/--version] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
638
659
  | Flag | Description |
639
660
  |------|-------------|
640
661
  | `-r HOST:PORT` | Attach to a remote debugpy server |
641
- | `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated) |
642
- | `--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) |
643
664
  | `--cwd DIR` | Working directory for the debuggee |
644
665
  | `--python PATH` | Python interpreter for the debuggee |
645
666
  | `--no-just-my-code` | Step into stdlib/site-packages code instead of skipping it
@@ -661,12 +682,25 @@ On Windows, it uses `%APPDATA%\tdb\`.
661
682
 
662
683
  | File | Contents |
663
684
  |------|----------|
664
- | `config.json` | User preferences (keybinding scheme) |
685
+ | `config.json` | User preferences (keybinding scheme, color theme, step mode) |
665
686
  | `breakpoints.json` | Breakpoints from previous sessions, keyed by project directory |
666
687
 
667
688
  Breakpoints are saved on exit and restored when debugging a program in the same
668
689
  directory. Each project's breakpoints are independent.
669
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
+
670
704
  ## Tech Stack
671
705
 
672
706
  - [textual](https://github.com/Textualize/textual) : TUI framework
@@ -99,14 +99,16 @@ tdb my_program.py
99
99
  # debug with arguments
100
100
  tdb my_program.py arg1 arg2
101
101
 
102
- # 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)
103
105
  tdb -k 20 -k 35 -k module.py:14 my_program.py arg1 arg2
104
106
 
105
107
  # use a specific virtualenv
106
108
  tdb --python /path/to/venv/bin/python my_program.py
107
109
 
108
110
  # step into, or stop at tracebacks in library code
109
- 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
110
112
 
111
113
  # run until first breakpoint or exit
112
114
  tdb --no-stop-on-entry my_program.py
@@ -177,7 +179,7 @@ A cursor line (blue) tracks your position; the current execution line is highlig
177
179
  | Key | Menu |
178
180
  |-----|------|
179
181
  | `Alt+F` | File (open a different script to debug) |
180
- | `Alt+C` | Configure (Color Theme, Keybindings) |
182
+ | `Alt+C` | Configure (Color Theme, Keybindings, Step Mode) |
181
183
  | `Alt+T` | Threads |
182
184
  | `Alt+P` | Processes |
183
185
  | `Alt+A` | Async Tasks |
@@ -212,13 +214,15 @@ those for gdb/pdb, with some aliases and extras thrown in for convenience.
212
214
 
213
215
  | Key | Action |
214
216
  |-----|--------|
215
- | `n` | Step over (next line) |
217
+ | `n` | Step over (next statement) |
216
218
  | `s` | Step into function call |
217
219
  | `o` / `f` / `r` | Step out of current function (also aliased as "finish" and "return") |
218
220
  | `c` | Continue execution |
219
221
  | `p` | Pause a running program |
220
222
  | `t` | Run to cursor position |
221
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) |
222
226
  | `R` | Restart the debug session |
223
227
  | `Ctrl+Q` | Quit |
224
228
 
@@ -227,6 +231,23 @@ those for gdb/pdb, with some aliases and extras thrown in for convenience.
227
231
  normally and stops at the return point. A true gdb-style immediate-return (skipping
228
232
  remaining code in the function without executing side effects) is not supported by DAP/debugpy.
229
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
+
230
251
  ### Breakpoints
231
252
 
232
253
  Click the gutter in the Code View to toggle a breakpoint, or press `b` in Debug mode.
@@ -638,8 +659,8 @@ usage: tdb [-h] [-v/--version] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
638
659
  | Flag | Description |
639
660
  |------|-------------|
640
661
  | `-r HOST:PORT` | Attach to a remote debugpy server |
641
- | `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated) |
642
- | `--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) |
643
664
  | `--cwd DIR` | Working directory for the debuggee |
644
665
  | `--python PATH` | Python interpreter for the debuggee |
645
666
  | `--no-just-my-code` | Step into stdlib/site-packages code instead of skipping it
@@ -661,12 +682,25 @@ On Windows, it uses `%APPDATA%\tdb\`.
661
682
 
662
683
  | File | Contents |
663
684
  |------|----------|
664
- | `config.json` | User preferences (keybinding scheme) |
685
+ | `config.json` | User preferences (keybinding scheme, color theme, step mode) |
665
686
  | `breakpoints.json` | Breakpoints from previous sessions, keyed by project directory |
666
687
 
667
688
  Breakpoints are saved on exit and restored when debugging a program in the same
668
689
  directory. Each project's breakpoints are independent.
669
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
+
670
704
  ## Tech Stack
671
705
 
672
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.1"
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."""