vspdb 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
vspdb-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kaustubh Mhatre (^_^)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
vspdb-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.4
2
+ Name: vspdb
3
+ Version: 0.1.0
4
+ Summary: Run a Python script under the VS Code debugger from the terminal, halting on the first line.
5
+ Author-email: Kaustubh <kmhatre14@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/kmhatre14/vspdb
8
+ Project-URL: Repository, https://github.com/kmhatre14/vspdb
9
+ Project-URL: Issues, https://github.com/kmhatre14/vspdb/issues
10
+ Keywords: debugpy,vscode,debugger,pdb,debugging,cli,stopOnEntry
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Software Development :: Debuggers
23
+ Requires-Python: >=3.8
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: debugpy>=1.6
27
+ Dynamic: license-file
28
+
29
+ # vspdb — VS Code pdb for the terminal
30
+
31
+ Debug any Python script (with arguments) in the VS Code debugger straight from
32
+ the terminal — no hand-written `launch.json`, no `debug.py` wrapper.
33
+
34
+ ```bash
35
+ python -m vspdb my_script.py arg1 arg2
36
+ ```
37
+
38
+ vspdb starts a `debugpy` debug server, gets VS Code to attach, and **halts on the
39
+ first line of your script** — just like `stopOnEntry`. It ships a small VS Code
40
+ helper (installed automatically) so this works with **zero clicks**.
41
+
42
+ ---
43
+
44
+ ## Install — one package, that's it
45
+
46
+ ```bash
47
+ pip install vspdb # (or: pip install -e . from this folder)
48
+ ```
49
+
50
+ That's the only thing you install. vspdb ships a tiny VS Code helper extension
51
+ **inside** the package and installs it into VS Code for you on first use. So:
52
+
53
+ ```bash
54
+ python -m vspdb my_script.py 10 # explicit (or: vspdb my_script.py 10)
55
+ ```
56
+
57
+ The **first** run prints a one-time note that it installed the helper — **reload
58
+ the VS Code window once** (`Ctrl+Shift+P` → "Developer: Reload Window"). After
59
+ that it's fully automatic.
60
+
61
+ > Tip: install into the same interpreter VS Code uses for the workspace
62
+ > (`Ctrl+Shift+P` → "Python: Select Interpreter") so breakpoints line up. You can
63
+ > also pre-install the helper without running a script: `python -m vspdb --setup`.
64
+
65
+ ---
66
+
67
+ ## How you use it: zero clicks
68
+
69
+ Once the helper is active (after that one reload), just run scripts — each one
70
+ attaches and halts on its first line, **no debug panel, no F5**:
71
+
72
+ ```bash
73
+ python -m vspdb my_script.py 10
74
+ python -m vspdb another.py --flag
75
+ ```
76
+
77
+ The helper keeps a `debugpy` listener ready in VS Code (one per window, on a
78
+ per-project port) and vspdb connects to the right window automatically.
79
+
80
+ ### Don't want the helper? (no-extension fallback)
81
+
82
+ Run with `--no-ext` (or set `VSPDB_NO_AUTO_EXTENSION=1`). Then vspdb won't touch
83
+ VS Code; instead it writes two configs to `.vscode/launch.json` and you attach
84
+ manually:
85
+
86
+ - **vspdb: Listen** — start it once per window (F5), then runs are zero-click.
87
+ - **vspdb: Attach** — vspdb waits; press F5 once per run.
88
+
89
+ ---
90
+
91
+ ## Options
92
+
93
+ ```
94
+ python -m vspdb [options] <script.py> [script args...]
95
+ ```
96
+
97
+ Everything **after** the script name is passed to your script untouched (so your
98
+ script can use `--port`, `--host`, etc. without clashing with vspdb).
99
+
100
+ | Option | Description |
101
+ | --- | --- |
102
+ | `--port, -p PORT` | Debug port (default: `5678`). |
103
+ | `--host HOST` | Debug host/interface (default: `127.0.0.1`). |
104
+ | `--listen` | Force listen mode (open the port, wait for VS Code). |
105
+ | `--connect` | Force connect mode (attach to a VS Code that is already listening). |
106
+ | `--no-wait` | Don't wait for VS Code; run immediately (disables stop-on-entry). |
107
+ | `--no-stop` | Attach, but don't halt on the first line. |
108
+ | `--no-launch-json` | Don't create/update `.vscode/launch.json`. |
109
+ | `--setup` | (Re)install the bundled VS Code helper extension and exit. |
110
+ | `--no-ext` | Don't auto-install the VS Code helper on this run. |
111
+
112
+ The `--port` default is read from `.vscode/vspdb.json` (written by the helper) if
113
+ present, else `5678`. Disable auto-install globally with `VSPDB_NO_AUTO_EXTENSION=1`.
114
+
115
+ Examples:
116
+
117
+ ```bash
118
+ # Different port
119
+ python -m vspdb --port 5690 my_script.py 10
120
+
121
+ # Your script gets its own --port; vspdb does not consume it
122
+ python -m vspdb server.py --port 8080 --debug
123
+
124
+ # Run under the debugger but don't stop on entry (rely on your own breakpoints)
125
+ python -m vspdb --no-stop my_script.py 10
126
+ ```
127
+
128
+ ---
129
+
130
+ ## How it works
131
+
132
+ 1. Parses `vspdb` options, then treats the first positional as the script and the
133
+ rest as the script's `argv`.
134
+ 2. Installs the bundled VS Code helper into VS Code if it isn't there yet (unless
135
+ `--no-ext`), and ensures `.vscode/launch.json` has the manual-fallback attach
136
+ configs (merging, never clobbering, any existing file).
137
+ 3. Resolves the port: if `--port` isn't given, it walks up from the script's
138
+ directory for `.vscode/vspdb.json` (written by the auto-listen extension) so it
139
+ attaches to the window that owns the script's project — important when several
140
+ VS Code windows are open. Then it **connects** if that port is being listened
141
+ on, otherwise **listens** and waits for VS Code.
142
+ 4. Once the client is attached, it runs your script as `__main__` with the right
143
+ `sys.argv`, having injected a `debugpy.breakpoint()` at the first executable
144
+ line so the debugger halts there. Original line numbers are preserved, so
145
+ tracebacks stay accurate.
146
+
147
+ ---
148
+
149
+ ## Requirements
150
+
151
+ - Python 3.8+
152
+ - `debugpy` (installed automatically)
153
+ - VS Code with the Python / Python Debugger extension
154
+
155
+ ## Author
156
+
157
+ Kaustubh — [kmhatre14@gmail.com](mailto:kmhatre14@gmail.com) · [github.com/kmhatre14](https://github.com/kmhatre14)
158
+
159
+ ## License
160
+
161
+ [MIT](LICENSE). Built with assistance from Claude Opus 4.8.
vspdb-0.1.0/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # vspdb — VS Code pdb for the terminal
2
+
3
+ Debug any Python script (with arguments) in the VS Code debugger straight from
4
+ the terminal — no hand-written `launch.json`, no `debug.py` wrapper.
5
+
6
+ ```bash
7
+ python -m vspdb my_script.py arg1 arg2
8
+ ```
9
+
10
+ vspdb starts a `debugpy` debug server, gets VS Code to attach, and **halts on the
11
+ first line of your script** — just like `stopOnEntry`. It ships a small VS Code
12
+ helper (installed automatically) so this works with **zero clicks**.
13
+
14
+ ---
15
+
16
+ ## Install — one package, that's it
17
+
18
+ ```bash
19
+ pip install vspdb # (or: pip install -e . from this folder)
20
+ ```
21
+
22
+ That's the only thing you install. vspdb ships a tiny VS Code helper extension
23
+ **inside** the package and installs it into VS Code for you on first use. So:
24
+
25
+ ```bash
26
+ python -m vspdb my_script.py 10 # explicit (or: vspdb my_script.py 10)
27
+ ```
28
+
29
+ The **first** run prints a one-time note that it installed the helper — **reload
30
+ the VS Code window once** (`Ctrl+Shift+P` → "Developer: Reload Window"). After
31
+ that it's fully automatic.
32
+
33
+ > Tip: install into the same interpreter VS Code uses for the workspace
34
+ > (`Ctrl+Shift+P` → "Python: Select Interpreter") so breakpoints line up. You can
35
+ > also pre-install the helper without running a script: `python -m vspdb --setup`.
36
+
37
+ ---
38
+
39
+ ## How you use it: zero clicks
40
+
41
+ Once the helper is active (after that one reload), just run scripts — each one
42
+ attaches and halts on its first line, **no debug panel, no F5**:
43
+
44
+ ```bash
45
+ python -m vspdb my_script.py 10
46
+ python -m vspdb another.py --flag
47
+ ```
48
+
49
+ The helper keeps a `debugpy` listener ready in VS Code (one per window, on a
50
+ per-project port) and vspdb connects to the right window automatically.
51
+
52
+ ### Don't want the helper? (no-extension fallback)
53
+
54
+ Run with `--no-ext` (or set `VSPDB_NO_AUTO_EXTENSION=1`). Then vspdb won't touch
55
+ VS Code; instead it writes two configs to `.vscode/launch.json` and you attach
56
+ manually:
57
+
58
+ - **vspdb: Listen** — start it once per window (F5), then runs are zero-click.
59
+ - **vspdb: Attach** — vspdb waits; press F5 once per run.
60
+
61
+ ---
62
+
63
+ ## Options
64
+
65
+ ```
66
+ python -m vspdb [options] <script.py> [script args...]
67
+ ```
68
+
69
+ Everything **after** the script name is passed to your script untouched (so your
70
+ script can use `--port`, `--host`, etc. without clashing with vspdb).
71
+
72
+ | Option | Description |
73
+ | --- | --- |
74
+ | `--port, -p PORT` | Debug port (default: `5678`). |
75
+ | `--host HOST` | Debug host/interface (default: `127.0.0.1`). |
76
+ | `--listen` | Force listen mode (open the port, wait for VS Code). |
77
+ | `--connect` | Force connect mode (attach to a VS Code that is already listening). |
78
+ | `--no-wait` | Don't wait for VS Code; run immediately (disables stop-on-entry). |
79
+ | `--no-stop` | Attach, but don't halt on the first line. |
80
+ | `--no-launch-json` | Don't create/update `.vscode/launch.json`. |
81
+ | `--setup` | (Re)install the bundled VS Code helper extension and exit. |
82
+ | `--no-ext` | Don't auto-install the VS Code helper on this run. |
83
+
84
+ The `--port` default is read from `.vscode/vspdb.json` (written by the helper) if
85
+ present, else `5678`. Disable auto-install globally with `VSPDB_NO_AUTO_EXTENSION=1`.
86
+
87
+ Examples:
88
+
89
+ ```bash
90
+ # Different port
91
+ python -m vspdb --port 5690 my_script.py 10
92
+
93
+ # Your script gets its own --port; vspdb does not consume it
94
+ python -m vspdb server.py --port 8080 --debug
95
+
96
+ # Run under the debugger but don't stop on entry (rely on your own breakpoints)
97
+ python -m vspdb --no-stop my_script.py 10
98
+ ```
99
+
100
+ ---
101
+
102
+ ## How it works
103
+
104
+ 1. Parses `vspdb` options, then treats the first positional as the script and the
105
+ rest as the script's `argv`.
106
+ 2. Installs the bundled VS Code helper into VS Code if it isn't there yet (unless
107
+ `--no-ext`), and ensures `.vscode/launch.json` has the manual-fallback attach
108
+ configs (merging, never clobbering, any existing file).
109
+ 3. Resolves the port: if `--port` isn't given, it walks up from the script's
110
+ directory for `.vscode/vspdb.json` (written by the auto-listen extension) so it
111
+ attaches to the window that owns the script's project — important when several
112
+ VS Code windows are open. Then it **connects** if that port is being listened
113
+ on, otherwise **listens** and waits for VS Code.
114
+ 4. Once the client is attached, it runs your script as `__main__` with the right
115
+ `sys.argv`, having injected a `debugpy.breakpoint()` at the first executable
116
+ line so the debugger halts there. Original line numbers are preserved, so
117
+ tracebacks stay accurate.
118
+
119
+ ---
120
+
121
+ ## Requirements
122
+
123
+ - Python 3.8+
124
+ - `debugpy` (installed automatically)
125
+ - VS Code with the Python / Python Debugger extension
126
+
127
+ ## Author
128
+
129
+ Kaustubh — [kmhatre14@gmail.com](mailto:kmhatre14@gmail.com) · [github.com/kmhatre14](https://github.com/kmhatre14)
130
+
131
+ ## License
132
+
133
+ [MIT](LICENSE). Built with assistance from Claude Opus 4.8.
@@ -0,0 +1,44 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vspdb"
7
+ version = "0.1.0"
8
+ description = "Run a Python script under the VS Code debugger from the terminal, halting on the first line."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Kaustubh", email = "kmhatre14@gmail.com" }]
13
+ keywords = ["debugpy", "vscode", "debugger", "pdb", "debugging", "cli", "stopOnEntry"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.8",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Topic :: Software Development :: Debuggers",
27
+ ]
28
+ dependencies = ["debugpy>=1.6"]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/kmhatre14/vspdb"
32
+ Repository = "https://github.com/kmhatre14/vspdb"
33
+ Issues = "https://github.com/kmhatre14/vspdb/issues"
34
+
35
+ [project.scripts]
36
+ vspdb = "vspdb.cli:main"
37
+
38
+ [tool.setuptools]
39
+ packages = ["vspdb"]
40
+
41
+ # Ship the bundled VS Code helper extension inside the wheel so `python -m vspdb`
42
+ # can install it into VS Code automatically.
43
+ [tool.setuptools.package-data]
44
+ vspdb = ["vscode_extension/*"]
vspdb-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,11 @@
1
+ """vspdb — run a Python script under the VS Code debugger straight from the terminal.
2
+
3
+ python -m vspdb my_script.py arg1 arg2
4
+
5
+ See :mod:`vspdb.cli` for the implementation.
6
+ """
7
+
8
+ from vspdb.cli import main
9
+
10
+ __all__ = ["main"]
11
+ __version__ = "0.1.0"
@@ -0,0 +1,8 @@
1
+ """Enable ``python -m vspdb``."""
2
+
3
+ import sys
4
+
5
+ from vspdb.cli import main
6
+
7
+ if __name__ == "__main__":
8
+ sys.exit(main())
@@ -0,0 +1,469 @@
1
+ """vspdb — run a Python script under the VS Code debugger from the terminal.
2
+
3
+ The goal: instead of hand-editing ``.vscode/launch.json`` every time you want to
4
+ debug a script that takes arguments, you just run::
5
+
6
+ python -m vspdb my_script.py arg1 arg2
7
+
8
+ vspdb starts a ``debugpy`` debug server, makes VS Code attach to it, and halts on
9
+ the first line of *your* script — exactly as if you had set ``stopOnEntry``.
10
+
11
+ It supports two flows (and picks the right one automatically):
12
+
13
+ * **Listen flow (zero clicks per run).** Start the auto-generated
14
+ "vspdb: Listen" configuration in VS Code once. VS Code then listens on the
15
+ port; every ``python -m vspdb ...`` connects straight to it.
16
+ * **Attach flow (one F5 per run).** If nothing is listening, vspdb opens the
17
+ port itself and waits. Press F5 and pick "vspdb: Attach" to attach.
18
+
19
+ Either way the ``.vscode/launch.json`` entries are created for you on first run.
20
+ """
21
+
22
+ import argparse
23
+ import ast
24
+ import json
25
+ import os
26
+ import re
27
+ import shutil
28
+ import socket
29
+ import sys
30
+ from pathlib import Path
31
+
32
+ DEFAULT_HOST = "127.0.0.1"
33
+ DEFAULT_PORT = 5678
34
+
35
+ ATTACH_CONFIG_NAME = "vspdb: Attach (connect to running script)"
36
+ LISTEN_CONFIG_NAME = "vspdb: Listen (zero-click, start me first)"
37
+
38
+
39
+ # --------------------------------------------------------------------------- #
40
+ # Argument handling
41
+ # --------------------------------------------------------------------------- #
42
+ def _split_argv(argv):
43
+ """Split our own options from the target script and its arguments.
44
+
45
+ Everything up to the first positional token is treated as a vspdb option;
46
+ that first positional is the script, and everything after it belongs to the
47
+ script (so the script can freely use ``--port`` etc. without clashing).
48
+ """
49
+ value_flags = {"--port", "-p", "--host"}
50
+ opts = []
51
+ i = 0
52
+ while i < len(argv):
53
+ tok = argv[i]
54
+ if not tok.startswith("-"):
55
+ return opts, tok, argv[i + 1:]
56
+ opts.append(tok)
57
+ # consume the value of "--port 5678" style flags (but not "--port=5678")
58
+ if tok in value_flags and "=" not in tok:
59
+ if i + 1 < len(argv):
60
+ opts.append(argv[i + 1])
61
+ i += 2
62
+ continue
63
+ i += 1
64
+ return opts, None, []
65
+
66
+
67
+ def _build_parser():
68
+ parser = argparse.ArgumentParser(
69
+ prog="python -m vspdb",
70
+ description="Run a Python script under the VS Code debugger, halting on the first line.",
71
+ usage="python -m vspdb [options] <script.py> [script args...]",
72
+ )
73
+ parser.add_argument("--port", "-p", type=int, default=None,
74
+ help="Debug port. Default: read from .vscode/vspdb.json if the "
75
+ f"auto-listen extension wrote one, else {DEFAULT_PORT}.")
76
+ parser.add_argument("--host", default=None,
77
+ help=f"Debug host/interface (default: {DEFAULT_HOST}).")
78
+ mode = parser.add_mutually_exclusive_group()
79
+ mode.add_argument("--listen", action="store_true",
80
+ help="Force listen mode: open the port and wait for VS Code to attach.")
81
+ mode.add_argument("--connect", action="store_true",
82
+ help="Force connect mode: attach to a VS Code that is already listening.")
83
+ parser.add_argument("--no-wait", action="store_true",
84
+ help="Don't block waiting for VS Code; run immediately (no stop-on-entry).")
85
+ parser.add_argument("--no-stop", action="store_true",
86
+ help="Attach but do not halt on the first line.")
87
+ parser.add_argument("--no-launch-json", action="store_true",
88
+ help="Do not create/update .vscode/launch.json.")
89
+ parser.add_argument("--setup", action="store_true",
90
+ help="(Re)install the bundled VS Code helper extension and exit.")
91
+ parser.add_argument("--no-ext", action="store_true",
92
+ help="Don't auto-install the VS Code helper extension on this run.")
93
+ return parser
94
+
95
+
96
+ # --------------------------------------------------------------------------- #
97
+ # .vscode/launch.json management
98
+ # --------------------------------------------------------------------------- #
99
+ def _vspdb_configs(host, port):
100
+ """The two attach configurations vspdb keeps in launch.json."""
101
+ return [
102
+ {
103
+ "name": ATTACH_CONFIG_NAME,
104
+ "type": "debugpy",
105
+ "request": "attach",
106
+ "connect": {"host": host, "port": port},
107
+ "justMyCode": False,
108
+ },
109
+ {
110
+ "name": LISTEN_CONFIG_NAME,
111
+ "type": "debugpy",
112
+ "request": "attach",
113
+ "listen": {"host": host, "port": port},
114
+ "justMyCode": False,
115
+ },
116
+ ]
117
+
118
+
119
+ def _strip_jsonc(text):
120
+ """Best-effort strip of // and /* */ comments and trailing commas (JSONC)."""
121
+ out = []
122
+ i, n = 0, len(text)
123
+ in_str = False
124
+ quote = ""
125
+ while i < n:
126
+ c = text[i]
127
+ if in_str:
128
+ out.append(c)
129
+ if c == "\\" and i + 1 < n:
130
+ out.append(text[i + 1])
131
+ i += 2
132
+ continue
133
+ if c == quote:
134
+ in_str = False
135
+ i += 1
136
+ continue
137
+ if c in ('"', "'"):
138
+ in_str = True
139
+ quote = c
140
+ out.append(c)
141
+ i += 1
142
+ continue
143
+ if c == "/" and i + 1 < n and text[i + 1] == "/":
144
+ while i < n and text[i] != "\n":
145
+ i += 1
146
+ continue
147
+ if c == "/" and i + 1 < n and text[i + 1] == "*":
148
+ i += 2
149
+ while i + 1 < n and not (text[i] == "*" and text[i + 1] == "/"):
150
+ i += 1
151
+ i += 2
152
+ continue
153
+ out.append(c)
154
+ i += 1
155
+ result = "".join(out)
156
+ return re.sub(r",(\s*[}\]])", r"\1", result)
157
+
158
+
159
+ def _load_launch_json(path):
160
+ raw = path.read_text()
161
+ try:
162
+ return json.loads(raw)
163
+ except json.JSONDecodeError:
164
+ return json.loads(_strip_jsonc(raw))
165
+
166
+
167
+ def ensure_launch_json(root, host, port):
168
+ """Create/merge vspdb's attach configs into ``<root>/.vscode/launch.json``.
169
+
170
+ Existing user configurations are preserved; we only add ours if a config of
171
+ the same name isn't present. If the file exists but can't be parsed, we leave
172
+ it untouched and print the configs so the user can add them manually.
173
+ """
174
+ vscode_dir = root / ".vscode"
175
+ path = vscode_dir / "launch.json"
176
+ data = {"version": "0.2.0", "configurations": []}
177
+
178
+ if path.exists():
179
+ try:
180
+ parsed = _load_launch_json(path)
181
+ except Exception:
182
+ print(f"vspdb: could not parse {path}; not modifying it. "
183
+ "Add these configurations manually if needed:", file=sys.stderr)
184
+ print(json.dumps(_vspdb_configs(host, port), indent=4), file=sys.stderr)
185
+ return
186
+ if isinstance(parsed, dict):
187
+ data = parsed
188
+ data.setdefault("version", "0.2.0")
189
+ data.setdefault("configurations", [])
190
+
191
+ existing_names = {c.get("name") for c in data["configurations"] if isinstance(c, dict)}
192
+ added = False
193
+ for cfg in _vspdb_configs(host, port):
194
+ if cfg["name"] not in existing_names:
195
+ data["configurations"].append(cfg)
196
+ added = True
197
+
198
+ if added:
199
+ vscode_dir.mkdir(exist_ok=True)
200
+ path.write_text(json.dumps(data, indent=4) + "\n")
201
+ print(f"vspdb: wrote attach configs to {path}", file=sys.stderr)
202
+
203
+
204
+ # --------------------------------------------------------------------------- #
205
+ # Connection handling
206
+ # --------------------------------------------------------------------------- #
207
+ def _extension_source():
208
+ """Path to the bundled VS Code extension shipped inside the package."""
209
+ return Path(__file__).resolve().parent / "vscode_extension"
210
+
211
+
212
+ def _vscode_extension_bases():
213
+ """Existing VS Code extensions directories (local + Remote/SSH server)."""
214
+ home = Path.home()
215
+ bases = []
216
+ for rel in (".vscode/extensions", ".vscode-server/extensions", ".vscode-remote/extensions"):
217
+ p = home / rel
218
+ if p.is_dir():
219
+ bases.append(p)
220
+ return bases
221
+
222
+
223
+ def ensure_extension_installed(force=False):
224
+ """Install the bundled auto-listen extension into VS Code if not already there.
225
+
226
+ Returns (newly_installed, folder_name). Best-effort: never raises. Skips
227
+ cleanly when no VS Code extensions directory exists (e.g. VS Code not used).
228
+ """
229
+ src = _extension_source()
230
+ try:
231
+ pkg = json.loads((src / "package.json").read_text())
232
+ except Exception:
233
+ return False, None
234
+
235
+ folder = "{}.{}-{}".format(
236
+ pkg.get("publisher", "vspdb"), pkg.get("name", "vspdb-autolisten"),
237
+ pkg.get("version", "0.0.0"))
238
+ files = ["package.json", "extension.js", "README.md"]
239
+ newly = False
240
+
241
+ bases = _vscode_extension_bases()
242
+ for base in bases:
243
+ dest = base / folder
244
+ # Remove stale/older copies of this extension so VS Code doesn't load two
245
+ # listeners (e.g. an earlier manual install under a different publisher).
246
+ for old in base.glob("*vspdb-autolisten*"):
247
+ if old.resolve() != dest.resolve():
248
+ shutil.rmtree(old, ignore_errors=True)
249
+ if force or not dest.exists():
250
+ try:
251
+ shutil.rmtree(dest, ignore_errors=True)
252
+ dest.mkdir(parents=True, exist_ok=True)
253
+ for name in files:
254
+ s = src / name
255
+ if s.exists():
256
+ shutil.copy2(s, dest / name)
257
+ newly = True
258
+ except Exception:
259
+ pass
260
+
261
+ return newly, (folder if bases else None)
262
+
263
+
264
+ def discover_endpoint(start_dir, explicit_host, explicit_port):
265
+ """Resolve (host, port, source) for the debug connection.
266
+
267
+ An explicit ``--port`` always wins. Otherwise we walk up from ``start_dir``
268
+ (the script's directory) looking for ``.vscode/vspdb.json`` — written by the
269
+ auto-listen VS Code extension — so the run attaches to the window that owns
270
+ the script's project, not some other open window. Falls back to the defaults.
271
+ """
272
+ host, port, source = explicit_host, explicit_port, "default"
273
+
274
+ if explicit_port is not None:
275
+ source = "--port"
276
+ else:
277
+ d = start_dir
278
+ while True:
279
+ candidate = d / ".vscode" / "vspdb.json"
280
+ if candidate.is_file():
281
+ try:
282
+ data = json.loads(candidate.read_text())
283
+ except Exception:
284
+ data = {}
285
+ if data.get("port"):
286
+ port = int(data["port"])
287
+ if host is None:
288
+ host = data.get("host")
289
+ source = str(candidate)
290
+ break
291
+ if d.parent == d:
292
+ break
293
+ d = d.parent
294
+
295
+ return host or DEFAULT_HOST, port if port is not None else DEFAULT_PORT, source
296
+
297
+
298
+ def _is_listening(host, port):
299
+ """True if something is already accepting connections on host:port."""
300
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
301
+ sock.settimeout(0.3)
302
+ return sock.connect_ex((host, port)) == 0
303
+
304
+
305
+ def _decide_mode(ns, host, port):
306
+ if ns.listen:
307
+ return "listen"
308
+ if ns.connect:
309
+ return "connect"
310
+ # Auto: if VS Code is already listening (Listen config running), connect to it.
311
+ return "connect" if _is_listening(host, port) else "listen"
312
+
313
+
314
+ # --------------------------------------------------------------------------- #
315
+ # Running the target with stop-on-entry
316
+ # --------------------------------------------------------------------------- #
317
+ def _set_location(node, lineno, col):
318
+ for child in ast.walk(node):
319
+ child.lineno = lineno
320
+ child.col_offset = col
321
+ if hasattr(child, "end_lineno"):
322
+ child.end_lineno = lineno
323
+ if hasattr(child, "end_col_offset"):
324
+ child.end_col_offset = col + 1
325
+
326
+
327
+ def _compile_with_entry_breakpoint(source, filename):
328
+ """Compile ``source`` with a ``debugpy.breakpoint()`` injected at the first
329
+ real statement, so the debugger halts on the first line of the user's code.
330
+
331
+ The injected call carries the line number of the first executable statement,
332
+ so VS Code shows the stop on that line. Original line numbers are preserved,
333
+ so tracebacks remain accurate.
334
+ """
335
+ tree = ast.parse(source, filename)
336
+ body = tree.body
337
+ insert_at = 0
338
+
339
+ # Skip a leading module docstring.
340
+ if (body and isinstance(body[0], ast.Expr)
341
+ and isinstance(getattr(body[0], "value", None), ast.Constant)
342
+ and isinstance(body[0].value.value, str)):
343
+ insert_at = 1
344
+ # Skip __future__ imports (which must remain first).
345
+ while (insert_at < len(body) and isinstance(body[insert_at], ast.ImportFrom)
346
+ and body[insert_at].module == "__future__"):
347
+ insert_at += 1
348
+
349
+ if insert_at >= len(body):
350
+ return compile(tree, filename, "exec") # nothing to stop on
351
+
352
+ anchor = body[insert_at]
353
+ injected = ast.parse("__import__('debugpy').breakpoint()").body
354
+ for stmt in injected:
355
+ _set_location(stmt, anchor.lineno, anchor.col_offset)
356
+ body[insert_at:insert_at] = injected
357
+ ast.fix_missing_locations(tree)
358
+ return compile(tree, filename, "exec")
359
+
360
+
361
+ def run_script(script_path, script_args, stop_on_entry):
362
+ """Execute ``script_path`` as ``__main__`` with ``script_args`` as argv."""
363
+ source = script_path.read_text()
364
+ if stop_on_entry:
365
+ try:
366
+ code = _compile_with_entry_breakpoint(source, str(script_path))
367
+ except SyntaxError:
368
+ code = compile(source, str(script_path), "exec") # surface real error on exec
369
+ else:
370
+ code = compile(source, str(script_path), "exec")
371
+
372
+ sys.argv = [str(script_path)] + list(script_args)
373
+ sys.path.insert(0, str(script_path.parent))
374
+
375
+ namespace = {
376
+ "__name__": "__main__",
377
+ "__file__": str(script_path),
378
+ "__package__": None,
379
+ "__doc__": None,
380
+ "__loader__": None,
381
+ "__spec__": None,
382
+ }
383
+ exec(code, namespace)
384
+
385
+
386
+ # --------------------------------------------------------------------------- #
387
+ # Entry point
388
+ # --------------------------------------------------------------------------- #
389
+ def main(argv=None):
390
+ argv = list(sys.argv[1:] if argv is None else argv)
391
+ opts, script, script_args = _split_argv(argv)
392
+
393
+ parser = _build_parser()
394
+ ns = parser.parse_args(opts)
395
+
396
+ # `--setup`: just (re)install the VS Code helper and exit. No script needed.
397
+ if ns.setup:
398
+ _, folder = ensure_extension_installed(force=True)
399
+ if folder:
400
+ print(f"vspdb: installed VS Code helper '{folder}'.", file=sys.stderr)
401
+ print("vspdb: reload VS Code once (Ctrl+Shift+P -> 'Developer: Reload "
402
+ "Window') to activate it.", file=sys.stderr)
403
+ else:
404
+ print("vspdb: no VS Code extensions directory found; nothing to install.",
405
+ file=sys.stderr)
406
+ return 0
407
+
408
+ if script is None:
409
+ parser.print_help()
410
+ return 2
411
+
412
+ try:
413
+ import debugpy
414
+ except ImportError:
415
+ print("vspdb requires 'debugpy'. Install it with:\n pip install debugpy",
416
+ file=sys.stderr)
417
+ return 1
418
+
419
+ script_path = Path(script).resolve()
420
+ if not script_path.exists():
421
+ print(f"vspdb: script not found: {script}", file=sys.stderr)
422
+ return 1
423
+
424
+ # Auto-install the bundled VS Code helper so a single `pip install vspdb` is
425
+ # all the user needs. Opt out with --no-ext or VSPDB_NO_AUTO_EXTENSION=1.
426
+ if not ns.no_ext and not os.environ.get("VSPDB_NO_AUTO_EXTENSION"):
427
+ newly, folder = ensure_extension_installed()
428
+ if newly and folder:
429
+ print(f"vspdb: installed its VS Code helper '{folder}'. Reload VS Code once "
430
+ "(Ctrl+Shift+P -> 'Developer: Reload Window') to enable zero-click "
431
+ "attach.", file=sys.stderr)
432
+
433
+ host, port, source = discover_endpoint(script_path.parent, ns.host, ns.port)
434
+ if source not in ("default", "--port"):
435
+ print(f"vspdb: using {host}:{port} from {source}", file=sys.stderr)
436
+
437
+ if not ns.no_launch_json:
438
+ ensure_launch_json(Path.cwd(), host, port)
439
+
440
+ mode = _decide_mode(ns, host, port)
441
+ client_attached = False
442
+
443
+ if mode == "connect":
444
+ print(f"vspdb: connecting to VS Code listener at {host}:{port} ...", file=sys.stderr)
445
+ try:
446
+ debugpy.connect((host, port))
447
+ client_attached = True
448
+ print("vspdb: attached to VS Code.", file=sys.stderr)
449
+ except Exception as exc: # noqa: BLE001 - report and fall back to listen
450
+ print(f"vspdb: could not connect ({exc}); falling back to listen mode.",
451
+ file=sys.stderr)
452
+ mode = "listen"
453
+
454
+ if mode == "listen":
455
+ debugpy.listen((host, port))
456
+ print(f"vspdb: debug server listening on {host}:{port}", file=sys.stderr)
457
+ if ns.no_wait:
458
+ print("vspdb: --no-wait set; running without waiting for VS Code.", file=sys.stderr)
459
+ else:
460
+ print(f"vspdb: in VS Code, press F5 and choose '{ATTACH_CONFIG_NAME}'.",
461
+ file=sys.stderr)
462
+ print("vspdb: waiting for the debugger to attach ...", file=sys.stderr)
463
+ debugpy.wait_for_client()
464
+ client_attached = True
465
+ print("vspdb: attached. Running script.", file=sys.stderr)
466
+
467
+ stop_on_entry = client_attached and not ns.no_stop
468
+ run_script(script_path, script_args, stop_on_entry)
469
+ return 0
@@ -0,0 +1,32 @@
1
+ # vspdb auto-listen (VS Code extension)
2
+
3
+ The in-editor helper for [vspdb](https://pypi.org/project/vspdb/). It keeps a
4
+ `debugpy` listener running so:
5
+
6
+ ```bash
7
+ python -m vspdb my_script.py 10
8
+ ```
9
+
10
+ attaches the debugger and halts on the first line **with zero clicks**.
11
+
12
+ > You normally don't install this yourself. The `vspdb` Python package bundles it
13
+ > and installs it into VS Code automatically the first time you run
14
+ > `python -m vspdb` (or `python -m vspdb --setup`). After that, reload the VS Code
15
+ > window once to activate it.
16
+
17
+ ## What it does
18
+
19
+ - On window open, starts a `debugpy` **attach + listen** session on a port unique
20
+ to that workspace, and records it in `<workspace>/.vscode/vspdb.json`.
21
+ - Restarts the listener after each session ends, so every run attaches.
22
+ - `python -m vspdb` reads `vspdb.json` and connects to the right window.
23
+
24
+ ## Settings
25
+
26
+ | Setting | Default | Meaning |
27
+ | --- | --- | --- |
28
+ | `vspdb.host` | `127.0.0.1` | Must match `python -m vspdb --host`. |
29
+ | `vspdb.port` | `5678` | Base port (each window offsets from here). |
30
+ | `vspdb.autoStart` | `true` | Auto-start/restart the listener. |
31
+
32
+ Command: `Ctrl+Shift+P` → **"vspdb: Start debug listener now"**.
@@ -0,0 +1,128 @@
1
+ // vspdb auto-listen — keeps a debugpy listener alive so `python -m vspdb`
2
+ // attaches and stops on entry with zero clicks.
3
+ //
4
+ // This extension is installed automatically by the `vspdb` Python package
5
+ // (`python -m vspdb` copies it into VS Code on first run). You normally never
6
+ // install it by hand.
7
+ //
8
+ // Multi-window safe: each VS Code window picks a port derived from its workspace
9
+ // folder (so two projects don't fight over one port) and records the chosen
10
+ // host/port in <workspace>/.vscode/vspdb.json. `python -m vspdb` reads that file
11
+ // to connect to THIS project's window, not some other open window.
12
+
13
+ const vscode = require("vscode");
14
+ const net = require("net");
15
+ const fs = require("fs");
16
+ const path = require("path");
17
+
18
+ const CONFIG_NAME = "vspdb auto-listen";
19
+ let restarting = false;
20
+ let currentPort = null;
21
+
22
+ function settings() {
23
+ const cfg = vscode.workspace.getConfiguration("vspdb");
24
+ return {
25
+ host: cfg.get("host", "127.0.0.1"),
26
+ basePort: cfg.get("port", 5678),
27
+ autoStart: cfg.get("autoStart", true),
28
+ };
29
+ }
30
+
31
+ function workspaceFolder() {
32
+ const folders = vscode.workspace.workspaceFolders;
33
+ return folders && folders.length ? folders[0] : undefined;
34
+ }
35
+
36
+ // Stable per-workspace starting port so different windows don't all start at the
37
+ // same number (which would race for one port).
38
+ function startingPort(basePort, key) {
39
+ let h = 0;
40
+ for (let i = 0; i < key.length; i++) h = (h * 31 + key.charCodeAt(i)) >>> 0;
41
+ return basePort + (h % 200); // basePort .. basePort+199
42
+ }
43
+
44
+ function findFreePort(host, from) {
45
+ return new Promise((resolve) => {
46
+ let port = from;
47
+ const tryPort = () => {
48
+ const srv = net.createServer();
49
+ srv.once("error", () => {
50
+ port += 1;
51
+ if (port > from + 250) return resolve(from);
52
+ tryPort();
53
+ });
54
+ srv.once("listening", () => srv.close(() => resolve(port)));
55
+ srv.listen(port, host);
56
+ };
57
+ tryPort();
58
+ });
59
+ }
60
+
61
+ function writePortFile(folder, host, port) {
62
+ try {
63
+ const dir = path.join(folder.uri.fsPath, ".vscode");
64
+ fs.mkdirSync(dir, { recursive: true });
65
+ fs.writeFileSync(
66
+ path.join(dir, "vspdb.json"),
67
+ JSON.stringify({ host, port }, null, 2) + "\n"
68
+ );
69
+ } catch (e) {
70
+ /* ignore */
71
+ }
72
+ }
73
+
74
+ async function startListener(reusePort) {
75
+ const { host, basePort } = settings();
76
+ const folder = workspaceFolder();
77
+ const from = reusePort || startingPort(basePort, folder ? folder.uri.fsPath : "default");
78
+ const port = await findFreePort(host, from);
79
+ currentPort = port;
80
+ if (folder) writePortFile(folder, host, port);
81
+
82
+ const config = {
83
+ name: CONFIG_NAME,
84
+ type: "debugpy",
85
+ request: "attach",
86
+ listen: { host, port },
87
+ justMyCode: false,
88
+ };
89
+ vscode.debug.startDebugging(folder, config).then(
90
+ (ok) => {
91
+ if (ok) {
92
+ vscode.window.setStatusBarMessage(`vspdb: listening on ${host}:${port}`, 4000);
93
+ } else {
94
+ vscode.window.setStatusBarMessage(
95
+ `vspdb: could not start listener on ${host}:${port}`,
96
+ 4000
97
+ );
98
+ }
99
+ },
100
+ () => {}
101
+ );
102
+ }
103
+
104
+ function activate(context) {
105
+ if (settings().autoStart) startListener();
106
+
107
+ context.subscriptions.push(
108
+ vscode.commands.registerCommand("vspdb.startListener", () => startListener())
109
+ );
110
+
111
+ // After a vspdb session ends (debuggee disconnected), bring the listener back
112
+ // on the same port so the next terminal run attaches with no clicks.
113
+ context.subscriptions.push(
114
+ vscode.debug.onDidTerminateDebugSession((session) => {
115
+ if (session.name === CONFIG_NAME && settings().autoStart && !restarting) {
116
+ restarting = true;
117
+ setTimeout(() => {
118
+ restarting = false;
119
+ startListener(currentPort);
120
+ }, 300);
121
+ }
122
+ })
123
+ );
124
+ }
125
+
126
+ function deactivate() {}
127
+
128
+ module.exports = { activate, deactivate };
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "vspdb-autolisten",
3
+ "displayName": "vspdb auto-listen",
4
+ "description": "Keeps a debugpy listener running so `python -m vspdb` attaches and stops on entry with zero clicks.",
5
+ "version": "0.1.0",
6
+ "publisher": "vspdb",
7
+ "license": "MIT",
8
+ "engines": {
9
+ "vscode": "^1.60.0"
10
+ },
11
+ "categories": [
12
+ "Debuggers",
13
+ "Other"
14
+ ],
15
+ "keywords": [
16
+ "vspdb",
17
+ "debugpy",
18
+ "debugger",
19
+ "attach",
20
+ "stopOnEntry"
21
+ ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/kmhatre14/vspdb.git",
25
+ "directory": "vspdb/vscode_extension"
26
+ },
27
+ "homepage": "https://github.com/kmhatre14/vspdb",
28
+ "bugs": {
29
+ "url": "https://github.com/kmhatre14/vspdb/issues"
30
+ },
31
+ "activationEvents": [
32
+ "onStartupFinished"
33
+ ],
34
+ "main": "./extension.js",
35
+ "contributes": {
36
+ "commands": [
37
+ {
38
+ "command": "vspdb.startListener",
39
+ "title": "vspdb: Start debug listener now"
40
+ }
41
+ ],
42
+ "configuration": {
43
+ "title": "vspdb",
44
+ "properties": {
45
+ "vspdb.host": {
46
+ "type": "string",
47
+ "default": "127.0.0.1",
48
+ "description": "Host/interface the vspdb listener binds to (must match `python -m vspdb --host`)."
49
+ },
50
+ "vspdb.port": {
51
+ "type": "number",
52
+ "default": 5678,
53
+ "description": "Base port the vspdb listener uses (must match `python -m vspdb --port`)."
54
+ },
55
+ "vspdb.autoStart": {
56
+ "type": "boolean",
57
+ "default": true,
58
+ "description": "Automatically start the listener when a window opens, and restart it after each debug session ends."
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.4
2
+ Name: vspdb
3
+ Version: 0.1.0
4
+ Summary: Run a Python script under the VS Code debugger from the terminal, halting on the first line.
5
+ Author-email: Kaustubh <kmhatre14@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/kmhatre14/vspdb
8
+ Project-URL: Repository, https://github.com/kmhatre14/vspdb
9
+ Project-URL: Issues, https://github.com/kmhatre14/vspdb/issues
10
+ Keywords: debugpy,vscode,debugger,pdb,debugging,cli,stopOnEntry
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Software Development :: Debuggers
23
+ Requires-Python: >=3.8
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: debugpy>=1.6
27
+ Dynamic: license-file
28
+
29
+ # vspdb — VS Code pdb for the terminal
30
+
31
+ Debug any Python script (with arguments) in the VS Code debugger straight from
32
+ the terminal — no hand-written `launch.json`, no `debug.py` wrapper.
33
+
34
+ ```bash
35
+ python -m vspdb my_script.py arg1 arg2
36
+ ```
37
+
38
+ vspdb starts a `debugpy` debug server, gets VS Code to attach, and **halts on the
39
+ first line of your script** — just like `stopOnEntry`. It ships a small VS Code
40
+ helper (installed automatically) so this works with **zero clicks**.
41
+
42
+ ---
43
+
44
+ ## Install — one package, that's it
45
+
46
+ ```bash
47
+ pip install vspdb # (or: pip install -e . from this folder)
48
+ ```
49
+
50
+ That's the only thing you install. vspdb ships a tiny VS Code helper extension
51
+ **inside** the package and installs it into VS Code for you on first use. So:
52
+
53
+ ```bash
54
+ python -m vspdb my_script.py 10 # explicit (or: vspdb my_script.py 10)
55
+ ```
56
+
57
+ The **first** run prints a one-time note that it installed the helper — **reload
58
+ the VS Code window once** (`Ctrl+Shift+P` → "Developer: Reload Window"). After
59
+ that it's fully automatic.
60
+
61
+ > Tip: install into the same interpreter VS Code uses for the workspace
62
+ > (`Ctrl+Shift+P` → "Python: Select Interpreter") so breakpoints line up. You can
63
+ > also pre-install the helper without running a script: `python -m vspdb --setup`.
64
+
65
+ ---
66
+
67
+ ## How you use it: zero clicks
68
+
69
+ Once the helper is active (after that one reload), just run scripts — each one
70
+ attaches and halts on its first line, **no debug panel, no F5**:
71
+
72
+ ```bash
73
+ python -m vspdb my_script.py 10
74
+ python -m vspdb another.py --flag
75
+ ```
76
+
77
+ The helper keeps a `debugpy` listener ready in VS Code (one per window, on a
78
+ per-project port) and vspdb connects to the right window automatically.
79
+
80
+ ### Don't want the helper? (no-extension fallback)
81
+
82
+ Run with `--no-ext` (or set `VSPDB_NO_AUTO_EXTENSION=1`). Then vspdb won't touch
83
+ VS Code; instead it writes two configs to `.vscode/launch.json` and you attach
84
+ manually:
85
+
86
+ - **vspdb: Listen** — start it once per window (F5), then runs are zero-click.
87
+ - **vspdb: Attach** — vspdb waits; press F5 once per run.
88
+
89
+ ---
90
+
91
+ ## Options
92
+
93
+ ```
94
+ python -m vspdb [options] <script.py> [script args...]
95
+ ```
96
+
97
+ Everything **after** the script name is passed to your script untouched (so your
98
+ script can use `--port`, `--host`, etc. without clashing with vspdb).
99
+
100
+ | Option | Description |
101
+ | --- | --- |
102
+ | `--port, -p PORT` | Debug port (default: `5678`). |
103
+ | `--host HOST` | Debug host/interface (default: `127.0.0.1`). |
104
+ | `--listen` | Force listen mode (open the port, wait for VS Code). |
105
+ | `--connect` | Force connect mode (attach to a VS Code that is already listening). |
106
+ | `--no-wait` | Don't wait for VS Code; run immediately (disables stop-on-entry). |
107
+ | `--no-stop` | Attach, but don't halt on the first line. |
108
+ | `--no-launch-json` | Don't create/update `.vscode/launch.json`. |
109
+ | `--setup` | (Re)install the bundled VS Code helper extension and exit. |
110
+ | `--no-ext` | Don't auto-install the VS Code helper on this run. |
111
+
112
+ The `--port` default is read from `.vscode/vspdb.json` (written by the helper) if
113
+ present, else `5678`. Disable auto-install globally with `VSPDB_NO_AUTO_EXTENSION=1`.
114
+
115
+ Examples:
116
+
117
+ ```bash
118
+ # Different port
119
+ python -m vspdb --port 5690 my_script.py 10
120
+
121
+ # Your script gets its own --port; vspdb does not consume it
122
+ python -m vspdb server.py --port 8080 --debug
123
+
124
+ # Run under the debugger but don't stop on entry (rely on your own breakpoints)
125
+ python -m vspdb --no-stop my_script.py 10
126
+ ```
127
+
128
+ ---
129
+
130
+ ## How it works
131
+
132
+ 1. Parses `vspdb` options, then treats the first positional as the script and the
133
+ rest as the script's `argv`.
134
+ 2. Installs the bundled VS Code helper into VS Code if it isn't there yet (unless
135
+ `--no-ext`), and ensures `.vscode/launch.json` has the manual-fallback attach
136
+ configs (merging, never clobbering, any existing file).
137
+ 3. Resolves the port: if `--port` isn't given, it walks up from the script's
138
+ directory for `.vscode/vspdb.json` (written by the auto-listen extension) so it
139
+ attaches to the window that owns the script's project — important when several
140
+ VS Code windows are open. Then it **connects** if that port is being listened
141
+ on, otherwise **listens** and waits for VS Code.
142
+ 4. Once the client is attached, it runs your script as `__main__` with the right
143
+ `sys.argv`, having injected a `debugpy.breakpoint()` at the first executable
144
+ line so the debugger halts there. Original line numbers are preserved, so
145
+ tracebacks stay accurate.
146
+
147
+ ---
148
+
149
+ ## Requirements
150
+
151
+ - Python 3.8+
152
+ - `debugpy` (installed automatically)
153
+ - VS Code with the Python / Python Debugger extension
154
+
155
+ ## Author
156
+
157
+ Kaustubh — [kmhatre14@gmail.com](mailto:kmhatre14@gmail.com) · [github.com/kmhatre14](https://github.com/kmhatre14)
158
+
159
+ ## License
160
+
161
+ [MIT](LICENSE). Built with assistance from Claude Opus 4.8.
@@ -0,0 +1,15 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ vspdb/__init__.py
5
+ vspdb/__main__.py
6
+ vspdb/cli.py
7
+ vspdb.egg-info/PKG-INFO
8
+ vspdb.egg-info/SOURCES.txt
9
+ vspdb.egg-info/dependency_links.txt
10
+ vspdb.egg-info/entry_points.txt
11
+ vspdb.egg-info/requires.txt
12
+ vspdb.egg-info/top_level.txt
13
+ vspdb/vscode_extension/README.md
14
+ vspdb/vscode_extension/extension.js
15
+ vspdb/vscode_extension/package.json
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ vspdb = vspdb.cli:main
@@ -0,0 +1 @@
1
+ debugpy>=1.6
@@ -0,0 +1 @@
1
+ vspdb