DAP Client is a generic client-side implementation of the Debug Adapter Protocol (DAP) for Python. It provides a clean, strongly-typed API for interacting with debug adapters but is decoupled from the transport layer (IO), allowing integration into any framework (synchronous, asynchronous, GUI, etc.).
For a comprehensive example of how to build a debugger UI with this client, see sandbox.py implementation.
- Protocol-First: Implements the DAP state machine and message parsing without forcing a specific IO model.
- Strongly Typed: Uses Pydantic models for request arguments and event bodies.
- Easy Integration: Can be used with
subprocess,socket,asyncio, or any other transport mechanism.
pip install dap-pythonIf you are developing/contributing to the code, use pip install -e . instead. I also support using poetry.
The DAPClient requires an IO handler to send and receive bytes from the debug adapter. The library acts as a translation layer:
- You feed it raw bytes from the adapter -> It yields high-level
Eventobjects. - You call methods like
client.launch()-> It buffers raw bytes for you to send to the adapter.
This example assumes you have an IO handler similar to sandbox/dap_io.py.
import time
from dap.client import DAPClient
from dap.events import InitializedEvent
# Import your IO handler (see sandbox/dap_io.py for a reference implementation)
from sandbox.dap_io import IO
# 1. Start the debug adapter (e.g., debugpy)
adapter_cmd = "python -m debugpy.adapter"
io = IO(adapter_cmd)
io.start()
# 2. Initialize the DAP Client
client = DAPClient(
clientID="my-debugger",
clientName="My Custom Debugger",
locale="en-US",
pathFormat="path"
)
# 3. Send the Initialize Request
# client.send() returns the raw bytes that need to be sent to the adapter
io.write(client.send())
# 4. Event Loop
try:
while True:
# Read raw data from the adapter's stdout
data = io.read()
if data:
# Feed data to the client to parse events
events = client.recv(data)
for event in events:
print(f"Received: {event}")
if isinstance(event, InitializedEvent):
print("Adapter Initialized!")
# Once initialized, we can configure and launch
client.launch(program="script.py", console="internalConsole")
client.configuration_done()
# Flush requests generated by the above calls
io.write(client.send())
time.sleep(0.01)
except KeyboardInterrupt:
io.stop()The library is designed around the DAPClient class in dap.client.
[ Debug Adapter ] <== bytes ==> [ IO Handler ] <== bytes ==> [ DAPClient ] <== Objects ==> [ Your Application ]
- Send: You call
client.step_in(threadId=1). The client updates its internal state and queues the JSON-RPC message. You callclient.send()to get the bytes and write them to your transport. - Receive: You read bytes from your transport and call
client.recv(bytes). The client parses the buffer and yieldsEvent,Response, orRequestobjects.
The main entry point. Common methods include:
launch(...)/attach(...)set_breakpoints(...)/configuration_done()continue_execution(...)/step_in(...)/next(...)stack_trace(...)/scopes(...)/variables(...)disconnect()
See dap/client.py for the full list of supported methods and arguments.