Watchpoints (Data + Expression)
Dapper supports persistent watchpoints through DAP setDataBreakpoints.
What works now
- Variable watchpoints (write-triggered):
frame:<frameId>:var:<name> - Variable read watchpoints (Python 3.12+):
frame:<frameId>:var:<name>withaccessType: "read"or"readWrite" - Expression watchpoints (value-change-triggered):
frame:<frameId>:expr:<expression> - Conditions and hit conditions on watchpoints (
condition,hitCondition) - Stop reason emitted as
data breakpoint
Expression watchpoints are re-evaluated whenever execution stops on a line, and Dapper breaks when the expression value changes from its previous snapshot.
Quick example
Set a data breakpoint with an expression payload:
{
"command": "setDataBreakpoints",
"arguments": {
"breakpoints": [
{
"dataId": "frame:42:expr:len(queue)",
"accessType": "write"
}
]
}
}
Set a read watchpoint (Python 3.12+):
{
"command": "setDataBreakpoints",
"arguments": {
"breakpoints": [
{
"dataId": "frame:42:var:counter",
"accessType": "read"
}
]
}
}
Read-watchpoint behavior matrix
| Python runtime | Requested accessType |
Effective behavior |
|---|---|---|
3.12+ (sys.monitoring available) |
write |
Write-triggered watchpoint |
3.12+ (sys.monitoring available) |
read |
Read-triggered watchpoint (variable-name loads) |
3.12+ (sys.monitoring available) |
readWrite |
Read + write-triggered watchpoint |
| 3.11 and earlier | write |
Write-triggered watchpoint |
| 3.11 and earlier | read |
Graceful fallback to write-triggered semantics |
| 3.11 and earlier | readWrite |
Graceful fallback to write-triggered semantics |
Strict expression watch policy
By default, expression watchpoints run in permissive mode.
Enable strict mode with launch/attach config:
{
"strictExpressionWatchPolicy": true
}
Or with launcher CLI:
python -m dapper.launcher \
--program app.py \
--ipc tcp --ipc-host 127.0.0.1 --ipc-port 4711 \
--strict-expression-watch-policy
Not allowed in strict mode
When strict mode is enabled, expression watchpoints are blocked if they contain any of the following policy tokens:
__importimport(open(exec(eval(compile(globals(locals(vars(os.sys.subprocesssocket
Typical blocked examples in strict mode:
x.__class__open("/tmp/a")__import__("os")os.getenv("HOME")
Current limitations
- Read watchpoints require Python 3.12+ (
sys.monitoringbackend). - On Python 3.11 and earlier,
accessType: "read"/"readWrite"is accepted but gracefully downgraded to write semantics. - Read detection is currently limited to variable-name loads (e.g. locals/globals);
attribute-read precision (
obj.attr) is not guaranteed yet. - Expression watchpoints are evaluated in the active runtime frame context.
- External/subprocess parity for expression watchpoint delivery is a follow-up item.