Skip to content

Commit

Permalink
feat: add support for arbitrary types
Browse files Browse the repository at this point in the history
  • Loading branch information
JonathanSerafini authored Aug 17, 2023
1 parent b859489 commit 7c9ccc9
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 4 deletions.
2 changes: 1 addition & 1 deletion fast_depends/__about__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""FastDepends - extracted and cleared from HTTP domain FastAPI Dependency Injection System"""

__version__ = "2.1.6"
__version__ = "2.1.7"
10 changes: 10 additions & 0 deletions fast_depends/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,27 @@
PYDANTIC_V2 = PYDANTIC_VERSION.startswith("2.")

if PYDANTIC_V2:
from pydantic import ConfigDict
from pydantic._internal._typing_extra import (
eval_type_lenient as evaluate_forwardref,
)
from pydantic.fields import FieldInfo

class CreateBaseModel(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)

else:
from pydantic.fields import ModelField as FieldInfo # type: ignore
from pydantic.typing import evaluate_forwardref # type: ignore[no-redef]

class CreateBaseModel(BaseModel):
class Config:
arbitrary_types_allowed = True


__all__ = (
"BaseModel",
"CreateBaseModel",
"FieldInfo",
"create_model",
"evaluate_forwardref",
Expand Down
8 changes: 5 additions & 3 deletions fast_depends/core/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
get_origin,
)

from fast_depends._compat import create_model
from fast_depends._compat import create_model, CreateBaseModel
from fast_depends.core.model import CallModel
from fast_depends.dependencies import Depends
from fast_depends.library import CustomField
Expand Down Expand Up @@ -155,11 +155,13 @@ def build_call_model(
elif param.name not in ("args", "kwargs"):
positional_args.append(param.name)

func_model = create_model(name, **class_fields) # type: ignore
func_model = create_model(name, __base__=CreateBaseModel, **class_fields) # type: ignore

if cast and return_annotation and return_annotation is not inspect._empty:
response_model = create_model(
"ResponseModel", response=(return_annotation, ...)
"ResponseModel",
__base__=CreateBaseModel,
response=(return_annotation, ...),
)
else:
response_model = None
Expand Down
26 changes: 26 additions & 0 deletions tests/async/test_cast.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,32 @@ async def some_func(a, b: int):
assert isinstance(await some_func(1, "2"), int)


@pytest.mark.asyncio
async def test_arbitrary_args():
class ArbitraryType:
def __init__(self):
self.value = "value"

@inject
async def some_func(a: ArbitraryType):
return a

assert isinstance(await some_func(ArbitraryType()), ArbitraryType)


@pytest.mark.asyncio
async def test_arbitrary_response():
class ArbitraryType:
def __init__(self):
self.value = "value"

@inject
async def some_func(a: ArbitraryType) -> ArbitraryType:
return a

assert isinstance(await some_func(ArbitraryType()), ArbitraryType)


@pytest.mark.asyncio
async def test_types_casting():
@inject
Expand Down
24 changes: 24 additions & 0 deletions tests/sync/test_cast.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,30 @@ def some_func(a, b: int):
assert isinstance(some_func(1, "2"), int)


async def test_arbitrary_args():
class ArbitraryType:
def __init__(self):
self.value = "value"

@inject
def some_func(a: ArbitraryType):
return a

assert isinstance(await some_func(ArbitraryType()), ArbitraryType)


async def test_arbitrary_response():
class ArbitraryType:
def __init__(self):
self.value = "value"

@inject
def some_func(a: ArbitraryType) -> ArbitraryType:
return a

assert isinstance(await some_func(ArbitraryType()), ArbitraryType)


def test_validation_error():
@inject
def some_func(a, b: str = Field(..., max_length=1)): # pragma: no cover
Expand Down

0 comments on commit 7c9ccc9

Please sign in to comment.