Factory provider docs update (#287)
* Update index page * Update providers index page * Make a little wording fix on containers index page * Refactor factory provider docs header * Update factory injection docs * Update factory init injections example and picture * Start work on underlying providers * Finish the docs for factory arguments to the underlying providers * Edit providers delegation section * Edit section about specialized factory provider * Edit abstract factory section * Edit FactoryAggregate docs * Add meta keywords and description
|
@ -12,7 +12,7 @@ There are several use cases how you can use containers:
|
|||
container ``Users``, that contains all functional parts of the ``users``
|
||||
package).
|
||||
|
||||
Containers package API docs - :py:mod:`dependency_injector.containers`.
|
||||
Containers module API docs - :py:mod:`dependency_injector.containers`.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 32 KiB |
|
@ -1,193 +1,167 @@
|
|||
Factory providers
|
||||
-----------------
|
||||
Factory provider
|
||||
----------------
|
||||
|
||||
.. meta::
|
||||
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Factory,Abstract Factory,
|
||||
Pattern,Example,Aggregate
|
||||
:description: Factory provider helps to implement dependency injection in Python. This page
|
||||
demonstrates how to use Factory provider, inject the dependencies, and assemble
|
||||
object graphs passing the injections deep inside. It also provides the examples
|
||||
of the Abstract Factory pattern & provider and Factories Aggregation pattern.
|
||||
|
||||
.. currentmodule:: dependency_injector.providers
|
||||
|
||||
:py:class:`Factory` provider creates new instance of specified class on every
|
||||
call.
|
||||
|
||||
Nothing could be better than brief example:
|
||||
|
||||
.. image:: /images/providers/factory.png
|
||||
:width: 80%
|
||||
:align: center
|
||||
:py:class:`Factory` provider creates new objects.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
Factory providers and __init__ injections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
The first argument of the ``Factory`` provider is a class, a factory function or a method
|
||||
that creates an object.
|
||||
|
||||
:py:class:`Factory` takes a various number of positional and keyword arguments
|
||||
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 instance arguments.
|
||||
The rest of the ``Factory`` positional and keyword arguments are the dependencies.
|
||||
``Factory`` injects the dependencies every time when creates a new object. The dependencies are
|
||||
injected following these rules:
|
||||
|
||||
Injections are done according to the next rules:
|
||||
+ If the dependency is a provider, this provider is called and the result of the call is injected.
|
||||
+ If you need to inject the provider itself, you should use the ``.provider`` attribute. More at
|
||||
:ref:`factory_providers_delegation`.
|
||||
+ All other dependencies are injected *"as is"*.
|
||||
+ Positional context arguments are appended after ``Factory`` positional dependencies.
|
||||
+ Keyword context arguments have the priority over the ``Factory`` keyword dependencies with the
|
||||
same name.
|
||||
|
||||
+ 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 :ref:`factory_providers_delegation`.
|
||||
+ 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__()`` injections which
|
||||
injectable values are also provided by another factories:
|
||||
|
||||
.. image:: /images/providers/factory_init_injections.png
|
||||
.. image:: images/factory_init_injections.png
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_init_injections.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
|
||||
Factory providers and building complex object graphs
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Passing arguments to the underlying providers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can use :py:class:`Factory` provider to build complex object graphs.
|
||||
``Factory`` provider can pass the arguments to the underlying providers. This helps when you need
|
||||
to assemble a nested objects graph and pass the arguments deep inside.
|
||||
|
||||
Consider next example:
|
||||
Consider the example:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_deep_init_injections.py
|
||||
.. image:: images/factory_init_injections_underlying.png
|
||||
|
||||
To create an ``Algorithm`` you need to provide all the dependencies: ``ClassificationTask``,
|
||||
``Loss``, and ``Regularizer``. The last object in the chain, the ``Regularizer`` has a dependency
|
||||
on the ``alpha`` value. The ``alpha`` value varies from algorithm to algorithm:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
Algorithm(
|
||||
task=ClassificationTask(
|
||||
loss=Loss(
|
||||
regularizer=Regularizer(
|
||||
alpha=alpha, # <-- the dependency
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
``Factory`` provider helps to deal with the such assembly. You need to create the factories for
|
||||
all the classes and use special double-underscore ``__`` syntax for passing the ``alpha`` argument:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_init_injections_underlying.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 24-35,39,42,45
|
||||
|
||||
.. note::
|
||||
When you use ``__`` separator in the name of the keyword argument the ``Factory`` looks for
|
||||
the dependency with the same name as the left part of the ``__`` expression.
|
||||
|
||||
You can use ``__`` separator in the name of the keyword argument to pass the value to the child
|
||||
factory, e.g. ``algorithm_factory(task__loss__regularizer__alpha=0.5)``.
|
||||
.. code-block::
|
||||
|
||||
<dependency>__<keyword for the underlying provider>=<value>
|
||||
|
||||
If ``<dependency>`` is found the underlying provider will receive the
|
||||
``<keyword for the underlying provider>=<value>`` as an argument.
|
||||
|
||||
.. _factory_providers_delegation:
|
||||
|
||||
Factory providers delegation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Passing providers to the objects
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:py:class:`Factory` provider could be delegated to any other provider via any
|
||||
kind of injection.
|
||||
When you need to inject the provider itself, but not the result of its call, use the ``.provider``
|
||||
attribute of the provider that you're going to inject.
|
||||
|
||||
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`.
|
||||
|
||||
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`
|
||||
method, that returns delegate wrapper for current factory.
|
||||
+ ``Factory(...).provider`` - getting factory :py:attr:`Factory.provider`
|
||||
attribute, that returns delegate wrapper for current factory (alias of
|
||||
``Factory(...).delegate()`` method).
|
||||
|
||||
Example:
|
||||
|
||||
.. image:: /images/providers/factory_delegation.png
|
||||
:width: 85%
|
||||
:align: center
|
||||
.. image:: images/factory_delegation.png
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_delegation.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 25
|
||||
|
||||
.. _factory_providers_specialization:
|
||||
.. note:: Any provider has a ``.provider`` attribute.
|
||||
|
||||
Factory providers specialization
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Specializing the provided type
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:py:class:`Factory` provider could be specialized for any kind of needs via
|
||||
creating its subclasses.
|
||||
|
||||
One of such specialization features is a limitation to :py:class:`Factory`
|
||||
provided type:
|
||||
You can create a specialized ``Factory`` provider that provides only specific type. For doing
|
||||
this you need to create a subclass of the ``Factory`` provider and define the ``provided_type``
|
||||
class attribute.
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_provided_type.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 12-14
|
||||
|
||||
.. _abstract_factory_providers:
|
||||
Abstract factory
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Abstract factory providers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
:py:class:`AbstractFactory` provider helps when you need to create a provider of some base class
|
||||
and the particular implementation is not yet know. ``AbstractFactory`` provider is a ``Factory``
|
||||
provider with two peculiarities:
|
||||
|
||||
:py:class:`AbstractFactory` provider is a :py:class:`Factory` provider that
|
||||
must be explicitly overridden before calling.
|
||||
+ Provides only objects of a specified type.
|
||||
+ Must be overridden before usage.
|
||||
|
||||
.. note::
|
||||
|
||||
Overriding of :py:class:`AbstractFactory` provider is possible only by
|
||||
another :py:class:`Factory` provider.
|
||||
|
||||
:py:class:`AbstractFactory` provider is useful when it is needed to specify
|
||||
explicitly that it only provides abstraction, but not an implementation.
|
||||
Client code must override such factories with factories that provide particular
|
||||
implementations. Otherwise, :py:class:`AbstractFactory` will raise an error
|
||||
on attempt of calling it. At the same time, :py:class:`AbstractFactory` is
|
||||
regular provider that could be injected into other providers (or used for
|
||||
any other kind of bindings) without being overridden. After
|
||||
:py:class:`AbstractFactory` provider has been overridden, its behaviour is
|
||||
identical to regular :py:class:`Factory` provider.
|
||||
|
||||
Example:
|
||||
|
||||
.. image:: /images/providers/abstract_factory.png
|
||||
.. image:: images/abstract_factory.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
Listing of ``cache.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/abstract_factory/cache.py
|
||||
.. literalinclude:: ../../examples/providers/abstract_factory.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 32
|
||||
|
||||
Listing of ``example.py``:
|
||||
Factory aggregate
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. literalinclude:: ../../examples/providers/abstract_factory/example.py
|
||||
:py:class:`FactoryAggregate` provider aggregates multiple factories. When you call the
|
||||
``FactoryAggregate`` it delegates the call to one of the factories.
|
||||
|
||||
The aggregated factories are associated with the string names. When you call the
|
||||
``FactoryAggregate`` you have to provide one of the these names as a first argument.
|
||||
``FactoryAggregate`` looks for the factory with a matching name and delegates it the work. The
|
||||
rest of the arguments are passed to the delegated ``Factory``.
|
||||
|
||||
.. image:: images/factory_aggregate.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_aggregate.py
|
||||
:language: python
|
||||
:lines: 3-
|
||||
:emphasize-lines: 31-35,43
|
||||
|
||||
Factory aggregate providers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
You can get a dictionary of the aggregated factories using the ``.factories`` attribute of the
|
||||
``FactoryAggregate``. To get a game factories dictionary from the previous example you can use
|
||||
``game_factory.factories`` attribute.
|
||||
|
||||
:py:class:`FactoryAggregate` provider is a special type of provider that
|
||||
aggregates other :py:class:`Factory` providers.
|
||||
You can also access an aggregated factory as an attribute. To create the ``Chess`` object from the
|
||||
previous example you can do ``chess = game_factory.chess('John', 'Jane')``.
|
||||
|
||||
.. note::
|
||||
You can not override the ``FactoryAggregate`` provider.
|
||||
|
||||
:py:class:`FactoryAggregate` is not overridable. Calling of
|
||||
:py:meth:`FactoryAggregate.override` will result in raising of an
|
||||
exception.
|
||||
|
||||
Next prototype might be the best demonstration of
|
||||
:py:class:`FactoryAggregate` features:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_aggregate/prototype.py
|
||||
:language: python
|
||||
|
||||
Example below shows one of the :py:class:`FactoryAggregate` use cases, when
|
||||
concrete implementation (game) must be selected based on dynamic input (CLI).
|
||||
|
||||
Listing of ``games.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_aggregate/games.py
|
||||
:language: python
|
||||
|
||||
Listing of ``example.py``:
|
||||
|
||||
.. literalinclude:: ../../examples/providers/factory_aggregate/example.py
|
||||
:language: python
|
||||
.. note::
|
||||
When you inject the ``FactoryAggregate`` provider it is passed "as is".
|
||||
|
||||
.. disqus::
|
||||
|
|
BIN
docs/providers/images/abstract_factory.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
docs/providers/images/factory_aggregate.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
docs/providers/images/factory_delegation.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/providers/images/factory_init_injections.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
docs/providers/images/factory_init_injections_underlying.png
Normal file
After Width: | Height: | Size: 26 KiB |
|
@ -3,18 +3,32 @@
|
|||
Providers
|
||||
=========
|
||||
|
||||
Providers are strategies of accessing objects. They define how particular
|
||||
objects are provided.
|
||||
Providers help to assemble the objects. They create objects and inject the dependencies.
|
||||
|
||||
Every provider is callable (implements ``__call__()``). Every call to provider
|
||||
instance returns provided result, according to the providing strategy of
|
||||
particular provider.
|
||||
Each provider is a callable. You call the provider like a function when you need to create an
|
||||
object. Provider retrieves the underlying dependencies and inject them into the created object.
|
||||
It causes the cascade effect that helps to assemble object graphs.
|
||||
|
||||
Current documentation section consists from description of standard providers
|
||||
library and some useful information like overriding of providers and writing
|
||||
of custom providers.
|
||||
.. code-block:: bash
|
||||
|
||||
Providers package API docs - :py:mod:`dependency_injector.providers`
|
||||
provider1()
|
||||
│
|
||||
├──> provider2()
|
||||
│
|
||||
├──> provider3()
|
||||
│ │
|
||||
│ └──> provider4()
|
||||
│
|
||||
└──> provider5()
|
||||
│
|
||||
└──> provider6()
|
||||
|
||||
Another providers feature is an overriding. Any of the providers can be overridden by another
|
||||
provider. When provider is overridden it calls to the overriding provider instead of providing
|
||||
the object by its own. This helps in testing. This also helps in overriding API clients with
|
||||
stubs for the development or staging environment.
|
||||
|
||||
Providers module API docs - :py:mod:`dependency_injector.providers`
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
75
examples/providers/abstract_factory.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
"""`AbstractFactory` providers example."""
|
||||
|
||||
import abc
|
||||
import dataclasses
|
||||
import random
|
||||
from typing import List
|
||||
|
||||
from dependency_injector import providers
|
||||
|
||||
|
||||
class AbstractCacheClient(metaclass=abc.ABCMeta):
|
||||
...
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class RedisCacheClient(AbstractCacheClient):
|
||||
host: str
|
||||
port: int
|
||||
db: int
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class MemcachedCacheClient(AbstractCacheClient):
|
||||
hosts: List[str]
|
||||
port: int
|
||||
prefix: str
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Service:
|
||||
cache: AbstractCacheClient
|
||||
|
||||
|
||||
cache_client_factory = providers.AbstractFactory(AbstractCacheClient)
|
||||
service_factory = providers.Factory(
|
||||
Service,
|
||||
cache=cache_client_factory,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cache_type = random.choice(['redis', 'memcached', None])
|
||||
|
||||
if cache_type == 'redis':
|
||||
cache_client_factory.override(
|
||||
providers.Factory(
|
||||
RedisCacheClient,
|
||||
host='localhost',
|
||||
port=6379,
|
||||
db=0,
|
||||
),
|
||||
)
|
||||
elif cache_type == 'memcached':
|
||||
cache_client_factory.override(
|
||||
providers.Factory(
|
||||
MemcachedCacheClient,
|
||||
hosts=['10.0.1.1'],
|
||||
port=11211,
|
||||
prefix='my_app',
|
||||
),
|
||||
)
|
||||
|
||||
service = service_factory()
|
||||
print(service.cache)
|
||||
# The output depends on cache_type variable value.
|
||||
#
|
||||
# If the value is 'redis':
|
||||
# RedisCacheClient(host='localhost', port=6379, db=0)
|
||||
#
|
||||
# If the value is 'memcached':
|
||||
# MemcachedCacheClient(hosts=['10.0.1.1'], port=11211, prefix='my_app')
|
||||
#
|
||||
# If the value is None:
|
||||
# Error: AbstractFactory(<class '__main__.AbstractCacheClient'>) must be
|
||||
# overridden before calling
|
|
@ -1,25 +0,0 @@
|
|||
"""Example hierarchy of cache clients with abstract base class."""
|
||||
|
||||
|
||||
class AbstractCacheClient:
|
||||
"""Abstract cache client."""
|
||||
|
||||
|
||||
class RedisCacheClient(AbstractCacheClient):
|
||||
"""Cache client implementation based on Redis."""
|
||||
|
||||
def __init__(self, host, port, db):
|
||||
"""Initialize instance."""
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.db = db
|
||||
|
||||
|
||||
class MemcacheCacheClient(AbstractCacheClient):
|
||||
"""Cache client implementation based on Memcached."""
|
||||
|
||||
def __init__(self, hosts, port, prefix):
|
||||
"""Initialize instance."""
|
||||
self.hosts = hosts
|
||||
self.port = port
|
||||
self.prefix = prefix
|
|
@ -1,36 +0,0 @@
|
|||
"""`AbstractFactory` providers example."""
|
||||
|
||||
import cache
|
||||
|
||||
import dependency_injector.providers as providers
|
||||
|
||||
|
||||
# Define abstract cache client factory:
|
||||
cache_client_factory = providers.AbstractFactory(cache.AbstractCacheClient)
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Override abstract factory with redis client factory:
|
||||
cache_client_factory.override(
|
||||
providers.Factory(
|
||||
cache.RedisCacheClient,
|
||||
host='localhost',
|
||||
port=6379,
|
||||
db=0,
|
||||
),
|
||||
)
|
||||
redis_cache = cache_client_factory()
|
||||
print(redis_cache)
|
||||
# <cache.RedisCacheClient object at 0x10975bc50>
|
||||
|
||||
# Override abstract factory with memcache client factory:
|
||||
cache_client_factory.override(
|
||||
providers.Factory(
|
||||
cache.MemcacheCacheClient,
|
||||
hosts=['10.0.1.1', '10.0.1.2', '10.0.1.3'],
|
||||
port=11211,
|
||||
prefix='my_app',
|
||||
),
|
||||
)
|
||||
memcache_cache = cache_client_factory()
|
||||
print(memcache_cache)
|
||||
# <cache.MemcacheCacheClient object at 0x10975bc90>
|
|
@ -1,15 +1,15 @@
|
|||
"""`Factory` providers example."""
|
||||
|
||||
import collections
|
||||
|
||||
import dependency_injector.providers as providers
|
||||
from dependency_injector import providers
|
||||
|
||||
|
||||
User = collections.namedtuple('User', [])
|
||||
class User:
|
||||
...
|
||||
|
||||
|
||||
# Factory provider creates new instance of specified class on every call.
|
||||
users_factory = providers.Factory(User)
|
||||
|
||||
# Creating several User objects:
|
||||
user1 = users_factory() # Same as: user1 = User()
|
||||
user2 = users_factory() # Same as: user2 = User()
|
||||
|
||||
if __name__ == '__main__':
|
||||
user1 = users_factory()
|
||||
user2 = users_factory()
|
||||
|
|
55
examples/providers/factory_aggregate.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
"""`FactoryAggregate` provider example."""
|
||||
|
||||
import dataclasses
|
||||
import sys
|
||||
|
||||
from dependency_injector import providers
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Game:
|
||||
player1: str
|
||||
player2: str
|
||||
|
||||
def play(self):
|
||||
print(
|
||||
f'{self.player1} and {self.player2} are '
|
||||
f'playing {self.__class__.__name__.lower()}'
|
||||
)
|
||||
|
||||
|
||||
class Chess(Game):
|
||||
...
|
||||
|
||||
|
||||
class Checkers(Game):
|
||||
...
|
||||
|
||||
|
||||
class Ludo(Game):
|
||||
...
|
||||
|
||||
|
||||
game_factory = providers.FactoryAggregate(
|
||||
chess=providers.Factory(Chess),
|
||||
checkers=providers.Factory(Checkers),
|
||||
ludo=providers.Factory(Ludo),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
game_type = sys.argv[1].lower()
|
||||
player1 = sys.argv[2].capitalize()
|
||||
player2 = sys.argv[3].capitalize()
|
||||
|
||||
selected_game = game_factory(game_type, player1, player2)
|
||||
selected_game.play()
|
||||
|
||||
# $ python factory_aggregate.py chess John Jane
|
||||
# John and Jane are playing chess
|
||||
#
|
||||
# $ python factory_aggregate.py checkers John Jane
|
||||
# John and Jane are playing checkers
|
||||
#
|
||||
# $ python factory_aggregate.py ludo John Jane
|
||||
# John and Jane are playing ludo
|
|
@ -1,31 +0,0 @@
|
|||
"""`FactoryAggregate` providers example."""
|
||||
|
||||
import sys
|
||||
|
||||
import dependency_injector.providers as providers
|
||||
|
||||
from games import Chess, Checkers, Ludo
|
||||
|
||||
|
||||
game_factory = providers.FactoryAggregate(
|
||||
chess=providers.Factory(Chess),
|
||||
checkers=providers.Factory(Checkers),
|
||||
ludo=providers.Factory(Ludo),
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
game_type = sys.argv[1].lower()
|
||||
player1 = sys.argv[2].capitalize()
|
||||
player2 = sys.argv[3].capitalize()
|
||||
|
||||
selected_game = game_factory(game_type, player1, player2)
|
||||
selected_game.play()
|
||||
|
||||
# $ python example.py chess John Jane
|
||||
# John and Jane are playing chess
|
||||
#
|
||||
# $ python example.py checkers John Jane
|
||||
# John and Jane are playing checkers
|
||||
#
|
||||
# $ python example.py ludo John Jane
|
||||
# John and Jane are playing ludo
|
|
@ -1,27 +0,0 @@
|
|||
"""Example games module."""
|
||||
|
||||
|
||||
class Game:
|
||||
"""Base game class."""
|
||||
|
||||
def __init__(self, player1, player2):
|
||||
"""Initialize instance."""
|
||||
self.player1 = player1
|
||||
self.player2 = player2
|
||||
|
||||
def play(self):
|
||||
"""Play game."""
|
||||
print('{0} and {1} are playing {2}'.format(
|
||||
self.player1, self.player2, self.__class__.__name__.lower()))
|
||||
|
||||
|
||||
class Chess(Game):
|
||||
"""Chess game."""
|
||||
|
||||
|
||||
class Checkers(Game):
|
||||
"""Checkers game."""
|
||||
|
||||
|
||||
class Ludo(Game):
|
||||
"""Ludo game."""
|
|
@ -1,17 +0,0 @@
|
|||
"""FactoryAggregate provider prototype."""
|
||||
|
||||
|
||||
class FactoryAggregate:
|
||||
"""FactoryAggregate provider prototype."""
|
||||
|
||||
def __init__(self, **factories):
|
||||
"""Initialize instance."""
|
||||
self.factories = factories
|
||||
|
||||
def __call__(self, factory_name, *args, **kwargs):
|
||||
"""Create object."""
|
||||
return self.factories[factory_name](*args, **kwargs)
|
||||
|
||||
def __getattr__(self, factory_name):
|
||||
"""Return factory with specified name."""
|
||||
return self.factories[factory_name]
|
|
@ -1,82 +1,37 @@
|
|||
"""`Factory` providers delegation example."""
|
||||
|
||||
import collections
|
||||
from typing import Callable, List
|
||||
|
||||
import dependency_injector.providers as providers
|
||||
|
||||
|
||||
Photo = collections.namedtuple('Photo', [])
|
||||
from dependency_injector import providers
|
||||
|
||||
|
||||
class User:
|
||||
"""Example user model."""
|
||||
|
||||
def __init__(self, photos_factory):
|
||||
"""Initialize instance."""
|
||||
self.photos_factory = photos_factory
|
||||
self._main_photo = None
|
||||
|
||||
@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
|
||||
def __init__(self, uid: int) -> None:
|
||||
self.uid = uid
|
||||
|
||||
|
||||
# Defining User and Photo factories using DelegatedFactory provider:
|
||||
photos_factory = providers.DelegatedFactory(Photo)
|
||||
users_factory = providers.Factory(
|
||||
User,
|
||||
photos_factory=photos_factory,
|
||||
)
|
||||
class UserRepository:
|
||||
def __init__(self, user_factory: Callable[..., User]) -> None:
|
||||
self.user_factory = user_factory
|
||||
|
||||
# or using Delegate(Factory(...))
|
||||
def get_all(self) -> List[User]:
|
||||
return [
|
||||
self.user_factory(**user_data)
|
||||
for user_data in [{'uid': 1}, {'uid': 2}]
|
||||
]
|
||||
|
||||
photos_factory = providers.Factory(Photo)
|
||||
users_factory = providers.Factory(
|
||||
User,
|
||||
photos_factory=providers.Delegate(photos_factory),
|
||||
|
||||
user_factory = providers.Factory(User)
|
||||
user_repository_factory = providers.Factory(
|
||||
UserRepository,
|
||||
user_factory=user_factory.provider,
|
||||
)
|
||||
|
||||
|
||||
# or using Factory(...).delegate()
|
||||
if __name__ == '__main__':
|
||||
user_repository = user_repository_factory()
|
||||
|
||||
photos_factory = providers.Factory(Photo)
|
||||
users_factory = providers.Factory(
|
||||
User,
|
||||
photos_factory=photos_factory.delegate(),
|
||||
)
|
||||
user1, user2 = user_repository.get_all()
|
||||
|
||||
|
||||
# Creating several User objects:
|
||||
user1 = users_factory()
|
||||
user2 = users_factory()
|
||||
|
||||
# Same as:
|
||||
# user1 = User(photos_factory=photos_factory)
|
||||
# user2 = User(photos_factory=photos_factory)
|
||||
|
||||
# Making some asserts:
|
||||
assert isinstance(user1.main_photo, Photo)
|
||||
assert isinstance(user2.main_photo, Photo)
|
||||
|
||||
# or using Factory(...).provider
|
||||
|
||||
photos_factory = providers.Factory(Photo)
|
||||
users_factory = providers.Factory(
|
||||
User,
|
||||
photos_factory=photos_factory.provider,
|
||||
)
|
||||
|
||||
# Creating several User objects:
|
||||
user1 = users_factory()
|
||||
user2 = users_factory()
|
||||
|
||||
# Same as:
|
||||
# user1 = User(photos_factory=photos_factory)
|
||||
# user2 = User(photos_factory=photos_factory)
|
||||
|
||||
# Making some asserts:
|
||||
assert isinstance(user1.main_photo, Photo)
|
||||
assert isinstance(user2.main_photo, Photo)
|
||||
assert user1.uid == 1
|
||||
assert user2.uid == 2
|
||||
|
|
|
@ -1,39 +1,36 @@
|
|||
"""`Factory` providers init injections example."""
|
||||
|
||||
import collections
|
||||
|
||||
import dependency_injector.providers as providers
|
||||
from dependency_injector import 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())
|
||||
class Photo:
|
||||
...
|
||||
|
||||
|
||||
# Context keyword arguments have priority on keyword argument injections:
|
||||
main_photo = Photo()
|
||||
credit_card = CreditCard()
|
||||
class User:
|
||||
def __init__(self, uid: int, main_photo: Photo) -> None:
|
||||
self.uid = uid
|
||||
self.main_photo = main_photo
|
||||
|
||||
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)
|
||||
|
||||
photo_factory = providers.Factory(Photo)
|
||||
user_factory = providers.Factory(
|
||||
User,
|
||||
main_photo=photo_factory,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
user1 = user_factory(1)
|
||||
# Same as: # user1 = User(1, main_photo=Photo())
|
||||
|
||||
user2 = user_factory(2)
|
||||
# Same as: # user2 = User(2, main_photo=Photo())
|
||||
|
||||
# Context keyword arguments have a priority:
|
||||
another_photo = Photo()
|
||||
user3 = user_factory(
|
||||
uid=3,
|
||||
main_photo=another_photo,
|
||||
)
|
||||
# Same as: # user3 = User(uid=3, main_photo=another_photo)
|
||||
|
|
|
@ -4,22 +4,22 @@ from dependency_injector import providers
|
|||
|
||||
|
||||
class Regularizer:
|
||||
def __init__(self, alpha):
|
||||
def __init__(self, alpha: float) -> None:
|
||||
self.alpha = alpha
|
||||
|
||||
|
||||
class Loss:
|
||||
def __init__(self, regularizer):
|
||||
def __init__(self, regularizer: Regularizer) -> None:
|
||||
self.regularizer = regularizer
|
||||
|
||||
|
||||
class ClassificationTask:
|
||||
def __init__(self, loss):
|
||||
def __init__(self, loss: Loss) -> None:
|
||||
self.loss = loss
|
||||
|
||||
|
||||
class Algorithm:
|
||||
def __init__(self, task):
|
||||
def __init__(self, task: ClassificationTask) -> None:
|
||||
self.task = task
|
||||
|
||||
|
|
@ -1,30 +1,29 @@
|
|||
"""`Factory` specialization with limitation to provided type example."""
|
||||
|
||||
import dependency_injector.providers as providers
|
||||
import dependency_injector.errors as errors
|
||||
from dependency_injector import providers, errors
|
||||
|
||||
|
||||
class BaseService:
|
||||
"""Base service class."""
|
||||
...
|
||||
|
||||
|
||||
class SomeService(BaseService):
|
||||
"""Some service."""
|
||||
...
|
||||
|
||||
|
||||
class ServiceProvider(providers.Factory):
|
||||
"""Service provider."""
|
||||
|
||||
provided_type = BaseService
|
||||
|
||||
|
||||
# Creating service provider with correct provided type:
|
||||
# Creating service provider with a correct provided type:
|
||||
some_service_provider = ServiceProvider(SomeService)
|
||||
|
||||
# Trying to create service provider incorrect provided type:
|
||||
# Trying to create service provider an incorrect provided type:
|
||||
try:
|
||||
some_service_provider = ServiceProvider(object)
|
||||
except errors.Error as exception:
|
||||
print(exception)
|
||||
# The output is:
|
||||
# <class '__main__.ServiceProvider'> can provide only
|
||||
# <class '__main__.BaseService'> instances
|
||||
|
|