Source code for injectable.autowiring.autowired_decorator

import inspect
from functools import wraps
from typing import TypeVar, Callable, Any

from injectable.autowiring.autowired_type import _Autowired
from injectable.errors import AutowiringError

T = TypeVar("T", bound=Callable[..., Any])


[docs]def autowired(func: T) -> T: """ Function decorator to setup dependency injection autowiring. Only parameters annotated with :class:`Autowired <injectable.Autowired>` will be autowired for injection. If no parameter is annotated with :class:`Autowired <injectable.Autowired>` an :class:`AutowiringError <injectable.errors.AutowiringError>` will be raised. An :class:`AutowiringError <injectable.errors.AutowiringError>` will also be raised if a parameter annotated with :class:`Autowired <injectable.Autowired>` is given a default value or if a non Autowired-annotated positional parameter is placed after an Autowired-annotated positional parameter. Before attempting to call an autowired function make sure :meth:`load_injection_container <injectable.load_injection_container>` was invoked. .. note:: This decorator can be applied to any function, not only an `__init__` method. .. note:: This decorator accepts no arguments and must be used without trailing parenthesis. Usage:: >>> from injectable import Autowired, autowired >>> >>> @autowired ... def foo(dep: Autowired(...)): ... ... """ signature = inspect.signature(func) autowired_parameters = [] for index, parameter in enumerate(signature.parameters.values()): if not isinstance(parameter.annotation, _Autowired): if len(autowired_parameters) == 0 or parameter.kind in [ parameter.KEYWORD_ONLY, parameter.VAR_KEYWORD, ]: continue raise AutowiringError( "Non-Autowired positional parameter follows Autowired parameter" ) if parameter.default is not parameter.empty: raise AutowiringError("Default value assigned to Autowired parameter") if parameter.kind in (parameter.VAR_POSITIONAL, parameter.VAR_KEYWORD): raise AutowiringError(f"Autowired parameter is of kind {parameter.kind}") autowired_parameters.append(parameter) if len(autowired_parameters) == 0: raise AutowiringError("No parameter is typed with 'Autowired'") @wraps(func) def wrapper(*args, **kwargs): bound_arguments = signature.bind_partial(*args, **kwargs).arguments args = list(args) for parameter in autowired_parameters: if parameter.name in bound_arguments: continue dependency = parameter.annotation.inject() if parameter.kind is parameter.POSITIONAL_ONLY: args.append(dependency) else: kwargs[parameter.name] = dependency return func(*args, **kwargs) return wrapper