Open
Show file tree
Hide file tree
Changes from all commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Failed to load files.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
#!/usr/bin/env python3

Check failure on line 1 in python-ecosys/debugpy/dap_monitor.py

View workflow job for this annotation

Actions / ruff

Ruff (EXE001)

python-ecosys/debugpy/dap_monitor.py:1:1: EXE001 Shebang is present but file is not executable
"""DAP protocol monitor - sits between VS Code and MicroPython debugpy."""

import socket
import threading
import json
import time
import sys

class DAPMonitor:
def __init__(self, listen_port=5679, target_host='127.0.0.1', target_port=5678):
self.disconnect = False
self.listen_port = listen_port
self.target_host = target_host
self.target_port = target_port
self.client_sock = None
self.server_sock = None

Check failure on line 18 in python-ecosys/debugpy/dap_monitor.py

View workflow job for this annotation

Actions / ruff

Ruff (W293)

python-ecosys/debugpy/dap_monitor.py:18:1: W293 Blank line contains whitespace
def start(self):
"""Start the DAP monitor proxy."""
print(f"DAP Monitor starting on port {self.listen_port}")
print(f"Will forward to {self.target_host}:{self.target_port}")
print("Start MicroPython debugpy server first, then connect VS Code to port 5679")

Check failure on line 24 in python-ecosys/debugpy/dap_monitor.py

View workflow job for this annotation

Actions / ruff

Ruff (W293)

python-ecosys/debugpy/dap_monitor.py:24:1: W293 Blank line contains whitespace
# Create listening socket
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listener.bind(('127.0.0.1', self.listen_port))
listener.listen(1)

Check failure on line 30 in python-ecosys/debugpy/dap_monitor.py

View workflow job for this annotation

Actions / ruff

Ruff (W293)

python-ecosys/debugpy/dap_monitor.py:30:1: W293 Blank line contains whitespace
print(f"Listening for VS Code connection on port {self.listen_port}...")

Check failure on line 32 in python-ecosys/debugpy/dap_monitor.py

View workflow job for this annotation

Actions / ruff

Ruff (W293)

python-ecosys/debugpy/dap_monitor.py:32:1: W293 Blank line contains whitespace
try:
# Wait for VS Code to connect
self.client_sock, client_addr = listener.accept()
print(f"VS Code connected from {client_addr}")

Check failure on line 37 in python-ecosys/debugpy/dap_monitor.py

View workflow job for this annotation

Actions / ruff

Ruff (W293)

python-ecosys/debugpy/dap_monitor.py:37:1: W293 Blank line contains whitespace
# Connect to MicroPython debugpy server
self.server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_sock.connect((self.target_host, self.target_port))
print(f"Connected to MicroPython debugpy at {self.target_host}:{self.target_port}")

Check failure on line 42 in python-ecosys/debugpy/dap_monitor.py

View workflow job for this annotation

Actions / ruff

Ruff (W293)

python-ecosys/debugpy/dap_monitor.py:42:1: W293 Blank line contains whitespace
# Start forwarding threads
threading.Thread(target=self.forward_client_to_server, daemon=True).start()
threading.Thread(target=self.forward_server_to_client, daemon=True).start()

Check failure on line 46 in python-ecosys/debugpy/dap_monitor.py

View workflow job for this annotation

Actions / ruff

Ruff (W293)

python-ecosys/debugpy/dap_monitor.py:46:1: W293 Blank line contains whitespace
print("DAP Monitor active - press Ctrl+C to stop")
while not self.disconnect:
time.sleep(1)

except KeyboardInterrupt:
print("\nStopping DAP Monitor...")
except Exception as e:
print(f"Error: {e}")
finally:
self.cleanup()

Check failure on line 57 in python-ecosys/debugpy/dap_monitor.py

View workflow job for this annotation

Actions / ruff

Ruff (W293)

python-ecosys/debugpy/dap_monitor.py:57:1: W293 Blank line contains whitespace
def forward_client_to_server(self):
"""Forward messages from VS Code client to MicroPython server."""
try:
while True:
data = self.receive_dap_message(self.client_sock, "VS Code")
if data is None:
break
self.send_raw_data(self.server_sock, data)
except Exception as e:
print(f"Client->Server forwarding error: {e}")

Check failure on line 68 in python-ecosys/debugpy/dap_monitor.py

View workflow job for this annotation

Actions / ruff

Ruff (W293)

python-ecosys/debugpy/dap_monitor.py:68:1: W293 Blank line contains whitespace
def forward_server_to_client(self):
"""Forward messages from MicroPython server to VS Code client."""
try:
while True:
data = self.receive_dap_message(self.server_sock, "MicroPython")
if data is None:
break
self.send_raw_data(self.client_sock, data)
except Exception as e:
print(f"Server->Client forwarding error: {e}")

def receive_dap_message(self, sock, source):
"""Receive and log a DAP message."""
try:
# Read headers
header = b""
while b"\r\n\r\n" not in header:
byte = sock.recv(1)
if not byte:
return None
header += byte

# Parse content length
header_str = header.decode('utf-8')
content_length = 0
for line in header_str.split('\r\n'):
if line.startswith('Content-Length:'):
content_length = int(line.split(':', 1)[1].strip())
break

if content_length == 0:
return None

# Read content
content = b""
while len(content) < content_length:
chunk = sock.recv(content_length - len(content))
if not chunk:
return None
content += chunk

# Parse and Log the message
message = self.parse_dap(source, content)
self.log_dap_message(source, message)
# Check for disconnect command
if message:
if "disconnect" == message.get('command', message.get('event', 'unknown')):
print(f"\n[{source}] Disconnect command received, stopping monitor.")
self.disconnect = True
return header + content
except Exception as e:
print(f"Error receiving from {source}: {e}")
return None

def parse_dap(self, source, content):
"""Parse DAP message and log it."""
try:
message = json.loads(content.decode('utf-8'))
return message
except json.JSONDecodeError:
print(f"\n[{source}] Invalid JSON: {content}")
return None

def log_dap_message(self, source, message):
"""Log DAP message details."""
msg_type = message.get('type', 'unknown')
command = message.get('command', message.get('event', 'unknown'))
seq = message.get('seq', 0)

print(f"\n[{source}] {msg_type.upper()}: {command} (seq={seq})")

if msg_type == 'request':
args = message.get('arguments', {})
if args:
print(f" Arguments: {json.dumps(args, indent=2)}")
elif msg_type == 'response':
success = message.get('success', False)
req_seq = message.get('request_seq', 0)
print(f" Success: {success}, Request Seq: {req_seq}")
body = message.get('body')
if body:
print(f" Body: {json.dumps(body, indent=2)}")
msg = message.get('message')
if msg:
print(f" Message: {msg}")
elif msg_type == 'event':
body = message.get('body', {})
if body:
print(f" Body: {json.dumps(body, indent=2)}")

def send_raw_data(self, sock, data):
"""Send raw data to socket."""
try:
sock.send(data)
except Exception as e:
print(f"Error sending data: {e}")

def cleanup(self):
"""Clean up sockets."""
if self.client_sock:
self.client_sock.close()
if self.server_sock:
self.server_sock.close()

if __name__ == "__main__":
monitor = DAPMonitor()
monitor.start()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
"""MicroPython debugpy implementation.

A minimal port of debugpy for MicroPython to enable VS Code debugging support.
This implementation focuses on the core DAP (Debug Adapter Protocol) functionality
needed for basic debugging operations like breakpoints, stepping, and variable inspection.
"""

__version__ = "0.1.0"

from .public_api import listen, wait_for_client, breakpoint, debug_this_thread
from .common.constants import DEFAULT_HOST, DEFAULT_PORT

__all__ = [
"listen",
"wait_for_client",
"breakpoint",
"debug_this_thread",
"DEFAULT_HOST",
"DEFAULT_PORT",
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
# Common utilities and constants for debugpy
Loading
Loading