Metadata-Version: 2.4
Name: pmgfal
Version: 0.0.3
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Rust
Classifier: Topic :: Software Development :: Code Generators
Classifier: Typing :: Typed
License-File: LICENSE
Summary: pydantic model generator for atproto lexicons
Keywords: atproto,bluesky,lexicon,pydantic,codegen
Author-email: zzstoatzz <thrast36@gmail.com>
License-Expression: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM

# 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/models

# 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:
- macos: `~/Library/Caches/pmgfal/`
- linux: `~/.cache/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/
│   └── models/
│       └── .gitkeep
└── pyproject.toml
```

### 2. generate models

```bash
uvx pmgfal ./lexicons -o ./src/models -p fm.plyr
```

### 3. use in your code

```python
from your_project.models 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/models -p fm.plyr
        language: system
        files: ^lexicons/.*\.json$
        pass_filenames: false
```

**option b: justfile**

```just
# justfile
generate:
    uvx pmgfal ./lexicons -o ./src/models -p fm.plyr
```

**option c: github actions**

```yaml
# .github/workflows/ci.yml
- name: generate models
  run: uvx pmgfal ./lexicons -o ./src/models -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

