The PyFly CLI provides command-line tools for project scaffolding, application management, database migrations, environment diagnostics, and framework information.
Install: uv sync --extra cli (requires Click, Rich, Jinja2, questionary)
Entry point: pyfly (registered as a console script via pyproject.toml)
- Overview
- Global Options
- pyfly new
- pyfly generate
- pyfly run
- pyfly info
- pyfly doctor
- pyfly db
- pyfly license
- pyfly sbom
- App Introspection
- pyfly shell
- pyfly openapi
- Development Workflow
The CLI is built with Click for command parsing and Rich for beautiful terminal output. When you run pyfly --help, you'll see an ASCII banner followed by the command listing.
All commands are organized as a Click group under the pyfly entry point:
pyfly
├── new — Create a new project
├── generate — Scaffold individual artifacts (alias: g)
│ ├── controller — REST or web/SSR controller + test
│ ├── service — @service class + test
│ ├── repository — Repository (SQL / Mongo / in-memory)
│ ├── entity — Model/entity (Pydantic + ORM)
│ ├── dto — Request/response DTOs
│ ├── aggregate — DDD aggregate root with domain events
│ ├── command — CQRS command + handler
│ ├── query — CQRS query + handler
│ ├── event — Domain event + listener
│ ├── saga — Saga orchestration skeleton
│ ├── scheduled — Scheduled job
│ ├── shell-command — @shell_component command
│ ├── migration — Database migration (delegates to pyfly db migrate)
│ └── resource — Full CRUD stack (entity + dto + repo + service + controller + test)
├── run — Start the application server [--profile/-p, -D/--define, --env, --debug, --watch]
├── info — Display framework information
├── doctor — Diagnose environment
├── db — Database migration commands
│ ├── init — Initialize Alembic
│ ├── migrate — Generate migration
│ ├── upgrade — Apply migrations
│ ├── downgrade — Revert migrations
│ ├── current — Show current revision
│ ├── history — List migration history
│ ├── heads — Show head revision(s)
│ ├── show — Show details of a revision
│ ├── revision — Create a new revision
│ ├── stamp — Stamp DB without running migrations
│ ├── merge — Merge multiple heads
│ └── reset — Downgrade to base then upgrade to head
├── license — Display the Apache 2.0 license
├── sbom — Software Bill of Materials
├── routes — List HTTP route mappings (offline or --url)
├── beans — List container beans (offline or --url)
├── env — Show resolved config and active profiles (offline or --url)
├── health — Show application health (offline or --url)
├── metrics — List metrics or a single metric detail (offline or --url)
├── conditions — Show auto-configuration condition report (offline or --url)
├── actuator — GET any actuator endpoint of a running app (--url required)
├── shell — Interactive REPL with the booted application context
└── openapi — Export the OpenAPI schema (--format json|yaml, -o FILE)
The CLI module follows the same patterns as the rest of PyFly:
src/pyfly/cli/
├── __init__.py
├── main.py # PyFlyCLI group, command registration
├── console.py # Shared Rich console, theme, print_banner()
├── new.py # pyfly new — project scaffolding (questionary TUI + Click CLI)
├── run.py # pyfly run — application server
├── info.py # pyfly info — environment information
├── doctor.py # pyfly doctor — environment diagnostics
├── db.py # pyfly db — Alembic migration management
├── license.py # pyfly license — display Apache 2.0 license
├── sbom.py # pyfly sbom — Software Bill of Materials
├── templates.py # Jinja2-based template renderer
└── templates/ # Jinja2 template files (.j2)
├── pyproject.toml.j2
├── app.py.j2
├── pyfly.yaml.j2
├── dockerfile.j2
├── readme.md.j2
├── ...
├── hex/ # Hexagonal archetype templates
├── web/ # Web (SSR) archetype templates
└── cli/ # CLI archetype templates
The CLI uses a custom Rich theme for consistent, colored output across all commands:
| Style | Color | Usage |
|---|---|---|
info |
Cyan | Informational messages, labels |
success |
Bold green | Success indicators (checkmarks) |
warning |
Bold yellow | Warnings (non-fatal issues) |
error |
Bold red | Errors (fatal issues) |
pyfly |
Bold magenta | PyFly branding elements |
dim |
Dim | Secondary text, descriptions |
All commands share a single console instance from pyfly.cli.console, ensuring consistent styling. The print_banner() function renders the PyFly ASCII art banner.
pyfly --version # Show PyFly version (from pyproject.toml)
pyfly --help # Show help with ASCII banner and all commandsThe --version flag reads the version from the pyfly package metadata.
Create a new PyFly project with a complete directory structure, configuration files, and starter code. Supports six archetypes, selective feature inclusion, and an interactive mode.
pyfly new <name> [OPTIONS] # Direct mode
pyfly new [OPTIONS] # Interactive mode (prompts for all options)| Argument | Required | Description |
|---|---|---|
name |
No | Project name. Omit to enter interactive mode. |
The project name is converted to a valid Python package name: my-service becomes my_service for the package directory. In interactive mode the wizard also prompts for a Package name (pre-filled with that derived value); if you change it, the custom package name is honored and used for the src/<package>/ layout and all template imports.
| Option | Default | Description |
|---|---|---|
--archetype |
core |
Project archetype (see below) |
--features |
Per archetype | Comma-separated PyFly extras (e.g. web,data-relational,cache) |
--directory |
. |
Parent directory where the project folder will be created |
--list |
— | Print available archetypes and features, then exit |
--git |
false |
Initialize a git repository with an initial commit after scaffolding |
--no-input |
false |
Never prompt; fail if a required name is missing (CI-friendly) |
| Archetype | Description | Default Features |
|---|---|---|
core |
Minimal microservice | (none) |
web-api |
Full REST API with layered architecture | web |
fastapi-api |
Full REST API with FastAPI and native OpenAPI | fastapi |
web |
Server-rendered web application with HTML templates | web |
hexagonal |
Hexagonal architecture (ports & adapters) | web |
library |
Reusable library package | (none) |
cli |
Command-line application with interactive shell | shell |
Features control which PyFly extras are included as dependencies and which config sections are generated:
| Feature | What it adds |
|---|---|
web |
HTTP server (Starlette), REST controllers, OpenAPI docs |
fastapi |
HTTP server (FastAPI), REST controllers, native OpenAPI |
granian |
Granian ASGI server (Rust/tokio, highest throughput) |
hypercorn |
Hypercorn ASGI server (HTTP/2 and HTTP/3 support) |
data-relational |
Data Relational -- SQL databases (SQLAlchemy ORM) |
data-document |
Data Document -- Document databases (Beanie ODM) |
eda |
Event-driven architecture, in-memory event bus |
cache |
Caching with in-memory adapter |
client |
Resilient HTTP client with retry and circuit breaker |
security |
JWT authentication, password hashing |
scheduling |
Cron-based task scheduling |
observability |
Prometheus metrics, OpenTelemetry tracing |
cqrs |
Command/Query Responsibility Segregation |
shell |
Spring Shell-inspired CLI commands with DI |
The core archetype creates a minimal microservice with configuration and Docker support:
my-service/
├── pyproject.toml
├── pyfly.yaml
├── Dockerfile
├── README.md
├── .gitignore
├── .env.example
├── src/
│ └── my_service/
│ ├── __init__.py
│ ├── app.py
│ └── main.py
└── tests/
├── __init__.py
└── conftest.py
The web-api archetype creates a full REST API with layered controllers, services, models, and repositories — all using PyFly stereotypes. The example uses a Todo CRUD API with title, completed, and description fields:
my-api/
├── pyproject.toml
├── pyfly.yaml
├── Dockerfile
├── README.md
├── .gitignore
├── .env.example
├── src/
│ └── my_api/
│ ├── __init__.py
│ ├── app.py
│ ├── main.py
│ ├── controllers/
│ │ ├── __init__.py
│ │ ├── health_controller.py # @rest_controller — /health
│ │ └── todo_controller.py # @rest_controller — CRUD /todos
│ ├── services/
│ │ ├── __init__.py
│ │ └── todo_service.py # @service — business logic
│ ├── models/
│ │ ├── __init__.py
│ │ └── todo.py # Pydantic request/response DTOs
│ └── repositories/
│ ├── __init__.py
│ └── todo_repository.py # @repository — in-memory store
└── tests/
├── __init__.py
├── conftest.py
└── test_todo_service.py
The web archetype creates a server-rendered HTML application with Jinja2 templates, static assets, and the @controller stereotype:
my-site/
├── pyproject.toml
├── pyfly.yaml
├── Dockerfile
├── README.md
├── .gitignore
├── .env.example
├── src/
│ └── my_site/
│ ├── __init__.py
│ ├── app.py
│ ├── main.py # ASGI entry with StaticFiles mount
│ ├── controllers/
│ │ ├── __init__.py
│ │ ├── health_controller.py # @rest_controller — /health
│ │ └── home_controller.py # @controller — / and /about
│ ├── services/
│ │ ├── __init__.py
│ │ └── page_service.py # @service — page context data
│ ├── templates/
│ │ ├── base.html # Base layout with nav and footer
│ │ ├── home.html # Home page
│ │ └── about.html # About page
│ └── static/
│ └── css/
│ └── style.css # Minimal stylesheet
└── tests/
├── __init__.py
├── conftest.py
└── test_home_controller.py
The web archetype uses @controller (instead of @rest_controller) for endpoints that return TemplateResponse objects. The main.py mounts a /static route for serving CSS, JavaScript, and images via Starlette's StaticFiles.
The hexagonal archetype creates a ports-and-adapters project with explicit domain, application, infrastructure, and API layers:
my-hex/
├── pyproject.toml
├── pyfly.yaml
├── Dockerfile
├── README.md
├── .gitignore
├── .env.example
├── src/
│ └── my_hex/
│ ├── __init__.py
│ ├── app.py
│ ├── main.py
│ ├── domain/
│ │ ├── __init__.py
│ │ ├── models.py # Domain entities (dataclasses)
│ │ ├── events.py # Domain events
│ │ └── ports/
│ │ ├── __init__.py
│ │ ├── inbound.py # Use-case Protocols
│ │ └── outbound.py # Repository Protocols
│ ├── application/
│ │ ├── __init__.py
│ │ └── services.py # @service — implements inbound ports
│ ├── infrastructure/
│ │ ├── __init__.py
│ │ ├── config.py # @configuration beans
│ │ └── adapters/
│ │ ├── __init__.py
│ │ └── persistence.py # @repository — implements outbound ports
│ └── api/
│ ├── __init__.py
│ ├── controllers.py # @rest_controller
│ └── dto.py # Pydantic request/response DTOs
└── tests/
├── __init__.py
├── conftest.py
├── domain/
│ ├── __init__.py
│ └── test_models.py
└── application/
├── __init__.py
└── test_services.py
The library archetype creates a minimal reusable library with PEP 561 py.typed marker:
my-library/
├── pyproject.toml
├── README.md
├── .gitignore
├── src/
│ └── my_library/
│ ├── __init__.py
│ └── py.typed
└── tests/
├── __init__.py
└── conftest.py
The cli archetype creates a command-line application with interactive shell, DI-powered commands, and service layer:
my-tool/
├── pyproject.toml
├── pyfly.yaml
├── Dockerfile
├── README.md
├── .gitignore
├── .env.example
├── src/
│ └── my_tool/
│ ├── __init__.py
│ ├── app.py
│ ├── main.py # CLI entry point (asyncio.run)
│ ├── commands/
│ │ ├── __init__.py
│ │ └── hello_command.py # @shell_component — example commands
│ └── services/
│ ├── __init__.py
│ └── greeting_service.py # @service — business logic
└── tests/
├── __init__.py
├── conftest.py
└── test_hello_command.py
The CLI archetype differs from other archetypes in several ways:
- No ASGI entry point — uses
asyncio.run(pyfly.run())instead of uvicorn/Starlette - No web section in
pyfly.yaml— no port, no adapter config - No
module:field in config — no ASGI app to reference - Shell enabled —
pyfly.shell.enabled: trueis set automatically - Dockerfile uses
CMD ["python", "-m", "my_tool.main"]instead of uvicorn, noEXPOSE
When pyfly new is run without a NAME argument, it enters interactive mode with a full-featured TUI experience powered by questionary:
$ pyfly new
╭─────────────────────────────────╮
│ PyFly Project Generator │
╰─────────────────────────────────╯
? Project name: my-service
? Package name: my_service
? Select archetype: (use arrow keys)
❯ core Minimal microservice with DI container and config
web-api Full REST API with controller/service/repository layers
web Server-rendered HTML with Jinja2 templates and static assets
hexagonal Clean architecture with domain isolation
library Reusable library with py.typed and packaging best practices
cli Command-line application with interactive shell and DI
? Select features: (space to toggle, enter to confirm)
❯ [x] web HTTP server, REST controllers, OpenAPI docs
[ ] data-relational Data Relational — SQL databases (SQLAlchemy ORM)
[ ] data-document Data Document — Document databases (Beanie ODM)
[ ] cache Caching with in-memory adapter
[ ] security JWT authentication, password hashing
[ ] eda Event-driven architecture, in-memory event bus
[ ] client Resilient HTTP client with retry and circuit breaker
[ ] scheduling Cron-based task scheduling
[ ] observability Prometheus metrics, OpenTelemetry tracing
[ ] cqrs Command/Query Responsibility Segregation
[ ] shell Spring Shell-inspired CLI commands with DI
╭─ Project Summary ───────────────╮
│ Name: my-service │
│ Package: my_service │
│ Archetype: web-api │
│ Features: web, data-relational │
╰─────────────────────────────────╯
? Create this project? Yes
Features:
- Arrow-key navigation for archetype selection (single-select)
- Space-bar toggling for feature selection (multi-select with checkbox)
- A dedicated Package name prompt — defaults to the package name derived from
the project name, but any custom value you enter is passed through to
generate_project(..., package_name=...)and used as the actual package directory and import root - Confirmation summary with Rich-styled panel before creation
- Graceful handling of Ctrl+C (exits cleanly without traceback)
The library archetype skips the feature selection step since libraries don't include PyFly extras.
If the target directory already exists, the command exits with an error. If an unknown feature is specified, the command lists valid features and exits.
# Create a microservice (core archetype, no features)
pyfly new order-service
# Create a REST API (includes health controller, Todo CRUD example)
pyfly new order-api --archetype web-api
# Create a server-rendered web application with HTML templates
pyfly new my-site --archetype web
# Create a hexagonal project with data and cache
pyfly new order-svc --archetype hexagonal --features web,data-relational,cache
# Create a CLI tool with interactive shell
pyfly new admin-tool --archetype cli
# Create a shared library
pyfly new common-utils --archetype library
# Create in a specific directory
pyfly new payment-service --directory /projects
# List all archetypes and features, then exit
pyfly new --list
# Scaffold and initialize a git repo with an initial commit
pyfly new order-service --git
# CI-friendly: fail fast if name is missing (no prompts)
pyfly new order-service --archetype web-api --no-input
# Interactive mode
pyfly newAfter creation, the CLI displays a Rich tree panel showing all created files and a hint to navigate into the project.
Scaffold individual artifacts into the current project. The generator detects the
project package and archetype automatically (from pyfly.yaml / src/ layout).
| Subcommand | Creates |
|---|---|
generate controller <Name> |
A REST controller (or web/SSR controller for the web archetype) + test |
generate service <Name> |
A @service class + test |
generate repository <Name> |
A repository (SQLAlchemy / Mongo / in-memory, data-aware) |
generate entity <Name> |
A model/entity (Pydantic + ORM when data is enabled) |
generate dto <Name> |
Request/response DTOs |
generate aggregate <Name> |
A DDD aggregate root with domain events |
generate command <Name> |
A CQRS command + handler |
generate query <Name> |
A CQRS query + handler |
generate event <Name> |
A domain event + listener |
generate saga <Name> |
A saga orchestration skeleton |
generate scheduled <Name> |
A scheduled job |
generate shell-command <Name> |
A @shell_component command |
generate migration |
A database migration (delegates to pyfly db migrate) |
generate resource <Name> |
Full CRUD stack: entity + dto + repository + service + controller + test |
Common flags: --dry-run (show planned files without writing), --force (overwrite existing files).
pyfly g service Pricing
pyfly g resource Product
pyfly g command OpenWallet --dry-runStart the PyFly application using the auto-configured ASGI server (Granian, Uvicorn, or Hypercorn).
pyfly run [OPTIONS]| Option | Default | Description |
|---|---|---|
--host |
0.0.0.0 |
Bind address |
--port |
From pyfly.yaml or 8080 |
Port number (resolved from: CLI flag → pyfly.web.port in config → 8080) |
--server |
From config or auto |
ASGI server: granian, uvicorn, hypercorn (auto-selects highest-priority installed) |
--workers |
From config or 0 |
Number of worker processes (0 = cpu_count) |
--reload |
false |
Enable auto-reload on code changes (for development) |
--app |
Auto-discovered | Application import path (e.g., myapp.main:app) |
--profile, -p <name> |
— | Activate a named profile (sets PYFLY_PROFILES_ACTIVE); repeatable or comma-separated |
-D, --define KEY=VALUE |
— | Override a config value at runtime (e.g. -D web.port=9000); the pyfly. prefix may be included or omitted; repeatable |
--env KEY=VALUE |
— | Set a raw environment variable for the app process; repeatable |
--debug |
false |
Enable debug logging (sets pyfly.logging.level.root=DEBUG) |
--watch <dir> |
— | Extra directory to watch in reload mode (implies --reload); repeatable |
Before discovery, pyfly run automatically adds the src/ directory to sys.path if it exists. This allows running from a src-layout project root without installing the project first.
When --app is not provided, pyfly run attempts to discover the application automatically using a three-stage resolution:
- Canonical: reads
pyfly.app.modulefrompyfly.yaml(nested underpyfly:key) - Flat fallback: reads
app.modulefrompyfly.yaml(flat layout) - Auto-discovery: scans
src/<package>/main.pyand constructs the import path
# pyfly.yaml — canonical layout
pyfly:
app:
module: my_service.main:appIf neither --app nor a discoverable config file is found, the command exits with a clear error:
No application found.
Provide --app flag or create a pyfly.yaml in the current directory.
When --server is not specified, pyfly run auto-selects the highest-priority installed ASGI server:
| Priority | Server | Condition |
|---|---|---|
| 1 | Granian | granian is importable |
| 2 | Uvicorn | uvicorn is importable |
| 3 | Hypercorn | hypercorn is importable |
Requires at least one ASGI server to be installed (Granian, Uvicorn, or Hypercorn). If none is available, the command suggests installing the web extra:
✗ No ASGI server found.
Install the web extra: pyfly[web]
# Development with auto-reload
pyfly run --reload
# Force Granian with 4 workers
pyfly run --server granian --workers 4
# Custom port
pyfly run --port 3000
# Explicit app path
pyfly run --app my_service.app:Application
# Production binding with Granian on all cores
pyfly run --host 0.0.0.0 --port 80 --server granian --workers 0
# Activate a profile (sets PYFLY_PROFILES_ACTIVE=staging)
pyfly run --profile staging
# Activate multiple profiles
pyfly run -p prod -p cloud
# or comma-separated
pyfly run --profile prod,cloud
# Override config values at runtime
pyfly run -D web.port=9000 -D logging.level.root=INFO
# Pass raw environment variables to the app process
pyfly run --env DATABASE_URL=postgresql://localhost/mydb --env FEATURE_X=1
# Enable debug logging
pyfly run --debug
# Watch extra directories in reload mode (implies --reload)
pyfly run --watch src/templates --watch src/static
# Combined: staging profile, debug, watch templates
pyfly run -p staging --debug --watch src/templatesDisplay comprehensive information about the PyFly installation and environment.
pyfly infoThe command displays two Rich tables:
1. Environment Table
| Field | Example |
|---|---|
| Python | 3.12.5 |
| Platform | macOS-14.5-arm64 |
| Architecture | arm64 |
2. Installed Extras Table
Shows the installation status of each optional module by attempting to import the underlying library. The table has two columns — Extra and Status:
| Extra | Detection Module |
|---|---|
web |
starlette |
data-relational |
sqlalchemy |
data-document |
beanie |
eda |
aiokafka |
kafka |
aiokafka |
rabbitmq |
aio_pika |
redis |
redis |
cache |
redis |
client |
httpx |
observability |
prometheus_client |
security |
jwt |
scheduling |
croniter |
cli |
rich |
Each extra shows as either installed (green) or not installed (dimmed).
This is useful for verifying which framework modules are available after installation, especially when using selective extras rather than full.
Run a comprehensive health check on your development environment, verifying Python version, tools, and PyFly installation.
pyfly doctor1. Python Version
Verifies Python >= 3.12. Displays the exact version with a pass/fail indicator.
✓ Python 3.12.5 # Pass
✗ Python 3.11.2 (requires >=3.12) # Fail
2. Virtual Environment
Checks if a virtual environment is active by comparing sys.prefix to sys.base_prefix. Shows a warning if not in a venv — this is non-fatal but recommended.
✓ Virtual environment active # In venv
! No virtual environment detected # Warning
3. Required Tools
Checks that essential tools are available on PATH using shutil.which():
| Tool | Purpose | Impact if Missing |
|---|---|---|
git |
Version control | Overall check fails |
uv |
Package manager | Overall check fails |
4. Optional Tools
Checks for recommended development tools:
| Tool | Purpose | Impact if Missing |
|---|---|---|
uvicorn |
ASGI server (pyfly run) |
Shown as - (dimmed) |
alembic |
Database migrations (pyfly db) |
Shown as - (dimmed) |
ruff |
Linter & formatter | Shown as - (dimmed) |
mypy |
Type checker | Shown as - (dimmed) |
Missing optional tools are shown with a - dash indicator (dimmed), while missing required tools use ✗ (red) and cause the overall check to fail.
5. PyFly Installation
Verifies that PyFly itself is importable and displays the installed version:
✓ pyfly v26.05.01
The doctor command ends with a clear summary:
- All checks passed! (green) — Environment is ready
- Some issues found. See above for details. (yellow) — Action needed
Database migration commands powered by Alembic. These commands wrap Alembic's functionality with PyFly-specific defaults and Rich output.
The migrate, upgrade, and downgrade subcommands expect alembic.ini to exist in the current directory (created by pyfly db init).
All pyfly db subcommands require Alembic to be installed. If not available:
✗ alembic is not installed.
Install the data-relational extra: pyfly[data-relational]
Initialize the Alembic migration environment in the current directory.
pyfly db initWhat it does:
- Creates an
alembic/directory with Alembic's standard structure - Creates
alembic.iniconfiguration file - Overwrites
alembic/env.pywith a PyFly-customized template that includes:async_engine_from_configfor async database support (asyncpg, aiosqlite)Base.metadatafrompyfly.data.relational.sqlalchemyas the target metadata for autogeneration- Support for both offline (SQL script) and online (async connection) migration modes
Error handling: If an alembic/ directory already exists, the command exits with an error rather than overwriting.
✗ Directory 'alembic' already exists. Remove it first if you want to re-initialize.
Auto-generate a new migration revision by comparing your SQLAlchemy models to the current database state.
pyfly db migrate [-m "description"]| Option | Description |
|---|---|
-m, --message |
Revision message describing the changes |
Examples:
pyfly db migrate -m "add user table"
pyfly db migrate -m "add order status column"This runs Alembic's revision --autogenerate, which:
- Compares your current
Base.metadata(all entity models) with the database - Generates upgrade/downgrade functions in a new version file under
alembic/versions/
Prerequisites: alembic.ini must exist (run pyfly db init first).
Apply pending migrations to bring the database up to a specific revision.
pyfly db upgrade [REVISION]| Argument | Default | Description |
|---|---|---|
revision |
head |
Target revision (use head for latest) |
Examples:
pyfly db upgrade # Apply all pending migrations
pyfly db upgrade head # Same as above (explicit)
pyfly db upgrade abc123 # Upgrade to specific revisionRevert the database to a previous revision.
pyfly db downgrade REVISION| Argument | Required | Description |
|---|---|---|
revision |
Yes | Target revision to downgrade to |
Examples:
pyfly db downgrade -1 # Revert one migration step
pyfly db downgrade abc123 # Revert to specific revision
pyfly db downgrade base # Revert all migrationsShow the current revision of the database.
pyfly db current [-v]| Option | Description |
|---|---|
-v, --verbose |
Show full revision details |
Examples:
pyfly db current # Show current revision identifier
pyfly db current -v # Show current revision with full detailsList the migration history.
pyfly db history [-v]| Option | Description |
|---|---|
-v, --verbose |
Show full details for each revision |
Examples:
pyfly db history # List all revisions
pyfly db history -v # List with full detailsShow the current head revision(s). Multiple heads indicate a branched migration graph.
pyfly db heads [-v]| Option | Description |
|---|---|
-v, --verbose |
Show full revision details |
Examples:
pyfly db heads # Show head revision(s)
pyfly db heads -v # Show with full detailsShow the details of a specific revision.
pyfly db show REVISION| Argument | Required | Description |
|---|---|---|
revision |
Yes | Revision identifier to inspect |
Examples:
pyfly db show abc123 # Show details of revision abc123
pyfly db show head # Show details of the current headCreate a new migration revision. By default creates an empty revision; use --autogenerate to diff your SQLAlchemy models against the database.
pyfly db revision [-m MSG] [--autogenerate]| Option | Description |
|---|---|
-m, --message |
Revision message describing the change |
--autogenerate |
Diff models against the database to generate upgrade/downgrade functions |
Note: pyfly db migrate is a shortcut for pyfly db revision --autogenerate. Use pyfly db revision directly when you need an empty revision with custom upgrade/downgrade logic.
Examples:
pyfly db revision -m "add index on users.email" # Empty revision
pyfly db revision -m "add order table" --autogenerate # Model-diff revisionStamp the database with a specific revision without running any migration SQL. Useful for marking an existing database as being at a known state.
pyfly db stamp REVISION| Argument | Required | Description |
|---|---|---|
revision |
Yes | Revision to stamp (e.g. head, or a specific revision ID) |
Examples:
pyfly db stamp head # Mark the DB as at the current head
pyfly db stamp abc123 # Mark the DB as at a specific revisionMerge two or more migration heads into a single new revision. Required when the migration graph has diverged and pyfly db heads shows multiple heads.
pyfly db merge <rev>... [-m MSG]| Argument/Option | Required | Description |
|---|---|---|
rev... |
Yes | Two or more revision identifiers to merge |
-m, --message |
No | Merge revision message |
Examples:
pyfly db merge abc123 def456 # Merge two heads
pyfly db merge abc123 def456 -m "merge branches" # With a messageDestructive: downgrade the database all the way to base (removing all applied migrations), then upgrade back to head. Prompts for confirmation unless --yes is passed.
pyfly db reset [--yes]| Option | Description |
|---|---|
--yes |
Skip the confirmation prompt (for scripted/CI use) |
Warning: This drops and re-applies all migrations. All data will be lost unless your downgrade functions preserve it. Use with care.
Examples:
pyfly db reset # Prompt before resetting
pyfly db reset --yes # Reset without prompting (CI/scripts)Instead of (or in addition to) running pyfly db upgrade by hand, PyFly can apply migrations automatically on application startup — the Flyway-style auto-migrate equivalent. This reuses the same Alembic environment created by pyfly db init, so the CLI commands above continue to work unchanged.
It is opt-in via configuration, not a CLI flag. Enable it in pyfly.yaml:
pyfly:
data:
relational:
url: postgresql+asyncpg://user:pass@localhost:5432/app
migrations:
enabled: true # run `alembic upgrade head` on startup
config: alembic.ini # Alembic config path (default: alembic.ini)
revision: head # target revision (default: head)When enabled, the app runs alembic upgrade <revision> against the same datasource (pyfly.data.relational.url) during startup. If alembic.ini is not found, the migration step is skipped with a warning suggesting you run pyfly db init — startup is not aborted. See Data Relational — Run Migrations on Startup for the full configuration reference.
Display the Apache 2.0 license text for the PyFly Framework.
pyfly licenseThe command first tries to read the LICENSE file from the installed package resources, then falls back to the project root filesystem. If no license file is found, it displays a summary with a link to the Apache 2.0 license.
Display the Software Bill of Materials (SBOM) — a table of all PyFly dependencies with their required and installed versions.
pyfly sbom [OPTIONS]| Option | Description |
|---|---|
--json |
Output as JSON instead of a Rich table |
The command displays a Rich table with three columns:
| Column | Description |
|---|---|
| Package | Dependency name |
| Required | Version specifier from pyproject.toml |
| Installed | Installed version (green) or not installed (yellow) |
The JSON output includes the PyFly version, license, and full dependency list — useful for compliance and auditing pipelines.
# Display as Rich table
pyfly sbom
# Export as JSON
pyfly sbom --jsonPyFly ships six commands for inspecting a running or offline application context. Each command is dual-mode:
- Offline (default): boots the application from source using
PYFLY_APP(or auto-detected frompyfly.yaml/src/) and introspects the in-process context directly. - Remote (
--url http://host:port): queries the/actuator/<endpoint>HTTP endpoint of an already-running application — no local boot required.
All six commands accept --json to emit raw JSON suitable for piping or scripting. Without --json, output is rendered via Rich for human readability.
List HTTP route mappings registered in the application.
pyfly routes [--url URL] [--json]| Option | Description |
|---|---|
--url URL |
Query a running app's /actuator/mappings instead of booting locally |
--json |
Output raw JSON |
Examples:
# Offline: boot the app and list routes
pyfly routes
# Remote: query a running instance
pyfly routes --url http://localhost:8080
# Raw JSON for scripting
pyfly routes --json | jq '.contexts[].mappings.dispatcherServlets'List all beans registered in the DI container.
pyfly beans [--url URL] [--json]| Option | Description |
|---|---|
--url URL |
Query a running app's /actuator/beans instead of booting locally |
--json |
Output raw JSON |
Examples:
pyfly beans
pyfly beans --url http://localhost:8080 --jsonShow the resolved configuration environment — active profiles, property sources, and individual property values.
pyfly env [--url URL] [--json]| Option | Description |
|---|---|
--url URL |
Query a running app's /actuator/env instead of booting locally |
--json |
Output raw JSON |
Examples:
pyfly env
pyfly env --url http://staging:8080 --json | jq '.activeProfiles'Show the application health status (analogous to Spring Boot's /actuator/health).
pyfly health [--url URL] [--json]| Option | Description |
|---|---|
--url URL |
Query a running app's /actuator/health instead of booting locally |
--json |
Output raw JSON |
Examples:
pyfly health
pyfly health --url http://localhost:8080List all available metrics names, or show the detail of a single metric.
pyfly metrics [NAME] [--url URL] [--json]| Argument/Option | Description |
|---|---|
NAME |
Optional metric name to fetch the detail for |
--url URL |
Query a running app's /actuator/metrics instead of booting locally |
--json |
Output raw JSON |
Examples:
# List all metric names (offline)
pyfly metrics
# Show detail for a specific metric (remote)
pyfly metrics http.server.requests --url http://localhost:8080
# Raw JSON
pyfly metrics --jsonShow the auto-configuration condition report — which conditions passed or failed and why.
pyfly conditions [--url URL] [--json]| Option | Description |
|---|---|
--url URL |
Query a running app's /actuator/conditions instead of booting locally |
--json |
Output raw JSON |
Examples:
pyfly conditions
pyfly conditions --url http://localhost:8080 --jsonGET any arbitrary actuator endpoint from a running application. This command is remote-only — --url is required.
pyfly actuator <endpoint> --url URL [--json]| Argument/Option | Required | Description |
|---|---|---|
endpoint |
Yes | Actuator path segment (e.g. info, loggers, threaddump) |
--url URL |
Yes | Base URL of the running application |
--json |
No | Output raw JSON (implied when the endpoint returns JSON) |
Examples:
# Fetch /actuator/info from a running app
pyfly actuator info --url http://localhost:8080
# Fetch /actuator/loggers and filter with jq
pyfly actuator loggers --url http://prod:8080 --json | jq '.loggers | keys'
# Inspect thread dump
pyfly actuator threaddump --url http://localhost:8080Open an interactive Python REPL with the booted application context pre-loaded. Useful for ad-hoc inspection, one-off data operations, and debugging beans interactively.
pyfly shell [-c EXPR] [--app APP]| Option | Description |
|---|---|
-c EXPR |
Execute a single Python expression and exit (non-interactive one-shot mode) |
--app APP |
Application import path (e.g. myapp.main:App); auto-detected from pyfly.yaml if omitted |
The following names are available in the shell without any import:
| Name | Description |
|---|---|
ctx |
The booted ApplicationContext |
container |
The DI container (ctx.container) |
bean(key) |
Shorthand for ctx.get_bean(key) — resolve any bean by type or name |
# Interactive shell
pyfly shell
# One-shot: print the active profile
pyfly shell -c "print(ctx.environment.active_profiles)"
# Resolve a bean and inspect it
pyfly shell -c "svc = bean('UserService'); print(svc)"
# Point at a specific app
pyfly shell --app myapp.app:ApplicationExport the application's OpenAPI schema to stdout or a file.
pyfly openapi [--format FORMAT] [-o FILE] [--app APP]| Option | Default | Description |
|---|---|---|
--format FORMAT |
json |
Output format: json or yaml |
-o FILE |
stdout | Write the schema to FILE instead of stdout |
--app APP |
Auto-detected | Application import path (e.g. myapp.main:App) |
# Print the JSON schema to stdout
pyfly openapi
# Save as YAML
pyfly openapi --format yaml -o openapi.yaml
# Pipe into a validator
pyfly openapi | npx @redocly/cli lint /dev/stdin
# Export JSON to a file
pyfly openapi --format json -o docs/openapi.jsonHere's a typical workflow using the CLI tools throughout the lifecycle of a PyFly project:
# 1. Check your environment is ready
pyfly doctor
# 2. Create a new project
pyfly new order-service
cd order-service
# 3. Verify framework info and installed extras
pyfly info
# 4. Initialize database migrations
pyfly db init
# 5. Create initial migration from your entity models
pyfly db migrate -m "initial schema"
# 6. Apply the migration
pyfly db upgrade
# 7. Start developing with auto-reload
pyfly run --reload
# 8. As you add/modify entities, generate new migrations
pyfly db migrate -m "add order status"
pyfly db upgrade
# 9. If you need to roll back a migration
pyfly db downgrade -1The CLI is built on Click, making it straightforward to add custom commands for your project. The entry point is defined in pyproject.toml:
[project.scripts]
pyfly = "pyfly.cli.main:cli"To add custom commands, create a Click command and register it with the CLI group:
import click
from pyfly.cli.main import cli
@click.command()
@click.argument("entity_name")
def generate_command(entity_name: str) -> None:
"""Generate boilerplate for a new entity."""
click.echo(f"Generating {entity_name} entity, repository, and service...")
cli.add_command(generate_command, name="generate")Thin wrappers over the project's own tools (exit code is passed through):
pyfly test [pytest args…] # run the test suite (pytest)
pyfly lint [ruff args…] # ruff check
pyfly format [--check] [paths…] # ruff format
pyfly typecheck [mypy args…] # mypy (defaults to src/)Manage PyFly feature extras in an existing project's pyproject.toml:
pyfly features # list features and which are enabled here
pyfly add cache security # add extras to the pyfly[...] dependency
pyfly remove cache --yes # remove extras (‑‑yes skips the prompt)After add/remove, run uv sync to install. add also prints feature-specific tips.
pyfly build wheel # uv build --wheel
pyfly build sdist # uv build --sdist
pyfly build info -o build-info.json # git SHA + timestamp for /actuator/info
pyfly build image --tag app:1 --builder pack # OCI image via Cloud Native Buildpacks
pyfly build image --tag app:1 --builder docker # OCI image via Dockereval "$(pyfly completion bash)" # or zsh; for fish: pyfly completion fish | source
pyfly upgrade # upgrade the installed pyfly package (uv/pip)Third-party packages can publish CLI subcommands under the pyfly.cli_plugins
entry-point group; they are auto-registered on the pyfly CLI.
# in a plugin package's pyproject.toml
[project.entry-points."pyfly.cli_plugins"]
mycmd = "my_pkg.cli:mycmd" # a click.Command/Grouppyfly plugins list # show discovered CLI plugins