Skip to content

Commit 5d491e0

Browse files
pi-anlclaude
andcommitted
debugpy: Fix VS Code path mapping to prevent read-only file copies.
When breakpoints are hit, VS Code was opening read-only copies of source files instead of the original workspace files due to path mismatches between VS Code's absolute paths and MicroPython's runtime paths. Changes: - Add path mapping dictionary to track VS Code path <-> runtime path relationships - Enhance breakpoint matching to handle relative paths and basename matches - Update stack trace reporting to use mapped VS Code paths - Add debug logging for path mapping diagnostics - Fix VS Code launch configuration (debugpy -> python, enable logging) This ensures VS Code correctly opens the original editable source files when debugging, rather than creating read-only temporary copies. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> Signed-off-by: Andrew Leech <[email protected]>
1 parent c4202e4 commit 5d491e0

File tree

3 files changed

+56
-5
lines changed

3 files changed

+56
-5
lines changed

python-ecosys/debugpy/debugpy/server/debug_session.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@ def _handle_set_breakpoints(self, seq, args):
263263
filename = source.get("path", "<unknown>")
264264
breakpoints = args.get("breakpoints", [])
265265

266+
# Debug log the source information
267+
self._debug_print(f"[DAP] setBreakpoints source info: {source}")
268+
266269
# Set breakpoints in pdb adapter
267270
actual_breakpoints = self.pdb.set_breakpoints(filename, breakpoints)
268271

python-ecosys/debugpy/debugpy/server/pdb_adapter.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import sys
44
import time
5+
import os
56
from ..common.constants import (
67
TRACE_CALL, TRACE_LINE, TRACE_RETURN, TRACE_EXCEPTION,
78
SCOPE_LOCALS, SCOPE_GLOBALS
@@ -21,11 +22,27 @@ def __init__(self):
2122
self.continue_event = False
2223
self.variables_cache = {} # frameId -> variables
2324
self.frame_id_counter = 1
25+
self.path_mapping = {} # runtime_path -> vscode_path mapping
2426

2527
def _debug_print(self, message):
2628
"""Print debug message only if debug logging is enabled."""
2729
if hasattr(self, '_debug_session') and self._debug_session.debug_logging:
2830
print(message)
31+
32+
def _normalize_path(self, path):
33+
"""Normalize a file path for consistent comparisons."""
34+
# Convert to absolute path if possible
35+
try:
36+
if hasattr(os.path, 'abspath'):
37+
path = os.path.abspath(path)
38+
elif hasattr(os.path, 'realpath'):
39+
path = os.path.realpath(path)
40+
except:
41+
pass
42+
43+
# Ensure consistent separators
44+
path = path.replace('\\', '/')
45+
return path
2946

3047
def set_trace_function(self, trace_func):
3148
"""Install the trace function."""
@@ -39,6 +56,9 @@ def set_breakpoints(self, filename, breakpoints):
3956
self.breakpoints[filename] = {}
4057
actual_breakpoints = []
4158

59+
# Debug log the breakpoint path
60+
self._debug_print(f"[PDB] Setting breakpoints for file: {filename}")
61+
4262
for bp in breakpoints:
4363
line = bp.get("line")
4464
if line:
@@ -73,12 +93,23 @@ def should_stop(self, frame, event, arg):
7393
if filename in self.breakpoints:
7494
if lineno in self.breakpoints[filename]:
7595
self._debug_print(f"[PDB] HIT BREAKPOINT (exact match) at {filename}:{lineno}")
96+
# Record the path mapping (in this case, they're already the same)
97+
self.path_mapping[filename] = filename
7698
self.hit_breakpoint = True
7799
return True
78100

79101
# Also try checking by basename for path mismatches
80102
def basename(path):
81103
return path.split('/')[-1] if '/' in path else path
104+
105+
# Check if this might be a relative path match
106+
def ends_with_path(full_path, relative_path):
107+
"""Check if full_path ends with relative_path components."""
108+
full_parts = full_path.replace('\\', '/').split('/')
109+
rel_parts = relative_path.replace('\\', '/').split('/')
110+
if len(rel_parts) > len(full_parts):
111+
return False
112+
return full_parts[-len(rel_parts):] == rel_parts
82113

83114
file_basename = basename(filename)
84115
self._debug_print(f"[PDB] Fallback basename match: '{file_basename}' vs available files")
@@ -89,6 +120,18 @@ def basename(path):
89120
self._debug_print(f"[PDB] Basename match found! Checking line {lineno} in {list(self.breakpoints[bp_file].keys())}")
90121
if lineno in self.breakpoints[bp_file]:
91122
self._debug_print(f"[PDB] HIT BREAKPOINT (fallback basename match) at {filename}:{lineno} -> {bp_file}")
123+
# Record the path mapping so we can report the correct path in stack traces
124+
self.path_mapping[filename] = bp_file
125+
self.hit_breakpoint = True
126+
return True
127+
128+
# Also check if the runtime path might be relative and the breakpoint path absolute
129+
if ends_with_path(bp_file, filename):
130+
self._debug_print(f"[PDB] Relative path match: {bp_file} ends with {filename}")
131+
if lineno in self.breakpoints[bp_file]:
132+
self._debug_print(f"[PDB] HIT BREAKPOINT (relative path match) at {filename}:{lineno} -> {bp_file}")
133+
# Record the path mapping so we can report the correct path in stack traces
134+
self.path_mapping[filename] = bp_file
92135
self.hit_breakpoint = True
93136
return True
94137

@@ -171,11 +214,16 @@ def get_stack_trace(self):
171214
name = frame.f_code.co_name
172215
line = frame.f_lineno
173216

217+
# Use the VS Code path if we have a mapping, otherwise use the original path
218+
display_path = self.path_mapping.get(filename, filename)
219+
if filename != display_path:
220+
self._debug_print(f"[PDB] Stack trace path mapping: {filename} -> {display_path}")
221+
174222
# Create frame info
175223
frames.append({
176224
"id": frame_id,
177225
"name": name,
178-
"source": {"path": filename},
226+
"source": {"path": display_path},
179227
"line": line,
180228
"column": 1,
181229
"endLine": line,

python-ecosys/debugpy/vscode_launch_example.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@
22
"version": "0.2.0",
33
"configurations": [
44
{
5-
"name": "Micropython Attach",
6-
"type": "debugpy",
5+
"name": "Attach to MicroPython",
6+
"type": "python",
77
"request": "attach",
88
"connect": {
99
"host": "localhost",
1010
"port": 5678
1111
},
1212
"pathMappings": [
1313
{
14-
"localRoot": "${workspaceFolder}/lib/micropython-lib/python-ecosys/debugpy",
14+
"localRoot": "${workspaceFolder}",
1515
"remoteRoot": "."
1616
}
1717
],
18-
// "logToFile": true,
18+
"logToFile": true,
1919
"justMyCode": false
2020
}
2121
]

0 commit comments

Comments
 (0)