A pluggable API specification generator for OpenAPI/Swagger specifications
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Utility functions for OpenAPI reference building, docstring processing, dictionary manipulation, and YAML serialization with docstring parsing capabilities. These utilities support the core APISpec functionality and provide helpers for common OpenAPI operations.
Build OpenAPI reference objects for component linking and reusability.
def build_reference(
component_type: str,
openapi_major_version: int,
component_name: str
) -> dict[str, str]:
"""
Build OpenAPI reference object for a component.
Parameters:
- component_type: Type of component ('schema', 'parameter', 'response', 'header', 'example', 'security_scheme')
- openapi_major_version: OpenAPI major version (2 or 3)
- component_name: Name of the component to reference
Returns:
Dictionary with $ref key pointing to the component location
Examples:
- OpenAPI 2.x: {"$ref": "#/definitions/User"}
- OpenAPI 3.x: {"$ref": "#/components/schemas/User"}
"""Process and clean docstrings for use in OpenAPI documentation.
def trim_docstring(docstring: str) -> str:
"""
Uniformly trim leading/trailing whitespace from docstrings.
Parameters:
- docstring: Raw docstring text
Returns:
Cleaned docstring with consistent indentation removed
Based on PEP 257 docstring handling conventions.
"""
def dedent(content: str) -> str:
"""
Remove leading indent from a block of text.
Parameters:
- content: Text content with potential leading indentation
Returns:
Text with consistent leading indentation removed
Used for processing multiline docstrings and descriptions.
More robust than textwrap.dedent for mixed indentation patterns.
"""Utility for deep dictionary updates and merging.
def deepupdate(original: dict, update: dict) -> dict:
"""
Recursively update a dictionary without overwriting nested dictionaries.
Parameters:
- original: Original dictionary to update
- update: Dictionary containing updates
Returns:
Updated dictionary with deep merging of nested structures
Nested dictionaries are merged rather than replaced entirely.
"""Convert dictionaries to YAML format with OpenAPI-optimized settings.
def dict_to_yaml(dic: dict, yaml_dump_kwargs: Any | None = None) -> str:
"""
Serialize dictionary to YAML string.
Parameters:
- dic: Dictionary to serialize
- yaml_dump_kwargs: Additional arguments passed to yaml.dump()
Returns:
YAML string representation
Default behavior preserves key ordering (sort_keys=False) to respect
schema field ordering in OpenAPI specifications.
"""Extract YAML content and OpenAPI operations from function docstrings.
def load_yaml_from_docstring(docstring: str) -> dict:
"""
Load YAML content from docstring.
Parameters:
- docstring: Function or method docstring
Returns:
Dictionary parsed from YAML section of docstring
Looks for YAML content after '---' delimiter in docstring.
Returns empty dict if no YAML section found.
"""
def load_operations_from_docstring(docstring: str) -> dict:
"""
Extract OpenAPI operations from docstring YAML.
Parameters:
- docstring: Function or method docstring containing YAML
Returns:
Dictionary mapping HTTP methods to operation definitions
Filters YAML content to only include valid HTTP methods and x- extensions.
"""Configuration constants for OpenAPI specification structure.
COMPONENT_SUBSECTIONS: dict[int, dict[str, str]] = {
2: {
"schema": "definitions",
"response": "responses",
"parameter": "parameters",
"security_scheme": "securityDefinitions"
},
3: {
"schema": "schemas",
"response": "responses",
"parameter": "parameters",
"header": "headers",
"example": "examples",
"security_scheme": "securitySchemes"
}
}
PATH_KEYS: set[str] = {"get", "put", "post", "delete", "options", "head", "patch"}from apispec.utils import build_reference
# OpenAPI 3.x references
schema_ref = build_reference("schema", 3, "User")
# {"$ref": "#/components/schemas/User"}
param_ref = build_reference("parameter", 3, "UserId")
# {"$ref": "#/components/parameters/UserId"}
# OpenAPI 2.x references
schema_ref_v2 = build_reference("schema", 2, "User")
# {"$ref": "#/definitions/User"}
response_ref_v2 = build_reference("response", 2, "NotFound")
# {"$ref": "#/responses/NotFound"}from apispec.utils import trim_docstring, dedent
# Clean up docstring formatting
raw_docstring = '''
This is a function that does something important.
It has multiple lines with inconsistent indentation.
Returns:
Something useful
'''
cleaned = trim_docstring(raw_docstring)
# "This is a function that does something important.\n\n It has multiple lines with inconsistent indentation.\n\nReturns:\n Something useful"
dedented = dedent(cleaned)
# "This is a function that does something important.\n\nIt has multiple lines with inconsistent indentation.\n\nReturns:\nSomething useful"from apispec.utils import deepupdate
original = {
"info": {"title": "My API"},
"paths": {
"/users": {
"get": {"summary": "List users"}
}
}
}
update = {
"info": {"version": "1.0.0", "description": "API description"},
"paths": {
"/users": {
"post": {"summary": "Create user"}
}
}
}
result = deepupdate(original, update)
# {
# "info": {"title": "My API", "version": "1.0.0", "description": "API description"},
# "paths": {
# "/users": {
# "get": {"summary": "List users"},
# "post": {"summary": "Create user"}
# }
# }
# }from apispec.yaml_utils import dict_to_yaml, load_operations_from_docstring
# Convert specification to YAML
spec_dict = {"openapi": "3.0.2", "info": {"title": "API", "version": "1.0.0"}}
yaml_output = dict_to_yaml(spec_dict)
# Custom YAML formatting
yaml_sorted = dict_to_yaml(spec_dict, {"sort_keys": True, "indent": 4})from apispec.yaml_utils import load_operations_from_docstring
def get_user(user_id):
"""Get user by ID.
---
get:
summary: Retrieve user information
parameters:
- name: user_id
in: path
required: true
schema:
type: integer
responses:
200:
description: User found
content:
application/json:
schema:
$ref: '#/components/schemas/User'
404:
description: User not found
"""
pass
# Extract operations from docstring
operations = load_operations_from_docstring(get_user.__doc__)
# {
# "get": {
# "summary": "Retrieve user information",
# "parameters": [...],
# "responses": {...}
# }
# }
# Use with APISpec
spec.path("/users/{user_id}", operations=operations)from apispec.yaml_utils import load_operations_from_docstring
from apispec.utils import trim_docstring
def extract_openapi_from_view(view_function):
"""Helper to extract OpenAPI spec from framework view function."""
if not view_function.__doc__:
return {}
# Clean and parse docstring
cleaned_doc = trim_docstring(view_function.__doc__)
operations = load_operations_from_docstring(cleaned_doc)
# Add operation ID if not specified
for method, operation in operations.items():
operation.setdefault('operationId', f"{method}_{view_function.__name__}")
return operations
# Usage in framework plugin
class FrameworkPlugin(BasePlugin):
def operation_helper(self, path=None, operations=None, **kwargs):
view_function = kwargs.get('view_function')
if view_function and operations:
doc_operations = extract_openapi_from_view(view_function)
operations.update(doc_operations)All utility functions may raise APISpec-related exceptions for error conditions.
class APISpecError(Exception):
"""Base class for all apispec-related errors."""
class OpenAPIError(APISpecError):
"""Raised when OpenAPI spec validation fails."""Install with Tessl CLI
npx tessl i tessl/pypi-apispec