pydantic model generator for atproto lexicons
1# pmgfal
2
3pydantic model generator for atproto lexicons
4
5rust-powered lexicon parsing and python code generation.
6
7## install
8
9```bash
10uv add pmgfal
11```
12
13## usage
14
15```bash
16# auto-detect ./lexicons or current dir
17uvx pmgfal
18
19# explicit paths
20uvx pmgfal ./lexicons -o ./src/atproto
21
22# filter by namespace
23uvx pmgfal -p fm.plyr
24
25# force regeneration (skip cache)
26uvx pmgfal --no-cache
27```
28
29## caching
30
31pmgfal caches generated models based on a hash of your lexicon files. on subsequent runs with unchanged lexicons, it copies from cache instead of regenerating.
32
33cache location:
34- unix (linux/macos/bsd): `~/.cache/pmgfal/` (or `$XDG_CACHE_HOME/pmgfal/`)
35- windows: `%LOCALAPPDATA%/pmgfal/`
36
37the cache key includes:
38- pmgfal version (cache invalidates on upgrade)
39- namespace prefix filter
40- content of all lexicon json files
41
42## output
43
44```python
45# auto-generated by pmgfal - do not edit
46
47from __future__ import annotations
48
49from pydantic import BaseModel, Field
50
51
52class FmPlyrTrack(BaseModel):
53 """fm.plyr.track record"""
54
55 uri: str
56 title: str
57 artist: str
58 duration_ms: int | None = Field(default=None, alias="durationMs")
59```
60
61## adoption guide
62
63### 1. add lexicons to your project
64
65```
66your-project/
67├── lexicons/
68│ └── fm/
69│ └── plyr/
70│ ├── track.json
71│ ├── like.json
72│ └── comment.json
73├── src/
74│ └── atproto/
75│ └── .gitkeep
76└── pyproject.toml
77```
78
79### 2. generate models
80
81```bash
82uvx pmgfal ./lexicons -o ./src/atproto -p fm.plyr
83```
84
85### 3. use in your code
86
87```python
88from your_project.atproto import FmPlyrTrack, FmPlyrLike
89
90track = FmPlyrTrack(
91 uri="at://did:plc:xyz/fm.plyr.track/123",
92 title="my song",
93 artist="me",
94)
95```
96
97### 4. regenerate when lexicons change
98
99**option a: pre-commit hook**
100
101```yaml
102# .pre-commit-config.yaml
103repos:
104 - repo: local
105 hooks:
106 - id: pmgfal
107 name: generate atproto models
108 entry: uvx pmgfal ./lexicons -o ./src/atproto -p fm.plyr
109 language: system
110 files: ^lexicons/.*\.json$
111 pass_filenames: false
112```
113
114**option b: justfile**
115
116```just
117# justfile
118generate:
119 uvx pmgfal ./lexicons -o ./src/atproto -p fm.plyr
120```
121
122**option c: github actions**
123
124```yaml
125# .github/workflows/ci.yml
126- name: generate models
127 run: uvx pmgfal ./lexicons -o ./src/atproto -p fm.plyr
128```
129
130caching ensures regeneration is fast (~0.3s for 300 lexicons) when files haven't changed.
131
132## external refs
133
134pmgfal bundles all `com.atproto.*` lexicons and automatically resolves external refs. for example, if your lexicon references `com.atproto.repo.strongRef`, pmgfal generates:
135
136```python
137class ComAtprotoRepoStrongRef(BaseModel):
138 uri: str
139 cid: str
140
141class FmPlyrLike(BaseModel):
142 subject: ComAtprotoRepoStrongRef # properly typed!
143 created_at: str = Field(alias="createdAt")
144```
145
146## how it works
147
1481. parses lexicon json using [atrium-lex](https://github.com/atrium-rs/atrium) (rust)
1492. resolves internal (`#localDef`) and external (`com.atproto.*`) refs
1503. generates pydantic v2 models with field aliases
1514. outputs standalone python - no atproto sdk dependency