textual-debugger 0.2.4__tar.gz → 0.2.6__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 (115) hide show
  1. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/PKG-INFO +82 -11
  2. {textual_debugger-0.2.4/src/tdb → textual_debugger-0.2.6}/README.md +81 -10
  3. {textual_debugger-0.2.4 → textual_debugger-0.2.6/src/tdb}/README.md +81 -10
  4. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/__init__.py +1 -1
  5. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/bash/server.py +114 -2
  6. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/bash/session.py +139 -35
  7. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/bash/tdb_harness.sh +15 -5
  8. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/perl/helpers.pl +22 -0
  9. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/perl/server.py +214 -5
  10. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/perl/session.py +91 -18
  11. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/breakpoints.py +8 -2
  12. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/guardian.py +210 -40
  13. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/inspect.py +9 -3
  14. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/instrumenter.py +20 -6
  15. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/models.py +1 -0
  16. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/runtime.py +15 -5
  17. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/server.py +275 -15
  18. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/session.py +442 -87
  19. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/transport.py +107 -21
  20. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/app.py +103 -2
  21. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/cli.py +70 -0
  22. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/dap/client.py +13 -1
  23. textual_debugger-0.2.6/src/tdb/dap/reverse.py +54 -0
  24. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/languages/base.py +6 -0
  25. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/languages/bash.py +2 -1
  26. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/languages/cpp.py +12 -1
  27. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/languages/perl.py +2 -1
  28. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/languages/python.py +1 -0
  29. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/languages/tcsh.py +2 -1
  30. textual_debugger-0.2.6/src/tdb/run_mode.py +271 -0
  31. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/session/controller.py +36 -4
  32. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/session/event_bus.py +49 -0
  33. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/modals.py +44 -0
  34. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/textual_debugger.egg-info/PKG-INFO +82 -11
  35. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/textual_debugger.egg-info/SOURCES.txt +2 -0
  36. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/LICENSE +0 -0
  37. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/pyproject.toml +0 -0
  38. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/setup.cfg +0 -0
  39. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/__main__.py +0 -0
  40. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/_timeouts.py +0 -0
  41. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/__init__.py +0 -0
  42. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/bash/__init__.py +0 -0
  43. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/bash/__main__.py +0 -0
  44. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/bash/declares.py +0 -0
  45. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/perl/Devel/TdbCompile.pm +0 -0
  46. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/perl/Devel/TdbRemote.pm +0 -0
  47. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/perl/__init__.py +0 -0
  48. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/perl/__main__.py +0 -0
  49. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/perl/protocol.py +0 -0
  50. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/perl/refs.py +0 -0
  51. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/__init__.py +0 -0
  52. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/__main__.py +0 -0
  53. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/cli.py +0 -0
  54. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/protocol.py +0 -0
  55. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/adapters/tcsh/scanner.py +0 -0
  56. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/app_handlers/__init__.py +0 -0
  57. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/app_handlers/dap_events.py +0 -0
  58. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/app_handlers/inspection.py +0 -0
  59. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/app_handlers/routing.py +0 -0
  60. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/app_handlers/ui_panels.py +0 -0
  61. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/app_helpers.py +0 -0
  62. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/breakpoint_hook.py +0 -0
  63. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/dap/__init__.py +0 -0
  64. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/dap/messages.py +0 -0
  65. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/dap/protocol.py +0 -0
  66. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/dap/types.py +0 -0
  67. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/inspection.py +0 -0
  68. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/inspection_full.py +0 -0
  69. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/keybindings/__init__.py +0 -0
  70. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/languages/__init__.py +0 -0
  71. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/languages/errors.py +0 -0
  72. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/languages/registry.py +0 -0
  73. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/mcp/__init__.py +0 -0
  74. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/mcp/__main__.py +0 -0
  75. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/mcp/server.py +0 -0
  76. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/mcp/session.py +0 -0
  77. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/persist.py +0 -0
  78. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/post_mortem.py +0 -0
  79. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/processes_cache.py +0 -0
  80. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/replay.py +0 -0
  81. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/server/__init__.py +0 -0
  82. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/server/app.py +0 -0
  83. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/server/event_handler.py +0 -0
  84. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/server/handlers.py +0 -0
  85. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/server/rpc_types.py +0 -0
  86. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/server/runner.py +0 -0
  87. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/session/__init__.py +0 -0
  88. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/session/child_processes.py +0 -0
  89. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/session/inspect_service.py +0 -0
  90. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/session/messages.py +0 -0
  91. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/session/post_mortem_loader.py +0 -0
  92. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/session/recorder.py +0 -0
  93. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/session/state.py +0 -0
  94. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/session/statement_stepper.py +0 -0
  95. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/session/terminal.py +0 -0
  96. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/session/textual_handler.py +0 -0
  97. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/source_analysis.py +0 -0
  98. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/__init__.py +0 -0
  99. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/_inspection_modal.py +0 -0
  100. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/async_tasks_modal.py +0 -0
  101. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/breakpoint_view.py +0 -0
  102. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/code_view.py +0 -0
  103. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/console_view.py +0 -0
  104. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/evaluate_console.py +0 -0
  105. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/full_contents_modal.py +0 -0
  106. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/menu_bar.py +0 -0
  107. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/processes_modal.py +0 -0
  108. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/stack_view.py +0 -0
  109. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/status_bar.py +0 -0
  110. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/threads_modal.py +0 -0
  111. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/tdb/widgets/variable_view.py +0 -0
  112. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/textual_debugger.egg-info/dependency_links.txt +0 -0
  113. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/textual_debugger.egg-info/entry_points.txt +0 -0
  114. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/src/textual_debugger.egg-info/requires.txt +0 -0
  115. {textual_debugger-0.2.4 → textual_debugger-0.2.6}/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.2.4
3
+ Version: 0.2.6
4
4
  Summary: A TUI debugger for Python, Perl, and other languages. Based on textual.
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:
@@ -142,6 +142,8 @@ as open source.
142
142
  - OpenAI, for providing access to Codex through the
143
143
  [Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
144
144
 
145
+ This project was inspired by Andreas Klöckner's excellent [pudb](https://pypi.org/project/pudb/) debugger.
146
+
145
147
  ## Gallery
146
148
  <p align="center">
147
149
  <img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
@@ -303,8 +305,9 @@ process inspectors and wait graph, the evaluate console's trailing-`?` help,
303
305
  the post-mortem / `tdb.breakpoint()` hooks (those hooks live inside Python
304
306
  programs by nature). Remote attach (`-r`) also works for Perl (see
305
307
  [Perl](#perl), `Devel::TdbRemote` in place of `debugpy.listen()`), but not
306
- for C/C++, Bash, or Tcsh. `--terminal` is
307
- currently ignored for non-Python targets.
308
+ for C/C++, Bash, or Tcsh. `--terminal` works for every launch-mode
309
+ language — Python, Perl, Bash, Tcsh, and C/C++ via `--adapter lldb-dap` —
310
+ see [External Terminal Support](#external-terminal-support).
308
311
 
309
312
  **Bash limitations (v1):** the bash adapter uses bash's own `DEBUG` trap and
310
313
  has a smaller feature envelope than Python/Perl:
@@ -366,6 +369,9 @@ See [Tcsh](#tcsh) below for launch details.
366
369
  commands, so evaluate expressions with an explicit `print`, e.g.
367
370
  `print x` rather than bare `x` (bare `x` collides with GDB's
368
371
  examine-memory command). `lldb-dap` evaluates bare expressions directly.
372
+ - `--terminal` (see [External Terminal Support](#external-terminal-support))
373
+ requires `--adapter lldb-dap`; GDB's DAP mode has no terminal integration
374
+ and `tdb` refuses `--terminal` with the default `gdb` adapter.
369
375
 
370
376
  ### Perl
371
377
 
@@ -379,6 +385,9 @@ hood, so it works with any Perl already on the system.
379
385
  tdb script.pl
380
386
  ```
381
387
 
388
+ `--terminal` is supported (see
389
+ [External Terminal Support](#external-terminal-support)).
390
+
382
391
  **Compile-time code (`BEGIN` blocks):** Perl runs `BEGIN` blocks and the
383
392
  `use` statements that are themselves `BEGIN` blocks while it is still
384
393
  *compiling* your program, before stock `perl5db` ever stops. `tdb` arms the
@@ -500,7 +509,8 @@ Most notably, a breakpoint can't be set on a `func() {` line itself (the
500
509
  `DEBUG` trap never fires there), stops never land inside subshells or
501
510
  pipeline segments, child bash processes run uninstrumented, and only the
502
511
  innermost frame's locals are inspectable. There is no remote-attach mode for
503
- Bash.
512
+ Bash. `--terminal` is supported (see
513
+ [External Terminal Support](#external-terminal-support)).
504
514
 
505
515
  The Variables view shows three scopes for bash: Locals (innermost frame
506
516
  only), Globals (unexported shell variables), and Environment (exported
@@ -529,7 +539,8 @@ stack navigation across `source` nesting, variable inspection, and the
529
539
  evaluate console with the v1 caveats listed in
530
540
  [What works for non-Python languages](#what-works-for-non-python-languages),
531
541
  most notably: no conditional breakpoints, no pause of a free-running
532
- script, and stepping is per logical source line.
542
+ script, and stepping is per logical source line. `--terminal` is supported
543
+ (see [External Terminal Support](#external-terminal-support)).
533
544
 
534
545
  The Variables view shows four scopes for tcsh: Shell Variables (`set`),
535
546
  Environment (`setenv`), Aliases, and Arguments (`argv`). All of them show
@@ -1007,22 +1018,82 @@ application directory and a shared library directory) in one invocation.
1007
1018
 
1008
1019
  ### External Terminal Support
1009
1020
 
1010
- Some Python programs, notably text user interfaces, use terminal control
1011
- codes and require direct access to the terminal to function properly.
1012
- Such programs can be debugged with `tdb` by having it launch the debuggee in
1013
- a separate terminal:
1021
+ Some programs, notably text user interfaces, use terminal control codes and
1022
+ require direct access to the terminal to function properly. Such programs
1023
+ can be debugged with `tdb` by having it launch the debuggee in a separate
1024
+ terminal:
1014
1025
 
1015
1026
  ```bash
1016
1027
  tdb --terminal xterm my_tui_app.py
1017
1028
  ```
1018
1029
 
1019
- The debuggee runs in a separate window of the specified terminal. Supported choices:
1030
+ `--terminal` works for every language `tdb` can *launch* (as opposed to
1031
+ attach to): Python, Perl, Bash, Tcsh, and C/C++ via `--adapter lldb-dap`.
1032
+ The default C/C++ adapter, `gdb -i dap`, has no terminal integration --
1033
+ `tdb` rejects `--terminal` up front with an error pointing at
1034
+ `--adapter lldb-dap` instead. `--terminal` also only applies when `tdb`
1035
+ launches the program itself; it is rejected for remote-attach (`-r`), since
1036
+ there's no program for `tdb` to spawn a terminal around.
1037
+
1038
+ The debuggee runs in a separate window of the specified terminal, including
1039
+ all of its stdin/stdout/stderr -- keyboard input, program output, and
1040
+ anything the program itself draws to the terminal all happen in that
1041
+ window, not in `tdb`'s Console View. Supported choices:
1020
1042
  `xterm`, `konsole`, `gnome-terminal`, `ghostty`, `kitty`, `iterm2`, `warp`,
1021
1043
  `wezterm`, `terminator`. The selected terminal must be on `PATH`. Debugging
1022
- proceeds as usual in the terminal where `tdb` was invoked.
1044
+ (breakpoints, stepping, variable inspection, the evaluate console) proceeds
1045
+ as usual in the terminal where `tdb` was invoked.
1023
1046
 
1024
1047
  This feature only works in graphical environments where external terminals are available.
1025
1048
 
1049
+ ### Run Mode (`--run`)
1050
+
1051
+ For inspecting a program that appears hung: run it headless, at full speed, with no
1052
+ breakpoints installed and no stop-on-entry, then drop into the full TUI the moment you
1053
+ actually need it -- without paying for either up front.
1054
+
1055
+ ```bash
1056
+ tdb --run my_program.py args...
1057
+ ```
1058
+
1059
+ The debuggee's stdout/stderr stream straight to the terminal. If the program exits on
1060
+ its own, `tdb` exits with the same code:
1061
+
1062
+ ```bash
1063
+ tdb --run my_program.py; echo $? # my_program.py's own exit code
1064
+ ```
1065
+
1066
+ **Interrupting:** press Ctrl-C in the terminal (any platform), or, on Unix, send
1067
+ `SIGUSR1` to `tdb`'s pid from another terminal: `kill -USR1 <tdb pid>`. Either one pauses
1068
+ the debuggee at its currently executing line and opens the TUI with full debugging
1069
+ available -- breakpoints, stepping, variable inspection, the evaluate console. The
1070
+ debuggee itself never receives the signal (`tdb`'s adapter runs in its own process
1071
+ group), so its own `SIGINT` handling is undisturbed.
1072
+
1073
+ **Quitting an adopted session** adds a third choice to the usual quit dialog:
1074
+ - `d` (or `q`) -- detach and resume: the program keeps running headlessly; interrupt it
1075
+ again later the same way.
1076
+ - `t` -- terminate the program and quit `tdb`.
1077
+ - `Esc` -- cancel, stay in the TUI.
1078
+
1079
+ A breakpoint set during one of these episodes stays live after you detach; if it's hit,
1080
+ `tdb` reopens the TUI there, just like an interrupt.
1081
+
1082
+ If the program instead dies on an unhandled exception -- in Python this includes a
1083
+ nonzero `sys.exit()`, which `debugpy` treats as an uncaught `SystemExit` -- `tdb` opens
1084
+ the TUI at the point of failure instead of exiting silently, so you can inspect the
1085
+ crash. Exit-code passthrough applies only to a clean exit during the headless phase.
1086
+
1087
+ Supported languages: Python, Perl, Bash, Tcsh, and C/C++ (both `gdb` and `lldb-dap`).
1088
+ `--run` is rejected up front for any other language.
1089
+
1090
+ **Limitation:** pausing is cooperative. If the debuggee is blocked inside a single
1091
+ blocking external call or syscall, the pause can't land until that call returns --
1092
+ `tdb` prints a notice and opens the TUI once the stop actually arrives.
1093
+
1094
+ **Incompatible flags:** `-r`, `-k`/`-t`, `--record`, `--replay`, `--server`,
1095
+ `--headless`, `--mcp`, `--terminal`.
1096
+
1026
1097
  ### Keybinding Schemes
1027
1098
 
1028
1099
  ```bash
@@ -74,6 +74,8 @@ as open source.
74
74
  - OpenAI, for providing access to Codex through the
75
75
  [Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
76
76
 
77
+ This project was inspired by Andreas Klöckner's excellent [pudb](https://pypi.org/project/pudb/) debugger.
78
+
77
79
  ## Gallery
78
80
  <p align="center">
79
81
  <img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
@@ -235,8 +237,9 @@ process inspectors and wait graph, the evaluate console's trailing-`?` help,
235
237
  the post-mortem / `tdb.breakpoint()` hooks (those hooks live inside Python
236
238
  programs by nature). Remote attach (`-r`) also works for Perl (see
237
239
  [Perl](#perl), `Devel::TdbRemote` in place of `debugpy.listen()`), but not
238
- for C/C++, Bash, or Tcsh. `--terminal` is
239
- currently ignored for non-Python targets.
240
+ for C/C++, Bash, or Tcsh. `--terminal` works for every launch-mode
241
+ language — Python, Perl, Bash, Tcsh, and C/C++ via `--adapter lldb-dap` —
242
+ see [External Terminal Support](#external-terminal-support).
240
243
 
241
244
  **Bash limitations (v1):** the bash adapter uses bash's own `DEBUG` trap and
242
245
  has a smaller feature envelope than Python/Perl:
@@ -298,6 +301,9 @@ See [Tcsh](#tcsh) below for launch details.
298
301
  commands, so evaluate expressions with an explicit `print`, e.g.
299
302
  `print x` rather than bare `x` (bare `x` collides with GDB's
300
303
  examine-memory command). `lldb-dap` evaluates bare expressions directly.
304
+ - `--terminal` (see [External Terminal Support](#external-terminal-support))
305
+ requires `--adapter lldb-dap`; GDB's DAP mode has no terminal integration
306
+ and `tdb` refuses `--terminal` with the default `gdb` adapter.
301
307
 
302
308
  ### Perl
303
309
 
@@ -311,6 +317,9 @@ hood, so it works with any Perl already on the system.
311
317
  tdb script.pl
312
318
  ```
313
319
 
320
+ `--terminal` is supported (see
321
+ [External Terminal Support](#external-terminal-support)).
322
+
314
323
  **Compile-time code (`BEGIN` blocks):** Perl runs `BEGIN` blocks and the
315
324
  `use` statements that are themselves `BEGIN` blocks while it is still
316
325
  *compiling* your program, before stock `perl5db` ever stops. `tdb` arms the
@@ -432,7 +441,8 @@ Most notably, a breakpoint can't be set on a `func() {` line itself (the
432
441
  `DEBUG` trap never fires there), stops never land inside subshells or
433
442
  pipeline segments, child bash processes run uninstrumented, and only the
434
443
  innermost frame's locals are inspectable. There is no remote-attach mode for
435
- Bash.
444
+ Bash. `--terminal` is supported (see
445
+ [External Terminal Support](#external-terminal-support)).
436
446
 
437
447
  The Variables view shows three scopes for bash: Locals (innermost frame
438
448
  only), Globals (unexported shell variables), and Environment (exported
@@ -461,7 +471,8 @@ stack navigation across `source` nesting, variable inspection, and the
461
471
  evaluate console with the v1 caveats listed in
462
472
  [What works for non-Python languages](#what-works-for-non-python-languages),
463
473
  most notably: no conditional breakpoints, no pause of a free-running
464
- script, and stepping is per logical source line.
474
+ script, and stepping is per logical source line. `--terminal` is supported
475
+ (see [External Terminal Support](#external-terminal-support)).
465
476
 
466
477
  The Variables view shows four scopes for tcsh: Shell Variables (`set`),
467
478
  Environment (`setenv`), Aliases, and Arguments (`argv`). All of them show
@@ -939,22 +950,82 @@ application directory and a shared library directory) in one invocation.
939
950
 
940
951
  ### External Terminal Support
941
952
 
942
- Some Python programs, notably text user interfaces, use terminal control
943
- codes and require direct access to the terminal to function properly.
944
- Such programs can be debugged with `tdb` by having it launch the debuggee in
945
- a separate terminal:
953
+ Some programs, notably text user interfaces, use terminal control codes and
954
+ require direct access to the terminal to function properly. Such programs
955
+ can be debugged with `tdb` by having it launch the debuggee in a separate
956
+ terminal:
946
957
 
947
958
  ```bash
948
959
  tdb --terminal xterm my_tui_app.py
949
960
  ```
950
961
 
951
- The debuggee runs in a separate window of the specified terminal. Supported choices:
962
+ `--terminal` works for every language `tdb` can *launch* (as opposed to
963
+ attach to): Python, Perl, Bash, Tcsh, and C/C++ via `--adapter lldb-dap`.
964
+ The default C/C++ adapter, `gdb -i dap`, has no terminal integration --
965
+ `tdb` rejects `--terminal` up front with an error pointing at
966
+ `--adapter lldb-dap` instead. `--terminal` also only applies when `tdb`
967
+ launches the program itself; it is rejected for remote-attach (`-r`), since
968
+ there's no program for `tdb` to spawn a terminal around.
969
+
970
+ The debuggee runs in a separate window of the specified terminal, including
971
+ all of its stdin/stdout/stderr -- keyboard input, program output, and
972
+ anything the program itself draws to the terminal all happen in that
973
+ window, not in `tdb`'s Console View. Supported choices:
952
974
  `xterm`, `konsole`, `gnome-terminal`, `ghostty`, `kitty`, `iterm2`, `warp`,
953
975
  `wezterm`, `terminator`. The selected terminal must be on `PATH`. Debugging
954
- proceeds as usual in the terminal where `tdb` was invoked.
976
+ (breakpoints, stepping, variable inspection, the evaluate console) proceeds
977
+ as usual in the terminal where `tdb` was invoked.
955
978
 
956
979
  This feature only works in graphical environments where external terminals are available.
957
980
 
981
+ ### Run Mode (`--run`)
982
+
983
+ For inspecting a program that appears hung: run it headless, at full speed, with no
984
+ breakpoints installed and no stop-on-entry, then drop into the full TUI the moment you
985
+ actually need it -- without paying for either up front.
986
+
987
+ ```bash
988
+ tdb --run my_program.py args...
989
+ ```
990
+
991
+ The debuggee's stdout/stderr stream straight to the terminal. If the program exits on
992
+ its own, `tdb` exits with the same code:
993
+
994
+ ```bash
995
+ tdb --run my_program.py; echo $? # my_program.py's own exit code
996
+ ```
997
+
998
+ **Interrupting:** press Ctrl-C in the terminal (any platform), or, on Unix, send
999
+ `SIGUSR1` to `tdb`'s pid from another terminal: `kill -USR1 <tdb pid>`. Either one pauses
1000
+ the debuggee at its currently executing line and opens the TUI with full debugging
1001
+ available -- breakpoints, stepping, variable inspection, the evaluate console. The
1002
+ debuggee itself never receives the signal (`tdb`'s adapter runs in its own process
1003
+ group), so its own `SIGINT` handling is undisturbed.
1004
+
1005
+ **Quitting an adopted session** adds a third choice to the usual quit dialog:
1006
+ - `d` (or `q`) -- detach and resume: the program keeps running headlessly; interrupt it
1007
+ again later the same way.
1008
+ - `t` -- terminate the program and quit `tdb`.
1009
+ - `Esc` -- cancel, stay in the TUI.
1010
+
1011
+ A breakpoint set during one of these episodes stays live after you detach; if it's hit,
1012
+ `tdb` reopens the TUI there, just like an interrupt.
1013
+
1014
+ If the program instead dies on an unhandled exception -- in Python this includes a
1015
+ nonzero `sys.exit()`, which `debugpy` treats as an uncaught `SystemExit` -- `tdb` opens
1016
+ the TUI at the point of failure instead of exiting silently, so you can inspect the
1017
+ crash. Exit-code passthrough applies only to a clean exit during the headless phase.
1018
+
1019
+ Supported languages: Python, Perl, Bash, Tcsh, and C/C++ (both `gdb` and `lldb-dap`).
1020
+ `--run` is rejected up front for any other language.
1021
+
1022
+ **Limitation:** pausing is cooperative. If the debuggee is blocked inside a single
1023
+ blocking external call or syscall, the pause can't land until that call returns --
1024
+ `tdb` prints a notice and opens the TUI once the stop actually arrives.
1025
+
1026
+ **Incompatible flags:** `-r`, `-k`/`-t`, `--record`, `--replay`, `--server`,
1027
+ `--headless`, `--mcp`, `--terminal`.
1028
+
958
1029
  ### Keybinding Schemes
959
1030
 
960
1031
  ```bash
@@ -74,6 +74,8 @@ as open source.
74
74
  - OpenAI, for providing access to Codex through the
75
75
  [Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
76
76
 
77
+ This project was inspired by Andreas Klöckner's excellent [pudb](https://pypi.org/project/pudb/) debugger.
78
+
77
79
  ## Gallery
78
80
  <p align="center">
79
81
  <img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
@@ -235,8 +237,9 @@ process inspectors and wait graph, the evaluate console's trailing-`?` help,
235
237
  the post-mortem / `tdb.breakpoint()` hooks (those hooks live inside Python
236
238
  programs by nature). Remote attach (`-r`) also works for Perl (see
237
239
  [Perl](#perl), `Devel::TdbRemote` in place of `debugpy.listen()`), but not
238
- for C/C++, Bash, or Tcsh. `--terminal` is
239
- currently ignored for non-Python targets.
240
+ for C/C++, Bash, or Tcsh. `--terminal` works for every launch-mode
241
+ language — Python, Perl, Bash, Tcsh, and C/C++ via `--adapter lldb-dap` —
242
+ see [External Terminal Support](#external-terminal-support).
240
243
 
241
244
  **Bash limitations (v1):** the bash adapter uses bash's own `DEBUG` trap and
242
245
  has a smaller feature envelope than Python/Perl:
@@ -298,6 +301,9 @@ See [Tcsh](#tcsh) below for launch details.
298
301
  commands, so evaluate expressions with an explicit `print`, e.g.
299
302
  `print x` rather than bare `x` (bare `x` collides with GDB's
300
303
  examine-memory command). `lldb-dap` evaluates bare expressions directly.
304
+ - `--terminal` (see [External Terminal Support](#external-terminal-support))
305
+ requires `--adapter lldb-dap`; GDB's DAP mode has no terminal integration
306
+ and `tdb` refuses `--terminal` with the default `gdb` adapter.
301
307
 
302
308
  ### Perl
303
309
 
@@ -311,6 +317,9 @@ hood, so it works with any Perl already on the system.
311
317
  tdb script.pl
312
318
  ```
313
319
 
320
+ `--terminal` is supported (see
321
+ [External Terminal Support](#external-terminal-support)).
322
+
314
323
  **Compile-time code (`BEGIN` blocks):** Perl runs `BEGIN` blocks and the
315
324
  `use` statements that are themselves `BEGIN` blocks while it is still
316
325
  *compiling* your program, before stock `perl5db` ever stops. `tdb` arms the
@@ -432,7 +441,8 @@ Most notably, a breakpoint can't be set on a `func() {` line itself (the
432
441
  `DEBUG` trap never fires there), stops never land inside subshells or
433
442
  pipeline segments, child bash processes run uninstrumented, and only the
434
443
  innermost frame's locals are inspectable. There is no remote-attach mode for
435
- Bash.
444
+ Bash. `--terminal` is supported (see
445
+ [External Terminal Support](#external-terminal-support)).
436
446
 
437
447
  The Variables view shows three scopes for bash: Locals (innermost frame
438
448
  only), Globals (unexported shell variables), and Environment (exported
@@ -461,7 +471,8 @@ stack navigation across `source` nesting, variable inspection, and the
461
471
  evaluate console with the v1 caveats listed in
462
472
  [What works for non-Python languages](#what-works-for-non-python-languages),
463
473
  most notably: no conditional breakpoints, no pause of a free-running
464
- script, and stepping is per logical source line.
474
+ script, and stepping is per logical source line. `--terminal` is supported
475
+ (see [External Terminal Support](#external-terminal-support)).
465
476
 
466
477
  The Variables view shows four scopes for tcsh: Shell Variables (`set`),
467
478
  Environment (`setenv`), Aliases, and Arguments (`argv`). All of them show
@@ -939,22 +950,82 @@ application directory and a shared library directory) in one invocation.
939
950
 
940
951
  ### External Terminal Support
941
952
 
942
- Some Python programs, notably text user interfaces, use terminal control
943
- codes and require direct access to the terminal to function properly.
944
- Such programs can be debugged with `tdb` by having it launch the debuggee in
945
- a separate terminal:
953
+ Some programs, notably text user interfaces, use terminal control codes and
954
+ require direct access to the terminal to function properly. Such programs
955
+ can be debugged with `tdb` by having it launch the debuggee in a separate
956
+ terminal:
946
957
 
947
958
  ```bash
948
959
  tdb --terminal xterm my_tui_app.py
949
960
  ```
950
961
 
951
- The debuggee runs in a separate window of the specified terminal. Supported choices:
962
+ `--terminal` works for every language `tdb` can *launch* (as opposed to
963
+ attach to): Python, Perl, Bash, Tcsh, and C/C++ via `--adapter lldb-dap`.
964
+ The default C/C++ adapter, `gdb -i dap`, has no terminal integration --
965
+ `tdb` rejects `--terminal` up front with an error pointing at
966
+ `--adapter lldb-dap` instead. `--terminal` also only applies when `tdb`
967
+ launches the program itself; it is rejected for remote-attach (`-r`), since
968
+ there's no program for `tdb` to spawn a terminal around.
969
+
970
+ The debuggee runs in a separate window of the specified terminal, including
971
+ all of its stdin/stdout/stderr -- keyboard input, program output, and
972
+ anything the program itself draws to the terminal all happen in that
973
+ window, not in `tdb`'s Console View. Supported choices:
952
974
  `xterm`, `konsole`, `gnome-terminal`, `ghostty`, `kitty`, `iterm2`, `warp`,
953
975
  `wezterm`, `terminator`. The selected terminal must be on `PATH`. Debugging
954
- proceeds as usual in the terminal where `tdb` was invoked.
976
+ (breakpoints, stepping, variable inspection, the evaluate console) proceeds
977
+ as usual in the terminal where `tdb` was invoked.
955
978
 
956
979
  This feature only works in graphical environments where external terminals are available.
957
980
 
981
+ ### Run Mode (`--run`)
982
+
983
+ For inspecting a program that appears hung: run it headless, at full speed, with no
984
+ breakpoints installed and no stop-on-entry, then drop into the full TUI the moment you
985
+ actually need it -- without paying for either up front.
986
+
987
+ ```bash
988
+ tdb --run my_program.py args...
989
+ ```
990
+
991
+ The debuggee's stdout/stderr stream straight to the terminal. If the program exits on
992
+ its own, `tdb` exits with the same code:
993
+
994
+ ```bash
995
+ tdb --run my_program.py; echo $? # my_program.py's own exit code
996
+ ```
997
+
998
+ **Interrupting:** press Ctrl-C in the terminal (any platform), or, on Unix, send
999
+ `SIGUSR1` to `tdb`'s pid from another terminal: `kill -USR1 <tdb pid>`. Either one pauses
1000
+ the debuggee at its currently executing line and opens the TUI with full debugging
1001
+ available -- breakpoints, stepping, variable inspection, the evaluate console. The
1002
+ debuggee itself never receives the signal (`tdb`'s adapter runs in its own process
1003
+ group), so its own `SIGINT` handling is undisturbed.
1004
+
1005
+ **Quitting an adopted session** adds a third choice to the usual quit dialog:
1006
+ - `d` (or `q`) -- detach and resume: the program keeps running headlessly; interrupt it
1007
+ again later the same way.
1008
+ - `t` -- terminate the program and quit `tdb`.
1009
+ - `Esc` -- cancel, stay in the TUI.
1010
+
1011
+ A breakpoint set during one of these episodes stays live after you detach; if it's hit,
1012
+ `tdb` reopens the TUI there, just like an interrupt.
1013
+
1014
+ If the program instead dies on an unhandled exception -- in Python this includes a
1015
+ nonzero `sys.exit()`, which `debugpy` treats as an uncaught `SystemExit` -- `tdb` opens
1016
+ the TUI at the point of failure instead of exiting silently, so you can inspect the
1017
+ crash. Exit-code passthrough applies only to a clean exit during the headless phase.
1018
+
1019
+ Supported languages: Python, Perl, Bash, Tcsh, and C/C++ (both `gdb` and `lldb-dap`).
1020
+ `--run` is rejected up front for any other language.
1021
+
1022
+ **Limitation:** pausing is cooperative. If the debuggee is blocked inside a single
1023
+ blocking external call or syscall, the pause can't land until that call returns --
1024
+ `tdb` prints a notice and opens the TUI once the stop actually arrives.
1025
+
1026
+ **Incompatible flags:** `-r`, `-k`/`-t`, `--record`, `--replay`, `--server`,
1027
+ `--headless`, `--mcp`, `--terminal`.
1028
+
958
1029
  ### Keybinding Schemes
959
1030
 
960
1031
  ```bash
@@ -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.2.4" # trailing digit odd == dev version
4
+ __version__ = "0.2.6" # trailing digit odd == dev version
5
5
 
6
6
  __all__ = ["breakpoint", "exception_hook"]
@@ -15,6 +15,7 @@ from typing import Any, Awaitable, Callable
15
15
 
16
16
  from tdb.dap.messages import Event, Request, Response, parse_message
17
17
  from tdb.dap.protocol import encode_message, read_message
18
+ from tdb.dap.reverse import ReverseRequester
18
19
 
19
20
  from .declares import BashVar
20
21
  from .session import BashProtocolError, BashSession
@@ -47,6 +48,14 @@ class BashDapServer:
47
48
  self._refs: dict[int, tuple] = {}
48
49
  self._next_ref = 1
49
50
  self._stack_cache: list[dict] | None = None
51
+ self._reverse = ReverseRequester(self._write, self._next_seq)
52
+ self._client_supports_run_in_terminal = False
53
+ # Strong ref: asyncio only holds a weak reference to a bare
54
+ # ensure_future() task, so it can be garbage-collected mid-flight
55
+ # if nothing else keeps it alive. An externalTerminal launch runs
56
+ # in the background so run()'s read loop can service the
57
+ # runInTerminal reverse-request reply (see _on_launch).
58
+ self._launch_task: asyncio.Future | None = None
50
59
  self.handlers: dict[str, Callable[[Request], Awaitable[None]]] = {}
51
60
  for name in dir(self):
52
61
  if name.startswith("_on_"):
@@ -92,6 +101,9 @@ class BashDapServer:
92
101
  except (ConnectionError, asyncio.IncompleteReadError, EOFError):
93
102
  break
94
103
  msg = parse_message(raw)
104
+ if self._reverse.route(msg):
105
+ await self._writer.drain()
106
+ continue
95
107
  if not isinstance(msg, Request):
96
108
  continue
97
109
  handler = self.handlers.get(msg.command)
@@ -131,15 +143,90 @@ class BashDapServer:
131
143
 
132
144
  # ---- lifecycle ----
133
145
  async def _on_initialize(self, request: Request) -> None:
146
+ self._client_supports_run_in_terminal = bool(
147
+ request.arguments.get("supportsRunInTerminalRequest")
148
+ )
134
149
  self.send_response(request, CAPABILITIES)
135
150
 
151
+ async def _cancel_launch_task(self) -> None:
152
+ """Cancel and await an in-flight externalTerminal `_finish_launch`
153
+ background task before disconnect/terminate tear down the session.
154
+
155
+ Without this, a still-running launch continuation can assign
156
+ `self.session = BashSession(...)` (the first thing `_finish_launch`
157
+ does, synchronously, before any await) AFTER disconnect/terminate's
158
+ own `if self.session is not None` check already ran and found
159
+ nothing to stop -- leaking a live (or about-to-be-live) debuggee
160
+ with nothing left to ever call session.stop() on it. Cancelling and
161
+ awaiting first makes the eventual `self.session is not None` check
162
+ below see any session the task managed to create before it was
163
+ cancelled, so it still gets torn down.
164
+ """
165
+ task, self._launch_task = self._launch_task, None
166
+ if task is None or task.done():
167
+ return
168
+ task.cancel()
169
+ try:
170
+ await task
171
+ except asyncio.CancelledError:
172
+ pass
173
+ except Exception:
174
+ log.exception("launch task raised during cancellation")
175
+
136
176
  async def _on_launch(self, request: Request) -> None:
137
177
  args = request.arguments
138
178
  program = args.get("program", "")
139
179
  if not os.path.isfile(program):
140
180
  self.send_error(request, f"program not found: {program}")
141
181
  return
182
+ run_in_terminal = None
183
+ if args.get("console") == "externalTerminal":
184
+ if not self._client_supports_run_in_terminal:
185
+ self.send_error(
186
+ request,
187
+ "externalTerminal launch requires a client that "
188
+ "supports the runInTerminal reverse request",
189
+ )
190
+ return
191
+
192
+ async def run_in_terminal(cmd, cwd, env):
193
+ await self._reverse.request(
194
+ "runInTerminal",
195
+ {
196
+ "kind": "external",
197
+ "title": "tdb bash debuggee",
198
+ "cwd": cwd,
199
+ "args": cmd,
200
+ "env": env,
201
+ },
202
+ )
203
+
142
204
  self._stop_on_entry = bool(args.get("stopOnEntry", True))
205
+ if run_in_terminal is not None:
206
+ # session.launch() awaits self._reverse.request("runInTerminal",
207
+ # ...), whose reply can only be read/routed by THIS coroutine's
208
+ # own run() loop -- but run() is what's calling _on_launch, and
209
+ # it awaits handler(msg) synchronously before reading the next
210
+ # message. Awaiting inline here would deadlock: the reply can
211
+ # never arrive because nothing is reading stdin anymore. Run
212
+ # the rest of launch as a background task (strong ref, per the
213
+ # repo's task-GC pitfall) so _on_launch returns immediately,
214
+ # letting run() go back to reading -- including the eventual
215
+ # runInTerminal response, which self._reverse.route(msg)
216
+ # handles directly in run() without going through a handler.
217
+ self._launch_task = asyncio.ensure_future(
218
+ self._finish_launch(request, program, args, run_in_terminal)
219
+ )
220
+ return
221
+ await self._finish_launch(request, program, args, run_in_terminal)
222
+
223
+ async def _finish_launch(
224
+ self,
225
+ request: Request,
226
+ program: str,
227
+ args: dict,
228
+ run_in_terminal,
229
+ ) -> None:
143
230
  self.session = BashSession(
144
231
  self._forward_output, self._on_session_stop, self._on_session_exit
145
232
  )
@@ -150,15 +237,39 @@ class BashDapServer:
150
237
  cwd=args.get("cwd") or os.getcwd(),
151
238
  env=args.get("env"),
152
239
  bash=args.get("bash") or "bash",
240
+ run_in_terminal=run_in_terminal,
153
241
  )
154
- except BashProtocolError as e:
155
- await self.session.stop()
242
+ except Exception as e:
243
+ # Deliberately broad (mirrors run()'s own blanket `except
244
+ # Exception` net around handler dispatch): BashProtocolError
245
+ # and ReverseRequestError are the expected failures, but
246
+ # session.launch() can also raise e.g. bare
247
+ # asyncio.TimeoutError (ReverseRequester.request()'s own
248
+ # wait_for) or OSError. In the terminal-mode (background-task)
249
+ # case this coroutine is NOT awaited inline by run() -- there
250
+ # is no other except-Exception net standing behind this one --
251
+ # so anything narrower here would leave the client's launch
252
+ # request unanswered forever and silently strand the exception
253
+ # on self._launch_task.
254
+ #
255
+ # asyncio.CancelledError deliberately propagates past this
256
+ # (BaseException, not Exception, on py>=3.8): that's
257
+ # _on_disconnect/_on_terminate cancelling an in-flight launch,
258
+ # not a launch failure, and they do their own cleanup.
259
+ try:
260
+ await self.session.stop()
261
+ except Exception:
262
+ log.exception("session.stop() failed while tearing down failed launch")
156
263
  self.session = None
264
+ if not isinstance(e, BashProtocolError):
265
+ log.exception("launch failed unexpectedly")
157
266
  self.send_error(request, str(e))
267
+ await self._writer.drain()
158
268
  return
159
269
  self._launched = True
160
270
  self._start_request = request
161
271
  self.send_event("initialized")
272
+ await self._writer.drain()
162
273
  # response is sent by _on_configurationDone (DAP ordering)
163
274
 
164
275
  async def _on_configurationDone(self, request: Request) -> None:
@@ -171,6 +282,7 @@ class BashDapServer:
171
282
  self.session.resume("step" if self._stop_on_entry else "continue")
172
283
 
173
284
  async def _on_disconnect(self, request: Request) -> None:
285
+ await self._cancel_launch_task()
174
286
  if self.session is not None:
175
287
  await self.session.stop()
176
288
  self.session = None