From fb6deaec96598826fb839752b102ce34fecb6797 Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Mon, 23 Nov 2015 13:48:07 +0200 Subject: [PATCH] Update factory provider API docs --- dependency_injector/providers.py | 192 ++++++++++++++++++++++++++----- docs/api/providers.rst | 1 + 2 files changed, 162 insertions(+), 31 deletions(-) diff --git a/dependency_injector/providers.py b/dependency_injector/providers.py index f9d8cd30..00f9a024 100644 --- a/dependency_injector/providers.py +++ b/dependency_injector/providers.py @@ -16,7 +16,23 @@ from .errors import Error class Provider(object): - """Base provider class.""" + """Base provider class. + + :py:class:`Provider` is callable (implements ``__call__`` method). Every + call to provider object returns provided result, according to the providing + strategy of particular provider. This ``callable`` functionality is a + regular part of providers API and it should be the same for all provider's + subclasses. + + :py:class:`Provider` implements provider overriding logic that should be + also common for all providers. + + Implementation of particular providing strategy should be done in + :py:meth:`Provider._provide` of :py:class:`Provider` subclass. Current + method is called every time when not overridden provider is called. + + All providers should extend this class. + """ __IS_PROVIDER__ = True __slots__ = ('overridden_by',) @@ -26,7 +42,15 @@ class Provider(object): self.overridden_by = None def __call__(self, *args, **kwargs): - """Return provided instance.""" + """Return provided instance. + + Implementation of current method adds ``callable`` functionality for + providers API and it should be common for all provider's subclasses. + Also this method implements provider overriding logic that is also + common for all providers. Implementation of particular providing + strategy should be done in :py:meth:`Provider._provide` of + :py:class:`Provider` subclass. + """ if self.overridden_by: return self.last_overriding(*args, **kwargs) return self._provide(*args, **kwargs) @@ -40,12 +64,33 @@ class Provider(object): """ raise NotImplementedError() - def delegate(self): - """Return provider's delegate.""" - return Delegate(self) + @property + def is_overridden(self): + """Read-only property that is set to ``True`` if provider is overridden. + + :rtype: bool + """ + return bool(self.overridden_by) + + @property + def last_overriding(self): + """Read-only reference to the last overriding provider, if any. + + :type: :py:class:`Provider` + """ + try: + return self.overridden_by[-1] + except (TypeError, IndexError): + raise Error('Provider {0} is not overridden'.format(str(self))) def override(self, provider): - """Override provider with another provider.""" + """Override provider with another provider. + + :param provider: overriding provider + :type provider: :py:class:`Provider` + + :raise: :py:exc:`dependency_injector.errors.Error` + """ if provider is self: raise Error('Provider {0} could not be overridden ' 'with itself'.format(self)) @@ -54,29 +99,29 @@ class Provider(object): else: self.overridden_by += (ensure_is_provider(provider),) - @property - def is_overridden(self): - """Check if provider is overridden by another provider.""" - return bool(self.overridden_by) - - @property - def last_overriding(self): - """Return last overriding provider.""" - try: - return self.overridden_by[-1] - except (TypeError, IndexError): - raise Error('Provider {0} is not overridden'.format(str(self))) - def reset_last_overriding(self): - """Reset last overriding provider.""" + """Reset last overriding provider. + + :rtype: None + """ if not self.is_overridden: raise Error('Provider {0} is not overridden'.format(str(self))) self.overridden_by = self.overridden_by[:-1] def reset_override(self): - """Reset all overriding providers.""" + """Reset all overriding providers. + + :rtype: None + """ self.overridden_by = None + def delegate(self): + """Return provider's delegate. + + :rtype: :py:class:`Delegate` + """ + return Delegate(self) + class Delegate(Provider): """Provider's delegate.""" @@ -92,36 +137,126 @@ class Delegate(Provider): super(Delegate, self).__init__() def _provide(self, *args, **kwargs): - """Return provided instance.""" + """Return provided instance. + + :param args: tuple of context positional arguments + :type args: tuple[object] + + :param kwargs: dictionary of context keyword arguments + :type kwargs: dict[str, object] + + :rtype: object + """ return self.delegated class Factory(Provider): - """Factory provider. + """:py:class:`Factory` provider creates new instance on every call. - Factory provider creates new instance of specified class on every call. + :py:class:`Factory` supports different syntaxes of passing injections: + + + simplified one syntax for passing positional and keyword argument + injections only: + + .. code-block:: python + + factory = Factory(SomeClass, 'arg1', 'arg2', arg3=3, arg4=4) + + - extended (full) one syntax for passing any type of injections: + + .. code-block:: python + + factory = Factory(SomeClass, + injections.Arg(1), + injections.Arg(2), + injections.KwArg('some_arg', 3), + injections.KwArg('other_arg', 4), + injections.Attribute('some_attribute', 5)) + + Retrieving of provided instance can be performed via calling + :py:class:`Factory` object: + + .. code-block:: python + + factory = Factory(SomeClass, + some_arg1=1, + some_arg2=2) + some_object = factory() """ __slots__ = ('provides', 'args', 'kwargs', 'attributes', 'methods') def __init__(self, provides, *args, **kwargs): - """Initializer.""" + """Initializer. + + :param provides: Class or other callable that provides object + for creation. + :type provides: type | callable + + :param args: Tuple of injections. + :type args: tuple + + :param kwargs: Dictionary of injections. + :type kwargs: dict + """ if not callable(provides): raise Error('Factory provider expects to get callable, ' + 'got {0} instead'.format(str(provides))) self.provides = provides + """Class or other callable that provides object for creation. + + :type: type | callable + """ + self.args = _parse_args_injections(args) + """Tuple of positional argument injections. + + :type: tuple[:py:class:`dependency_injector.injections.Arg`] + """ + self.kwargs = _parse_kwargs_injections(args, kwargs) + """Tuple of keyword argument injections. + + :type: tuple[:py:class:`dependency_injector.injections.KwArg`] + """ + self.attributes = tuple(injection for injection in args if is_attribute_injection(injection)) + """Tuple of attribute injections. + + :type: tuple[:py:class:`dependency_injector.injections.Attribute`] + """ + self.methods = tuple(injection for injection in args if is_method_injection(injection)) + """Tuple of method injections. + + :type: tuple[:py:class:`dependency_injector.injections.Method`] + """ + super(Factory, self).__init__() + @property + def injections(self): + """Read-only tuple of all injections. + + :rtype: tuple[:py:class:`dependency_injector.injections.Injection`] + """ + return self.args + self.kwargs + self.attributes + self.methods + def _provide(self, *args, **kwargs): - """Return provided instance.""" + """Return provided instance. + + :param args: tuple of context positional arguments + :type args: tuple[object] + + :param kwargs: dictionary of context keyword arguments + :type kwargs: dict[str, object] + + :rtype: object + """ instance = self.provides(*_get_injectable_args(args, self.args), **_get_injectable_kwargs(kwargs, self.kwargs)) for attribute in self.attributes: @@ -131,11 +266,6 @@ class Factory(Provider): return instance - @property - def injections(self): - """Return tuple of all injections.""" - return self.args + self.kwargs + self.attributes + self.methods - class Singleton(Provider): """Singleton provider. diff --git a/docs/api/providers.rst b/docs/api/providers.rst index 620960e9..5e47db10 100644 --- a/docs/api/providers.rst +++ b/docs/api/providers.rst @@ -4,3 +4,4 @@ .. automodule:: dependency_injector.providers :members: :member-order: bysource + :inherited-members: