From fcfcaf042ce17e53ce26879fecb34f30232d6d67 Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Sun, 14 Jun 2026 22:39:18 +0300 Subject: [PATCH] fix(fastapi): handle FastAPI 0.137 _IncludedRouter route type FastAPI 0.137.0 introduced the internal `_IncludedRouter` route type (a `BaseRoute` subclass with no `.path`) into `app.router.routes`, which broke the offline-docs helper that read `route.path` on every route via an unchecked `typing.cast(Route, route)`. Filter the docs routes by matching only real `Route` instances, leaving `_IncludedRouter` and other route types untouched (works on old and new FastAPI alike). Drop the now-unused `import typing`. `prometheus-fastapi-instrumentator` (<=8.0.0) has the same unguarded `route.path` access and is not yet fixed upstream, so cap `fastapi<0.137` in the `fastapi-metrics` extra until a fixed instrumentator ships. Upstream issue: trallnag/prometheus-fastapi-instrumentator#370 Co-Authored-By: Claude Opus 4.8 (1M context) --- lite_bootstrap/helpers/fastapi_helpers.py | 6 ++++-- pyproject.toml | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lite_bootstrap/helpers/fastapi_helpers.py b/lite_bootstrap/helpers/fastapi_helpers.py index 76518de..62804cc 100644 --- a/lite_bootstrap/helpers/fastapi_helpers.py +++ b/lite_bootstrap/helpers/fastapi_helpers.py @@ -1,5 +1,4 @@ import pathlib -import typing import warnings from lite_bootstrap import import_checker @@ -47,10 +46,13 @@ def enable_offline_docs( redoc_url: str = app.redoc_url or "/redoc" swagger_ui_oauth2_redirect_url: str = app.swagger_ui_oauth2_redirect_url or "/docs/oauth2-redirect" + replaced_urls = (docs_url, redoc_url, swagger_ui_oauth2_redirect_url) app.router.routes = [ route for route in app.router.routes - if typing.cast(Route, route).path not in (docs_url, redoc_url, swagger_ui_oauth2_redirect_url) + # Only the auto-generated docs endpoints are plain ``Route``s with a ``path``; other route + # types (e.g. FastAPI's ``_IncludedRouter``) lack ``.path`` and must be left untouched. + if not (isinstance(route, Route) and route.path in replaced_urls) ] static_dir_path = pathlib.Path(__file__).parent.parent / "static/fastapi_docs" diff --git a/pyproject.toml b/pyproject.toml index 6f03f31..82dd146 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,11 @@ free-all = [ "lite-bootstrap[sentry,otl,logging,pyroscope]", ] fastapi = [ - "fastapi", + # FastAPI 0.137 introduced the internal `_IncludedRouter` route type, which + # prometheus-fastapi-instrumentator (<=8.0.0) does not handle (it reads `route.path` + # unconditionally and raises AttributeError). Cap until a fixed instrumentator ships. + # Upstream issue: https://github.com/trallnag/prometheus-fastapi-instrumentator/issues/370 + "fastapi<0.137", ] fastapi-sentry = [ "lite-bootstrap[fastapi,sentry]",