Skip to main content

Generate Pydantic v2 models and FastAPI routers from OpenAPI 3.0/3.1 specs

Project description

pyoas

Generate Pydantic v2 models and FastAPI routers from an OpenAPI spec. Organized as a uv workspace with independent, installable packages.

Packages

Package Purpose
pyoas Foundation: spec loading, ref resolution, tag extraction, Jinja2 rendering, CLI
pyoas Pydantic v2 model generation from OpenAPI schemas
pyoas[fastapi] FastAPI router + service stub generation + test scaffolding
pyoas[claude] Claude Code skill generation (optional)

pyoas[fastapi] depends on pyoas; both depend on pyoas. pyoas[claude] depends on pyoas.

Quick start

# Install
uv add pyoas[fastapi]

# Create a minimal config
cat > pyoas.yaml << 'EOF'
spec: openapi.yaml
output:
  models: src/generated/models
  routers: src/generated/routers
EOF

# Generate Pydantic models and FastAPI routers
uv run pyoas generate

# Optionally scaffold service stubs and test files
uv run pyoas scaffold services

A more complete config with all optional features:

spec: openapi.yaml
output:
  models: src/generated/models
  routers: src/generated/routers
services:
  generate: true
  output: src/services
  import_path: myapp.services  # Python import path for service module
tests:
  generate: true
  output: tests/generated
  not_found_exception: "HTTPException(status_code=404, detail='Not found')"
skills:
  generate: true  # requires pyoas[claude]

Then:

uv run pyoas generate   # models + routers + service stubs + tests + skills

Configuration reference (pyoas.yaml)

spec

Path to the OpenAPI 3.0/3.1 spec file (YAML or JSON). Resolved relative to the config file. Required.

output

Key Default Description
models src/generated/models Output directory for generated model files
routers src/generated/routers Output directory for generated router files
models_import (derived) Python import path for models; derived from models path if omitted
routers_import (derived) Python import path for routers; derived from routers path if omitted
source_root src Filesystem prefix stripped when deriving Python import paths

default_tag

Default: "default". Operations with no tag are grouped under this name.

model_config

Key Default Description
extra "ignore" Pydantic extra setting for response/shared models
request_extra "forbid" Pydantic extra setting for request-only models
frozen false Makes generated models immutable
populate_by_name true Allow populating fields by Python name as well as alias

fields

Key Default Description
snake_case true Convert camelCase field names to snake_case with an alias
enums_as_literals true Render small enums as Literal[...] instead of Enum subclasses

format

Key Default Description
enabled true Run ruff format on generated files after writing

templates

Key Default Description
models null Path to a directory of custom Jinja2 templates overriding model templates
routers null Path to a directory of custom Jinja2 templates overriding router templates

services

Key Default Description
generate false Scaffold service stub files
output src/services Output directory for service files
overwrite false Overwrite existing service files on re-run
import_path "" Python import path used by routers to import the service (e.g. myapp.services)

tests

Key Default Description
generate false Scaffold pytest test stub files
output tests/generated Output directory for test files
overwrite false Overwrite existing test files on re-run (default: append new test classes only)
not_found_exception null Exception expression used in test_not_found stubs (e.g. HTTPException(status_code=404))

skills

Requires pyoas[claude] to be installed.

Key Default Description
generate false Generate Claude Code skill files
output .claude/commands Output directory for skill files
overwrite false Overwrite existing skill files on re-run

Generated output

Models (pyoas)

One file per tag: {models_output}/{tag}/models.py. Schemas referenced by multiple tags go to {models_output}/shared/models.py.

src/generated/models/
  __init__.py
  pets/
    models.py      # Pet, PetCreate, PetList, ...
  shared/
    models.py      # schemas used by more than one tag

Routers (pyoas[fastapi])

One file per tag: {routers_output}/{tag}/router.py. An __init__.py at the root re-exports all routers.

src/generated/routers/
  __init__.py      # from .pets import router as pets_router; ...
  pets/
    router.py      # APIRouter with typed endpoint stubs

Service stubs

One file per tag: {services_output}/{tag}.py. Scaffolded once; never overwritten by default.

src/services/
  pets.py          # PetsService class with async method stubs

Test scaffolding

One test file per tag plus a shared conftest.py with model factories.

tests/generated/
  conftest.py      # make_pet(), make_pet_list(), ...
  test_pets.py     # TestListPets, TestCreatePet, TestGetPet, ...

Each test class covers one endpoint and includes:

  • test_endpoint_exists — verifies the route returns something other than 404/405
  • Validation tests for required fields, numeric bounds, string constraints, enum violations
  • test_not_found — verifies 404 when the service raises the configured exception
  • test_success — happy-path stub (auto-implemented for GET/DELETE, stubbed for others)

CLI reference

pyoas models        # generate Pydantic models only
pyoas fastapi       # generate FastAPI routers only
pyoas generate      # generate models + routers (+ services/tests/skills if configured)
pyoas scaffold services  # scaffold service stubs (skips existing files)

All commands accept:

  • --config PATH — path to config file (default: pyoas.yaml)
  • --tags TAG1,TAG2 — limit generation to specific tags
  • --clean — purge output directory before generating

Claude Code integration (pyoas[claude])

Install pyoas[claude] and set skills.generate: true in your config. Running pyoas generate will write Claude Code skill files to .claude/commands/:

Skill Invocation Purpose
implement-tests.md /implement-tests tests/generated/test_pets.py Implement all pytest.skip("implement me") stubs in a test file
add-test-case.md /add-test-case tests/generated/test_pets.py "scenario" Add a new test method for the described scenario
review-generated.md /review-generated Cross-reference generated code against the OpenAPI spec and flag issues

Development

# Install all workspace packages in editable mode
uv sync --all-packages

# Run all tests
uv run pytest

# Run tests for a single package
uv run pytest packages/pyoas[fastapi]/

# Update snapshots
uv run pytest packages/pyoas[fastapi]/ --snapshot-update

# Lint and type-check
uv run ruff check packages/
uv run mypy packages/

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyoas-0.1.0.tar.gz (58.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyoas-0.1.0-py3-none-any.whl (77.0 kB view details)

Uploaded Python 3

File details

Details for the file pyoas-0.1.0.tar.gz.

File metadata

  • Download URL: pyoas-0.1.0.tar.gz
  • Upload date:
  • Size: 58.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pyoas-0.1.0.tar.gz
Algorithm Hash digest
SHA256 38dc5019893f7ff8908f668d92b6c673dcec0c952c86d1a1fe9de6b2ca864faf
MD5 45a644f1d4d7f232b11e604cf5edff28
BLAKE2b-256 8a873e78a9e859947b4154a05b5015582e42395845c4c67e5f71b0276bd7cbce

See more details on using hashes here.

File details

Details for the file pyoas-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pyoas-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 77.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pyoas-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cf3bc06246e058747b3d5bb18a1979764c658e87f07a73930e638e21fe63db02
MD5 2f212af881161bdf17ec40d0ce10b6fe
BLAKE2b-256 e7bd5b70c4b28f21720fd88c6fefb98c405f137db32b49dfa9425594754699a4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page