-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* auto inject implementation * remove unused code * fix bug with call with args in Object provider * fix litestar tests --------- Co-authored-by: ivan
- Loading branch information
1 parent
47ed678
commit 689db10
Showing
28 changed files
with
541 additions
and
243 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
from injection import providers | ||
from injection.__version__ import __version__ | ||
from injection.base_container import DeclarativeContainer | ||
from injection.inject import inject | ||
from injection.inject.auto_inject import auto_inject | ||
from injection.inject.inject import inject | ||
from injection.provide import Provide |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import inspect | ||
import sys | ||
from functools import wraps | ||
from typing import Any, Callable, Coroutine, Dict, Optional, Type, TypeVar, Union, cast | ||
|
||
from injection.base_container import DeclarativeContainer | ||
from injection.inject.exceptions import DuplicatedFactoryTypeAutoInjectionError | ||
from injection.inject.inject import _resolve_markers | ||
from injection.provide import Provide | ||
|
||
if sys.version_info < (3, 10): | ||
from typing_extensions import ParamSpec | ||
else: | ||
from typing import ParamSpec | ||
|
||
T = TypeVar("T") | ||
P = ParamSpec("P") | ||
Markers = Dict[str, Provide] | ||
_ContainerType = Union[Type[DeclarativeContainer], DeclarativeContainer] | ||
|
||
|
||
def _resolve_signature_args_with_types_from_container( | ||
*, | ||
signature: inspect.Signature, | ||
target_container: _ContainerType, | ||
) -> Dict[str, Any]: | ||
resolved_signature_typed_args = {} | ||
|
||
for param_name, param in signature.parameters.items(): | ||
if not (param.annotation is not param.empty and param.default is param.empty): | ||
continue | ||
|
||
try: | ||
resolved = target_container.resolve_by_type(param.annotation) | ||
resolved_signature_typed_args[param_name] = resolved | ||
except DuplicatedFactoryTypeAutoInjectionError: | ||
raise | ||
|
||
# Ignore exceptions for cases for example django rest framework | ||
# endpoint may have parameter 'request' - we don't know how to handle a variety of parameters. | ||
# But anyway, after this the runtime will fail with an error if something goes wrong | ||
except Exception: # noqa: S112 | ||
continue | ||
|
||
return resolved_signature_typed_args | ||
|
||
|
||
def _get_sync_injected( | ||
*, | ||
f: Callable[P, T], | ||
markers: Markers, | ||
signature: inspect.Signature, | ||
target_container: _ContainerType, | ||
) -> Callable[P, T]: | ||
@wraps(f) | ||
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: | ||
resolved_signature_typed_args = ( | ||
_resolve_signature_args_with_types_from_container( | ||
signature=signature, | ||
target_container=target_container, | ||
) | ||
) | ||
|
||
provide_markers = { | ||
k: v | ||
for k, v in kwargs.items() | ||
if k not in markers and isinstance(v, Provide) | ||
} | ||
provide_markers.update(markers) | ||
resolved_values = _resolve_markers(provide_markers) | ||
|
||
kwargs.update(resolved_values) | ||
kwargs.update(resolved_signature_typed_args) | ||
return f(*args, **kwargs) | ||
|
||
return wrapper | ||
|
||
|
||
def _get_async_injected( | ||
*, | ||
f: Callable[P, Coroutine[Any, Any, T]], | ||
markers: Markers, | ||
signature: inspect.Signature, | ||
target_container: _ContainerType, | ||
) -> Callable[P, Coroutine[Any, Any, T]]: | ||
@wraps(f) | ||
async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: | ||
resolved_signature_typed_args = ( | ||
_resolve_signature_args_with_types_from_container( | ||
signature=signature, | ||
target_container=target_container, | ||
) | ||
) | ||
|
||
provide_markers = { | ||
k: v | ||
for k, v in kwargs.items() | ||
if k not in markers and isinstance(v, Provide) | ||
} | ||
provide_markers.update(markers) | ||
resolved_values = _resolve_markers(provide_markers) | ||
|
||
kwargs.update(resolved_values) | ||
kwargs.update(resolved_signature_typed_args) | ||
return await f(*args, **kwargs) | ||
|
||
return wrapper | ||
|
||
|
||
def auto_inject( | ||
f: Callable[P, T], | ||
target_container: Optional[_ContainerType] = None, | ||
) -> Callable[P, T]: | ||
"""Decorate callable with injecting decorator. Inject objects by types""" | ||
|
||
if target_container is None: | ||
container_subclasses = DeclarativeContainer.__subclasses__() | ||
|
||
if len(container_subclasses) > 1: | ||
msg = ( | ||
f"Found {len(container_subclasses)} containers, please specify " | ||
f"the required container explicitly in the parameter 'target_container'" | ||
) | ||
raise Exception(msg) | ||
|
||
target_container = container_subclasses[0] | ||
|
||
signature = inspect.signature(f) | ||
parameters = signature.parameters | ||
|
||
markers = { | ||
parameter_name: parameter_value.default | ||
for parameter_name, parameter_value in parameters.items() | ||
if isinstance(parameter_value.default, Provide) | ||
} | ||
|
||
if inspect.iscoroutinefunction(f): | ||
func_with_injected_params = _get_async_injected( | ||
f=f, | ||
markers=markers, | ||
signature=signature, | ||
target_container=target_container, | ||
) | ||
return cast(Callable[P, T], func_with_injected_params) | ||
|
||
return _get_sync_injected( | ||
f=f, | ||
markers=markers, | ||
signature=signature, | ||
target_container=target_container, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
class DuplicatedFactoryTypeAutoInjectionError(Exception): | ||
def __init__(self, type_: str) -> None: | ||
message = ( | ||
f"Cannot resolve auto inject because found " | ||
f"more than one provider for type '{type_}'" | ||
) | ||
super().__init__(message) |
Oops, something went wrong.