124 lines
4.9 KiB
Markdown
124 lines
4.9 KiB
Markdown
# 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`, `swd`, `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.
|
|
|
|
## Documentation Site
|
|
|
|
Live docs are at **https://openocd-python.warehack.ing** (Starlight/Astro, 28 pages).
|
|
|
|
Source repo: `git@git.supported.systems:warehack.ing/openocd-python-docs.git`
|
|
|
|
Deployment (remote server):
|
|
```bash
|
|
ssh -A warehack-ing@openocd.warehack.ing "cd openocd-python-docs && git pull && make prod"
|
|
```
|
|
|
|
When adding or changing library API surface (new subsystems, types, exceptions, methods), update the corresponding docs pages:
|
|
- `guides/` -- usage guides with async/sync tabs
|
|
- `reference/types.mdx` -- frozen dataclass tables
|
|
- `reference/exceptions.mdx` -- error hierarchy
|
|
- `reference/method-index.mdx` -- method signatures
|
|
- `index.mdx` -- subsystem count on homepage
|
|
- `astro.config.mjs` -- sidebar entries
|
|
|
|
## Publishing to PyPI
|
|
|
|
```bash
|
|
uv build
|
|
uv publish dist/openocd_python-<version>* --username __token__ --password "$(python3 -c "import configparser; c=configparser.ConfigParser(); c.read('$HOME/.pypirc'); print(c['pypi']['password'])")"
|
|
```
|
|
|
|
## Git Workflow
|
|
|
|
This repo lives as a subtree inside the `mcjtag` monorepo. Push to the Gitea remote via:
|
|
```bash
|
|
cd ~/claude/mcjtag && git subtree push --prefix=openocd-python origin main
|
|
```
|