From 66dc695071d8a040db72d5546eb848a58b41152e Mon Sep 17 00:00:00 2001 From: rijenkii Date: Tue, 10 Feb 2026 17:52:24 +0700 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20Replace=20`dict`=20by=20`Mappin?= =?UTF-8?q?g`=20on=20`HTTPException.headers`=20(#12997)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alejandra <90076947+alejsdev@users.noreply.github.com> Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- fastapi/exceptions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fastapi/exceptions.py b/fastapi/exceptions.py index 62b4674de..9924d0e2b 100644 --- a/fastapi/exceptions.py +++ b/fastapi/exceptions.py @@ -1,4 +1,4 @@ -from collections.abc import Sequence +from collections.abc import Mapping, Sequence from typing import Annotated, Any, Optional, TypedDict, Union from annotated_doc import Doc @@ -68,7 +68,7 @@ class HTTPException(StarletteHTTPException): ), ] = None, headers: Annotated[ - Optional[dict[str, str]], + Optional[Mapping[str, str]], Doc( """ Any headers to send to the client in the response. From 363aced75a26a5247855ea41b2bee53481be4d52 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 10 Feb 2026 10:52:51 +0000 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 71dc7bb74..fff7ccc33 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -7,6 +7,10 @@ hide: ## Latest Changes +### Features + +* ✨ Replace `dict` by `Mapping` on `HTTPException.headers`. PR [#12997](https://github.com/fastapi/fastapi/pull/12997) by [@rijenkii](https://github.com/rijenkii). + ### Internal * 🔧 Configure `test` workflow to run tests with `inline-snapshot=review`. PR [#14876](https://github.com/fastapi/fastapi/pull/14876) by [@YuriiMotov](https://github.com/YuriiMotov). From df950111fe97c8040e49f4600d6f563456bbdba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20S=C3=A1nchez=20Castro?= <72013291+JavierSanchezCastro@users.noreply.github.com> Date: Tue, 10 Feb 2026 11:58:40 +0100 Subject: [PATCH 3/6] =?UTF-8?q?=E2=9C=A8=20Show=20a=20clear=20error=20on?= =?UTF-8?q?=20attempt=20to=20include=20router=20into=20itself=20(#14258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Javier Sánchez Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com> --- fastapi/routing.py | 4 ++++ tests/test_router_circular_import.py | 12 ++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/test_router_circular_import.py diff --git a/fastapi/routing.py b/fastapi/routing.py index 16a89ef3e..ed873fda8 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -1393,6 +1393,10 @@ class APIRouter(routing.Router): app.include_router(internal_router) ``` """ + assert self is not router, ( + "Cannot include the same APIRouter instance into itself. " + "Did you mean to include a different router?" + ) if prefix: assert prefix.startswith("/"), "A path prefix must start with '/'" assert not prefix.endswith("/"), ( diff --git a/tests/test_router_circular_import.py b/tests/test_router_circular_import.py new file mode 100644 index 000000000..492a26d00 --- /dev/null +++ b/tests/test_router_circular_import.py @@ -0,0 +1,12 @@ +import pytest +from fastapi import APIRouter + + +def test_router_circular_import(): + router = APIRouter() + + with pytest.raises( + AssertionError, + match="Cannot include the same APIRouter instance into itself. Did you mean to include a different router?", + ): + router.include_router(router) From 8bdb0d22423fdfbc4e12eee1baa339ffa960471d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 10 Feb 2026 10:59:10 +0000 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index fff7ccc33..495bf83b4 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -9,6 +9,7 @@ hide: ### Features +* ✨ Show a clear error on attempt to include router into itself. PR [#14258](https://github.com/fastapi/fastapi/pull/14258) by [@JavierSanchezCastro](https://github.com/JavierSanchezCastro). * ✨ Replace `dict` by `Mapping` on `HTTPException.headers`. PR [#12997](https://github.com/fastapi/fastapi/pull/12997) by [@rijenkii](https://github.com/rijenkii). ### Internal From 25270fcee0c71c3bc661e40501fa3838beb6d99b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 10 Feb 2026 03:36:53 -0800 Subject: [PATCH 5/6] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Simplify=20reading=20f?= =?UTF-8?q?iles=20in=20memory,=20do=20it=20sequentially=20instead=20of=20(?= =?UTF-8?q?fake)=20parallel=20(#14884)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastapi/dependencies/utils.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 80f9c76e9..23d8cd9fb 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -1,7 +1,7 @@ import dataclasses import inspect import sys -from collections.abc import Coroutine, Mapping, Sequence +from collections.abc import Mapping, Sequence from contextlib import AsyncExitStack, contextmanager from copy import copy, deepcopy from dataclasses import dataclass @@ -15,7 +15,6 @@ from typing import ( cast, ) -import anyio from fastapi import params from fastapi._compat import ( ModelField, @@ -903,16 +902,8 @@ async def _extract_form_body( # For types assert isinstance(value, sequence_types) results: list[Union[bytes, str]] = [] - - async def process_fn( - fn: Callable[[], Coroutine[Any, Any, Any]], - ) -> None: - result = await fn() - results.append(result) # noqa: B023 - - async with anyio.create_task_group() as tg: - for sub_value in value: - tg.start_soon(process_fn, sub_value.read) + for sub_value in value: + results.append(await sub_value.read()) value = serialize_sequence_value(field=field, value=results) if value is not None: values[get_validation_alias(field)] = value From 3f1cc8f8f5fabaf85a0c3d9428004001af2486a4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 10 Feb 2026 11:37:19 +0000 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/en/docs/release-notes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 495bf83b4..72be2c6bd 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -12,6 +12,10 @@ hide: * ✨ Show a clear error on attempt to include router into itself. PR [#14258](https://github.com/fastapi/fastapi/pull/14258) by [@JavierSanchezCastro](https://github.com/JavierSanchezCastro). * ✨ Replace `dict` by `Mapping` on `HTTPException.headers`. PR [#12997](https://github.com/fastapi/fastapi/pull/12997) by [@rijenkii](https://github.com/rijenkii). +### Refactors + +* ♻️ Simplify reading files in memory, do it sequentially instead of (fake) parallel. PR [#14884](https://github.com/fastapi/fastapi/pull/14884) by [@tiangolo](https://github.com/tiangolo). + ### Internal * 🔧 Configure `test` workflow to run tests with `inline-snapshot=review`. PR [#14876](https://github.com/fastapi/fastapi/pull/14876) by [@YuriiMotov](https://github.com/YuriiMotov).