# 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.
There's no need to explicitly use :func:`inject` or :data:`Inject` with the callable as it's assumed that, if the callable has annotated parameters, they're meant to be provided automatically. It wouldn't make sense any other way, as there's no mechanism to provide parameters to the callable at a later time, so either they'll be injected or there'll be a `CallError`.
::
>>> 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.
Binding `T` to an instance of `T` like
::
binder.bind(A, to=A('some', 'thing'))
is, for convenience, a shortcut for
::
binder.bind(A, to=InstanceProvider(A('some', 'thing'))).
Likewise, binding to a callable like
::
binder.bind(A, to=some_callable)
is a shortcut for
::
binder.bind(A, to=CallableProvider(some_callable))
and, as such, if `some_callable` there has any annotated parameters they'll be provided automatically without having to use :func:`inject` or :data:`Inject` with the callable.
`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 instance of :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, ) # On Python 3.5.3 calling call_with_injection() with object.__init__ would fail further # down the line as get_type_hints(object.__init__) raises an exception until Python 3.5.4. # And since object.__init__ doesn't do anything useful and can't have any injectable # arguments we can skip calling it altogether. See GH-135 for more information. 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 """ |