Skip to content

Commit 53486da

Browse files
epwalshclaude
andcommitted
Add CLAUDE.md with development commands and architecture overview
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b5de1fe commit 53486da

1 file changed

Lines changed: 56 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Commands
6+
7+
```bash
8+
# Install in editable mode with dev dependencies
9+
pip install -e ".[dev]"
10+
11+
# Run all tests
12+
pytest
13+
14+
# Run a single test file
15+
pytest src/test/encode_test.py
16+
17+
# Run a single test by name
18+
pytest src/test/encode_test.py -k "test_name"
19+
20+
# Lint
21+
ruff check src/
22+
23+
# Type check
24+
mypy src/
25+
26+
# Format
27+
black src/
28+
isort src/
29+
```
30+
31+
## Architecture
32+
33+
This is a small Python library (`src/dataclass_extensions/`) that adds encode/decode, merge, and polymorphism capabilities to standard Python dataclasses. Requires Python 3.10+.
34+
35+
### Public API (all exported from `__init__.py`)
36+
37+
- **`encode`** (`encode.py`) — Singleton `Encoder` instance. Converts dataclasses (recursively) to JSON-safe dicts. Handles `datetime`, `pathlib.Path`, `Enum`, lists, dicts, sets. Supports `Registrable` subclasses by injecting a `"type"` key. Custom encoders registered via `encode.register_encoder(fn, *types)`.
38+
39+
- **`decode`** (`decode.py`) — Singleton `Decoder` instance. Reconstructs typed dataclasses from dicts using `typing.get_type_hints()`. Handles union types, generic containers (`list`, `set`, `tuple`, `dict`, `Sequence`, `Mapping`), forward references, `typing_extensions.Self`, `TypeAliasType` (3.12+), `Literal`, and `Registrable` dispatch via the `"type"` key. Raises `DecodeError` (subclass of `TypeError`) with chained failure messages. Custom decoders registered via `decode.register_decoder(fn, *types)`.
40+
41+
- **`merge`** (`merge.py`) — Creates a new dataclass instance by overlaying dict values onto an existing instance. Recurses into nested dataclass fields when the incoming value is a dict. Uses `_coerce` from `decode.py` for type coercion.
42+
43+
- **`merge_from_dotlist`** (`merge.py`) — Like `merge`, but accepts strings of the form `"field=value"` or `"--field=value"`. Values parsed as YAML. Dot notation targets nested fields (`"optimizer.lr=0.001"`).
44+
45+
- **`Registrable`** (`registrable.py`) — Base class enabling polymorphic encode/decode. Subclasses register with `@MyBase.register("name")`. Encoded with a `"type"` field; decoded by dispatching on that field.
46+
47+
- **`required_field`** (`utils.py`) — Helper for required dataclass fields without defaults.
48+
49+
- **`Dataclass`** (`types.py`) — Protocol type for type-checking dataclass instances.
50+
51+
### Key design patterns
52+
53+
- `encode` and `decode` are module-level singleton instances of `Encoder`/`Decoder` classes, not plain functions. Custom handlers are stored as class variables (`ClassVar`), so they're shared across all uses.
54+
- `decode.py`'s `_coerce` function is internal but reused by `merge.py` — avoid changing its signature.
55+
- Tests live in `src/test/` and are discovered by pytest via `testpaths = "src/test/"` in `pyproject.toml`.
56+
- `src/test/_type_alias.py` is intentionally excluded from ruff and mypy (it uses syntax only valid on newer Python versions).

0 commit comments

Comments
 (0)