|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +This file provides guidelines and context for AI coding agents working on the |
| 4 | +py-key-value project. For human developers, see [DEVELOPING.md](DEVELOPING.md). |
| 5 | + |
| 6 | +## Development Workflow |
| 7 | + |
| 8 | +### Required Pre-commit Checks |
| 9 | + |
| 10 | +All three checks must pass before committing: |
| 11 | + |
| 12 | +1. `make lint` - Runs Ruff formatting and linting (Python + Markdown) |
| 13 | +2. `make typecheck` - Runs Basedpyright type checking |
| 14 | +3. `make codegen` - Regenerates sync library from async |
| 15 | + |
| 16 | +Or run all three together: |
| 17 | + |
| 18 | +```bash |
| 19 | +make precommit |
| 20 | +``` |
| 21 | + |
| 22 | +### Testing Requirements |
| 23 | + |
| 24 | +- All new features require tests in both async and sync packages |
| 25 | +- Run `make test` to execute all test suites |
| 26 | +- Run `make test-aio` for async package tests only |
| 27 | +- Run `make test-sync` for sync package tests only |
| 28 | +- Test coverage should be maintained |
| 29 | + |
| 30 | +## Architecture |
| 31 | + |
| 32 | +### Async-First Development |
| 33 | + |
| 34 | +**This is a critical constraint**: Always modify the async package first. |
| 35 | + |
| 36 | +- **Primary codebase**: `key-value/key-value-aio/` (async implementation) |
| 37 | +- **Generated codebase**: `key-value/key-value-sync/` (DO NOT EDIT DIRECTLY) |
| 38 | +- **Sync generation**: Run `make codegen` to generate sync from async |
| 39 | + |
| 40 | +The sync library is automatically generated from the async library using |
| 41 | +`scripts/build_sync_library.py`. All changes must be made to the async |
| 42 | +package first, then regenerated into the sync package. |
| 43 | + |
| 44 | +### Monorepo Structure |
| 45 | + |
| 46 | +```text |
| 47 | +key-value/ |
| 48 | +├── key-value-aio/ # Primary async library |
| 49 | +├── key-value-sync/ # Generated sync library (DO NOT EDIT) |
| 50 | +├── key-value-shared/ # Shared utilities and types |
| 51 | +└── key-value-shared-test/ # Shared test utilities |
| 52 | +scripts/ |
| 53 | +├── build_sync_library.py # Codegen script for sync library |
| 54 | +└── bump_versions.py # Version management script |
| 55 | +``` |
| 56 | + |
| 57 | +## Code Style & Conventions |
| 58 | + |
| 59 | +### Python |
| 60 | + |
| 61 | +- **Formatter/Linter**: Ruff (configured in `pyproject.toml`) |
| 62 | +- **Line length**: 140 characters |
| 63 | +- **Type checker**: Basedpyright (strict mode) |
| 64 | +- **Runtime type checking**: Beartype (can be disabled via |
| 65 | + `PY_KEY_VALUE_DISABLE_BEARTYPE=true`) |
| 66 | +- **Python version**: 3.10+ (sync codegen targets 3.10) |
| 67 | + |
| 68 | +### Markdown |
| 69 | + |
| 70 | +- **Linter**: markdownlint (`.markdownlint.jsonc`) |
| 71 | +- **Line length**: 80 characters (excluding code blocks and tables) |
| 72 | + |
| 73 | +## Common Pitfalls |
| 74 | + |
| 75 | +### ManagedEntry Wrapper Objects |
| 76 | + |
| 77 | +Raw values are **NEVER** stored directly in backends. The `ManagedEntry` wrapper |
| 78 | +(from `key_value/shared/utils/managed_entry.py`) wraps values with metadata |
| 79 | +like TTL and creation timestamp, typically serialized to/from JSON. |
| 80 | + |
| 81 | +When implementing or debugging stores, remember that what's stored is not |
| 82 | +the raw value but a `ManagedEntry` containing: |
| 83 | + |
| 84 | +- The actual value |
| 85 | +- Creation timestamp |
| 86 | +- TTL metadata |
| 87 | + |
| 88 | +### Python Version Compatibility |
| 89 | + |
| 90 | +The sync codegen targets Python 3.10. Running the codegen script with a |
| 91 | +different Python version may produce unexpected results or compatibility |
| 92 | +issues. Use Python 3.10 when running `make codegen`. |
| 93 | + |
| 94 | +### Optional Backend Dependencies |
| 95 | + |
| 96 | +Store implementations have optional dependencies. Install extras as needed: |
| 97 | + |
| 98 | +```bash |
| 99 | +pip install py-key-value-aio[redis] # Redis support |
| 100 | +pip install py-key-value-aio[dynamodb] # DynamoDB support |
| 101 | +pip install py-key-value-aio[mongodb] # MongoDB support |
| 102 | +# etc. - see README.md for full list |
| 103 | +``` |
| 104 | + |
| 105 | +### Sync Package is Generated |
| 106 | + |
| 107 | +**Never edit files in `key-value/key-value-sync/` directly**. Any changes |
| 108 | +will be overwritten when `make codegen` runs. Always make changes in the |
| 109 | +async package and regenerate. Always run `make codegen` after making changes |
| 110 | +to the async package. You will need to include the generated code in your pull |
| 111 | +request. Nobody will generate it for you. This also means pull requests will contain |
| 112 | +two copies of your changes, this is intentional! |
| 113 | + |
| 114 | +## Make Commands Reference |
| 115 | + |
| 116 | +| Command | Purpose | |
| 117 | +|---------|---------| |
| 118 | +| `make sync` | Install all dependencies | |
| 119 | +| `make install` | Alias for `make sync` | |
| 120 | +| `make lint` | Lint Python + Markdown | |
| 121 | +| `make typecheck` | Run Basedpyright type checking | |
| 122 | +| `make test` | Run all test suites | |
| 123 | +| `make test-aio` | Run async package tests | |
| 124 | +| `make test-sync` | Run sync package tests | |
| 125 | +| `make test-shared` | Run shared package tests | |
| 126 | +| `make codegen` | Generate sync library from async | |
| 127 | +| `make precommit` | Run lint + typecheck + codegen | |
| 128 | +| `make build` | Build all packages | |
| 129 | + |
| 130 | +### Per-Project Commands |
| 131 | + |
| 132 | +Add `PROJECT=<path>` to target a specific package: |
| 133 | + |
| 134 | +```bash |
| 135 | +make lint PROJECT=key-value/key-value-aio |
| 136 | +make typecheck PROJECT=key-value/key-value-aio |
| 137 | +make test PROJECT=key-value/key-value-aio |
| 138 | +make build PROJECT=key-value/key-value-aio |
| 139 | +``` |
| 140 | + |
| 141 | +## Key Protocols and Interfaces |
| 142 | + |
| 143 | +### AsyncKeyValue Protocol |
| 144 | + |
| 145 | +The core async interface is `AsyncKeyValue` protocol from |
| 146 | +`key_value/aio/protocols/key_value.py`. All async stores implement this |
| 147 | +protocol, which defines: |
| 148 | + |
| 149 | +- `get`, `get_many` - Retrieve values |
| 150 | +- `put`, `put_many` - Store values with optional TTL |
| 151 | +- `delete`, `delete_many` - Remove values |
| 152 | +- `ttl`, `ttl_many` - Get TTL information |
| 153 | + |
| 154 | +### KeyValue Protocol (Sync) |
| 155 | + |
| 156 | +The sync mirror is `KeyValue` from `key_value/sync/code_gen/protocols/key_value.py`, |
| 157 | +generated from the async protocol. |
| 158 | + |
| 159 | +## Store Implementations |
| 160 | + |
| 161 | +Stores are located in: |
| 162 | + |
| 163 | +- Async: `key-value/key-value-aio/src/key_value/aio/stores/` |
| 164 | +- Sync: `key-value/key-value-sync/src/key_value/sync/code_gen/stores/` |
| 165 | + |
| 166 | +Available backends include: DynamoDB, Elasticsearch, Memcached, Memory, Disk, |
| 167 | +MongoDB, Redis, RocksDB, Valkey, Vault, Windows Registry, Keyring, and more. |
| 168 | + |
| 169 | +## Wrappers |
| 170 | + |
| 171 | +Wrappers add functionality to stores and are located in: |
| 172 | + |
| 173 | +- Async: `key-value/key-value-aio/src/key_value/aio/wrappers/` |
| 174 | +- Sync: `key-value/key-value-sync/src/key_value/sync/code_gen/wrappers/` |
| 175 | + |
| 176 | +Wrappers include: Compression, Encryption, Logging, Statistics, Retry, |
| 177 | +Timeout, Cache, Prefix, TTL clamping, and more. |
| 178 | + |
| 179 | +## Adapters |
| 180 | + |
| 181 | +Adapters simplify store interactions but don't implement the protocol directly. |
| 182 | +Located in: |
| 183 | + |
| 184 | +- Async: `key-value/key-value-aio/src/key_value/aio/adapters/` |
| 185 | +- Sync: `key-value/key-value-sync/src/key_value/sync/code_gen/adapters/` |
| 186 | + |
| 187 | +Key adapters: |
| 188 | + |
| 189 | +- `PydanticAdapter` - Type-safe Pydantic model storage |
| 190 | +- `RaiseOnMissingAdapter` - Raise exceptions for missing keys |
| 191 | + |
| 192 | +## Development Environment |
| 193 | + |
| 194 | +### Option 1: DevContainer (Recommended) |
| 195 | + |
| 196 | +The repository includes a DevContainer configuration for consistent development |
| 197 | +environments. Open in VSCode and select "Reopen in Container" when prompted. |
| 198 | + |
| 199 | +### Option 2: Local Development |
| 200 | + |
| 201 | +Prerequisites: |
| 202 | + |
| 203 | +- Python 3.10+ |
| 204 | +- `uv` for dependency management |
| 205 | +- Node.js and npm for markdown linting |
| 206 | + |
| 207 | +Setup: |
| 208 | + |
| 209 | +```bash |
| 210 | +make sync |
| 211 | +``` |
| 212 | + |
| 213 | +## CI/CD |
| 214 | + |
| 215 | +GitHub Actions workflows are in `.github/workflows/`: |
| 216 | + |
| 217 | +- `test.yml` - Run tests across packages |
| 218 | +- `publish.yml` - Publish packages to PyPI |
| 219 | +- `claude-on-mention.yml` - Claude Code assistant (can make PRs) |
| 220 | +- `claude-on-open-label.yml` - Claude triage assistant (read-only analysis) |
| 221 | + |
| 222 | +## Version Management |
| 223 | + |
| 224 | +To bump versions across all packages: |
| 225 | + |
| 226 | +```bash |
| 227 | +make bump-version VERSION=1.2.3 # Actual bump |
| 228 | +make bump-version-dry VERSION=1.2.3 # Dry run |
| 229 | +``` |
| 230 | + |
| 231 | +## Getting Help |
| 232 | + |
| 233 | +- For human developer documentation, see [DEVELOPING.md](DEVELOPING.md) |
| 234 | +- For library usage documentation, see [README.md](README.md) |
| 235 | +- For package-specific information, see READMEs in each package directory |
0 commit comments