# encoding: utf-8 # # Copyright (C) 2010 Alec Thomas <alec@swapoff.org> # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. # # Author: Alec Thomas <alec@swapoff.org>
:copyright: (c) 2012 by Alec Thomas :license: BSD """
Any, Callable, cast, Dict, Generic, Iterable, List, Optional, overload, Tuple, Type, TypeVar, Union, )
# Ignoring errors here as typing_extensions stub doesn't know about those things yet else:
class Annotated: # type: ignore pass
from typing import get_type_hints as _get_type_hints
def get_type_hints( obj: Callable[..., Any], globalns: Optional[Dict[str, Any]] = None, localns: Optional[Dict[str, Any]] = None, include_extras: bool = False, ) -> Dict[str, Any]: return _get_type_hints(obj, globalns, localns)
"""An experimental way to declare injectable dependencies utilizing a `PEP 593`_ implementation in `typing_extensions`.
Those two declarations are equivalent::
@inject def fun(t: SomeType) -> None: pass
def fun(t: Inject[SomeType]) -> None: pass
The advantage over using :func:`inject` is that if you have some noninjectable parameters it may be easier to spot what are they. Those two are equivalent::
@inject @noninjectable('s') def fun(t: SomeType, s: SomeOtherType) -> None: pass
def fun(t: Inject[SomeType], s: SomeOtherType) -> None: pass
.. seealso::
Function :func:`get_bindings` A way to inspect how various injection declarations interact with each other.
.. versionadded:: 0.18.0 .. note:: Requires Python 3.7+. .. note::
If you're using mypy you need the version 0.750 or newer to fully type-check code using this construct.
.. _PEP 593: https://www.python.org/dev/peps/pep-0593/ .. _typing_extensions: https://pypi.org/project/typing-extensions/ """
"""An experimental way to declare noninjectable dependencies utilizing a `PEP 593`_ implementation in `typing_extensions`.
Since :func:`inject` declares all function's parameters to be injectable there needs to be a way to opt out of it. This has been provided by :func:`noninjectable` but `noninjectable` suffers from two issues:
* You need to repeat the parameter name * The declaration may be relatively distance in space from the actual parameter declaration, thus hindering readability
`NoInject` solves both of those concerns, for example (those two declarations are equivalent)::
@inject @noninjectable('b') def fun(a: TypeA, b: TypeB) -> None: pass
@inject def fun(a: TypeA, b: NoInject[TypeB]) -> None: pass
.. seealso::
Function :func:`get_bindings` A way to inspect how various injection declarations interact with each other.
.. versionadded:: 0.18.0 .. note:: Requires Python 3.7+. .. note::
If you're using mypy you need the version 0.750 or newer to fully type-check code using this construct.
.. _PEP 593: https://www.python.org/dev/peps/pep-0593/ .. _typing_extensions: https://pypi.org/project/typing-extensions/ """
prev_cls, prev, tb = sys.exc_info() frames = inspect.getinnerframes(cast(types.TracebackType, tb)) if len(frames) > maximum_frames: exception = original raise exception.with_traceback(tb)
"""Base exception."""
"""Requirement could not be satisfied."""
super().__init__(owner, interface) self.owner = owner self.interface = interface
on = '%s has an ' % _describe(self.owner) if self.owner else '' return '%sunsatisfied requirement on %s' % (on, _describe(self.interface))
"""Call to callable object fails."""
if len(self.args) == 1: return self.args[0]
instance, method, args, kwargs, original_error, stack = self.args if hasattr(method, 'im_class'): instance = method.__self__ method_name = method.__func__.__name__ else: method_name = method.__name__
cls = instance.__class__.__name__ if instance is not None else ''
full_method = '.'.join((cls, method_name)).strip('.')
parameters = ', '.join( itertools.chain( (repr(arg) for arg in args), ('%s=%r' % (key, value) for (key, value) in kwargs.items()) ) ) return 'Call to %s(%s) failed: %s (injection stack: %r)' % ( full_method, parameters, original_error, [level[0] for level in stack], )
"""Circular dependency detected."""
"""Tried to bind to a type whose provider couldn't be determined."""
"""Tried to mark an unknown argument as noninjectable."""
"""Provides class instances."""
raise NotImplementedError # pragma: no cover
"""Provides instances from a given class, created using an Injector."""
"""Provides something using a callable.
The callable is called every time new value is requested from the provider.
::
>>> class MyClass: ... def __init__(self, value: int) -> None: ... self.value = value ... >>> def factory(): ... print('providing') ... return MyClass(42) ... >>> def configure(binder): ... binder.bind(MyClass, to=CallableProvider(factory)) ... >>> injector = Injector(configure) >>> injector.get(MyClass) is injector.get(MyClass) providing providing False """
return '%s(%r)' % (type(self).__name__, self._callable)
"""Provide a specific instance.
::
>>> class MyType: ... def __init__(self): ... self.contents = [] >>> def configure(binder): ... binder.bind(MyType, to=InstanceProvider(MyType())) ... >>> injector = Injector(configure) >>> injector.get(MyType) is injector.get(MyType) True >>> injector.get(MyType).contents.append('x') >>> injector.get(MyType).contents ['x'] """
return '%s(%r)' % (type(self).__name__, self._instance)
"""Provide a list of instances via other Providers."""
self._providers = [] # type: List[Provider[T]]
self._providers.append(provider)
return '%s(%r)' % (type(self).__name__, self._providers)
"""Used by :meth:`Binder.multibind` to flatten results of providers that return sequences."""
return [i for provider in self._providers for i in provider.get(injector)]
"""A provider for map bindings."""
map = {} # type: Dict[str, T] for provider in self._providers: map.update(provider.get(injector)) return map
"""A binding from an (interface,) to a provider in a scope."""
"""Bind interfaces to implementations.
.. note:: This class is instantiated internally for you and there's no need to instantiate it on your own. """
"""Create a new Binder.
:param injector: Injector we are binding for. :param auto_bind: Whether to automatically bind missing types. :param parent: Parent binder. """
self, interface: Type[T], to: Union[None, T, Callable[..., T], Provider[T]] = None, scope: Union[None, Type['Scope'], 'ScopeDecorator'] = None, ) -> None: """Bind an interface to an implementation.
`typing.List` and `typing.Dict` instances are reserved for multibindings and trying to bind them here will result in an error (use :meth:`multibind` instead)::
binder.bind(List[str], to=['hello', 'there']) # Error
:param interface: Type to bind. :param to: Instance or class to bind to, or an explicit :class:`Provider` subclass. :param scope: Optional :class:`Scope` in which to bind. """ raise Error( 'Type %s is reserved for multibindings. Use multibind instead of bind.' % (interface,) )
def multibind( self, interface: Type[List[T]], to: Union[List[T], Callable[..., List[T]], Provider[List[T]]], scope: Union[Type['Scope'], 'ScopeDecorator'] = None, ) -> None: # pragma: no cover pass
def multibind( self, interface: Type[Dict[K, V]], to: Union[Dict[K, V], Callable[..., Dict[K, V]], Provider[Dict[K, V]]], scope: Union[Type['Scope'], 'ScopeDecorator'] = None, ) -> None: # pragma: no cover pass
self, interface: type, to: Any, scope: Union['ScopeDecorator', Type['Scope']] = None ) -> None: """Creates or extends a multi-binding.
A multi-binding contributes values to a list or to a dictionary. For example::
binder.multibind(List[str], to=['some', 'strings']) binder.multibind(List[str], to=['other', 'strings']) injector.get(List[str]) # ['some', 'strings', 'other', 'strings']
binder.multibind(Dict[str, int], to={'key': 11}) binder.multibind(Dict[str, int], to={'other_key': 33}) injector.get(Dict[str, int]) # {'key': 11, 'other_key': 33}
.. versionchanged:: 0.17.0 Added support for using `typing.Dict` and `typing.List` instances as interfaces. Deprecated support for `MappingKey`, `SequenceKey` and single-item lists and dictionaries as interfaces.
:param interface: typing.Dict or typing.List instance to bind to. :param to: Instance, class to bind to, or an explicit :class:`Provider` subclass. Must provide a list or a dictionary, depending on the interface. :param scope: Optional Scope in which to bind. """ if interface not in self._bindings: if ( isinstance(interface, dict) or isinstance(interface, type) and issubclass(interface, dict) or _get_origin(_punch_through_alias(interface)) is dict ): provider = MapBindProvider() # type: ListOfProviders else: provider = MultiBindProvider() binding = self.create_binding(interface, provider, scope) self._bindings[interface] = binding else: binding = self._bindings[interface] provider = binding.provider assert isinstance(provider, ListOfProviders) provider.append(self.provider_for(interface, to))
"""Install a module into this binder.
In this context the module is one of the following:
* function taking the :class:`Binder` as it's only parameter
::
def configure(binder): bind(str, to='s')
binder.install(configure)
* instance of :class:`Module` (instance of it's subclass counts)
::
class MyModule(Module): def configure(self, binder): binder.bind(str, to='s')
binder.install(MyModule())
* subclass of :class:`Module` - the subclass needs to be instantiable so if it expects any parameters they need to be injected
::
binder.install(MyModule) """ else:
self, interface: type, to: Any = None, scope: Union['ScopeDecorator', Type['Scope']] = None ) -> Binding: scope = scope.scope
raise TypeError('Injecting Any is not supported') raise Exception('ProviderOf cannot be bound to anything') to, ( types.FunctionType, types.LambdaType, types.MethodType, types.BuiltinFunctionType, types.BuiltinMethodType, ), ): return ClassProvider(cast(type, to))
name: binder.provider_for(None, provider) for (name, provider) in interface.kwargs.items() }
(target,) = interface.__args__ builder = interface(self.injector, target) return InstanceProvider(builder) origin is None and isinstance(base_type, (tuple, type)) and interface is not Any and isinstance(to, base_type) or origin in {dict, list} and isinstance(to, origin) ): return InstanceProvider(to)
else: raise UnknownProvider('couldn\'t determine provider for %r to %r' % (interface, to))
return self.parent._get_binding(key)
# The special interface is added here so that requesting a special # interface with auto_bind disabled works
raise UnsatisfiedRequirement(None, interface)
# "Special" interfaces are ones that you cannot bind yourself but # you can request them (for example you cannot bind ProviderOf(SomeClass) # to anything but you can inject ProviderOf(SomeClass) just fine return any(_is_specialization(interface, cls) for cls in [AssistedBuilder, ProviderOf])
# Starting with typing 3.5.3/Python 3.6 it is no longer necessarily true that # issubclass(SomeGeneric[X], SomeGeneric) so we need some other way to # determine whether a particular object is a generic class with type parameters # provided. Fortunately there seems to be __origin__ attribute that's useful here.
# We need to special-case Annotated as its __origin__ behaves differently than # other typing generic classes. See https://github.com/python/typing/pull/635 # for some details.
generic_class = type(generic_class) origin = type(origin) # __origin__ is generic_class is a special case to handle Union as # Union cannot be used in issubclass() check (it raises an exception # by design).
else: # To maintain compatibility we fall back to an issubclass check. def _is_specialization(cls: type, generic_class: Any) -> bool: return isinstance(cls, type) and cls is not Any and issubclass(cls, generic_class)
return type_.__supertype__ else:
# Older typing behaves differently there and stores Dict and List as origin, we need to be flexible. return list return dict
"""A Scope looks up the Provider for a binding.
By default (ie. :class:`NoScope` ) this simply returns the default :class:`Provider` . """
"""Configure the scope."""
"""Get a :class:`Provider` for a key.
:param key: The key to return a provider for. :param provider: The default Provider associated with the key. :returns: A Provider instance that can provide an instance of key. """ raise NotImplementedError # pragma: no cover
cast(Any, cls).__scope__ = self.scope binding = getattr(cls, '__binding__', None) if binding: new_binding = Binding(interface=binding.interface, provider=binding.provider, scope=self.scope) setattr(cls, '__binding__', new_binding) return cls
return 'ScopeDecorator(%s)' % self.scope.__name__
"""An unscoped provider."""
"""A :class:`Scope` that returns a per-Injector instance for a key.
:data:`singleton` can be used as a convenience class decorator.
>>> class A: pass >>> injector = Injector() >>> provider = ClassProvider(A) >>> singleton = SingletonScope(injector) >>> a = singleton.get(A, provider) >>> b = singleton.get(A, provider) >>> a is b True """
"""A :class:`Scope` that returns a per-thread instance for a key."""
self._locals = threading.local()
try: return getattr(self._locals, repr(key)) except AttributeError: provider = InstanceProvider(provider.get(self.injector)) setattr(self._locals, repr(key), provider) return provider
"""Configures injector and providers."""
"""Configure the binder.""" # We could not evaluate a forward reference at @provider-decoration time, we need to # try again now. try: annotations = get_type_hints(function) except NameError as e: raise NameError( 'Cannot avaluate forward reference annotation(s) in method %r belonging to %r: %s' % (function.__name__, type(self), e) ) from e return_type = annotations['return'] binding = function.__func__.__binding__ = Binding( interface=return_type, provider=binding.provider, scope=binding.scope ) binding.interface, to=types.MethodType(binding.provider, self), scope=binding.scope )
"""Override to configure bindings."""
""" :param modules: Optional - a configuration module or iterable of configuration modules. Each module will be installed in current :class:`Binder` using :meth:`Binder.install`.
Consult :meth:`Binder.install` documentation for the details.
:param auto_bind: Whether to automatically bind missing types. :param parent: Parent injector.
.. versionadded:: 0.7.5 ``use_annotations`` parameter
.. versionchanged:: 0.13.0 ``use_annotations`` parameter is removed """
self, modules: Union[_InstallableModuleType, Iterable[_InstallableModuleType]] = None, auto_bind: bool = True, parent: 'Injector' = None, ) -> None: # Stack of keys currently being injected. Used to detect circular # dependencies.
# Binder self, auto_bind=auto_bind, parent=parent.binder if parent is not None else None ) # type: Binder
# This line is needed to pelase mypy. We know we have Iteable of modules here.
# Bind some useful types
# Initialise modules
"""Get an instance of the given interface.
.. note::
Although this method is part of :class:`Injector`'s public interface it's meant to be used in limited set of circumstances.
For example, to create some kind of root object (application object) of your application (note that only one `get` call is needed, inside the `Application` class and any of its dependencies :func:`inject` can and should be used):
.. code-block:: python
class Application:
@inject def __init__(self, dep1: Dep1, dep2: Dep2): self.dep1 = dep1 self.dep2 = dep2
def run(self): self.dep1.something()
injector = Injector(configuration) application = injector.get(Application) application.run()
:param interface: Interface whose implementation we want. :param scope: Class of the Scope in which to resolve. :returns: An implementation of interface. """ scope = scope.scope # Fetch the corresponding Scope instance from the Binder.
'%sInjector.get(%r, scope=%r) using %r', self._log_prefix, interface, scope, binding.provider )
kwargs['parent'] = self return Injector(*args, **kwargs)
"""Create a new instance, satisfying any dependencies on cls."""
except TypeError as e: reraise( e, CallError(cls, getattr(cls.__new__, '__func__', cls.__new__), (), {}, e, self._stack), maximum_frames=2, ) except TypeError as e: reraise(e, CallError(instance, instance.__init__.__func__, (), additional_kwargs, e, self._stack))
self, callable: Callable[..., T], self_: Any = None, args: Any = (), kwargs: Any = {} ) -> T: """Call a callable and provide it's dependencies if needed.
:param self_: Instance of a class callable belongs to if it's a method, None otherwise. :param args: Arguments to pass to callable. :param kwargs: Keyword arguments to pass to callable. :type callable: callable :type args: tuple of objects :type kwargs: dict of string -> object :return: Value returned by callable. """
(k, v) for (k, v) in bindings.items() if k not in kwargs and k not in bound_arguments.arguments )
function=callable, bindings=needed, owner_key=self_.__class__ if self_ is not None else callable.__module__, )
except TypeError as e: reraise(e, CallError(self_, callable, args, dependencies, e, self._stack)) # Needed because of a mypy-related issue (https://github.com/python/mypy/issues/8129). assert False, "unreachable" # pragma: no cover
self, function: Callable, bindings: Dict[str, type], owner_key: object ) -> Dict[str, Any]: """Inject arguments into a function.
:param function: The function. :param bindings: Map of argument name to binding key to inject. :param owner_key: A key uniquely identifying the *scope* of this function. For a method this will be the owning class. :returns: Dictionary of resolved arguments. """
owner_key, function, bindings = k return '%s.%s(injecting %s)' % (tuple(map(_describe, k[:2])) + (dict(k[2]),))
raise CircularDependency( 'circular dependency detected: %s -> %s' % (' -> '.join(map(repr_key, self._stack)), repr_key(key)) )
except UnsatisfiedRequirement as e: if not e.owner: e = UnsatisfiedRequirement(owner_key, e.interface) raise e finally:
"""Get bindings of injectable parameters from a callable.
If the callable is not decorated with :func:`inject` and does not have any of its parameters declared as injectable using :data:`Inject` an empty dictionary will be returned. Otherwise the returned dictionary will contain a mapping between parameter names and their types with the exception of parameters excluded from dependency injection (either with :func:`noninjectable`, :data:`NoInject` or only explicit injection with :data:`Inject` being used). For example::
>>> def function1(a: int) -> None: ... pass ... >>> get_bindings(function1) {}
>>> @inject ... def function2(a: int) -> None: ... pass ... >>> get_bindings(function2) {'a': <class 'int'>}
>>> @inject ... @noninjectable('b') ... def function3(a: int, b: str) -> None: ... pass ... >>> get_bindings(function3) {'a': <class 'int'>}
>>> import sys, pytest >>> if sys.version_info < (3, 7, 0): ... pytest.skip('Python 3.7.0 required for sufficient Annotated support')
>>> # The simple case of no @inject but injection requested with Inject[...] >>> def function4(a: Inject[int], b: str) -> None: ... pass ... >>> get_bindings(function4) {'a': <class 'int'>}
>>> # Using @inject with Inject is redundant but it should not break anything >>> @inject ... def function5(a: Inject[int], b: str) -> None: ... pass ... >>> get_bindings(function5) {'a': <class 'int'>, 'b': <class 'str'>}
>>> # We need to be able to exclude a parameter from injection with NoInject >>> @inject ... def function6(a: int, b: NoInject[str]) -> None: ... pass ... >>> get_bindings(function6) {'a': <class 'int'>}
>>> # The presence of NoInject should not trigger anything on its own >>> def function7(a: int, b: NoInject[str]) -> None: ... pass ... >>> get_bindings(function7) {}
This function is used internally so by calling it you can learn what exactly Injector is going to try to provide to a callable. """ _is_specialization(v, Annotated) and _inject_marker in v.__metadata__ for v in type_hints.values() )
else:
callable, _infer_injected_bindings(callable, only_explicit_bindings=look_for_explicit_bindings) )
except NameError as e: raise _BindingNotYetAvailable(e)
# We don't care about the return value annotation as it doesn't matter # injection-wise.
# If we're dealing with a bound method get_type_hints will still return `self` annotation even though # it's already provided and we're not really interested in its type. So – drop it. self_name = spec.args[0] bindings.pop(self_name, None)
# variadic arguments aren't supported at the moment (this may change # in the future if someone has a good idea how to utilize them) bindings.pop(spec.varargs, None) bindings.pop(spec.varkw, None)
else:
# We don't treat Optional parameters in any special way at the moment. if TYPING353: union_members = v.__args__ else: union_members = v.__union_params__ new_members = tuple(set(union_members) - {type(None)}) # mypy stared complaining about this line for some reason: # error: Variable "new_members" is not valid as a type new_union = Union[new_members] # type: ignore # mypy complains about this construct: # error: The type alias is invalid in runtime context # See: https://github.com/python/mypy/issues/5354 bindings[k] = new_union # type: ignore
"""Decorator for :class:`Module` methods, registering a provider of a type.
>>> class MyModule(Module): ... @provider ... def provide_name(self) -> str: ... return 'Bob'
@provider-decoration implies @inject so you can omit it and things will work just the same:
>>> class MyModule2(Module): ... def configure(self, binder): ... binder.bind(int, to=654) ... ... @provider ... def provide_str(self, i: int) -> str: ... return str(i) ... >>> injector = Injector(MyModule2) >>> injector.get(str) '654' """
"""Like :func:`provider`, but for multibindings. Example usage::
class MyModule(Module): @multiprovider def provide_strs(self) -> List[str]: return ['str1']
class OtherModule(Module): @multiprovider def provide_strs_also(self) -> List[str]: return ['str2']
Injector([MyModule, OtherModule]).get(List[str]) # ['str1', 'str2']
See also: :meth:`Binder.multibind`.""" _mark_provider_function(function, allow_multi=True) return function
except NameError: return_type = '__deferred__' else:
raise Error( 'Function %s needs to be decorated with multiprovider instead of provider if it is to ' 'provide values to a multibinding of type %s' % (function.__name__, return_type) )
def inject(constructor_or_class: CallableT) -> CallableT: # pragma: no cover pass
def inject(constructor_or_class: Type[T]) -> Type[T]: # pragma: no cover pass
"""Decorator declaring parameters to be injected.
eg.
>>> class A: ... @inject ... def __init__(self, number: int, name: str): ... print([number, name]) ... >>> def configure(binder): ... binder.bind(A) ... binder.bind(int, to=123) ... binder.bind(str, to='Bob')
Use the Injector to get a new instance of A:
>>> a = Injector(configure).get(A) [123, 'Bob']
As a convenience one can decorate a class itself::
@inject class B: def __init__(self, dependency: Dependency): self.dependency = dependency
This is equivalent to decorating its constructor. In particular this provides integration with `dataclasses <https://docs.python.org/3/library/dataclasses.html>`_ (the order of decorator application is important here)::
@inject @dataclass class C: dependency: Dependency
.. note::
This decorator is to be used on class constructors (or, as a convenience, on classes). Using it on non-constructor methods worked in the past but it was an implementation detail rather than a design decision.
Third party libraries may, however, provide support for injecting dependencies into non-constructor methods or free functions in one form or another.
.. seealso::
Generic type :data:`Inject` A more explicit way to declare parameters as injectable.
Function :func:`get_bindings` A way to inspect how various injection declarations interact with each other.
.. versionchanged:: 0.16.2
(Re)added support for decorating classes with @inject. """ inject(cast(Any, constructor_or_class).__init__) else: except _BindingNotYetAvailable: cast(Any, function).__bindings__ = 'deferred'
"""Mark some parameters as not injectable.
This serves as documentation for people reading the code and will prevent Injector from ever attempting to provide the parameters.
For example:
>>> class Service: ... pass ... >>> class SomeClass: ... @inject ... @noninjectable('user_id') ... def __init__(self, service: Service, user_id: int): ... # ... ... pass
:func:`noninjectable` decorations can be stacked on top of each other and the order in which a function is decorated with :func:`inject` and :func:`noninjectable` doesn't matter.
.. seealso::
Generic type :data:`NoInject` A nicer way to declare parameters as noninjectable.
Function :func:`get_bindings` A way to inspect how various injection declarations interact with each other.
"""
raise UnknownArgument('Unable to mark unknown argument %s ' 'as non-injectable.' % arg)
function_bindings = {}
f = cast(Any, f).__func__
"""A BoundKey provides a key to a type with pre-injected arguments.
>>> class A: ... def __init__(self, a, b): ... self.a = a ... self.b = b >>> InjectedA = BoundKey(A, a=InstanceProvider(1), b=InstanceProvider(2)) >>> injector = Injector() >>> a = injector.get(InjectedA) >>> a.a, a.b (1, 2) """
self._injector = injector self._target = target
binder = self._injector.binder binding, _ = binder.get_binding(self._target) provider = binding.provider if not isinstance(provider, ClassProvider): raise Error( 'Assisted interface building works only with ClassProviders, ' 'got %r for %r' % (provider, binding.interface) )
return self._build_class(cast(Type[T], provider._cls), **kwargs)
return self._injector.create_object(cls, additional_kwargs=kwargs)
return self._build_class(self._target, **kwargs)
if hasattr(c, '__name__'): return cast(str, c.__name__) if type(c) in (tuple, list): return '[%s]' % c[0].__name__ return str(c)
"""Can be used to get a provider of an interface, for example:
>>> def provide_int(): ... print('providing') ... return 123 >>> >>> def configure(binder): ... binder.bind(int, to=provide_int) >>> >>> injector = Injector(configure) >>> provider = injector.get(ProviderOf[int]) >>> value = provider.get() providing >>> value 123 """
return '%s(%r, %r)' % (type(self).__name__, self._injector, self._interface)
"""Get an implementation for the specified interface."""
"""See if given callable is declared to want some dependencies injected.
Example use:
>>> def fun(i: int) -> str: ... return str(i)
>>> is_decorated_with_inject(fun) False >>> >>> @inject ... def fun2(i: int) -> str: ... return str(i)
>>> is_decorated_with_inject(fun2) True """ |