dap-python 1.0.1
Client for Debug Adapter Protocol
License: MIT
Requires Python: <4.0,>=3.12

# DAP Client: Debug Adapter Protocol Client for Python

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.).

For a comprehensive example of how to build a debugger UI with this client, see [sandbox.py](sandbox/sandbox.py) implementation.

## Key Features

- **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.

## Installation

```bash
pip install dap-python
```

If you are developing/contributing to the code, use `pip install -e .` instead. I also support using `poetry`.

## Quick Start

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 `Event` objects.
- You call methods like `client.launch()` -> It buffers raw bytes for you to send to the adapter.

### Example: minimal synchronous runner

This example assumes you have an IO handler similar to `sandbox/dap_io.py`.

```python
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()
```

## Architecture

The library is designed around the `DAPClient` class in `dap.client`.

### Data Flow

```
[ Debug Adapter ] <== bytes ==> [ IO Handler ] <== bytes ==> [ DAPClient ] <== Objects ==> [ Your Application ]
```

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.
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.

## API Reference

### `DAPClient`

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.