Update factory provider docs

This commit is contained in:
Roman Mogilatov 2016-06-08 16:39:53 +03:00
parent addaadac72
commit 14ca5651df
18 changed files with 102 additions and 412 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

View File

@ -24,137 +24,29 @@ that are used as ``__init__()`` injections. Every time, when
:py:class:`Factory` creates new one instance, positional and keyword
argument injections would be passed as an instance's arguments.
Such behaviour is very similar to the standard Python ``functools.partial``
object with several more things:
Injections are done according to the next rules:
+ All providers (instances of :py:class:`Provider`) are called every time
when injection needs to be done.
+ Providers could be injected "as is" (delegated), if it is defined obviously.
Check out `Factory providers delegation`_.
+ All other injectable values are provided *"as is"*
+ All other injectable values are provided *"as is"*.
+ Positional context arguments will be appended after :py:class:`Factory`
positional injections.
+ Keyword context arguments have priority on :py:class:`Factory` keyword
injections and will be merged over them.
For example, if injectable value of injection is a :py:class:`Factory`, it
will provide new one instance (as a result of its call) every time, when
injection needs to be done.
Example below is a little bit more complicated. It shows how to create
:py:class:`Factory` of particular class with ``__init__()`` argument
injections which injectable values are also provided by another factories:
.. note::
Current positional and keyword argument injections syntax (in the examples
below) is a **simplified one** version of full syntax. Examples of full
syntax and other types of injections could be found in sections below.
While positional / keyword argument injections may be the best way of
passing injections, current simplified syntax might be the preferable one
and could be widely used.
:py:class:`Factory` of particular class with ``__init__()`` injections which
injectable values are also provided by another factories:
.. image:: /images/providers/factory_init_injections.png
:width: 90%
:align: center
Example of usage positional argument injections:
.. literalinclude:: ../../examples/providers/factory_init_args.py
:language: python
:linenos:
Example of usage keyword argument injections:
.. literalinclude:: ../../examples/providers/factory_init_kwargs.py
:language: python
:linenos:
Factory providers and __init__ injections priority
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Next example shows how :py:class:`Factory` provider deals with positional and
keyword ``__init__()`` context arguments. In few words, :py:class:`Factory`
behaviour here is very like a standard Python ``functools.partial``:
- Positional context arguments will be appended after :py:class:`Factory`
positional injections.
- Keyword context arguments have priority on :py:class:`Factory` keyword
injections and will be merged over them.
So, please, follow the example below:
.. image:: /images/providers/factory_init_injections_and_contexts.png
.. literalinclude:: ../../examples/providers/factory_init_injections_and_contexts.py
:language: python
:linenos:
Factory providers and other types of injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Objects can take dependencies in different forms (some objects take init
arguments, other use attributes setting or method calls). It affects how
such objects are created and initialized.
:py:class:`Factory` provider takes various number of positional and keyword
arguments, that define what kinds of dependency injections have to be used.
All of those instructions are defined in
:py:mod:`dependency_injector.injections` module and are subclasses of
:py:class:`dependency_injector.injections.Injection`. There are several types
of injections that are used by :py:class:`Factory` provider:
+ :py:class:`dependency_injector.injections.Arg` - injection is done by
passing injectable value in object's ``__init__()`` method in time of
object's creation as positional argument. Takes injectable value only.
+ :py:class:`dependency_injector.injections.KwArg` - injection is done by
passing injectable value in object's ``__init__()`` method in time of
object's creation as keyword argument. Takes keyword name of
``__init__()`` argument and injectable value.
+ :py:class:`dependency_injector.injections.Attribute` - injection is done
by setting specified attribute with injectable value right after
object's creation. Takes attribute's name and injectable value.
+ :py:class:`dependency_injector.injections.Method` - injection is done by
calling of specified method with injectable value right after object's
creation and attribute injections are done. Takes method name and
injectable value.
All :py:class:`dependency_injector.injections.Injection`'s injectable values
are provided *"as is"*, except of providers (subclasses of
:py:class:`Provider`). Providers will be called every time, when injection
needs to be done.
Factory providers and attribute injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Example below shows how to create :py:class:`Factory` of particular class with
attribute injections. Those injections are done by setting specified attributes
with injectable values right after object's creation.
Example:
.. image:: /images/providers/factory_attribute_injections.png
.. literalinclude:: ../../examples/providers/factory_attribute_injections.py
:language: python
:linenos:
Factory providers and method injections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Current example shows how to create :py:class:`Factory` of particular class
with method injections. Those injections are done by calling of specified
method with injectable value right after object's creation and attribute
injections are done.
Method injections are not very popular in Python due Python best practices
(usage of public attributes instead of setter methods), but they may appear in
some cases.
Example:
.. image:: /images/providers/factory_method_injections.png
.. literalinclude:: ../../examples/providers/factory_method_injections.py
.. literalinclude:: ../../examples/providers/factory_init_injections.py
:language: python
:linenos:
@ -162,16 +54,26 @@ Factory providers delegation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Factory` provider could be delegated to any other provider via any
kind of injection. As it was mentioned earlier, if :py:class:`Factory` is
injectable value, it will be called every time when injection is done.
kind of injection.
As it was mentioned earlier, if :py:class:`Factory` is
injectable value, it will be called every time when injection needs to be
done. But sometimes there is a need to inject :py:class:`Factory` provider
itself (not a result of its call) as a dependency. Such injections are called
- *delegated provider injections*.
Saying in other words, delegation of factories - is a way to inject factories
themselves, instead of results of their calls.
:py:class:`Factory` delegation is performed by wrapping delegated
:py:class:`Factory` into special provider type - :py:class:`Delegate`, that
just returns wrapped :py:class:`Factory`. Saying in other words, delegation
of factories - is a way to inject factories themselves, instead of results
of their calls.
just returns wrapped :py:class:`Factory`.
Actually, there are two ways of creating factory delegates:
Actually, there are three ways for creating factory delegates:
+ ``DelegatedFactory(...)`` - use special type of factory -
:py:class:`DelegatedFactory`. Such factories are always injected as
delegates ("as is").
+ ``Delegate(Factory(...))`` - obviously wrapping factory into
:py:class:`Delegate` provider.
+ ``Factory(...).delegate()`` - calling factory :py:meth:`Factory.delegate`
@ -187,24 +89,14 @@ Example:
:language: python
:linenos:
Alternative way of doing :py:class:`Factory` delegation is an usage of
:py:class:`DelegatedFactory`. :py:class:`DelegatedFactory` is a
:py:class:`Factory` that is always injected "as is".
Example:
.. literalinclude:: ../../examples/providers/delegated_factory.py
:language: python
:linenos:
Factory providers specialization
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:py:class:`Factory` provider could be specialized for any kind of needs via
declaring its subclasses.
creating its subclasses.
One of such `builtin` features is a limitation to :py:class:`Factory` provided
type:
One of such specialization features is a limitation to :py:class:`Factory`
provided type:
.. literalinclude:: ../../examples/providers/factory_provided_type.py
:language: python

View File

@ -1,7 +1,7 @@
Providers
=========
Providers are strategies of accessing objects. They describe how particular
Providers are strategies of accessing objects. They define how particular
objects are provided.
Base providers class is - :py:class:`dependency_injector.providers.Provider`

View File

@ -1,46 +0,0 @@
"""`DelegatedFactory` providers example."""
import dependency_injector.providers as providers
class User(object):
"""Example class User."""
def __init__(self, photos_factory):
"""Initializer.
:param photos_factory: providers.Factory -> Photo
"""
self.photos_factory = photos_factory
self._main_photo = None
super(User, self).__init__()
@property
def main_photo(self):
"""Return user's main photo."""
if not self._main_photo:
self._main_photo = self.photos_factory()
return self._main_photo
class Photo(object):
"""Example class Photo."""
# User and Photo factories:
photos_factory = providers.DelegatedFactory(Photo)
users_factory = providers.DelegatedFactory(User,
photos_factory=photos_factory)
# Creating several User objects:
user1 = users_factory()
user2 = users_factory()
# Making some asserts:
assert isinstance(user1, User)
assert isinstance(user1.main_photo, Photo)
assert isinstance(user2, User)
assert isinstance(user2.main_photo, Photo)
assert user1 is not user2
assert user1.main_photo is not user2.main_photo

View File

@ -1,18 +1,14 @@
"""`Factory` providers example."""
import collections
import dependency_injector.providers as providers
class User(object):
"""Example class User."""
User = collections.namedtuple('User', [])
# Factory provider creates new instance of specified class on every call.
users_factory = providers.Factory(User)
# Creating several User objects:
user1 = users_factory()
user2 = users_factory()
# Making some asserts:
assert user1 is not user2
assert isinstance(user1, User) and isinstance(user2, User)
user1 = users_factory() # Same as: user1 = User()
user2 = users_factory() # Same as: user2 = User()

View File

@ -1,52 +0,0 @@
"""`Factory` providers with attribute injections example."""
import dependency_injector.providers as providers
import dependency_injector.injections as injections
class User(object):
"""Example class User."""
def __init__(self):
"""Initializer."""
self.main_photo = None
self.credit_card = None
class Photo(object):
"""Example class Photo."""
class CreditCard(object):
"""Example class CreditCard."""
# User, Photo and CreditCard factories:
credit_cards_factory = providers.Factory(CreditCard)
photos_factory = providers.Factory(Photo)
users_factory = providers.Factory(User,
injections.Attribute('main_photo',
photos_factory),
injections.Attribute('credit_card',
credit_cards_factory))
# Creating several User objects:
user1 = users_factory()
# Same as: user1 = User()
# user1.main_photo = Photo()
# user1.credit_card = CreditCard()
user2 = users_factory()
# Same as: user2 = User()
# user2.main_photo = Photo()
# user2.credit_card = CreditCard()
# Making some asserts:
assert user1 is not user2
assert isinstance(user1.main_photo, Photo)
assert isinstance(user1.credit_card, CreditCard)
assert isinstance(user2.main_photo, Photo)
assert isinstance(user2.credit_card, CreditCard)
assert user1.main_photo is not user2.main_photo
assert user1.credit_card is not user2.credit_card

View File

@ -1,19 +1,19 @@
"""`Factory` providers delegation example."""
from dependency_injector import providers
import collections
import dependency_injector.providers as providers
Photo = collections.namedtuple('Photo', [])
class User(object):
"""Example class User."""
"""Example user model."""
def __init__(self, photos_factory):
"""Initializer.
:param photos_factory: providers.Factory -> Photo
"""
"""Initializer."""
self.photos_factory = photos_factory
self._main_photo = None
super(User, self).__init__()
@property
def main_photo(self):
@ -23,24 +23,30 @@ class User(object):
return self._main_photo
class Photo(object):
"""Example class Photo."""
# Defining User and Photo factories using DelegatedFactory provider:
photos_factory = providers.DelegatedFactory(Photo)
users_factory = providers.DelegatedFactory(User,
photos_factory=photos_factory)
# or using Delegate(Factory(...))
photos_factory = providers.Factory(Photo)
users_factory = providers.Factory(User,
photos_factory=providers.Delegate(
photos_factory))
# or using Factory(...).delegate()
# User and Photo factories:
photos_factory = providers.Factory(Photo)
users_factory = providers.Factory(User,
photos_factory=photos_factory.delegate())
# Creating several User objects:
user1 = users_factory()
user2 = users_factory()
user1 = users_factory() # Same as: user1 = User(photos_factory=photos_factory)
user2 = users_factory() # Same as: user2 = User(photos_factory=photos_factory)
# Making some asserts:
assert isinstance(user1, User)
assert isinstance(user1.main_photo, Photo)
assert isinstance(user2, User)
assert isinstance(user2.main_photo, Photo)
assert user1 is not user2
assert user1.main_photo is not user2.main_photo

View File

@ -1,34 +0,0 @@
"""`Factory` providers with init positional injections example."""
import dependency_injector.providers as providers
class User(object):
"""Example class User."""
def __init__(self, main_photo):
"""Initializer."""
self.main_photo = main_photo
super(User, self).__init__()
class Photo(object):
"""Example class Photo."""
# User and Photo factories:
photos_factory = providers.Factory(Photo)
users_factory = providers.Factory(User, photos_factory)
# Creating several User objects:
user1 = users_factory() # Same as: user1 = User(Photo())
user2 = users_factory() # Same as: user2 = User(Photo())
# Making some asserts:
assert isinstance(user1, User)
assert isinstance(user1.main_photo, Photo)
assert isinstance(user2, User)
assert isinstance(user2.main_photo, Photo)
assert user1 is not user2
assert user1.main_photo is not user2.main_photo

View File

@ -0,0 +1,38 @@
"""`Factory` providers init injections example."""
import collections
import dependency_injector.providers as providers
CreditCard = collections.namedtuple('CreditCard', [])
Photo = collections.namedtuple('Photo', [])
User = collections.namedtuple('User', ['uid', 'main_photo', 'credit_card'])
# User, Photo and CreditCard factories:
credit_cards_factory = providers.Factory(CreditCard)
photos_factory = providers.Factory(Photo)
users_factory = providers.Factory(User,
main_photo=photos_factory,
credit_card=credit_cards_factory)
# Creating several User objects:
user1 = users_factory(1)
# Same as: user1 = User(1,
# main_photo=Photo(),
# credit_card=CreditCard())
user2 = users_factory(2)
# Same as: user2 = User(2,
# main_photo=Photo(),
# credit_card=CreditCard())
# Context keyword arguments have priority on keyword argument injections:
main_photo = Photo()
credit_card = CreditCard()
user3 = users_factory(3,
main_photo=main_photo,
credit_card=credit_card)
# Same as: user3 = User(3,
# main_photo=main_photo,
# credit_card=credit_card)

View File

@ -1,71 +0,0 @@
"""`Factory` providers with init injections priority example."""
import dependency_injector.providers as providers
class User(object):
"""Example class User.
Class User has to be provided with user id.
Also Class User has dependencies on class Photo and class CreditCard
objects.
All of the dependencies have to be provided like __init__ arguments.
"""
def __init__(self, id, main_photo, credit_card):
"""Initializer."""
self.id = id
self.main_photo = main_photo
self.credit_card = credit_card
super(User, self).__init__()
class Photo(object):
"""Example class Photo."""
class CreditCard(object):
"""Example class CreditCard."""
# User, Photo and CreditCard factories:
credit_cards_factory = providers.Factory(CreditCard)
photos_factory = providers.Factory(Photo)
users_factory = providers.Factory(User,
main_photo=photos_factory,
credit_card=credit_cards_factory)
# Creating several User objects:
user1 = users_factory(1)
# Same as: user1 = User(1,
# main_photo=Photo(),
# credit_card=CreditCard())
user2 = users_factory(2)
# Same as: user2 = User(2,
# main_photo=Photo(),
# credit_card=CreditCard())
# Making some asserts:
assert user1.id == 1
assert isinstance(user1.main_photo, Photo)
assert isinstance(user1.credit_card, CreditCard)
assert user2.id == 2
assert isinstance(user2.main_photo, Photo)
assert isinstance(user2.credit_card, CreditCard)
assert user1.main_photo is not user2.main_photo
assert user1.credit_card is not user2.credit_card
# Context keyword arguments have priority on keyword argument injections:
main_photo_mock = Photo()
credit_card_mock = CreditCard()
user3 = users_factory(3,
main_photo=main_photo_mock,
credit_card=credit_card_mock)
assert user3.id == 3
assert user3.main_photo is main_photo_mock
assert user3.credit_card is credit_card_mock

View File

@ -1,34 +0,0 @@
"""`Factory` providers with init keyword injections example."""
import dependency_injector.providers as providers
class User(object):
"""Example class User."""
def __init__(self, main_photo):
"""Initializer."""
self.main_photo = main_photo
super(User, self).__init__()
class Photo(object):
"""Example class Photo."""
# User and Photo factories:
photos_factory = providers.Factory(Photo)
users_factory = providers.Factory(User, main_photo=photos_factory)
# Creating several User objects:
user1 = users_factory() # Same as: user1 = User(main_photo=Photo())
user2 = users_factory() # Same as: user2 = User(main_photo=Photo())
# Making some asserts:
assert isinstance(user1, User)
assert isinstance(user1.main_photo, Photo)
assert isinstance(user2, User)
assert isinstance(user2.main_photo, Photo)
assert user1 is not user2
assert user1.main_photo is not user2.main_photo

View File

@ -1,4 +1,4 @@
"""`Factory` specialization for limitation to provided type example."""
"""`Factory` specialization with limitation to provided type example."""
import dependency_injector.providers as providers
import dependency_injector.errors as errors
@ -8,12 +8,8 @@ class BaseService(object):
"""Base service class."""
class UsersService(BaseService):
"""Users service."""
class PhotosService(BaseService):
"""Photos service."""
class SomeService(BaseService):
"""Some service."""
class ServiceProvider(providers.Factory):
@ -22,11 +18,10 @@ class ServiceProvider(providers.Factory):
provided_type = BaseService
# Creating several service providers with BaseService instances:
users_service_provider = ServiceProvider(UsersService)
photos_service_provider = ServiceProvider(PhotosService)
# Creating service provider with correct provided type:
some_service_provider = ServiceProvider(SomeService)
# Trying to create service provider with not a BaseService instance:
# Trying to create service provider incorrect provided type:
try:
some_service_provider = ServiceProvider(object)
except errors.Error as exception: