Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions documentation/integrations/unlisted-framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ issues.
If your framework inspects function signatures, things get a bit trickier. This is because dependencies can't be present
in function parameters.

To solve this, you can define a class with a `call` method (where dependencies are injected when the class is
To solve this, you can define a class with a `__call__` method (where dependencies are injected when the class is
instantiated), and use the `asfunction` decorator to turn it into a function.

The resulting function will have the same signature as the `call` method, but without the `self` parameter.
The resulting function will have the same signature as the `__call__` method, but without the `self` parameter.

Example:

Expand All @@ -28,7 +28,7 @@ from injection import asfunction
class DoSomething(NamedTuple):
service: MyService

def call(self):
def __call__(self):
self.service.do_work()
```

Expand Down
22 changes: 6 additions & 16 deletions injection/_core/asfunction.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
from abc import abstractmethod
from collections.abc import Callable
from functools import wraps
from inspect import iscoroutinefunction
from typing import Any, Protocol, runtime_checkable
from typing import Any

from injection._core.common.asynchronous import Caller
from injection._core.module import Module, mod

type AsFunctionWrappedType[**P, T] = type[AsFunctionCallable[P, T]]


@runtime_checkable
class AsFunctionCallable[**P, T](Protocol):
__slots__ = ()

@abstractmethod
def call(self, *args: P.args, **kwargs: P.kwargs) -> T:
raise NotImplementedError
type AsFunctionWrappedType[**P, T] = type[Callable[P, T]]


def asfunction[**P, T](
Expand All @@ -29,8 +19,8 @@ def asfunction[**P, T](
module = module or mod()

def decorator(wp: AsFunctionWrappedType[P, T]) -> Callable[P, T]:
fake_method = wp.call.__get__(NotImplemented)
factory: Caller[..., AsFunctionCallable[P, T]] = module.make_injected_function(
fake_method = wp.__call__.__get__(NotImplemented, wp)
Comment thread
remimd marked this conversation as resolved.
factory: Caller[..., Callable[P, T]] = module.make_injected_function(
wp,
threadsafe=threadsafe,
).__inject_metadata__
Expand All @@ -42,14 +32,14 @@ def decorator(wp: AsFunctionWrappedType[P, T]) -> Callable[P, T]:
@wraps(fake_method)
async def wrapper(*args: P.args, **kwargs: P.kwargs) -> Any:
self = await factory.acall()
return await self.call(*args, **kwargs) # type: ignore[misc]
return await self(*args, **kwargs) # type: ignore[misc]

else:

@wraps(fake_method)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
self = factory.call()
return self.call(*args, **kwargs)
return self(*args, **kwargs)

wrapper.__name__ = wp.__name__
wrapper.__qualname__ = wp.__qualname__
Expand Down
4 changes: 2 additions & 2 deletions tests/core/test_asfunction.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Dependency: ...
class SyncFunction(NamedTuple):
dependency: Dependency

def call(self):
def __call__(self):
return self.dependency

assert isinstance(SyncFunction(), Dependency)
Expand All @@ -28,7 +28,7 @@ async def dependency_recipe() -> Dependency:
class AsyncFunction(NamedTuple):
dependency: Dependency

async def call(self):
async def __call__(self):
return self.dependency

assert isinstance(await AsyncFunction(), Dependency)
Loading