Include development guidance for future contributors and pin dependency versions via uv.lock for reproducible builds.
3.8 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Build & Development Commands
# 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 managersSession.connect_sync()/Session.start_sync()-- sync context managers returningSyncSession
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-- aTclRpcConnectionconnected to the mocksession-- a fullSessionconnected 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.DDformat.