|
| 1 | +# DAP Client: Debug Adapter Protocol Client for Python |
| 2 | + |
| 3 | +DAP Client is a generic client-side implementation of the [Debug Adapter Protocol (DAP)](https://microsoft.github.io/debug-adapter-protocol/) 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.). |
| 4 | + |
| 5 | +For a comprehensive example of how to build a debugger UI with this client, see [sandbox.py](sandbox/sandbox.py) implementation. |
| 6 | + |
| 7 | +## Key Features |
| 8 | + |
| 9 | +- **Protocol-First**: Implements the DAP state machine and message parsing without forcing a specific IO model. |
| 10 | +- **Strongly Typed**: Uses Pydantic models for request arguments and event bodies. |
| 11 | +- **Easy Integration**: Can be used with `subprocess`, `socket`, `asyncio`, or any other transport mechanism. |
| 12 | + |
| 13 | +## Installation |
| 14 | + |
| 15 | +```bash |
| 16 | +pip install dap-python |
| 17 | +``` |
| 18 | + |
| 19 | +If you are developing/contributing to the code, use `pip install -e .` instead. I also support using `poetry`. |
| 20 | + |
| 21 | +## Quick Start |
| 22 | + |
| 23 | +The `DAPClient` requires an IO handler to send and receive bytes from the debug adapter. The library acts as a translation layer: |
| 24 | +- You feed it raw bytes from the adapter -> It yields high-level `Event` objects. |
| 25 | +- You call methods like `client.launch()` -> It buffers raw bytes for you to send to the adapter. |
| 26 | + |
| 27 | +### Example: minimal synchronous runner |
| 28 | + |
| 29 | +This example assumes you have an IO handler similar to `sandbox/dap_io.py`. |
| 30 | + |
| 31 | +```python |
| 32 | +import time |
| 33 | +from dap.client import DAPClient |
| 34 | +from dap.events import InitializedEvent |
| 35 | + |
| 36 | +# Import your IO handler (see sandbox/dap_io.py for a reference implementation) |
| 37 | +from sandbox.dap_io import IO |
| 38 | + |
| 39 | +# 1. Start the debug adapter (e.g., debugpy) |
| 40 | +adapter_cmd = "python -m debugpy.adapter" |
| 41 | +io = IO(adapter_cmd) |
| 42 | +io.start() |
| 43 | + |
| 44 | +# 2. Initialize the DAP Client |
| 45 | +client = DAPClient( |
| 46 | + clientID="my-debugger", |
| 47 | + clientName="My Custom Debugger", |
| 48 | + locale="en-US", |
| 49 | + pathFormat="path" |
| 50 | +) |
| 51 | + |
| 52 | +# 3. Send the Initialize Request |
| 53 | +# client.send() returns the raw bytes that need to be sent to the adapter |
| 54 | +io.write(client.send()) |
| 55 | + |
| 56 | +# 4. Event Loop |
| 57 | +try: |
| 58 | + while True: |
| 59 | + # Read raw data from the adapter's stdout |
| 60 | + data = io.read() |
| 61 | + if data: |
| 62 | + # Feed data to the client to parse events |
| 63 | + events = client.recv(data) |
| 64 | + |
| 65 | + for event in events: |
| 66 | + print(f"Received: {event}") |
| 67 | + |
| 68 | + if isinstance(event, InitializedEvent): |
| 69 | + print("Adapter Initialized!") |
| 70 | + # Once initialized, we can configure and launch |
| 71 | + client.launch(program="script.py", console="internalConsole") |
| 72 | + client.configuration_done() |
| 73 | + |
| 74 | + # Flush requests generated by the above calls |
| 75 | + io.write(client.send()) |
| 76 | + |
| 77 | + time.sleep(0.01) |
| 78 | +except KeyboardInterrupt: |
| 79 | + io.stop() |
| 80 | +``` |
| 81 | + |
| 82 | +## Architecture |
| 83 | + |
| 84 | +The library is designed around the `DAPClient` class in `dap.client`. |
| 85 | + |
| 86 | +### Data Flow |
| 87 | + |
| 88 | +``` |
| 89 | +[ Debug Adapter ] <== bytes ==> [ IO Handler ] <== bytes ==> [ DAPClient ] <== Objects ==> [ Your Application ] |
| 90 | +``` |
| 91 | + |
| 92 | +1. **Send**: You call `client.step_in(threadId=1)`. The client updates its internal state and queues the JSON-RPC message. You call `client.send()` to get the bytes and write them to your transport. |
| 93 | +2. **Receive**: You read bytes from your transport and call `client.recv(bytes)`. The client parses the buffer and yields `Event`, `Response`, or `Request` objects. |
| 94 | + |
| 95 | +## API Reference |
| 96 | + |
| 97 | +### `DAPClient` |
| 98 | + |
| 99 | +The main entry point. Common methods include: |
| 100 | +- `launch(...)` / `attach(...)` |
| 101 | +- `set_breakpoints(...)` / `configuration_done()` |
| 102 | +- `continue_execution(...)` / `step_in(...)` / `next(...)` |
| 103 | +- `stack_trace(...)` / `scopes(...)` / `variables(...)` |
| 104 | +- `disconnect()` |
| 105 | + |
| 106 | +See `dap/client.py` for the full list of supported methods and arguments. |
0 commit comments