PrestoPlot Development Assistant
Expert guidance for working with PrestoPlot, a Python library and CLI tool for text generation using YAML-based generative grammars. Supports idea generation, name generation, and story creation through a template system inspired by Tracery.
Project Overview
PrestoPlot provides:
YAML-based generative grammars with Python f-string syntaxCLI tool (`presto`) for running grammars and serving HTTP endpointsStorage layer with caching (MessagePack)Template rendering (f-string and Jinja2)Markov chain text generationDeterministic seeded generationCore Commands
**Testing:**
`./runtests.sh` - Run full test suite with coverage`uv run python -m pytest tests/test_specific.py` - Run single test`tox` - Test across multiple Python versions**Code Quality:**
`uv run ruff format src/ tests/` - Format code`uv run ruff check --fix src/ tests/` - Lint and auto-fix**Development:**
`uv sync --all-groups` - Install in development mode`uv build` - Build package`uv run presto --help` - Run CLIDevelopment Instructions
1. Code Style and Quality
When writing or editing Python code:
**Style:** PEP8 with CamelCase for classes, snake_case for variables/functions**Type Annotations:** Required for all functions, methods, and complex structures**Docstrings:** Google Style for all packages, modules, functions, classes, and methods**Variable Naming:** Concise, descriptive names; plural for collections; `is_`/`has_`/`should_` prefix for booleans**After changes:** Run `uv run ruff format` then `uv run ruff check --fix`2. Exception Handling
**Never raise:** `Exception`, `RuntimeError`, or built-in base exceptions**Define specific exceptions** in `src/prestoplot/exceptions.py`**Chain context:** Use `raise NewError from original_error`**Avoid interpolated strings** in exception messages; attach context as parametersExample:
```python
try:
...
except ValueError as exc:
raise GrammarParseError("Invalid grammar syntax", file_path) from exc
```
3. Testing Practices
**Always write tests first using pytest.**
**Organization:** Tests in `tests/` directory, named by module (e.g., `tests/test_storages.py` for `src/prestoplot/storages.py`)**Classes:** Group related tests in `TestX` classes (no `unittest.TestCase` inheritance)**Structure:** Arrange-Act-Assert pattern**Coverage:** Aim for 100% coverage under `src/`**Fixtures:** Move common fixtures to `tests/conftest.py`**Test one behavior per function** for clarity and isolation**Parametrized tests for multiple cases:**
```python
@pytest.mark.parametrize("input_val,expected", [(1, 2), (3, 4)])
def test_increment(input_val, expected):
assert increment(input_val) == expected
```
**Exception testing:**
```python
def test_raises_value_error():
with pytest.raises(ValueError, match="invalid input"):
parse_config("bad_input")
```
4. Test Coverage Pragmas
Use pragmas for untestable defensive code:
```python
except DeploymentError:
raise # pragma: no cover - defensive re-raise
if some_impossible_condition: # pragma: no branch
raise RuntimeError("This should never happen")
```
**TYPE_CHECKING blocks:**
```python
from typing import TYPE_CHECKING
if TYPE_CHECKING: # pragma: no cover
from prestoplot.types import GrammarConfig
```
5. Test Failure Resolution
**Fix failing tests immediately:**
1. Stop all development until failures are resolved
2. Identify root cause before making changes
3. Fix the underlying issue, not the test
4. Ensure all tests pass before continuing
5. Run full suite after fixes to prevent regression
6. Never ignore or skip failing tests without justification
6. Git Commit Style
Use conventional commit format:
**Format:** `<type>(<scope>): <description>`**Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`**First line:** Under 50 characters**Mood:** Present tense imperative ("add" not "added")Examples:
`feat(cli): add new grammar validation command``fix(storage): handle missing YAML files gracefully``test(grammars): add tests for include functionality`7. Creating New Conventions
When asked to create a new convention, add a section to `CLAUDE.md`:
Use second-level heading with short, descriptive titleFirst line elaborates on the "When..." of the headingUse bullet points for detailsUse full imperative sentencesKeep short and to the pointInclude brief examples for complex conventionsCore Architecture
**Storage Layer** (`storages.py`):
`FileStorage` - Loads YAML from filesystem`CompilingFileStorage` - Caches compiled grammars as MessagePack**Grammar Processing** (`grammars.py`):
Parses YAML with `include` supportf-string and Jinja2 template renderingRecursive grammar includes**Story Rendering** (`story.py`):
Entry point: `render_story(storage, name, start="Begin", seed=None, **kwargs)`Requires `Begin:` stanza in grammar files**Context Management** (`contexts.py`):
Manages random seeds and generation contextDeterministic generation with same seeds**Additional Components:**
`texts.py` - Template rendering engines`markov.py` - Markov chain generation`db.py` - Database utilities`http.py` - HTTP server`cli.py` - CLI entry pointGrammar File Format
YAML-based with Python f-string syntax:
**Required:** `Begin:` stanza as entry point**Includes:** `include:` to compose grammar files**Modes:** `reuse` (default), `pick` (no replacement), `markov` (chain generation)**Seeded generation:** Use keys (e.g., `{Name.character1}`)Test Structure
Unit tests in `tests/`Test data in `tests/data/` (sample YAML grammars)Coverage reporting enabled by default