Skip to content

Commit

Permalink
fix: FastAPI 0.112.3 compatibility (#1763)
Browse files Browse the repository at this point in the history
* fix: FastAPI 0.112.3 compatibility

* chore: bump FastAPI

* lint: fix mypy for latests FastAPI

* chore: bump version
  • Loading branch information
Lancetnik authored Sep 5, 2024
1 parent 9492712 commit fc26675
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 51 deletions.
2 changes: 1 addition & 1 deletion faststream/__about__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Simple and fast framework to create message brokers based microservices."""

__version__ = "0.5.20"
__version__ = "0.5.21"

SERVICE_NAME = f"faststream-{__version__}"
34 changes: 1 addition & 33 deletions faststream/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
import os
import sys
from importlib.metadata import version as get_version
from typing import Any, Callable, Dict, List, Mapping, Optional, Type, TypeVar, Union
from typing import Any, Callable, Dict, Mapping, Optional, Type, TypeVar, Union

from fast_depends._compat import PYDANTIC_V2 as PYDANTIC_V2
from fast_depends._compat import ( # type: ignore[attr-defined]
PYDANTIC_VERSION as PYDANTIC_VERSION,
)
from pydantic import BaseModel as BaseModel
from typing_extensions import Never

from faststream.types import AnyDict

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


IS_OPTIMIZED = os.getenv("PYTHONOPTIMIZE", False)


def is_test_env() -> bool:
return bool(os.getenv("PYTEST_CURRENT_TEST"))

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


try:
from fastapi import __version__ as FASTAPI_VERSION # noqa: N812

major, minor, *_ = map(int, FASTAPI_VERSION.split("."))
FASTAPI_V2 = major > 0 or minor > 100
FASTAPI_V106 = major > 0 or minor >= 106

if FASTAPI_V2:
from fastapi._compat import _normalize_errors
from fastapi.exceptions import RequestValidationError

def raise_fastapi_validation_error(errors: List[Any], body: AnyDict) -> Never:
raise RequestValidationError(_normalize_errors(errors), body=body)

else:
from pydantic import ( # type: ignore[assignment] # isort: skip
ValidationError as RequestValidationError,
)
from pydantic import create_model

ROUTER_VALIDATION_ERROR_MODEL = create_model("StreamRoute")

def raise_fastapi_validation_error(errors: List[Any], body: AnyDict) -> Never:
raise RequestValidationError(errors, ROUTER_VALIDATION_ERROR_MODEL) # type: ignore[misc]

except ImportError:
pass

JsonSchemaValue = Mapping[str, Any]

if PYDANTIC_V2:
Expand Down
115 changes: 115 additions & 0 deletions faststream/broker/fastapi/_compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, List, Optional

from fastapi import __version__ as FASTAPI_VERSION # noqa: N812
from fastapi.dependencies.utils import solve_dependencies
from starlette.background import BackgroundTasks
from typing_extensions import Never

from faststream.types import AnyDict

if TYPE_CHECKING:
from fastapi.dependencies.models import Dependant
from fastapi.requests import Request

major, minor, patch, *_ = map(int, FASTAPI_VERSION.split("."))
FASTAPI_V2 = major > 0 or minor > 100
FASTAPI_V106 = major > 0 or minor >= 106
FASTAPI_v102_3 = major > 0 or minor > 112 or (minor == 112 and patch > 2)

__all__ = (
"create_response_field",
"solve_faststream_dependency",
"raise_fastapi_validation_error",
"RequestValidationError",
)


@dataclass
class SolvedDependency:
values: AnyDict
errors: List[Any]
background_tasks: Optional[BackgroundTasks]


if FASTAPI_V2:
from fastapi._compat import _normalize_errors
from fastapi.exceptions import RequestValidationError

def raise_fastapi_validation_error(errors: List[Any], body: AnyDict) -> Never:
raise RequestValidationError(_normalize_errors(errors), body=body)

else:
from pydantic import ( # type: ignore[assignment]
ValidationError as RequestValidationError,
)
from pydantic import create_model

ROUTER_VALIDATION_ERROR_MODEL = create_model("StreamRoute")

def raise_fastapi_validation_error(errors: List[Any], body: AnyDict) -> Never:
raise RequestValidationError(errors, ROUTER_VALIDATION_ERROR_MODEL) # type: ignore[misc]


if FASTAPI_v102_3:
from fastapi.utils import (
create_model_field as create_response_field,
)

async def solve_faststream_dependency(
request: "Request",
dependant: "Dependant",
dependency_overrides_provider: Optional[Any],
**kwargs: Any,
) -> SolvedDependency:
solved_result = await solve_dependencies(
request=request,
body=request._body, # type: ignore[arg-type]
dependant=dependant,
dependency_overrides_provider=dependency_overrides_provider,
**kwargs,
)
values, errors, background = (
solved_result.values,
solved_result.errors,
solved_result.background_tasks,
)

return SolvedDependency(
values=values,
errors=errors,
background_tasks=background,
)

else:
from fastapi.utils import ( # type: ignore[attr-defined,no-redef]
create_response_field as create_response_field,
)

async def solve_faststream_dependency(
request: "Request",
dependant: "Dependant",
dependency_overrides_provider: Optional[Any],
**kwargs: Any,
) -> SolvedDependency:
solved_result = await solve_dependencies(
request=request,
body=request._body, # type: ignore[arg-type]
dependant=dependant,
dependency_overrides_provider=dependency_overrides_provider,
**kwargs,
)

(
values,
errors,
background,
_response,
_dependency_cache,
) = solved_result # type: ignore[misc]

return SolvedDependency(
values=values, # type: ignore[has-type]
errors=errors, # type: ignore[has-type]
background_tasks=background, # type: ignore[has-type]
)
29 changes: 13 additions & 16 deletions faststream/broker/fastapi/route.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,19 @@
Union,
)

from fastapi.dependencies.utils import solve_dependencies
from fastapi.routing import run_endpoint_function, serialize_response
from fastapi.utils import create_response_field
from starlette.requests import Request

from faststream._compat import FASTAPI_V106, raise_fastapi_validation_error
from faststream.broker.fastapi.get_dependant import get_fastapi_native_dependant
from faststream.broker.types import P_HandlerParams, T_HandlerReturn

from ._compat import (
FASTAPI_V106,
create_response_field,
raise_fastapi_validation_error,
solve_faststream_dependency,
)

if TYPE_CHECKING:
from fastapi import params
from fastapi._compat import ModelField
Expand Down Expand Up @@ -199,28 +203,21 @@ async def app(
request.scope["fastapi_astack"] = stack
kwargs = {}

solved_result = await solve_dependencies(
solved_result = await solve_faststream_dependency(
request=request,
body=request._body, # type: ignore[arg-type]
dependant=dependent,
dependency_overrides_provider=provider_factory(),
**kwargs, # type: ignore[arg-type]
**kwargs,
)

(
values,
errors,
raw_message.background, # type: ignore[attr-defined]
_response,
_dependency_cache,
) = solved_result
raw_message.background = solved_result.background_tasks # type: ignore[attr-defined]

if errors:
raise_fastapi_validation_error(errors, request._body) # type: ignore[arg-type]
if solved_result.errors:
raise_fastapi_validation_error(solved_result.errors, request._body) # type: ignore[arg-type]

raw_reponse = await run_endpoint_function(
dependant=dependent,
values=values,
values=solved_result.values,
is_coroutine=is_coroutine,
)

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ test-core = [

testing = [
"faststream[test-core]",
"fastapi==0.112.2",
"fastapi==0.112.3",
"pydantic-settings>=2.0.0,<3.0.0",
"httpx==0.27.2",
"PyYAML==6.0.2",
Expand Down

0 comments on commit fc26675

Please sign in to comment.