pydantic model generator for atproto lexicons
Python 51.9%
Rust 46.7%
Just 1.4%
Other 0.1%
14 1 0

Clone this repository

https://tangled.org/zzstoatzz.io/pmgfal
git@tangled.org:zzstoatzz.io/pmgfal

For self-hosted knots, clone URLs may differ based on your setup.

README.md

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#

  1. parses lexicon json using atrium-lex (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