google styled doc

This commit is contained in:
2026-03-08 00:29:23 +05:30
parent 37b892f695
commit f03e250763
7 changed files with 261 additions and 447 deletions

View File

@@ -1,6 +1,10 @@
""" """
FastAPI OpenAPI First — strict OpenAPI-first application bootstrap for FastAPI. FastAPI OpenAPI First — strict OpenAPI-first application bootstrap for FastAPI.
---
## Summary
FastAPI OpenAPI First is a **contract-first infrastructure library** that FastAPI OpenAPI First is a **contract-first infrastructure library** that
enforces OpenAPI as the single source of truth for FastAPI services. enforces OpenAPI as the single source of truth for FastAPI services.
@@ -9,28 +13,9 @@ deterministic, spec-driven application assembly. Every HTTP route,
method, and operation is defined in OpenAPI first and bound to Python method, and operation is defined in OpenAPI first and bound to Python
handlers explicitly via operationId. handlers explicitly via operationId.
The package is intentionally minimal and layered. Each module has a ---
single responsibility and exposes explicit contracts rather than
convenience facades.
---------------------------------------------------------------------- ## Installation
Architecture Overview
----------------------------------------------------------------------
The library is structured around four core responsibilities:
- loader: load and validate OpenAPI 3.x specifications (JSON/YAML)
- binder: bind OpenAPI operations to FastAPI routes via operationId
- app: OpenAPI-first FastAPI application bootstrap
- client: OpenAPI-first HTTP client driven by the same specification
- errors: explicit error hierarchy for contract violations
The package root acts as a **namespace**, not a facade. Consumers are
expected to import functionality explicitly from the appropriate module.
----------------------------------------------------------------------
Installation
----------------------------------------------------------------------
Install using pip: Install using pip:
@@ -40,44 +25,9 @@ Or with Poetry:
poetry add openapi-first poetry add openapi-first
Runtime dependencies are intentionally minimal: ---
- fastapi (server-side)
- httpx (client-side)
- openapi-spec-validator
- pyyaml (optional, for YAML specs)
The ASGI server (e.g., uvicorn) is an application-level dependency and is ## Quick start
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)
----------------------------------------------------------------------
Minimal OpenAPI-first FastAPI application: Minimal OpenAPI-first FastAPI application:
@@ -91,109 +41,43 @@ Minimal OpenAPI-first FastAPI application:
version="1.0.0", version="1.0.0",
) )
# Run with: OperationId-driven HTTP client:
# uvicorn my_service.main:api
Handler definitions (no decorators):
def get_health():
return {"status": "ok"}
OpenAPI snippet:
paths:
/health:
get:
operationId: get_health
responses:
"200":
description: OK
The binder guarantees:
- Every OpenAPI operationId has exactly one handler
- No undocumented routes exist
- All mismatches fail at application startup
----------------------------------------------------------------------
Client-Side Usage (OpenAPI → HTTP Client)
----------------------------------------------------------------------
The same OpenAPI specification can be used to construct a strict,
operationId-driven HTTP client.
Client construction:
from openapi_first.loader import load_openapi from openapi_first.loader import load_openapi
from openapi_first.client import OpenAPIClient from openapi_first.client import OpenAPIClient
spec = load_openapi("openapi.yaml") spec = load_openapi("openapi.yaml")
client = OpenAPIClient(spec) client = OpenAPIClient(spec)
Calling operations (operationId is the API):
response = client.get_health() response = client.get_health()
assert response.status_code == 200
assert response.json() == {"status": "ok"}
Path parameters must match the OpenAPI specification exactly: ---
response = client.get_item( ## Architecture
path_params={"item_id": 1}
)
Request bodies are passed explicitly: The library is structured around four core responsibilities:
response = client.create_item( - **loader**: Load and validate OpenAPI 3.x specifications (JSON/YAML)
body={"name": "Orange", "price": 0.8} - **binder**: Bind OpenAPI operations to FastAPI routes via operationId
) - **app**: OpenAPI-first FastAPI application bootstrap
- **client**: OpenAPI-first HTTP client driven by the same specification
- **errors**: Explicit error hierarchy for contract violations
Client guarantees: ---
- One callable per OpenAPI operationId
- No hardcoded URLs or HTTP methods in user code
- Path and body parameters must match the spec exactly
- Invalid or incomplete OpenAPI specs fail at client construction time
- No schema inference or mutation is performed
The client is transport-level only and returns `httpx.Response` ## Public API
objects directly. Response interpretation and validation are left to
the consumer or higher-level layers.
----------------------------------------------------------------------
Extensibility Model
----------------------------------------------------------------------
FastAPI OpenAPI First is designed to be extended via **explicit contracts**:
- Users MAY extend OpenAPI loading behavior (e.g. multi-file specs)
by wrapping or replacing `loader.load_openapi`
- Users MAY extend route binding behavior by building on top of
`binder.bind_routes`
- Users MAY layer additional validation (e.g. signature checks)
without modifying core modules
Users SHOULD NOT rely on FastAPI decorators for routing when using this
library. Mixing decorator-driven routes with OpenAPI-first routing
defeats the contract guarantees and is explicitly unsupported.
----------------------------------------------------------------------
Public API Surface
----------------------------------------------------------------------
The supported public API consists of the following top-level modules: The supported public API consists of the following top-level modules:
- openapi_first.app - `openapi_first.app`
- openapi_first.binder - `openapi_first.binder`
- openapi_first.loader - `openapi_first.loader`
- openapi_first.client - `openapi_first.client`
- openapi_first.errors - `openapi_first.errors`
Classes and functions should be imported explicitly from these modules. ---
No individual symbols are re-exported at the package root.
---------------------------------------------------------------------- ## Design Guarantees
Design Guarantees
----------------------------------------------------------------------
- OpenAPI is the single source of truth - OpenAPI is the single source of truth
- No undocumented routes can exist - No undocumented routes can exist
@@ -201,31 +85,8 @@ Design Guarantees
- All contract violations fail at application startup or client creation - All contract violations fail at application startup or client creation
- No hidden FastAPI magic or implicit behavior - No hidden FastAPI magic or implicit behavior
- Deterministic, testable application assembly - Deterministic, testable application assembly
- CI-friendly failure modes
FastAPI OpenAPI First favors correctness, explicitness, and contract ---
enforcement over convenience shortcuts.
## Core Philosophy
`FastAPI OpenAPI First` operates on the **Contract-as-Code** principle:
1. **Spec-Driven Routing**: The OpenAPI document *is* the router. Code only exists to fulfill established contracts.
2. **Startup Fail-Fast**: Binding mismatches (missing handlers or extra operations) are detected during app initialization, not at runtime.
3. **Decoupled Symmetry**: The same specification drives both the FastAPI server and the `httpx`-based client, ensuring type-safe communication.
## Documentation Design
Follow these "AI-Native" docstring principles to maximize developer and agent productivity:
### For Humans
- **Logical Grouping**: Document the Loader, Binder, and Client as distinct infrastructure layers.
- **Spec Snippets**: Always include the corresponding OpenAPI YAML/JSON snippet alongside Python examples.
### For LLMs
- **Full Path Linking**: Refer to cross-module dependencies using their full dotted paths (e.g., `openapi_first.loader.load_openapi`).
- **Complete Stubs**: Maintain high-fidelity `.pyi` stubs for all public interfaces to provide an optimized machine-context.
- **Traceable Errors**: Use specific `: description` pairs in `Raises` blocks to allow agents to accurately map errors to spec violations.
""" """
from . import app from . import app

View File

@@ -1,43 +1,32 @@
""" """
openapi_first.app
=========================
OpenAPI-first application bootstrap for FastAPI. OpenAPI-first application bootstrap for FastAPI.
---
## Summary
This module provides `OpenAPIFirstApp`, a thin but strict abstraction This module provides `OpenAPIFirstApp`, a thin but strict abstraction
that enforces OpenAPI as the single source of truth for a FastAPI service. that enforces OpenAPI as the single source of truth for a FastAPI service.
Core principles Notes:
--------------- **Core Principles:**
- The OpenAPI specification (JSON or YAML) defines the entire API surface.
- Every operationId in the OpenAPI spec must have a corresponding
Python handler function.
- Handlers are plain Python callables (no FastAPI decorators).
- FastAPI route registration is derived exclusively from the spec.
- FastAPI's autogenerated OpenAPI schema is fully overridden.
What this module does - The OpenAPI specification (JSON or YAML) defines the entire API surface
--------------------- - Every operationId in the OpenAPI spec must have a corresponding Python handler function
- Loads and validates an OpenAPI 3.x specification. - Handlers are plain Python callables (no FastAPI decorators)
- Dynamically binds HTTP routes to handler functions using operationId. - FastAPI route registration is derived exclusively from the spec
- Registers routes with FastAPI at application startup. - FastAPI's autogenerated OpenAPI schema is fully overridden
- Ensures runtime behavior matches the OpenAPI contract exactly.
What this module does NOT do **Responsibilities:**
----------------------------
- It does not generate OpenAPI specs.
- It does not generate client code.
- It does not introduce a new framework or lifecycle.
- It does not alter FastAPI dependency injection semantics.
Intended usage - Loads and validates an OpenAPI 3.x specification
-------------- - Dynamically binds HTTP routes to handler functions using operationId
This module is intended for teams that want: - Registers routes with FastAPI at application startup
- Ensures runtime behavior matches the OpenAPI contract exactly
- OpenAPI-first API development **Constraints:**
- Strong contract enforcement
- Minimal FastAPI boilerplate - This module intentionally does NOT: Generate OpenAPI specs, generate client code, introduce a new framework or lifecycle, or alter FastAPI dependency injection semantics.
- Predictable, CI-friendly failures for spec/implementation drift
""" """
from fastapi import FastAPI from fastapi import FastAPI
@@ -50,50 +39,30 @@ class OpenAPIFirstApp(FastAPI):
""" """
FastAPI application enforcing OpenAPI-first design. FastAPI application enforcing OpenAPI-first design.
`OpenAPIFirstApp` subclasses FastAPI and replaces manual route Notes:
registration with OpenAPI-driven binding. All routes are derived **Responsibilities:**
from the provided OpenAPI specification, and each operationId is
mapped to a Python function in the supplied routes module.
Parameters - `OpenAPIFirstApp` subclasses FastAPI and replaces manual route registration with OpenAPI-driven binding
---------- - All routes are derived from the provided OpenAPI specification, and each operationId is mapped to a Python function in the supplied routes module
openapi_path : str
Filesystem path to the OpenAPI 3.x specification file.
This specification is treated as the authoritative API contract.
routes_module : module **Guarantees:**
Python module containing handler functions whose names correspond
exactly to OpenAPI operationId values.
**fastapi_kwargs - No route can exist without an OpenAPI declaration
Additional keyword arguments passed directly to `fastapi.FastAPI` - No OpenAPI operation can exist without a handler
(e.g., title, version, middleware, lifespan handlers). - Swagger UI and `/openapi.json` always reflect the provided spec
- Handler functions remain framework-agnostic and testable
Raises Example:
------ ```python
OpenAPIFirstError from openapi_first import OpenAPIFirstApp
If the OpenAPI specification is invalid, or if any declared import app.routes as routes
operationId does not have a corresponding handler function.
Behavior guarantees app = OpenAPIFirstApp(
------------------- openapi_path="app/openapi.json",
- No route can exist without an OpenAPI declaration. routes_module=routes,
- No OpenAPI operation can exist without a handler. title="Example Service",
- Swagger UI and `/openapi.json` always reflect the provided spec. )
- Handler functions remain framework-agnostic and testable. ```
Example
-------
```python
from openapi_first import OpenAPIFirstApp
import app.routes as routes
app = OpenAPIFirstApp(
openapi_path="app/openapi.json",
routes_module=routes,
title="Example Service",
)
```
""" """
def __init__( def __init__(
@@ -103,6 +72,21 @@ class OpenAPIFirstApp(FastAPI):
routes_module, routes_module,
**fastapi_kwargs, **fastapi_kwargs,
): ):
"""
Initialize the application.
Args:
openapi_path (str):
Filesystem path to the OpenAPI 3.x specification file. This specification is treated as the authoritative API contract.
routes_module (module):
Python module containing handler functions whose names correspond exactly to OpenAPI operationId values.
**fastapi_kwargs (Any):
Additional keyword arguments passed directly to `fastapi.FastAPI` (e.g., title, version, middleware, lifespan handlers).
Raises:
OpenAPIFirstError:
If the OpenAPI specification is invalid, or if any declared operationId does not have a corresponding handler function.
"""
# Initialize FastAPI normally # Initialize FastAPI normally
super().__init__(**fastapi_kwargs) super().__init__(**fastapi_kwargs)

View File

@@ -1,36 +1,33 @@
""" """
openapi_first.binder
============================
OpenAPI-driven route binding for FastAPI. OpenAPI-driven route binding for FastAPI.
---
## Summary
This module is responsible for translating an OpenAPI 3.x specification This module is responsible for translating an OpenAPI 3.x specification
into concrete FastAPI routes. It enforces a strict one-to-one mapping into concrete FastAPI routes. It enforces a strict one-to-one mapping
between OpenAPI operations and Python handler functions using operationId. between OpenAPI operations and Python handler functions using operationId.
Core responsibility Notes:
------------------- **Core Responsibility:**
- Read path + method definitions from an OpenAPI specification
- Resolve each operationId to a Python callable
- Register routes with FastAPI using APIRoute
- Fail fast when contract violations are detected
Design constraints - Read path + method definitions from an OpenAPI specification
------------------ - Resolve each operationId to a Python callable
- All routes MUST be declared in the OpenAPI specification. - Register routes with FastAPI using APIRoute
- All OpenAPI operations MUST define an operationId. - Fail fast when contract violations are detected
- Every operationId MUST resolve to a handler function.
- Handlers are plain Python callables (no decorators required).
- No implicit route creation or inference is allowed.
This module intentionally does NOT: **Design Constraints:**
-------------------------------
- Perform request or response validation
- Generate Pydantic models
- Modify FastAPI dependency injection
- Interpret OpenAPI semantics beyond routing metadata
Those concerns belong to other layers or tooling. - All routes MUST be declared in the OpenAPI specification
- All OpenAPI operations MUST define an operationId
- Every operationId MUST resolve to a handler function
- Handlers are plain Python callables (no decorators required)
- No implicit route creation or inference is allowed
**Constraints:**
- This module intentionally does NOT: Perform request or response validation, generate Pydantic models, modify FastAPI dependency injection, or interpret OpenAPI semantics beyond routing metadata.
""" """
from fastapi.routing import APIRoute from fastapi.routing import APIRoute
@@ -42,33 +39,27 @@ def bind_routes(app, spec: dict, routes_module) -> None:
""" """
Bind OpenAPI operations to FastAPI routes. Bind OpenAPI operations to FastAPI routes.
Iterates through the OpenAPI specification paths and methods, Args:
resolves each operationId to a handler function, and registers app (fastapi.FastAPI):
a corresponding APIRoute on the FastAPI application. The FastAPI application instance to which routes will be added.
spec (dict):
Parsed OpenAPI 3.x specification dictionary.
routes_module (module):
Python module containing handler functions. Each handler's name MUST exactly match an OpenAPI operationId.
Parameters Raises:
---------- MissingOperationHandler:
app : fastapi.FastAPI If an operationId is missing from the spec or if no corresponding handler function exists in the routes module.
The FastAPI application instance to which routes will be added.
spec : dict Notes:
Parsed OpenAPI 3.x specification dictionary. **Responsibilities:**
routes_module : module - Iterates through the OpenAPI specification paths and methods
Python module containing handler functions. Each handler's - Resolves each operationId to a handler function, and registers a corresponding APIRoute on the FastAPI application
name MUST exactly match an OpenAPI operationId.
Raises **Guarantees:**
------
MissingOperationHandler
If an operationId is missing from the spec or if no corresponding
handler function exists in the routes module.
Behavior guarantees - Route registration is deterministic and spec-driven. No route decorators are required or supported. Handler resolution errors surface at application startup.
-------------------
- Route registration is deterministic and spec-driven.
- No route decorators are required or supported.
- Handler resolution errors surface at application startup.
""" """
paths = spec.get("paths", {}) paths = spec.get("paths", {})

View File

@@ -1,9 +1,10 @@
""" """
openapi_first.cli
========================
Command-line interface for FastAPI OpenAPI-first scaffolding utilities. Command-line interface for FastAPI OpenAPI-first scaffolding utilities.
---
## Summary
This CLI bootstraps OpenAPI-first FastAPI applications from versioned, This CLI bootstraps OpenAPI-first FastAPI applications from versioned,
bundled templates packaged with the library. bundled templates packaged with the library.
""" """
@@ -20,6 +21,10 @@ DEFAULT_TEMPLATE = "health_app"
def available_templates() -> list[str]: def available_templates() -> list[str]:
""" """
Return a list of available application templates. Return a list of available application templates.
Returns:
list[str]:
Sorted list of template names found in the internal templates directory.
""" """
root = resources.files("openapi_first.templates") root = resources.files("openapi_first.templates")
return sorted( return sorted(
@@ -32,6 +37,16 @@ def available_templates() -> list[str]:
def copy_template(template: str, target_dir: Path) -> None: def copy_template(template: str, target_dir: Path) -> None:
""" """
Copy a bundled OpenAPI-first application template into a directory. Copy a bundled OpenAPI-first application template into a directory.
Args:
template (str):
Name of the template to copy.
target_dir (Path):
Filesystem path where the template should be copied.
Raises:
FileNotFoundError:
If the requested template does not exist.
""" """
target_dir = target_dir.resolve() target_dir = target_dir.resolve()
target_dir.mkdir(parents=True, exist_ok=True) target_dir.mkdir(parents=True, exist_ok=True)

View File

@@ -1,59 +1,36 @@
""" """
openapi_first.client
====================
OpenAPI-first HTTP client for contract-driven services. OpenAPI-first HTTP client for contract-driven services.
---
## Summary
This module provides `OpenAPIClient`, a thin, strict HTTP client that This module provides `OpenAPIClient`, a thin, strict HTTP client that
derives all callable operations directly from an OpenAPI 3.x specification. derives all callable operations directly from an OpenAPI 3.x specification.
It is the client counterpart to `OpenAPIFirstApp`. It is the client counterpart to `OpenAPIFirstApp`.
Core principles Notes:
--------------- **Core Principles:**
- The OpenAPI specification is the single source of truth.
- Each operationId becomes a callable Python method.
- No implicit schema mutation or inference.
- No code generation step.
- Minimal abstraction over httpx.
What this module does - The OpenAPI specification is the single source of truth
--------------------- - Each operationId becomes a callable Python method
- Parses an OpenAPI 3.x specification. - No implicit schema mutation or inference
- Dynamically creates one callable per operationId. - No code generation step
- Enforces presence of: - Minimal abstraction over httpx
- servers
- paths
- operationId
- Formats path parameters safely.
- Handles JSON request bodies explicitly.
- Returns raw `httpx.Response` objects.
What this module does NOT do **Responsibilities:**
----------------------------
- It does not generate client code.
- It does not validate request/response schemas.
- It does not deserialize responses.
- It does not retry requests.
- It does not implement authentication helpers.
- It does not assume non-2xx responses are failures.
Intended usage - Parses an OpenAPI 3.x specification
-------------- - Dynamically creates one callable per operationId
This client is designed for: - Enforces presence of servers, paths, and operationId
- Formats path parameters safely
- Handles JSON request bodies explicitly
- Returns raw `httpx.Response` objects
- Service-to-service communication **Constraints:**
- Integration testing
- Contract-driven internal SDK usage
- Systems that want OpenAPI-first symmetry with `OpenAPIFirstApp`
Design constraints - This module intentionally does NOT: Generate client code, validate request/response schemas, deserialize responses, retry requests, implement authentication helpers, or assume non-2xx responses are failures.
------------------
- Only the first server in the OpenAPI `servers` list is used if
`base_url` is not explicitly provided.
- Only explicitly declared request bodies are allowed.
- `application/json` is handled natively; other media types are sent as raw content.
- All responses are returned as-is.
""" """
from typing import Any, Callable, Dict, Optional from typing import Any, Callable, Dict, Optional
@@ -65,73 +42,47 @@ from .errors import OpenAPIFirstError
class OpenAPIClientError(OpenAPIFirstError): class OpenAPIClientError(OpenAPIFirstError):
"""Raised when an OpenAPI client operation fails.""" """
Raised when an OpenAPI client operation fails.
"""
class OpenAPIClient: class OpenAPIClient:
""" """
OpenAPI-first HTTP client (httpx-based). OpenAPI-first HTTP client (httpx-based).
This client derives all callable methods directly from an OpenAPI 3.x Notes:
specification. Each operationId becomes a method on the client **Responsibilities:**
instance.
Design principles - This client derives all callable methods directly from an OpenAPI 3.x specification. Each operationId becomes a method on the client instance.
-----------------
- One callable per operationId
- Explicit parameters (path, query, headers, body)
- No implicit schema inference or mutation
- Returns raw httpx.Response objects
- No response validation or deserialization
Parameters **Guarantees:**
----------
spec : dict
Parsed OpenAPI 3.x specification.
base_url : str | None
Base URL of the target service. If omitted, the first entry
in the OpenAPI `servers` list is used.
client : httpx.Client | None
Optional preconfigured httpx client instance.
Raises - One callable per operationId
------ - Explicit parameters (path, query, headers, body)
OpenAPIClientError - No implicit schema inference or mutation
If: - Returns raw `httpx.Response` objects
- No servers are defined and base_url is not provided - No response validation or deserialization
- OpenAPI spec has no paths
- An operation is missing operationId
- Duplicate operationIds are detected
- Required path parameters are missing
- Required request body is missing
Example Example:
------- ```python
```python from openapi_first import loader, client
from openapi_first import loader, client
spec = loader.load_openapi("openapi.yaml") spec = loader.load_openapi("openapi.yaml")
api = client.OpenAPIClient( api = client.OpenAPIClient(
spec=spec, spec=spec,
base_url="http://localhost:8000", base_url="http://localhost:8000",
) )
# Call operationId: getUser # Call operationId: getUser
response = api.getUser( response = api.getUser(
path_params={"user_id": 123} path_params={"user_id": 123}
) )
print(response.status_code) print(response.status_code)
print(response.json()) print(response.json())
```
# Call operationId: createUser
response = api.createUser(
body={"name": "Bob"}
)
print(response.status_code)
```
""" """
def __init__( def __init__(
@@ -140,6 +91,21 @@ class OpenAPIClient:
base_url: Optional[str] = None, base_url: Optional[str] = None,
client: Optional[httpx.Client] = None, client: Optional[httpx.Client] = None,
) -> None: ) -> None:
"""
Initialize the OpenAPI client.
Args:
spec (dict[str, Any]):
Parsed OpenAPI 3.x specification.
base_url (str, optional):
Base URL of the target service. If omitted, the first entry in the OpenAPI `servers` list is used.
client (httpx.Client, optional):
Optional preconfigured httpx client instance.
Raises:
OpenAPIClientError:
If no servers are defined, spec has no paths, operationIds are missing/duplicate, or required parameters are missing.
"""
self.spec = spec self.spec = spec
self.base_url = base_url or self._resolve_base_url(spec) self.base_url = base_url or self._resolve_base_url(spec)
self.client = client or httpx.Client(base_url=self.base_url) self.client = client or httpx.Client(base_url=self.base_url)

View File

@@ -1,33 +1,34 @@
""" """
openapi_first.errors Exceptions for OpenAPI-first FastAPI applications.
============================
Custom exceptions for OpenAPI-first FastAPI applications. ---
## Summary
This module defines a small hierarchy of explicit, intention-revealing This module defines a small hierarchy of explicit, intention-revealing
exceptions used to signal contract violations between an OpenAPI exceptions used to signal contract violations between an OpenAPI
specification and its Python implementation. specification and its Python implementation.
Design principles Notes:
----------------- **Design Principles:**
- Errors represent *programmer mistakes*, not runtime conditions.
- All errors are raised during application startup.
- Messages are actionable and suitable for CI/CD output.
- Exceptions are explicit rather than reused from generic built-ins.
These errors should normally cause immediate application failure. - Errors represent programmer mistakes, not runtime conditions
- All errors are raised during application startup
- Messages are actionable and suitable for CI/CD output
- Exceptions are explicit rather than reused from generic built-ins
These errors should normally cause immediate application failure.
""" """
class OpenAPIFirstError(Exception): class OpenAPIFirstError(Exception):
""" """
Base exception for all OpenAPI-first enforcement errors. Base exception for all OpenAPI-first enforcement errors.
This exception exists to allow callers, test suites, and CI pipelines Notes:
to catch and distinguish OpenAPI contract violations from unrelated **Responsibilities:**
runtime errors.
All exceptions raised by the OpenAPI-first core should inherit from - This exception exists to allow callers, test suites, and CI pipelines to catch and distinguish OpenAPI contract violations from unrelated runtime errors
this type. - All exceptions raised by the OpenAPI-first core should inherit from this type
""" """
pass pass
@@ -36,27 +37,28 @@ class MissingOperationHandler(OpenAPIFirstError):
""" """
Raised when an OpenAPI operation cannot be resolved to a handler. Raised when an OpenAPI operation cannot be resolved to a handler.
This error occurs when: Notes:
- An OpenAPI operation does not define an operationId, or **Scenarios:**
- An operationId is defined but no matching function exists in the
provided routes module.
This represents a violation of the OpenAPI-first contract and - An OpenAPI operation does not define an operationId
indicates that the specification and implementation are out of sync. - An operationId is defined but no matching function exists in the provided routes module
**Guarantees:**
- This represents a violation of the OpenAPI-first contract and indicates that the specification and implementation are out of sync
""" """
def __init__(self, *, path: str, method: str, operation_id: str | None = None): def __init__(self, *, path: str, method: str, operation_id: str | None = None):
""" """
Parameters Initialize the error.
----------
path : str
The HTTP path declared in the OpenAPI specification.
method : str Args:
The HTTP method (as declared in the OpenAPI spec). path (str):
The HTTP path declared in the OpenAPI specification.
operation_id : str, optional method (str):
The operationId declared in the OpenAPI spec, if present. The HTTP method (as declared in the OpenAPI spec).
operation_id (str, optional):
The operationId declared in the OpenAPI spec, if present.
""" """
if operation_id: if operation_id:
message = ( message = (

View File

@@ -1,28 +1,27 @@
""" """
openapi_first.loaders
=============================
OpenAPI specification loading and validation utilities. OpenAPI specification loading and validation utilities.
---
## Summary
This module is responsible for loading an OpenAPI 3.x specification This module is responsible for loading an OpenAPI 3.x specification
from disk and validating it before it is used by the application. from disk and validating it before it is used by the application.
It enforces the principle that an invalid or malformed OpenAPI document It enforces the principle that an invalid or malformed OpenAPI document
must never reach the routing or runtime layers. must never reach the routing or runtime layers.
Design principles Notes:
----------------- **Design Principles:**
- OpenAPI is treated as an authoritative contract.
- Invalid specifications fail fast at application startup.
- Supported formats are JSON and YAML.
- Validation errors are surfaced clearly and early.
This module intentionally does NOT: - OpenAPI is treated as an authoritative contract
----------------------------------- - Invalid specifications fail fast at application startup
- Modify the OpenAPI document - Supported formats are JSON and YAML
- Infer missing fields - Validation errors are surfaced clearly and early
- Generate models or code
- Perform request/response validation at runtime **Constraints:**
- This module intentionally does NOT: Modify the OpenAPI document, infer missing fields, generate models or code, or perform request/response validation at runtime.
""" """
import json import json
@@ -39,8 +38,10 @@ class OpenAPISpecLoadError(OpenAPIFirstError):
""" """
Raised when an OpenAPI specification cannot be loaded or validated. Raised when an OpenAPI specification cannot be loaded or validated.
This error indicates that the OpenAPI document is unreadable, Notes:
malformed, or violates the OpenAPI 3.x specification. **Guarantees:**
- This error indicates that the OpenAPI document is unreadable, malformed, or violates the OpenAPI 3.x specification
""" """
pass pass
@@ -49,29 +50,23 @@ def load_openapi(path: str | Path) -> dict[str, Any]:
""" """
Load and validate an OpenAPI 3.x specification from disk. Load and validate an OpenAPI 3.x specification from disk.
The specification is parsed based on file extension and validated Args:
using a strict OpenAPI schema validator. Any error results in an path (str | Path):
immediate exception, preventing application startup. Filesystem path to an OpenAPI specification file. Supported extensions: .json, .yaml, .yml.
Parameters Returns:
---------- dict[str, Any]:
path : str or pathlib.Path Parsed and validated OpenAPI specification.
Filesystem path to an OpenAPI specification file.
Supported extensions:
- `.json`
- `.yaml`
- `.yml`
Returns Raises:
------- OpenAPISpecLoadError:
dict If the file does not exist, cannot be parsed, or fails OpenAPI schema validation.
Parsed and validated OpenAPI specification.
Raises Notes:
------ **Guarantees:**
OpenAPISpecLoadError
If the file does not exist, cannot be parsed, or fails - The specification is parsed based on file extension and validated using a strict OpenAPI schema validator
OpenAPI schema validation. - Any error results in an immediate exception, preventing application startup
""" """
spec_path = Path(path) spec_path = Path(path)