# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Build & Development Commands ```bash # Install dependencies (dev extras included) uv sync --extra dev # Run full test suite (mock-only, no hardware needed) uv run pytest # Run a single test file uv run pytest tests/test_target.py # Run a single test uv run pytest tests/test_target.py::test_halt # Run tests excluding hardware-dependent tests (explicit) uv run pytest -m "not hardware" # Lint uv run ruff check src/ tests/ uv run ruff format --check src/ tests/ # Auto-fix lint/format uv run ruff check --fix src/ tests/ uv run ruff format src/ tests/ # Run the CLI against a live OpenOCD instance uv run openocd-python info uv run openocd-python repl ``` ## Architecture ### Dual Async/Sync API Every subsystem exists as an async primary class and a `Sync*` wrapper. The async classes use `TclRpcConnection`; sync wrappers call `asyncio.run_until_complete()` on an internally-managed event loop. Entry points: - `Session.connect()` / `Session.start()` -- async context managers - `Session.connect_sync()` / `Session.start_sync()` -- sync context managers returning `SyncSession` ### Lazy Subsystem Initialization Session properties (`target`, `memory`, `registers`, `flash`, `jtag`, `breakpoints`, `rtt`, `svd`, `transport`, `events`) are lazily instantiated on first access. Each subsystem holds a reference to the shared connection. ### Connection Layer `TclRpcConnection` (primary) speaks the OpenOCD TCL RPC binary protocol on port 6666: command bytes terminated by `\x1a`, response bytes terminated by `\x1a`. It uses a **dual-socket design** -- a primary socket for request/response and a separate notification socket for async target events -- to prevent command/event interleaving. An async lock serializes commands. `TelnetConnection` is a fallback for environments without TCL RPC. Both implement the abstract interface in `connection/base.py`. ### Type System All return types are **frozen dataclasses** in `types.py`. Nothing mutable leaves the API surface. The exception hierarchy in `errors.py` provides fine-grained error types rooted at `OpenOCDError`. ### Parsing Strategy OpenOCD has no structured output format. Every subsystem uses regex parsing against command output strings. When modifying parsers, test against the mock server responses in `tests/mock_server.py` -- those strings are copied from real OpenOCD output. ### SVD Integration The `svd/` package wraps `cmsis-svd` to decode peripheral registers into named bitfields. It ties SVD metadata to live memory reads: `svd.read_register("GPIOA", "ODR")` returns a `DecodedRegister` with field names and values. ## Testing Tests run against `MockOpenOCDServer` (`tests/mock_server.py`), a TCP server that speaks the TCL RPC protocol and returns hardcoded responses for ~30 commands. The three core fixtures in `conftest.py`: - `mock_ocd` -- starts/stops the mock server, yields `(host, port, server)` - `connection` -- a `TclRpcConnection` connected to the mock - `session` -- a full `Session` connected to the mock All async tests run automatically via `asyncio_mode = "auto"` in pyproject.toml. The `@pytest.mark.hardware` marker gates tests that need a physical DAP-Link probe. ## Ruff Configuration Target: Python 3.11, 100-char lines. Rule sets: E, F, I, UP, B, SIM. ## Key Constraints - This library is used in safety-critical contexts (avionics, medical, automotive). Defensive parsing and explicit error handling matter. - OpenOCD output varies between versions. Parsers must handle multiple formats; check existing regex patterns before assuming a single format. - The sync API must never be called from inside an already-running async event loop (guarded in `SyncSession._get_or_create_loop()`). - Date-based versioning: `YYYY.MM.DD` format.