-
Notifications
You must be signed in to change notification settings - Fork 9
Creating a wrapper for dependency injection (mypy issue) #58
Comments
FWIW, @Finistere talked me through something similar in a discussion post |
Also, that's a cool idea for the project. I know there's another project seeking to do an alternate DI for FastAPI. |
Hi! Protocols are complex to use properly with I played a bit with it, but all solutions have their own problems:
from abc import ABC
from typing import Any, cast, Generic, Type, TypeVar
from fastapi import Depends
from typing_extensions import Protocol
from antidote import world
T = TypeVar("T")
class AbstractUserService(ABC):
...
class IUserService(Protocol):
...
# FastAPI Depends returns Any in all case, so whatever typing we may do it will be silently ignored.
def depends(dependency: object) -> Any:
return Depends(lambda: world.get[object](dependency))
def typed_depends(dependency: Type[T]) -> T:
return cast(T, Depends(lambda: world.get(dependency)))
class DependencyOf(Generic[T]):
def param(self) -> T:
# This is NOT part of the public API of Python
dependency: object = get_args(self.__orig_class__)[0] # type: ignore
return cast(T, Depends(lambda: world.get[object](dependency)))
async def example_route(service: IUserService = depends(IUserService)) -> None:
pass
async def example_route_v2(service: IUserService = typed_depends(AbstractUserService)
) -> None:
pass
async def example_route_v2b(service: IUserService = typed_depends(cast(Type[IUserService], IUserService))
) -> None:
pass
async def example_route_v3(service: IUserService = DependencyOf[IUserService]().param()) -> None:
pass Unfortunately there isn't a lot Antidote can do here. |
Well, not entirely true, with pure antidote you can use @inject
async def example_route(service: IUserService = inject.me()) -> None:
pass
|
Well, so It's actually kinda sad to be honest. I did some more research on that topic, and i encountered some opened issues in mypy related to that topic: mypy #4717, mypy #1843, mypy #5374. I've tried to find some ways to implement some kind of workaround for my use case, but as you said, there no perfect solution at the moment, I guess I will have to use Thanks anyway for your help, it did give me some new ideas how to tackle this problem, just unsafe ones (that Closing the issue, as it's something more related to mypy than antidote. |
Hi!
To give some context first, I'm currently trying to use antidote in a web backend made by FastAPI, and i'm trying to couple it's dependency injection system, with the one from antidote. I'm doing it, as the DI from FastAPI is limited to injecting into routes only, which is not enough in my case.
I did try to create a wrapper, that would allow me to couple those systems, but mypy does not like my implementation:
When trying to use the
InjectService
class, it shows the following error:The issue does not seem to be related to FastAPI, as i'm just not able to create a definition, that would allow me to pass a class with
@abstractmethod
, even without TypeVar. Even replacing theType[T]
withType[Any]
does not allow me to pass the class.Honestly, even though i tried, i cannot find out how you managed to create a type definition for
world.get
that does not make mypy scream at me when using a class with@abstractmethod
:(To give some more context, i would like to pass some data from fastapi dependencies (which would be in the
__call__
method), to the injected service. For example the user that made the request, which would allow me to pass all the services that user, without the need to do that manually every time.Is there maybe some way that would allow me to create such a wrapper? I did go through the docs, but i did not see anything that could help me. Thanks in advance :)
The text was updated successfully, but these errors were encountered: