feat: emit template-style route stubs (async, body param, service hint)
Add generate_routes(template_style=...) and CLI --template-style. When on, stubs use async def, a JSON body parameter named body (dict or Model), and with --inject-request a service-lookup hint comment before NotImplementedError. Default behaviour (def/payload) unchanged. +2 tests (32 passed), wiki + MCP bundle regenerated.
This commit is contained in:
@@ -28,6 +28,9 @@ Notes:
|
||||
instead of an opaque ``payload: dict``.
|
||||
- Passing ``package=True`` writes ``<resource>/routes.py`` (with
|
||||
``__init__.py`` sidecars) instead of a flat ``<resource>.py``.
|
||||
- Passing ``template_style=True`` emits ``async def`` handlers whose
|
||||
JSON body parameter is named ``body`` (matching the standards
|
||||
template) and adds a service-resolution hint comment.
|
||||
"""
|
||||
|
||||
import re
|
||||
@@ -66,6 +69,7 @@ def generate_routes(
|
||||
models_module: str = "models",
|
||||
inject_request: bool = False,
|
||||
package: bool = False,
|
||||
template_style: bool = False,
|
||||
) -> list[Path]:
|
||||
"""
|
||||
Generate route handler stubs from an OpenAPI specification.
|
||||
@@ -92,6 +96,11 @@ def generate_routes(
|
||||
package (bool, optional):
|
||||
If ``True``, emit ``<resource>/routes.py`` packages (each with an
|
||||
``__init__.py``) instead of flat ``<resource>.py`` modules.
|
||||
template_style (bool, optional):
|
||||
If ``True``, emit ``async def`` handlers with a JSON body parameter
|
||||
named ``body`` (the standards-template convention) plus a
|
||||
service-resolution hint comment, instead of sync ``def`` handlers
|
||||
taking ``payload``.
|
||||
|
||||
Returns:
|
||||
list[Path]:
|
||||
@@ -149,6 +158,7 @@ def generate_routes(
|
||||
use_models=use_models,
|
||||
models_module=models_module,
|
||||
inject_request=inject_request,
|
||||
template_style=template_style,
|
||||
)
|
||||
file_path.write_text(content, encoding="utf-8")
|
||||
generated_files.append(file_path)
|
||||
@@ -293,6 +303,7 @@ def _generate_resource_file(
|
||||
use_models: bool,
|
||||
models_module: str,
|
||||
inject_request: bool = False,
|
||||
template_style: bool = False,
|
||||
) -> str:
|
||||
"""Assemble the full Python source for a single resource file."""
|
||||
op_ids = [op.get("operationId", "") for _, _, op in operations]
|
||||
@@ -359,7 +370,8 @@ def _generate_resource_file(
|
||||
lines.append("")
|
||||
lines.extend(
|
||||
_generate_handler(
|
||||
path, http_method, operation, use_models, inject_request
|
||||
path, http_method, operation, use_models, inject_request,
|
||||
resource, template_style,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -373,10 +385,13 @@ def _generate_handler(
|
||||
operation: dict[str, Any],
|
||||
use_models: bool,
|
||||
inject_request: bool = False,
|
||||
resource: str = "",
|
||||
template_style: bool = False,
|
||||
) -> list[str]:
|
||||
"""Build the source lines for a single handler function."""
|
||||
operation_id = operation.get("operationId", "")
|
||||
summary = operation.get("summary", f"{http_method.upper()} {path}")
|
||||
body_param = "body" if template_style else "payload"
|
||||
|
||||
params: list[str] = []
|
||||
path_params: list[str] = []
|
||||
@@ -424,9 +439,9 @@ def _generate_handler(
|
||||
params.append(f"{field_name}: {annotation} = {factory}(None)")
|
||||
elif operation.get("requestBody"):
|
||||
if use_models and schema_name:
|
||||
params.append(f"payload: {schema_name}")
|
||||
params.append(f"{body_param}: {schema_name}")
|
||||
else:
|
||||
params.append("payload: dict")
|
||||
params.append(f"{body_param}: dict")
|
||||
|
||||
# Inject Response for non-200 success codes
|
||||
success_status = _get_success_status(operation)
|
||||
@@ -436,7 +451,8 @@ def _generate_handler(
|
||||
# Build function body
|
||||
lines: list[str] = []
|
||||
param_str = ", ".join(params)
|
||||
lines.append(f"def {operation_id}({param_str}):")
|
||||
prefix = "async def" if template_style else "def"
|
||||
lines.append(f"{prefix} {operation_id}({param_str}):")
|
||||
lines.append(f' """{summary}')
|
||||
|
||||
# Document parameters
|
||||
@@ -472,14 +488,20 @@ def _generate_handler(
|
||||
elif operation.get("requestBody"):
|
||||
if use_models and schema_name:
|
||||
lines.append("")
|
||||
lines.append(f" payload : {schema_name}")
|
||||
lines.append(f" {body_param} : {schema_name}")
|
||||
lines.append(" Request body.")
|
||||
else:
|
||||
lines.append("")
|
||||
lines.append(" payload : dict")
|
||||
lines.append(f" {body_param} : dict")
|
||||
lines.append(" Request body.")
|
||||
|
||||
lines.append(' """')
|
||||
if template_style and inject_request:
|
||||
svc_key = f"{_sanitize_identifier(resource)}_svc"
|
||||
lines.append(
|
||||
f' # Resolve the service: '
|
||||
f'request.app.state.db_resources["{svc_key}"]'
|
||||
)
|
||||
lines.append(" raise NotImplementedError")
|
||||
|
||||
return lines
|
||||
|
||||
Reference in New Issue
Block a user