Skip to content

Commit fc26675

Browse files
authored
fix: FastAPI 0.112.3 compatibility (#1763)
* fix: FastAPI 0.112.3 compatibility * chore: bump FastAPI * lint: fix mypy for latests FastAPI * chore: bump version
1 parent 9492712 commit fc26675

File tree

5 files changed

+131
-51
lines changed

5 files changed

+131
-51
lines changed

faststream/__about__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""Simple and fast framework to create message brokers based microservices."""
22

3-
__version__ = "0.5.20"
3+
__version__ = "0.5.21"
44

55
SERVICE_NAME = f"faststream-{__version__}"

faststream/_compat.py

+1-33
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
import os
33
import sys
44
from importlib.metadata import version as get_version
5-
from typing import Any, Callable, Dict, List, Mapping, Optional, Type, TypeVar, Union
5+
from typing import Any, Callable, Dict, Mapping, Optional, Type, TypeVar, Union
66

77
from fast_depends._compat import PYDANTIC_V2 as PYDANTIC_V2
88
from fast_depends._compat import ( # type: ignore[attr-defined]
99
PYDANTIC_VERSION as PYDANTIC_VERSION,
1010
)
1111
from pydantic import BaseModel as BaseModel
12-
from typing_extensions import Never
1312

1413
from faststream.types import AnyDict
1514

@@ -21,9 +20,6 @@
2120
ModelVar = TypeVar("ModelVar", bound=BaseModel)
2221

2322

24-
IS_OPTIMIZED = os.getenv("PYTHONOPTIMIZE", False)
25-
26-
2723
def is_test_env() -> bool:
2824
return bool(os.getenv("PYTEST_CURRENT_TEST"))
2925

@@ -59,34 +55,6 @@ def json_dumps(*a: Any, **kw: Any) -> bytes:
5955
return json.dumps(*a, **kw).encode()
6056

6157

62-
try:
63-
from fastapi import __version__ as FASTAPI_VERSION # noqa: N812
64-
65-
major, minor, *_ = map(int, FASTAPI_VERSION.split("."))
66-
FASTAPI_V2 = major > 0 or minor > 100
67-
FASTAPI_V106 = major > 0 or minor >= 106
68-
69-
if FASTAPI_V2:
70-
from fastapi._compat import _normalize_errors
71-
from fastapi.exceptions import RequestValidationError
72-
73-
def raise_fastapi_validation_error(errors: List[Any], body: AnyDict) -> Never:
74-
raise RequestValidationError(_normalize_errors(errors), body=body)
75-
76-
else:
77-
from pydantic import ( # type: ignore[assignment] # isort: skip
78-
ValidationError as RequestValidationError,
79-
)
80-
from pydantic import create_model
81-
82-
ROUTER_VALIDATION_ERROR_MODEL = create_model("StreamRoute")
83-
84-
def raise_fastapi_validation_error(errors: List[Any], body: AnyDict) -> Never:
85-
raise RequestValidationError(errors, ROUTER_VALIDATION_ERROR_MODEL) # type: ignore[misc]
86-
87-
except ImportError:
88-
pass
89-
9058
JsonSchemaValue = Mapping[str, Any]
9159

9260
if PYDANTIC_V2:

faststream/broker/fastapi/_compat.py

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
from dataclasses import dataclass
2+
from typing import TYPE_CHECKING, Any, List, Optional
3+
4+
from fastapi import __version__ as FASTAPI_VERSION # noqa: N812
5+
from fastapi.dependencies.utils import solve_dependencies
6+
from starlette.background import BackgroundTasks
7+
from typing_extensions import Never
8+
9+
from faststream.types import AnyDict
10+
11+
if TYPE_CHECKING:
12+
from fastapi.dependencies.models import Dependant
13+
from fastapi.requests import Request
14+
15+
major, minor, patch, *_ = map(int, FASTAPI_VERSION.split("."))
16+
FASTAPI_V2 = major > 0 or minor > 100
17+
FASTAPI_V106 = major > 0 or minor >= 106
18+
FASTAPI_v102_3 = major > 0 or minor > 112 or (minor == 112 and patch > 2)
19+
20+
__all__ = (
21+
"create_response_field",
22+
"solve_faststream_dependency",
23+
"raise_fastapi_validation_error",
24+
"RequestValidationError",
25+
)
26+
27+
28+
@dataclass
29+
class SolvedDependency:
30+
values: AnyDict
31+
errors: List[Any]
32+
background_tasks: Optional[BackgroundTasks]
33+
34+
35+
if FASTAPI_V2:
36+
from fastapi._compat import _normalize_errors
37+
from fastapi.exceptions import RequestValidationError
38+
39+
def raise_fastapi_validation_error(errors: List[Any], body: AnyDict) -> Never:
40+
raise RequestValidationError(_normalize_errors(errors), body=body)
41+
42+
else:
43+
from pydantic import ( # type: ignore[assignment]
44+
ValidationError as RequestValidationError,
45+
)
46+
from pydantic import create_model
47+
48+
ROUTER_VALIDATION_ERROR_MODEL = create_model("StreamRoute")
49+
50+
def raise_fastapi_validation_error(errors: List[Any], body: AnyDict) -> Never:
51+
raise RequestValidationError(errors, ROUTER_VALIDATION_ERROR_MODEL) # type: ignore[misc]
52+
53+
54+
if FASTAPI_v102_3:
55+
from fastapi.utils import (
56+
create_model_field as create_response_field,
57+
)
58+
59+
async def solve_faststream_dependency(
60+
request: "Request",
61+
dependant: "Dependant",
62+
dependency_overrides_provider: Optional[Any],
63+
**kwargs: Any,
64+
) -> SolvedDependency:
65+
solved_result = await solve_dependencies(
66+
request=request,
67+
body=request._body, # type: ignore[arg-type]
68+
dependant=dependant,
69+
dependency_overrides_provider=dependency_overrides_provider,
70+
**kwargs,
71+
)
72+
values, errors, background = (
73+
solved_result.values,
74+
solved_result.errors,
75+
solved_result.background_tasks,
76+
)
77+
78+
return SolvedDependency(
79+
values=values,
80+
errors=errors,
81+
background_tasks=background,
82+
)
83+
84+
else:
85+
from fastapi.utils import ( # type: ignore[attr-defined,no-redef]
86+
create_response_field as create_response_field,
87+
)
88+
89+
async def solve_faststream_dependency(
90+
request: "Request",
91+
dependant: "Dependant",
92+
dependency_overrides_provider: Optional[Any],
93+
**kwargs: Any,
94+
) -> SolvedDependency:
95+
solved_result = await solve_dependencies(
96+
request=request,
97+
body=request._body, # type: ignore[arg-type]
98+
dependant=dependant,
99+
dependency_overrides_provider=dependency_overrides_provider,
100+
**kwargs,
101+
)
102+
103+
(
104+
values,
105+
errors,
106+
background,
107+
_response,
108+
_dependency_cache,
109+
) = solved_result # type: ignore[misc]
110+
111+
return SolvedDependency(
112+
values=values, # type: ignore[has-type]
113+
errors=errors, # type: ignore[has-type]
114+
background_tasks=background, # type: ignore[has-type]
115+
)

faststream/broker/fastapi/route.py

+13-16
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,19 @@
1414
Union,
1515
)
1616

17-
from fastapi.dependencies.utils import solve_dependencies
1817
from fastapi.routing import run_endpoint_function, serialize_response
19-
from fastapi.utils import create_response_field
2018
from starlette.requests import Request
2119

22-
from faststream._compat import FASTAPI_V106, raise_fastapi_validation_error
2320
from faststream.broker.fastapi.get_dependant import get_fastapi_native_dependant
2421
from faststream.broker.types import P_HandlerParams, T_HandlerReturn
2522

23+
from ._compat import (
24+
FASTAPI_V106,
25+
create_response_field,
26+
raise_fastapi_validation_error,
27+
solve_faststream_dependency,
28+
)
29+
2630
if TYPE_CHECKING:
2731
from fastapi import params
2832
from fastapi._compat import ModelField
@@ -199,28 +203,21 @@ async def app(
199203
request.scope["fastapi_astack"] = stack
200204
kwargs = {}
201205

202-
solved_result = await solve_dependencies(
206+
solved_result = await solve_faststream_dependency(
203207
request=request,
204-
body=request._body, # type: ignore[arg-type]
205208
dependant=dependent,
206209
dependency_overrides_provider=provider_factory(),
207-
**kwargs, # type: ignore[arg-type]
210+
**kwargs,
208211
)
209212

210-
(
211-
values,
212-
errors,
213-
raw_message.background, # type: ignore[attr-defined]
214-
_response,
215-
_dependency_cache,
216-
) = solved_result
213+
raw_message.background = solved_result.background_tasks # type: ignore[attr-defined]
217214

218-
if errors:
219-
raise_fastapi_validation_error(errors, request._body) # type: ignore[arg-type]
215+
if solved_result.errors:
216+
raise_fastapi_validation_error(solved_result.errors, request._body) # type: ignore[arg-type]
220217

221218
raw_reponse = await run_endpoint_function(
222219
dependant=dependent,
223-
values=values,
220+
values=solved_result.values,
224221
is_coroutine=is_coroutine,
225222
)
226223

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ test-core = [
128128

129129
testing = [
130130
"faststream[test-core]",
131-
"fastapi==0.112.2",
131+
"fastapi==0.112.3",
132132
"pydantic-settings>=2.0.0,<3.0.0",
133133
"httpx==0.27.2",
134134
"PyYAML==6.0.2",

0 commit comments

Comments
 (0)