pmgfal#
pydantic model generator for atproto lexicons
rust-powered lexicon parsing and python code generation.
install#
uv add pmgfal
usage#
# 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#
# 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#
uvx pmgfal ./lexicons -o ./src/atproto -p fm.plyr
3. use in your code#
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
# .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
# justfile
generate:
uvx pmgfal ./lexicons -o ./src/atproto -p fm.plyr
option c: github actions
# .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:
class ComAtprotoRepoStrongRef(BaseModel):
uri: str
cid: str
class FmPlyrLike(BaseModel):
subject: ComAtprotoRepoStrongRef # properly typed!
created_at: str = Field(alias="createdAt")
how it works#
- parses lexicon json using atrium-lex (rust)
- resolves internal (
#localDef) and external (com.atproto.*) refs - generates pydantic v2 models with field aliases
- outputs standalone python - no atproto sdk dependency