Skip to content

Commit c98b355

Browse files
pi-anlclaude
andcommitted
python-ecosys/debugpy: Add VS Code debugging support for MicroPython.
This implementation provides a Debug Adapter Protocol (DAP) server that enables VS Code to debug MicroPython code with full breakpoint, stepping, and variable inspection capabilities. Features: - Manual breakpoints via debugpy.breakpoint() - Line breakpoints set from VS Code - Stack trace inspection - Variable scopes (locals/globals) - Source code viewing - Stepping (into/over/out) - Non-blocking architecture for MicroPython's single-threaded environment - Conditional debug logging based on VS Code's logToFile setting Implementation highlights: - Uses MicroPython's sys.settrace() for execution monitoring - Handles path mapping between VS Code and MicroPython - Efficient O(n) fibonacci demo (was O(2^n) recursive) - Compatible with MicroPython's limited frame object attributes - Comprehensive DAP protocol support Files: - debugpy/: Core debugging implementation - test_vscode.py: VS Code integration test - VSCODE_TESTING_GUIDE.md: Setup and usage instructions - dap_monitor.py: Protocol debugging utility Usage: ```python import debugpy debugpy.listen() # Start debug server debugpy.debug_this_thread() # Enable tracing debugpy.breakpoint() # Manual breakpoint ``` 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 6e24cff commit c98b355

15 files changed

+1656
-0
lines changed

python-ecosys/debugpy/README.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# MicroPython debugpy
2+
3+
A minimal implementation of debugpy for MicroPython, enabling remote debugging
4+
such as VS Code debugging support.
5+
6+
## Features
7+
8+
- Debug Adapter Protocol (DAP) support for VS Code integration
9+
- Basic debugging operations:
10+
- Breakpoints
11+
- Step over/into/out
12+
- Stack trace inspection
13+
- Variable inspection (globals, locals generally not supported)
14+
- Expression evaluation
15+
- Pause/continue execution
16+
17+
## Requirements
18+
19+
- MicroPython with `sys.settrace` support (enabled with `MICROPY_PY_SYS_SETTRACE`)
20+
- Socket support for network communication
21+
- JSON support for DAP message parsing
22+
23+
## Usage
24+
25+
### Basic Usage
26+
27+
```python
28+
import debugpy
29+
30+
# Start listening for debugger connections
31+
host, port = debugpy.listen() # Default: 127.0.0.1:5678
32+
print(f"Debugger listening on {host}:{port}")
33+
34+
# Enable debugging for current thread
35+
debugpy.debug_this_thread()
36+
37+
# Your code here...
38+
def my_function():
39+
x = 10
40+
y = 20
41+
result = x + y # Set breakpoint here in VS Code
42+
return result
43+
44+
result = my_function()
45+
print(f"Result: {result}")
46+
47+
# Manual breakpoint
48+
debugpy.breakpoint()
49+
```
50+
51+
### VS Code Configuration
52+
53+
Create a `.vscode/launch.json` file in your project:
54+
55+
```json
56+
{
57+
"version": "0.2.0",
58+
"configurations": [
59+
{
60+
"name": "Attach to MicroPython",
61+
"type": "python",
62+
"request": "attach",
63+
"connect": {
64+
"host": "127.0.0.1",
65+
"port": 5678
66+
},
67+
"pathMappings": [
68+
{
69+
"localRoot": "${workspaceFolder}",
70+
"remoteRoot": "."
71+
}
72+
],
73+
"justMyCode": false
74+
}
75+
]
76+
}
77+
```
78+
79+
### Testing
80+
81+
1. Build the MicroPython Unix coverage port:
82+
```bash
83+
cd ports/unix
84+
make CFLAGS_EXTRA="-DMICROPY_PY_SYS_SETTRACE=1"
85+
```
86+
87+
2. Run the test script:
88+
```bash
89+
cd lib/micropython-lib/python-ecosys/debugpy
90+
../../../../ports/unix/build-coverage/micropython test_debugpy.py
91+
```
92+
93+
3. In VS Code, open the debugpy folder and press F5 to attach the debugger
94+
95+
4. Set breakpoints in the test script and observe debugging functionality
96+
97+
## API Reference
98+
99+
### `debugpy.listen(port=5678, host="127.0.0.1")`
100+
101+
Start listening for debugger connections.
102+
103+
**Parameters:**
104+
- `port`: Port number to listen on (default: 5678)
105+
- `host`: Host address to bind to (default: "127.0.0.1")
106+
107+
**Returns:** Tuple of (host, port) actually used
108+
109+
### `debugpy.debug_this_thread()`
110+
111+
Enable debugging for the current thread by installing the trace function.
112+
113+
### `debugpy.breakpoint()`
114+
115+
Trigger a manual breakpoint that will pause execution if a debugger is attached.
116+
117+
### `debugpy.wait_for_client()`
118+
119+
Wait for the debugger client to connect and initialize.
120+
121+
### `debugpy.is_client_connected()`
122+
123+
Check if a debugger client is currently connected.
124+
125+
**Returns:** Boolean indicating connection status
126+
127+
### `debugpy.disconnect()`
128+
129+
Disconnect from the debugger client and clean up resources.
130+
131+
## Architecture
132+
133+
The implementation consists of several key components:
134+
135+
1. **Public API** (`public_api.py`): Main entry points for users
136+
2. **Debug Session** (`server/debug_session.py`): Handles DAP protocol communication
137+
3. **PDB Adapter** (`server/pdb_adapter.py`): Bridges DAP and MicroPython's trace system
138+
4. **Messaging** (`common/messaging.py`): JSON message handling for DAP
139+
5. **Constants** (`common/constants.py`): DAP protocol constants
140+
141+
## Limitations
142+
143+
This is a minimal implementation with the following limitations:
144+
145+
- Single-threaded debugging only
146+
- No conditional breakpoints
147+
- No function breakpoints
148+
- Limited variable inspection (no nested object expansion)
149+
- No step back functionality
150+
- No hot code reloading
151+
- Simplified stepping implementation
152+
153+
## Compatibility
154+
155+
Tested with:
156+
- MicroPython Unix port
157+
- VS Code with Python/debugpy extension
158+
- CPython 3.x (for comparison)
159+
160+
## Contributing
161+
162+
This implementation provides a foundation for MicroPython debugging. Contributions are welcome to add:
163+
164+
- Conditional breakpoint support
165+
- Better variable inspection
166+
- Multi-threading support
167+
- Performance optimizations
168+
- Additional DAP features
169+
170+
## License
171+
172+
MIT License - see the MicroPython project license for details.

python-ecosys/debugpy/dap_monitor.py

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#!/usr/bin/env python3
2+
"""DAP protocol monitor - sits between VS Code and MicroPython debugpy."""
3+
4+
import socket
5+
import threading
6+
import json
7+
import time
8+
import sys
9+
10+
class DAPMonitor:
11+
def __init__(self, listen_port=5679, target_host='127.0.0.1', target_port=5678):
12+
self.listen_port = listen_port
13+
self.target_host = target_host
14+
self.target_port = target_port
15+
self.client_sock = None
16+
self.server_sock = None
17+
18+
def start(self):
19+
"""Start the DAP monitor proxy."""
20+
print(f"DAP Monitor starting on port {self.listen_port}")
21+
print(f"Will forward to {self.target_host}:{self.target_port}")
22+
print("Start MicroPython debugpy server first, then connect VS Code to port 5679")
23+
24+
# Create listening socket
25+
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
26+
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
27+
listener.bind(('127.0.0.1', self.listen_port))
28+
listener.listen(1)
29+
30+
print(f"Listening for VS Code connection on port {self.listen_port}...")
31+
32+
try:
33+
# Wait for VS Code to connect
34+
self.client_sock, client_addr = listener.accept()
35+
print(f"VS Code connected from {client_addr}")
36+
37+
# Connect to MicroPython debugpy server
38+
self.server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
39+
self.server_sock.connect((self.target_host, self.target_port))
40+
print(f"Connected to MicroPython debugpy at {self.target_host}:{self.target_port}")
41+
42+
# Start forwarding threads
43+
threading.Thread(target=self.forward_client_to_server, daemon=True).start()
44+
threading.Thread(target=self.forward_server_to_client, daemon=True).start()
45+
46+
print("DAP Monitor active - press Ctrl+C to stop")
47+
while True:
48+
time.sleep(1)
49+
50+
except KeyboardInterrupt:
51+
print("\nStopping DAP Monitor...")
52+
except Exception as e:
53+
print(f"Error: {e}")
54+
finally:
55+
self.cleanup()
56+
57+
def forward_client_to_server(self):
58+
"""Forward messages from VS Code client to MicroPython server."""
59+
try:
60+
while True:
61+
data = self.receive_dap_message(self.client_sock, "VS Code")
62+
if data is None:
63+
break
64+
self.send_raw_data(self.server_sock, data)
65+
except Exception as e:
66+
print(f"Client->Server forwarding error: {e}")
67+
68+
def forward_server_to_client(self):
69+
"""Forward messages from MicroPython server to VS Code client."""
70+
try:
71+
while True:
72+
data = self.receive_dap_message(self.server_sock, "MicroPython")
73+
if data is None:
74+
break
75+
self.send_raw_data(self.client_sock, data)
76+
except Exception as e:
77+
print(f"Server->Client forwarding error: {e}")
78+
79+
def receive_dap_message(self, sock, source):
80+
"""Receive and log a DAP message."""
81+
try:
82+
# Read headers
83+
header = b""
84+
while b"\r\n\r\n" not in header:
85+
byte = sock.recv(1)
86+
if not byte:
87+
return None
88+
header += byte
89+
90+
# Parse content length
91+
header_str = header.decode('utf-8')
92+
content_length = 0
93+
for line in header_str.split('\r\n'):
94+
if line.startswith('Content-Length:'):
95+
content_length = int(line.split(':', 1)[1].strip())
96+
break
97+
98+
if content_length == 0:
99+
return None
100+
101+
# Read content
102+
content = b""
103+
while len(content) < content_length:
104+
chunk = sock.recv(content_length - len(content))
105+
if not chunk:
106+
return None
107+
content += chunk
108+
109+
# Log the message
110+
try:
111+
message = json.loads(content.decode('utf-8'))
112+
msg_type = message.get('type', 'unknown')
113+
command = message.get('command', message.get('event', 'unknown'))
114+
seq = message.get('seq', 0)
115+
116+
print(f"\n[{source}] {msg_type.upper()}: {command} (seq={seq})")
117+
118+
if msg_type == 'request':
119+
args = message.get('arguments', {})
120+
if args:
121+
print(f" Arguments: {json.dumps(args, indent=2)}")
122+
elif msg_type == 'response':
123+
success = message.get('success', False)
124+
req_seq = message.get('request_seq', 0)
125+
print(f" Success: {success}, Request Seq: {req_seq}")
126+
body = message.get('body')
127+
if body:
128+
print(f" Body: {json.dumps(body, indent=2)}")
129+
msg = message.get('message')
130+
if msg:
131+
print(f" Message: {msg}")
132+
elif msg_type == 'event':
133+
body = message.get('body', {})
134+
if body:
135+
print(f" Body: {json.dumps(body, indent=2)}")
136+
137+
except json.JSONDecodeError:
138+
print(f"\n[{source}] Invalid JSON: {content}")
139+
140+
return header + content
141+
142+
except Exception as e:
143+
print(f"Error receiving from {source}: {e}")
144+
return None
145+
146+
def send_raw_data(self, sock, data):
147+
"""Send raw data to socket."""
148+
try:
149+
sock.send(data)
150+
except Exception as e:
151+
print(f"Error sending data: {e}")
152+
153+
def cleanup(self):
154+
"""Clean up sockets."""
155+
if self.client_sock:
156+
self.client_sock.close()
157+
if self.server_sock:
158+
self.server_sock.close()
159+
160+
if __name__ == "__main__":
161+
monitor = DAPMonitor()
162+
monitor.start()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""MicroPython debugpy implementation.
2+
3+
A minimal port of debugpy for MicroPython to enable VS Code debugging support.
4+
This implementation focuses on the core DAP (Debug Adapter Protocol) functionality
5+
needed for basic debugging operations like breakpoints, stepping, and variable inspection.
6+
"""
7+
8+
__version__ = "0.1.0"
9+
10+
from .public_api import listen, wait_for_client, breakpoint, debug_this_thread
11+
from .common.constants import DEFAULT_HOST, DEFAULT_PORT
12+
13+
__all__ = [
14+
"listen",
15+
"wait_for_client",
16+
"breakpoint",
17+
"debug_this_thread",
18+
"DEFAULT_HOST",
19+
"DEFAULT_PORT",
20+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Common utilities and constants for debugpy

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy