Show HN: Python Debug Terminal for VSCode

ernstgnzlz1 pts0 comments

Python Debug Terminal - Visual Studio Marketplace

Skip to content

| Marketplace

Sign in

Visual Studio Code>Debuggers>Python Debug TerminalNew to Visual Studio Code? Get it now.

Python Debug Terminal<br>Ernesto F. González

1 install<br>| (1) | Free<br>A Python Debug Terminal: every Python process you launch attaches to the debugger automatically, the way the built-in JavaScript Debug Terminal works for Node.<br>Installation<br>Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.<br>Copy<br>Copied to clipboard

More Info

OverviewVersion HistoryQ & ARating & Review<br>Python Debug Terminal

A Python Debug Terminal for VS Code: open one, and every Python process you<br>launch from it attaches to the debugger automatically — including child<br>processes it spawns. The intention with this extension was to build a Python analogue<br>of the built-in JavaScript Debug Terminal.

┌──────────────────────┐ announces {pid, port, token} ┌────────────────────┐<br>│ Python Debug Term. │ ── sitecustomize phones home over TCP ──▶ │ Extension (rendez- │<br>│ (PYTHONPATH injected) │ │ vous TCP server) │<br>│ │ │ │<br>│ $ python app.py │ ◀── VS Code attaches (debugpy connect) ── │ startDebugging() │<br>└──────────────────────┘ └────────────────────┘

Usage

Open a Python Debug Terminal and use Python from it exactly as you normally<br>would — every process you launch attaches to the debugger on its own, and so do<br>any child processes it spawns. No launch configuration, no -m debugpy, no code<br>changes.

1. Open a Python Debug Terminal. Open the Command Palette (⇧⌘P)<br>and run Create Python Debug Terminal , or click the ⌄ dropdown next to the<br>+ in the terminal panel and pick Python Debug Terminal from the profile<br>list.

2. Set a breakpoint and run your script. Set breakpoints the usual way — by<br>clicking the editor gutter — then run your program from that terminal, e.g.<br>python app.py. The process attaches automatically and stops at your<br>breakpoints; the debug toolbar, call stack, and variables light up just as they<br>would for a normal launch.

3. Child processes come free. Anything your script spawns — e.g.<br>subprocess.run([sys.executable, ...]) — inherits the terminal's environment and<br>attaches as its own debug session, with no extra configuration. Try it with<br>example/app.py, which launches a child interpreter.

The terminal behaves like any other integrated terminal; the only difference is<br>the injected environment that makes each Python process phone home and attach.<br>See How it works for the mechanism, and Settings<br>to tune wait-for-attach behavior, the skip-list, forked-child handling, and more.

How it works

Injection. When the terminal opens, the extension prepends<br>pydebug/ to PYTHONPATH and sets PYDEBUG_IPC (the rendezvous<br>address) and PYDEBUG_TOKEN (a per-session secret). See<br>src/terminal.ts.

Bootstrap. pydebug/sitecustomize.py is<br>imported by CPython's site machinery at startup. It filters out tooling<br>noise, opens a debugpy listener on an ephemeral port, and announces<br>{pid, port, token, …} to the rendezvous server.

Attach. The extension validates the token and calls<br>vscode.debug.startDebugging with an ordinary debugpy attach-by-connect<br>config pointed at the announced port. See src/extension.ts.

Children come free. Environment variables inherit, so<br>subprocess.Popen([sys.executable, "worker.py"]) re-runs sitecustomize and<br>the worker attaches as its own session.

Why sitecustomize, not a .pth file

The obvious design — ship a .pth file whose import line runs the bootstrap —<br>does not work via PYTHONPATH. CPython only executes .pth files found in<br>site directories (site-packages), not arbitrary PYTHONPATH entries. A<br>sitecustomize module, by contrast, is imported automatically as long as it is<br>importable, which PYTHONPATH guarantees. This was verified empirically before<br>choosing the mechanism.

Because we may shadow a user's own sitecustomize, our bootstrap removes its own<br>directory from sys.path and then chains to any real sitecustomize it hid.

Why attach-by-connect, not a custom adapter

The debuggee could instead debugpy.connect() back to an extension-owned socket<br>that we adopt as the debug adapter transport (via a<br>DebugAdapterDescriptorFactory). That is the more faithful mirror of the JS<br>bootloader, but the socket-adoption factory is the one genuinely fragile piece.<br>This scaffold takes the robust path: the debuggee listens , announces its<br>port, and the extension attaches with a stock config. No custom adapter plumbing.

Known blind spots (by design)

python -S, -E, -I skip site processing / ignore PYTHONPATH → no attach.

os.fork() / multiprocessing (default fork on Linux) children never re-run<br>interpreter startup, so sitecustomize never fires. Enable<br>pythonDebugTerminal.handleForkedChildren to let debugpy patch fork/spawn<br>instead.

Anything that scrubs its environment before spawning breaks the inheritance<br>chain (same as NODE_OPTIONS in Node).

A shell rc file that unconditionally export PYTHONPATH=... can clobber our<br>prepend.

The debuggee...

python terminal debug attaches sitecustomize extension

Related Articles