docs, cli: enforce package-bound docs, add template scaffolding, and document CLI usage

- Restrict mkdocstrings generation to real Python packages (require __init__.py)
- Add explicit documentation section for CLI scaffolding and templates
- Generalize CLI to support multiple templates with dynamic discovery
- Package templates correctly for importlib.resources access
- Add fully documented health_app template (app entry point and handlers)
- Fix setuptools package-data configuration for bundled templates

These changes make documentation import-safe, clarify package boundaries,
and provide a deterministic, OpenAPI-first scaffolding workflow via CLI.
This commit is contained in:
2026-01-11 19:26:21 +05:30
parent 6180443327
commit 72b5be6976
13 changed files with 273 additions and 74 deletions

View File

@@ -49,6 +49,32 @@ Runtime dependencies are intentionally minimal:
The ASGI server (e.g., uvicorn) is an application-level dependency and is
not bundled with this library.
----------------------------------------------------------------------
Command-Line Interface (Scaffolding, Templates)
----------------------------------------------------------------------
FastAPI OpenAPI First ships with a small CLI for bootstrapping
OpenAPI-first FastAPI applications from bundled templates.
List available application templates:
openapi-first --list
Create a new application using the default template:
openapi-first
Create a new application using a specific template:
openapi-first health_app
Create a new application in a custom directory:
openapi-first health_app my-service
The CLI copies template files verbatim into the target directory.
No code is generated or modified beyond the copied scaffold.
----------------------------------------------------------------------
Server-Side Usage (OpenAPI → FastAPI)
----------------------------------------------------------------------

View File

@@ -4,16 +4,8 @@ openapi_first.cli
Command-line interface for FastAPI OpenAPI-first scaffolding utilities.
This module provides a small, focused CLI intended to help developers
quickly bootstrap OpenAPI-first FastAPI services using bundled project
templates.
Currently supported scaffolds:
- Health check service (minimal OpenAPI-first application)
The CLI copies versioned templates packaged with the library into a
user-specified directory, allowing rapid local development without
manual setup.
This CLI bootstraps OpenAPI-first FastAPI applications from versioned,
bundled templates packaged with the library.
"""
import argparse
@@ -22,68 +14,75 @@ from pathlib import Path
from importlib import resources
def copy_health_app_template(target_dir: Path) -> None:
DEFAULT_TEMPLATE = "health_app"
def available_templates() -> list[str]:
"""
Copy the bundled OpenAPI-first health app template into a directory.
Return a list of available application templates.
"""
root = resources.files("openapi_first.templates")
return sorted(
item.name
for item in root.iterdir()
if item.is_dir() and not item.name.startswith("_")
)
This function copies a fully working, minimal OpenAPI-first FastAPI
health check application from the package's embedded templates into
the specified target directory.
The target directory will be created if it does not already exist.
Existing files may be overwritten.
Parameters
----------
target_dir : pathlib.Path
Destination directory into which the health app template
should be copied.
Raises
------
FileNotFoundError
If the bundled health app template cannot be located.
def copy_template(template: str, target_dir: Path) -> None:
"""
Copy a bundled OpenAPI-first application template into a directory.
"""
target_dir = target_dir.resolve()
target_dir.mkdir(parents=True, exist_ok=True)
with resources.files("openapi_first.templates").joinpath(
"health_app"
) as src:
shutil.copytree(src, target_dir, dirs_exist_ok=True)
root = resources.files("openapi_first.templates")
src = root / template
if not src.exists():
raise FileNotFoundError(
f"Template '{template}' not found. "
f"Available templates: {', '.join(available_templates())}"
)
with resources.as_file(src) as path:
shutil.copytree(path, target_dir, dirs_exist_ok=True)
def main() -> None:
"""
Entry point for the FastAPI OpenAPI-first CLI.
Parses command-line arguments and initializes a new OpenAPI-first
health check application by copying the bundled template into the
specified directory.
If no target path is provided, the scaffold is created in a directory
named ``health-app`` in the current working directory.
Example
-------
Create a health app in the default directory::
openapi-first
Create a health app in a custom directory::
openapi-first my-service
"""
parser = argparse.ArgumentParser(
description="FastAPI OpenAPI-first scaffolding tools"
)
parser.add_argument(
"template",
nargs="?",
default=DEFAULT_TEMPLATE,
help=f"Template name (default: {DEFAULT_TEMPLATE})",
)
parser.add_argument(
"path",
nargs="?",
default="health-app",
help="Target directory for the health app",
default=None,
help="Target directory (defaults to template name)",
)
parser.add_argument(
"--list",
action="store_true",
help="List available templates and exit",
)
args = parser.parse_args()
copy_health_app_template(Path(args.path))
print(f"Health app created at {args.path}")
if args.list:
for name in available_templates():
print(name)
return
target = Path(args.path or args.template.replace("_", "-"))
try:
copy_template(args.template, target)
except Exception as exc:
raise SystemExit(str(exc)) from exc
print(f"Template '{args.template}' created at {target}")

View File

@@ -0,0 +1,18 @@
"""
Application templates for FastAPI OpenAPI First.
This package contains example and scaffolding templates intended to be
copied into user projects via the ``openapi-first`` CLI.
Templates in this package are:
- Reference implementations of OpenAPI-first services
- Not part of the ``openapi_first`` public or internal API
- Not intended to be imported as runtime dependencies
The presence of this file exists solely to:
- Mark the directory as an explicit Python package
- Enable deterministic tooling behavior (documentation, packaging)
- Avoid accidental traversal of non-package directories
No code in this package should be imported by library consumers.
"""

View File

@@ -0,0 +1,65 @@
"""
OpenAPI-first FastAPI application template.
This package contains a minimal, fully working example of an
OpenAPI-first FastAPI service built using the ``openapi_first`` library.
The application is assembled exclusively from:
- an OpenAPI specification (``openapi.yaml``)
- a handler namespace (``routes``)
No routing decorators, implicit behavior, or framework-specific
convenience abstractions are used. All HTTP routes, methods, and
operation bindings are defined in OpenAPI and enforced at application
startup.
This package is intended to be copied as a starting point for new
services via the ``openapi-first`` CLI. It is not part of the
``openapi_first`` library API surface.
----------------------------------------------------------------------
Scaffolding via CLI
----------------------------------------------------------------------
Create a new OpenAPI-first health check service using the bundled
template:
openapi-first health_app
Create the service in a custom directory:
openapi-first health_app my-health-service
List all available application templates:
openapi-first --list
The CLI copies template files verbatim into the target directory.
No code is generated or modified beyond the copied scaffold.
----------------------------------------------------------------------
Client Usage Example
----------------------------------------------------------------------
The same OpenAPI specification used by the server can be used to
construct a strict, operationId-driven HTTP client.
Example client call for the ``get_health`` operation:
from openapi_first.loader import load_openapi
from openapi_first.client import OpenAPIClient
spec = load_openapi("openapi.yaml")
client = OpenAPIClient(spec)
response = client.get_health()
assert response.status_code == 200
assert response.json() == {"status": "ok"}
Client guarantees:
- One callable per OpenAPI ``operationId``
- No hardcoded URLs or HTTP methods in user code
- Path and request parameters must match the OpenAPI specification
- Invalid or incomplete OpenAPI specs fail at client construction time
"""

View File

@@ -1,3 +1,31 @@
"""
Application entry point for an OpenAPI-first FastAPI service.
This module constructs a FastAPI application exclusively from an
OpenAPI specification and a handler namespace, without using
decorator-driven routing.
All HTTP routes, methods, and operation bindings are defined in the
OpenAPI document referenced by ``openapi_path``. Python callables
defined in the ``routes`` module are bound to OpenAPI operations
strictly via ``operationId``.
This module contains no routing logic, request handling, or framework
configuration beyond application assembly.
Design guarantees:
- OpenAPI is the single source of truth
- No undocumented routes can exist
- Every OpenAPI operationId must resolve to exactly one handler
- All contract violations fail at application startup
This file is intended to be used as the ASGI entry point.
Example:
uvicorn main:app
"""
from openapi_first.app import OpenAPIFirstApp
import routes

View File

@@ -1,2 +1,32 @@
"""
OpenAPI operation handlers.
This module defines pure Python callables that implement OpenAPI
operations for this service. Functions in this module are bound to HTTP
routes exclusively via OpenAPI ``operationId`` values.
No routing decorators, HTTP metadata, or framework-specific logic
should appear here. All request/response semantics are defined in the
OpenAPI specification.
This module serves solely as an operationId namespace.
"""
def get_health():
"""
Health check operation handler.
This function implements the OpenAPI operation identified by
``operationId: get_health``.
It contains no routing metadata or framework-specific logic.
Request binding, HTTP method, and response semantics are defined
exclusively by the OpenAPI specification.
Returns
-------
dict
A minimal liveness payload indicating service health.
"""
return {"status": "ok"}