From adeb02e162df293f9c32a3c58599085961ac7970 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Mon, 14 Sep 2026 00:04:23 +0530 Subject: [PATCH] fix: tighten binder/app route annotations (FastAPI/ModuleType); sync binder.pyi with signature; refresh MCP docs --- docs/mcp/modules/openapi_first.app.json | 11 +++++-- docs/mcp/modules/openapi_first.binder.json | 25 +++++++++------ docs/mcp/modules/openapi_first.json | 36 +++++++++++++++------- openapi_first/app.py | 5 +-- openapi_first/binder.py | 14 +++++---- openapi_first/binder.pyi | 8 ++++- 6 files changed, 68 insertions(+), 31 deletions(-) diff --git a/docs/mcp/modules/openapi_first.app.json b/docs/mcp/modules/openapi_first.app.json index c8df6bc..fdb22ca 100644 --- a/docs/mcp/modules/openapi_first.app.json +++ b/docs/mcp/modules/openapi_first.app.json @@ -18,6 +18,13 @@ "signature": "", "docstring": null }, + "ModuleType": { + "name": "ModuleType", + "kind": "alias", + "path": "openapi_first.app.ModuleType", + "signature": "", + "docstring": null + }, "Any": { "name": "Any", "kind": "alias", @@ -37,7 +44,7 @@ "kind": "function", "path": "openapi_first.app.bind_routes", "signature": "", - "docstring": "Bind OpenAPI operations to FastAPI routes.\n\nArgs:\n app (Any):\n The FastAPI application instance to which routes will be added.\n spec (dict):\n Parsed OpenAPI 3.x specification dictionary.\n routes_module (Any):\n Python module containing handler functions. Each handler's name MUST\n exactly match an OpenAPI `operationId`.\n security_deps (dict[str, list[Any]] | None):\n Optional mapping of ``METHOD:/path`` → ``list[Depends(...)]``\n generated from the spec's ``securitySchemes`` and per-operation\n ``security`` fields.\n\nRaises:\n MissingOperationHandler:\n If an ``operationId`` is missing from the spec or if no corresponding\n handler function exists in the routes module.\n\nNotes:\n **Responsibilities:**\n\n - Iterates through the OpenAPI specification paths and methods.\n - Resolves each ``operationId`` to a handler function, and registers\n a corresponding ``APIRoute`` on the FastAPI application.\n - Injects FastAPI ``Depends()`` for each security requirement found\n on the operation or inherited from the top-level ``security`` field.\n\n **Guarantees:**\n\n - Route registration is deterministic and spec-driven. No route\n decorators are required or supported. Handler resolution errors\n surface at application startup." + "docstring": "Bind OpenAPI operations to FastAPI routes.\n\nArgs:\n app (FastAPI):\n The FastAPI application instance to which routes will be added.\n spec (dict[str, Any]):\n Parsed OpenAPI 3.x specification dictionary.\n routes_module (ModuleType):\n Python module containing handler functions. Each handler's name MUST\n exactly match an OpenAPI `operationId`.\n security_deps (dict[str, list[Any]] | None):\n Optional mapping of ``METHOD:/path`` → ``list[Depends(...)]``\n generated from the spec's ``securitySchemes`` and per-operation\n ``security`` fields.\n\nRaises:\n MissingOperationHandler:\n If an ``operationId`` is missing from the spec or if no corresponding\n handler function exists in the routes module.\n\nNotes:\n **Responsibilities:**\n\n - Iterates through the OpenAPI specification paths and methods.\n - Resolves each ``operationId`` to a handler function, and registers\n a corresponding ``APIRoute`` on the FastAPI application.\n - Injects FastAPI ``Depends()`` for each security requirement found\n on the operation or inherited from the top-level ``security`` field.\n\n **Guarantees:**\n\n - Route registration is deterministic and spec-driven. No route\n decorators are required or supported. Handler resolution errors\n surface at application startup." }, "load_openapi": { "name": "load_openapi", @@ -64,7 +71,7 @@ "name": "OpenAPIFirstApp", "kind": "class", "path": "openapi_first.app.OpenAPIFirstApp", - "signature": "", + "signature": "", "docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each ``operationId`` is mapped to a Python function in the\n supplied routes module.\n - Auth dependencies are auto-injected from the spec's\n ``securitySchemes`` and per-operation ``security`` fields.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and ``/openapi.json`` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n - Auth enforcement is driven entirely by the spec — no manual\n middleware or decorators required.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```", "members": { "openapi": { diff --git a/docs/mcp/modules/openapi_first.binder.json b/docs/mcp/modules/openapi_first.binder.json index 7fe0ba1..605a031 100644 --- a/docs/mcp/modules/openapi_first.binder.json +++ b/docs/mcp/modules/openapi_first.binder.json @@ -4,6 +4,13 @@ "path": "openapi_first.binder", "docstring": "# Summary\n\nOpenAPI-driven route binding for FastAPI.\n\nThis module is responsible for translating an OpenAPI 3.x specification\ninto concrete FastAPI routes. It enforces a strict one-to-one mapping\nbetween OpenAPI operations and Python handler functions using `operationId`.\n\nNotes:\n **Core Responsibility:**\n\n - Read path + method definitions from an OpenAPI specification.\n - Resolve each `operationId` to a Python callable.\n - Register routes with FastAPI using `APIRoute`.\n - Fail fast when contract violations are detected.\n\n **Design Constraints:**\n\n - All routes MUST be declared in the OpenAPI specification.\n - All OpenAPI operations MUST define an `operationId`.\n - Every `operationId` MUST resolve to a handler function.\n - Handlers are plain Python callables (no decorators required).\n - No implicit route creation or inference is allowed.\n\n **Constraints:**\n\n - This module intentionally does NOT:\n - Perform request or response validation.\n - Generate Pydantic models.\n - Modify FastAPI dependency injection.\n - Interpret OpenAPI semantics beyond routing metadata.", "objects": { + "ModuleType": { + "name": "ModuleType", + "kind": "alias", + "path": "openapi_first.binder.ModuleType", + "signature": "", + "docstring": null + }, "Any": { "name": "Any", "kind": "alias", @@ -11,6 +18,13 @@ "signature": "", "docstring": null }, + "FastAPI": { + "name": "FastAPI", + "kind": "alias", + "path": "openapi_first.binder.FastAPI", + "signature": "", + "docstring": null + }, "APIRoute": { "name": "APIRoute", "kind": "alias", @@ -29,15 +43,8 @@ "name": "bind_routes", "kind": "function", "path": "openapi_first.binder.bind_routes", - "signature": "", - "docstring": "Bind OpenAPI operations to FastAPI routes.\n\nArgs:\n app (Any):\n The FastAPI application instance to which routes will be added.\n spec (dict):\n Parsed OpenAPI 3.x specification dictionary.\n routes_module (Any):\n Python module containing handler functions. Each handler's name MUST\n exactly match an OpenAPI `operationId`.\n security_deps (dict[str, list[Any]] | None):\n Optional mapping of ``METHOD:/path`` → ``list[Depends(...)]``\n generated from the spec's ``securitySchemes`` and per-operation\n ``security`` fields.\n\nRaises:\n MissingOperationHandler:\n If an ``operationId`` is missing from the spec or if no corresponding\n handler function exists in the routes module.\n\nNotes:\n **Responsibilities:**\n\n - Iterates through the OpenAPI specification paths and methods.\n - Resolves each ``operationId`` to a handler function, and registers\n a corresponding ``APIRoute`` on the FastAPI application.\n - Injects FastAPI ``Depends()`` for each security requirement found\n on the operation or inherited from the top-level ``security`` field.\n\n **Guarantees:**\n\n - Route registration is deterministic and spec-driven. No route\n decorators are required or supported. Handler resolution errors\n surface at application startup." - }, - "FastAPI": { - "name": "FastAPI", - "kind": "alias", - "path": "openapi_first.binder.FastAPI", - "signature": "", - "docstring": null + "signature": "", + "docstring": "Bind OpenAPI operations to FastAPI routes.\n\nArgs:\n app (FastAPI):\n The FastAPI application instance to which routes will be added.\n spec (dict[str, Any]):\n Parsed OpenAPI 3.x specification dictionary.\n routes_module (ModuleType):\n Python module containing handler functions. Each handler's name MUST\n exactly match an OpenAPI `operationId`.\n security_deps (dict[str, list[Any]] | None):\n Optional mapping of ``METHOD:/path`` → ``list[Depends(...)]``\n generated from the spec's ``securitySchemes`` and per-operation\n ``security`` fields.\n\nRaises:\n MissingOperationHandler:\n If an ``operationId`` is missing from the spec or if no corresponding\n handler function exists in the routes module.\n\nNotes:\n **Responsibilities:**\n\n - Iterates through the OpenAPI specification paths and methods.\n - Resolves each ``operationId`` to a handler function, and registers\n a corresponding ``APIRoute`` on the FastAPI application.\n - Injects FastAPI ``Depends()`` for each security requirement found\n on the operation or inherited from the top-level ``security`` field.\n\n **Guarantees:**\n\n - Route registration is deterministic and spec-driven. No route\n decorators are required or supported. Handler resolution errors\n surface at application startup." } } } diff --git a/docs/mcp/modules/openapi_first.json b/docs/mcp/modules/openapi_first.json index a9d481e..0ebae41 100644 --- a/docs/mcp/modules/openapi_first.json +++ b/docs/mcp/modules/openapi_first.json @@ -25,6 +25,13 @@ "signature": "", "docstring": null }, + "ModuleType": { + "name": "ModuleType", + "kind": "alias", + "path": "openapi_first.app.ModuleType", + "signature": "", + "docstring": null + }, "Any": { "name": "Any", "kind": "alias", @@ -44,7 +51,7 @@ "kind": "function", "path": "openapi_first.app.bind_routes", "signature": "", - "docstring": "Bind OpenAPI operations to FastAPI routes.\n\nArgs:\n app (Any):\n The FastAPI application instance to which routes will be added.\n spec (dict):\n Parsed OpenAPI 3.x specification dictionary.\n routes_module (Any):\n Python module containing handler functions. Each handler's name MUST\n exactly match an OpenAPI `operationId`.\n security_deps (dict[str, list[Any]] | None):\n Optional mapping of ``METHOD:/path`` → ``list[Depends(...)]``\n generated from the spec's ``securitySchemes`` and per-operation\n ``security`` fields.\n\nRaises:\n MissingOperationHandler:\n If an ``operationId`` is missing from the spec or if no corresponding\n handler function exists in the routes module.\n\nNotes:\n **Responsibilities:**\n\n - Iterates through the OpenAPI specification paths and methods.\n - Resolves each ``operationId`` to a handler function, and registers\n a corresponding ``APIRoute`` on the FastAPI application.\n - Injects FastAPI ``Depends()`` for each security requirement found\n on the operation or inherited from the top-level ``security`` field.\n\n **Guarantees:**\n\n - Route registration is deterministic and spec-driven. No route\n decorators are required or supported. Handler resolution errors\n surface at application startup." + "docstring": "Bind OpenAPI operations to FastAPI routes.\n\nArgs:\n app (FastAPI):\n The FastAPI application instance to which routes will be added.\n spec (dict[str, Any]):\n Parsed OpenAPI 3.x specification dictionary.\n routes_module (ModuleType):\n Python module containing handler functions. Each handler's name MUST\n exactly match an OpenAPI `operationId`.\n security_deps (dict[str, list[Any]] | None):\n Optional mapping of ``METHOD:/path`` → ``list[Depends(...)]``\n generated from the spec's ``securitySchemes`` and per-operation\n ``security`` fields.\n\nRaises:\n MissingOperationHandler:\n If an ``operationId`` is missing from the spec or if no corresponding\n handler function exists in the routes module.\n\nNotes:\n **Responsibilities:**\n\n - Iterates through the OpenAPI specification paths and methods.\n - Resolves each ``operationId`` to a handler function, and registers\n a corresponding ``APIRoute`` on the FastAPI application.\n - Injects FastAPI ``Depends()`` for each security requirement found\n on the operation or inherited from the top-level ``security`` field.\n\n **Guarantees:**\n\n - Route registration is deterministic and spec-driven. No route\n decorators are required or supported. Handler resolution errors\n surface at application startup." }, "load_openapi": { "name": "load_openapi", @@ -71,7 +78,7 @@ "name": "OpenAPIFirstApp", "kind": "class", "path": "openapi_first.app.OpenAPIFirstApp", - "signature": "", + "signature": "", "docstring": "FastAPI application enforcing OpenAPI-first design.\n\nNotes:\n **Responsibilities:**\n\n - `OpenAPIFirstApp` subclasses `FastAPI` and replaces manual route\n registration with OpenAPI-driven binding.\n - All routes are derived from the provided OpenAPI specification,\n and each ``operationId`` is mapped to a Python function in the\n supplied routes module.\n - Auth dependencies are auto-injected from the spec's\n ``securitySchemes`` and per-operation ``security`` fields.\n\n **Guarantees:**\n\n - No route can exist without an OpenAPI declaration.\n - No OpenAPI operation can exist without a handler.\n - Swagger UI and ``/openapi.json`` always reflect the provided spec.\n - Handler functions remain framework-agnostic and testable.\n - Auth enforcement is driven entirely by the spec — no manual\n middleware or decorators required.\n\nExample:\n ```python\n from openapi_first import OpenAPIFirstApp\n import app.routes as routes\n\n app = OpenAPIFirstApp(\n openapi_path=\"app/openapi.json\",\n routes_module=routes,\n title=\"Example Service\",\n )\n ```", "members": { "openapi": { @@ -92,6 +99,13 @@ "signature": null, "docstring": "# Summary\n\nOpenAPI-driven route binding for FastAPI.\n\nThis module is responsible for translating an OpenAPI 3.x specification\ninto concrete FastAPI routes. It enforces a strict one-to-one mapping\nbetween OpenAPI operations and Python handler functions using `operationId`.\n\nNotes:\n **Core Responsibility:**\n\n - Read path + method definitions from an OpenAPI specification.\n - Resolve each `operationId` to a Python callable.\n - Register routes with FastAPI using `APIRoute`.\n - Fail fast when contract violations are detected.\n\n **Design Constraints:**\n\n - All routes MUST be declared in the OpenAPI specification.\n - All OpenAPI operations MUST define an `operationId`.\n - Every `operationId` MUST resolve to a handler function.\n - Handlers are plain Python callables (no decorators required).\n - No implicit route creation or inference is allowed.\n\n **Constraints:**\n\n - This module intentionally does NOT:\n - Perform request or response validation.\n - Generate Pydantic models.\n - Modify FastAPI dependency injection.\n - Interpret OpenAPI semantics beyond routing metadata.", "members": { + "ModuleType": { + "name": "ModuleType", + "kind": "alias", + "path": "openapi_first.binder.ModuleType", + "signature": "", + "docstring": null + }, "Any": { "name": "Any", "kind": "alias", @@ -99,6 +113,13 @@ "signature": "", "docstring": null }, + "FastAPI": { + "name": "FastAPI", + "kind": "alias", + "path": "openapi_first.binder.FastAPI", + "signature": "", + "docstring": null + }, "APIRoute": { "name": "APIRoute", "kind": "alias", @@ -117,15 +138,8 @@ "name": "bind_routes", "kind": "function", "path": "openapi_first.binder.bind_routes", - "signature": "", - "docstring": "Bind OpenAPI operations to FastAPI routes.\n\nArgs:\n app (Any):\n The FastAPI application instance to which routes will be added.\n spec (dict):\n Parsed OpenAPI 3.x specification dictionary.\n routes_module (Any):\n Python module containing handler functions. Each handler's name MUST\n exactly match an OpenAPI `operationId`.\n security_deps (dict[str, list[Any]] | None):\n Optional mapping of ``METHOD:/path`` → ``list[Depends(...)]``\n generated from the spec's ``securitySchemes`` and per-operation\n ``security`` fields.\n\nRaises:\n MissingOperationHandler:\n If an ``operationId`` is missing from the spec or if no corresponding\n handler function exists in the routes module.\n\nNotes:\n **Responsibilities:**\n\n - Iterates through the OpenAPI specification paths and methods.\n - Resolves each ``operationId`` to a handler function, and registers\n a corresponding ``APIRoute`` on the FastAPI application.\n - Injects FastAPI ``Depends()`` for each security requirement found\n on the operation or inherited from the top-level ``security`` field.\n\n **Guarantees:**\n\n - Route registration is deterministic and spec-driven. No route\n decorators are required or supported. Handler resolution errors\n surface at application startup." - }, - "FastAPI": { - "name": "FastAPI", - "kind": "alias", - "path": "openapi_first.binder.FastAPI", - "signature": "", - "docstring": null + "signature": "", + "docstring": "Bind OpenAPI operations to FastAPI routes.\n\nArgs:\n app (FastAPI):\n The FastAPI application instance to which routes will be added.\n spec (dict[str, Any]):\n Parsed OpenAPI 3.x specification dictionary.\n routes_module (ModuleType):\n Python module containing handler functions. Each handler's name MUST\n exactly match an OpenAPI `operationId`.\n security_deps (dict[str, list[Any]] | None):\n Optional mapping of ``METHOD:/path`` → ``list[Depends(...)]``\n generated from the spec's ``securitySchemes`` and per-operation\n ``security`` fields.\n\nRaises:\n MissingOperationHandler:\n If an ``operationId`` is missing from the spec or if no corresponding\n handler function exists in the routes module.\n\nNotes:\n **Responsibilities:**\n\n - Iterates through the OpenAPI specification paths and methods.\n - Resolves each ``operationId`` to a handler function, and registers\n a corresponding ``APIRoute`` on the FastAPI application.\n - Injects FastAPI ``Depends()`` for each security requirement found\n on the operation or inherited from the top-level ``security`` field.\n\n **Guarantees:**\n\n - Route registration is deterministic and spec-driven. No route\n decorators are required or supported. Handler resolution errors\n surface at application startup." } } }, diff --git a/openapi_first/app.py b/openapi_first/app.py index 123f93e..0b7e3eb 100644 --- a/openapi_first/app.py +++ b/openapi_first/app.py @@ -34,6 +34,7 @@ Notes: import os import re +from types import ModuleType from typing import Any from fastapi import FastAPI @@ -106,7 +107,7 @@ class OpenAPIFirstApp(FastAPI): self, *, openapi_path: str, - routes_module: Any, + routes_module: ModuleType, **fastapi_kwargs: Any, ): """ @@ -116,7 +117,7 @@ class OpenAPIFirstApp(FastAPI): openapi_path (str): Filesystem path to the OpenAPI 3.x specification file. This specification is treated as the authoritative API contract. - routes_module (Any): + routes_module (ModuleType): Python module containing handler functions whose names correspond exactly to OpenAPI ``operationId`` values. **fastapi_kwargs (Any): diff --git a/openapi_first/binder.py b/openapi_first/binder.py index 6d3e293..3e9e23f 100644 --- a/openapi_first/binder.py +++ b/openapi_first/binder.py @@ -32,28 +32,30 @@ Notes: - Interpret OpenAPI semantics beyond routing metadata. """ +from types import ModuleType from typing import Any +from fastapi import FastAPI from fastapi.routing import APIRoute from .errors import MissingOperationHandler def bind_routes( - app: Any, - spec: dict, - routes_module: Any, + app: FastAPI, + spec: dict[str, Any], + routes_module: ModuleType, security_deps: dict[str, list[Any]] | None = None, ) -> None: """ Bind OpenAPI operations to FastAPI routes. Args: - app (Any): + app (FastAPI): The FastAPI application instance to which routes will be added. - spec (dict): + spec (dict[str, Any]): Parsed OpenAPI 3.x specification dictionary. - routes_module (Any): + routes_module (ModuleType): Python module containing handler functions. Each handler's name MUST exactly match an OpenAPI `operationId`. security_deps (dict[str, list[Any]] | None): diff --git a/openapi_first/binder.pyi b/openapi_first/binder.pyi index c408430..fd7b916 100644 --- a/openapi_first/binder.pyi +++ b/openapi_first/binder.pyi @@ -1,5 +1,11 @@ +from types import ModuleType from typing import Any from fastapi import FastAPI -def bind_routes(app: FastAPI, spec: dict[str, Any], routes_module: Any) -> None: ... +def bind_routes( + app: FastAPI, + spec: dict[str, Any], + routes_module: ModuleType, + security_deps: dict[str, list[Any]] | None = None, +) -> None: ...