# pmgfal pydantic model generator for atproto lexicons rust-powered lexicon parsing and python code generation. ## install ```bash uv add pmgfal ``` ## usage ```bash # auto-detect ./lexicons or current dir uvx pmgfal # explicit paths uvx pmgfal ./lexicons -o ./src/atproto # filter by namespace uvx pmgfal -p fm.plyr # force regeneration (skip cache) uvx pmgfal --no-cache ``` ## caching pmgfal caches generated models based on a hash of your lexicon files. on subsequent runs with unchanged lexicons, it copies from cache instead of regenerating. cache location: - unix (linux/macos/bsd): `~/.cache/pmgfal/` (or `$XDG_CACHE_HOME/pmgfal/`) - windows: `%LOCALAPPDATA%/pmgfal/` the cache key includes: - pmgfal version (cache invalidates on upgrade) - namespace prefix filter - content of all lexicon json files ## output ```python # auto-generated by pmgfal - do not edit from __future__ import annotations from pydantic import BaseModel, Field class FmPlyrTrack(BaseModel): """fm.plyr.track record""" uri: str title: str artist: str duration_ms: int | None = Field(default=None, alias="durationMs") ``` ## adoption guide ### 1. add lexicons to your project ``` your-project/ ├── lexicons/ │ └── fm/ │ └── plyr/ │ ├── track.json │ ├── like.json │ └── comment.json ├── src/ │ └── atproto/ │ └── .gitkeep └── pyproject.toml ``` ### 2. generate models ```bash uvx pmgfal ./lexicons -o ./src/atproto -p fm.plyr ``` ### 3. use in your code ```python from your_project.atproto import FmPlyrTrack, FmPlyrLike track = FmPlyrTrack( uri="at://did:plc:xyz/fm.plyr.track/123", title="my song", artist="me", ) ``` ### 4. regenerate when lexicons change **option a: pre-commit hook** ```yaml # .pre-commit-config.yaml repos: - repo: local hooks: - id: pmgfal name: generate atproto models entry: uvx pmgfal ./lexicons -o ./src/atproto -p fm.plyr language: system files: ^lexicons/.*\.json$ pass_filenames: false ``` **option b: justfile** ```just # justfile generate: uvx pmgfal ./lexicons -o ./src/atproto -p fm.plyr ``` **option c: github actions** ```yaml # .github/workflows/ci.yml - name: generate models run: uvx pmgfal ./lexicons -o ./src/atproto -p fm.plyr ``` caching ensures regeneration is fast (~0.3s for 300 lexicons) when files haven't changed. ## external refs pmgfal bundles all `com.atproto.*` lexicons and automatically resolves external refs. for example, if your lexicon references `com.atproto.repo.strongRef`, pmgfal generates: ```python class ComAtprotoRepoStrongRef(BaseModel): uri: str cid: str class FmPlyrLike(BaseModel): subject: ComAtprotoRepoStrongRef # properly typed! created_at: str = Field(alias="createdAt") ``` ## how it works 1. parses lexicon json using [atrium-lex](https://github.com/atrium-rs/atrium) (rust) 2. resolves internal (`#localDef`) and external (`com.atproto.*`) refs 3. generates pydantic v2 models with field aliases 4. outputs standalone python - no atproto sdk dependency