Merge branch 'master' into fix/uploadfile-downcast

This commit is contained in:
Motov Yurii 2026-02-10 12:47:24 +01:00 committed by GitHub
commit 27846b1b51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 30 additions and 14 deletions

View File

@ -7,6 +7,15 @@ hide:
## Latest Changes
### 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).
### 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).

View File

@ -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,
@ -900,16 +899,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)
else:
value = [

View File

@ -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.

View File

@ -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("/"), (

View File

@ -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)