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``
|
container ``Users``, that contains all functional parts of the ``users``
|
||||||
package).
|
package).
|
||||||
|
|
||||||
Containers package API docs - :py:mod:`dependency_injector.containers`.
|
Containers module API docs - :py:mod:`dependency_injector.containers`.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
: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
|
.. currentmodule:: dependency_injector.providers
|
||||||
|
|
||||||
:py:class:`Factory` provider creates new instance of specified class on every
|
:py:class:`Factory` provider creates new objects.
|
||||||
call.
|
|
||||||
|
|
||||||
Nothing could be better than brief example:
|
|
||||||
|
|
||||||
.. image:: /images/providers/factory.png
|
|
||||||
:width: 80%
|
|
||||||
:align: center
|
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/providers/factory.py
|
.. literalinclude:: ../../examples/providers/factory.py
|
||||||
:language: python
|
: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
|
The rest of the ``Factory`` positional and keyword arguments are the dependencies.
|
||||||
that are used as ``__init__()`` injections. Every time, when
|
``Factory`` injects the dependencies every time when creates a new object. The dependencies are
|
||||||
:py:class:`Factory` creates new one instance, positional and keyword
|
injected following these rules:
|
||||||
argument injections would be passed as instance arguments.
|
|
||||||
|
|
||||||
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
|
.. image:: images/factory_init_injections.png
|
||||||
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
|
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/providers/factory_init_injections.py
|
.. literalinclude:: ../../examples/providers/factory_init_injections.py
|
||||||
:language: python
|
: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
|
: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
|
.. code-block::
|
||||||
factory, e.g. ``algorithm_factory(task__loss__regularizer__alpha=0.5)``.
|
|
||||||
|
<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:
|
||||||
|
|
||||||
Factory providers delegation
|
Passing providers to the objects
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
:py:class:`Factory` provider could be delegated to any other provider via any
|
When you need to inject the provider itself, but not the result of its call, use the ``.provider``
|
||||||
kind of injection.
|
attribute of the provider that you're going to inject.
|
||||||
|
|
||||||
As it was mentioned earlier, if :py:class:`Factory` is
|
.. image:: images/factory_delegation.png
|
||||||
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
|
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/providers/factory_delegation.py
|
.. literalinclude:: ../../examples/providers/factory_delegation.py
|
||||||
:language: python
|
: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
|
You can create a specialized ``Factory`` provider that provides only specific type. For doing
|
||||||
creating its subclasses.
|
this you need to create a subclass of the ``Factory`` provider and define the ``provided_type``
|
||||||
|
class attribute.
|
||||||
One of such specialization features is a limitation to :py:class:`Factory`
|
|
||||||
provided type:
|
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/providers/factory_provided_type.py
|
.. literalinclude:: ../../examples/providers/factory_provided_type.py
|
||||||
:language: python
|
: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
|
+ Provides only objects of a specified type.
|
||||||
must be explicitly overridden before calling.
|
+ Must be overridden before usage.
|
||||||
|
|
||||||
.. note::
|
.. image:: images/abstract_factory.png
|
||||||
|
|
||||||
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
|
|
||||||
:width: 100%
|
:width: 100%
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
Listing of ``cache.py``:
|
.. literalinclude:: ../../examples/providers/abstract_factory.py
|
||||||
|
|
||||||
.. literalinclude:: ../../examples/providers/abstract_factory/cache.py
|
|
||||||
:language: python
|
: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
|
: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
|
You can also access an aggregated factory as an attribute. To create the ``Chess`` object from the
|
||||||
aggregates other :py:class:`Factory` providers.
|
previous example you can do ``chess = game_factory.chess('John', 'Jane')``.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
You can not override the ``FactoryAggregate`` provider.
|
||||||
|
|
||||||
:py:class:`FactoryAggregate` is not overridable. Calling of
|
.. note::
|
||||||
:py:meth:`FactoryAggregate.override` will result in raising of an
|
When you inject the ``FactoryAggregate`` provider it is passed "as is".
|
||||||
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
|
|
||||||
|
|
||||||
.. disqus::
|
.. 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
|
||||||
=========
|
=========
|
||||||
|
|
||||||
Providers are strategies of accessing objects. They define how particular
|
Providers help to assemble the objects. They create objects and inject the dependencies.
|
||||||
objects are provided.
|
|
||||||
|
|
||||||
Every provider is callable (implements ``__call__()``). Every call to provider
|
Each provider is a callable. You call the provider like a function when you need to create an
|
||||||
instance returns provided result, according to the providing strategy of
|
object. Provider retrieves the underlying dependencies and inject them into the created object.
|
||||||
particular provider.
|
It causes the cascade effect that helps to assemble object graphs.
|
||||||
|
|
||||||
Current documentation section consists from description of standard providers
|
.. code-block:: bash
|
||||||
library and some useful information like overriding of providers and writing
|
|
||||||
of custom providers.
|
|
||||||
|
|
||||||
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::
|
.. toctree::
|
||||||
:maxdepth: 2
|
: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."""
|
"""`Factory` providers example."""
|
||||||
|
|
||||||
import collections
|
from dependency_injector import providers
|
||||||
|
|
||||||
import dependency_injector.providers as providers
|
|
||||||
|
|
||||||
|
|
||||||
User = collections.namedtuple('User', [])
|
class User:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
# Factory provider creates new instance of specified class on every call.
|
|
||||||
users_factory = providers.Factory(User)
|
users_factory = providers.Factory(User)
|
||||||
|
|
||||||
# Creating several User objects:
|
|
||||||
user1 = users_factory() # Same as: user1 = User()
|
if __name__ == '__main__':
|
||||||
user2 = users_factory() # Same as: user2 = User()
|
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."""
|
"""`Factory` providers delegation example."""
|
||||||
|
|
||||||
import collections
|
from typing import Callable, List
|
||||||
|
|
||||||
import dependency_injector.providers as providers
|
from dependency_injector import providers
|
||||||
|
|
||||||
|
|
||||||
Photo = collections.namedtuple('Photo', [])
|
|
||||||
|
|
||||||
|
|
||||||
class User:
|
class User:
|
||||||
"""Example user model."""
|
def __init__(self, uid: int) -> None:
|
||||||
|
self.uid = uid
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
# Defining User and Photo factories using DelegatedFactory provider:
|
class UserRepository:
|
||||||
photos_factory = providers.DelegatedFactory(Photo)
|
def __init__(self, user_factory: Callable[..., User]) -> None:
|
||||||
users_factory = providers.Factory(
|
self.user_factory = user_factory
|
||||||
User,
|
|
||||||
photos_factory=photos_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_factory = providers.Factory(User)
|
||||||
User,
|
user_repository_factory = providers.Factory(
|
||||||
photos_factory=providers.Delegate(photos_factory),
|
UserRepository,
|
||||||
|
user_factory=user_factory.provider,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# or using Factory(...).delegate()
|
if __name__ == '__main__':
|
||||||
|
user_repository = user_repository_factory()
|
||||||
|
|
||||||
photos_factory = providers.Factory(Photo)
|
user1, user2 = user_repository.get_all()
|
||||||
users_factory = providers.Factory(
|
|
||||||
User,
|
|
||||||
photos_factory=photos_factory.delegate(),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
assert user1.uid == 1
|
||||||
# Creating several User objects:
|
assert user2.uid == 2
|
||||||
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)
|
|
||||||
|
|
|
@ -1,39 +1,36 @@
|
||||||
"""`Factory` providers init injections example."""
|
"""`Factory` providers init injections example."""
|
||||||
|
|
||||||
import collections
|
from dependency_injector import providers
|
||||||
|
|
||||||
import dependency_injector.providers as providers
|
|
||||||
|
|
||||||
|
|
||||||
CreditCard = collections.namedtuple('CreditCard', [])
|
class Photo:
|
||||||
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:
|
class User:
|
||||||
main_photo = Photo()
|
def __init__(self, uid: int, main_photo: Photo) -> None:
|
||||||
credit_card = CreditCard()
|
self.uid = uid
|
||||||
|
self.main_photo = main_photo
|
||||||
|
|
||||||
user3 = users_factory(3,
|
|
||||||
main_photo=main_photo,
|
photo_factory = providers.Factory(Photo)
|
||||||
credit_card=credit_card)
|
user_factory = providers.Factory(
|
||||||
# Same as: user3 = User(3,
|
User,
|
||||||
# main_photo=main_photo,
|
main_photo=photo_factory,
|
||||||
# credit_card=credit_card)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
class Regularizer:
|
||||||
def __init__(self, alpha):
|
def __init__(self, alpha: float) -> None:
|
||||||
self.alpha = alpha
|
self.alpha = alpha
|
||||||
|
|
||||||
|
|
||||||
class Loss:
|
class Loss:
|
||||||
def __init__(self, regularizer):
|
def __init__(self, regularizer: Regularizer) -> None:
|
||||||
self.regularizer = regularizer
|
self.regularizer = regularizer
|
||||||
|
|
||||||
|
|
||||||
class ClassificationTask:
|
class ClassificationTask:
|
||||||
def __init__(self, loss):
|
def __init__(self, loss: Loss) -> None:
|
||||||
self.loss = loss
|
self.loss = loss
|
||||||
|
|
||||||
|
|
||||||
class Algorithm:
|
class Algorithm:
|
||||||
def __init__(self, task):
|
def __init__(self, task: ClassificationTask) -> None:
|
||||||
self.task = task
|
self.task = task
|
||||||
|
|
||||||
|
|
|
@ -1,30 +1,29 @@
|
||||||
"""`Factory` specialization with limitation to provided type example."""
|
"""`Factory` specialization with limitation to provided type example."""
|
||||||
|
|
||||||
import dependency_injector.providers as providers
|
from dependency_injector import providers, errors
|
||||||
import dependency_injector.errors as errors
|
|
||||||
|
|
||||||
|
|
||||||
class BaseService:
|
class BaseService:
|
||||||
"""Base service class."""
|
...
|
||||||
|
|
||||||
|
|
||||||
class SomeService(BaseService):
|
class SomeService(BaseService):
|
||||||
"""Some service."""
|
...
|
||||||
|
|
||||||
|
|
||||||
class ServiceProvider(providers.Factory):
|
class ServiceProvider(providers.Factory):
|
||||||
"""Service provider."""
|
|
||||||
|
|
||||||
provided_type = BaseService
|
provided_type = BaseService
|
||||||
|
|
||||||
|
|
||||||
# Creating service provider with correct provided type:
|
# Creating service provider with a correct provided type:
|
||||||
some_service_provider = ServiceProvider(SomeService)
|
some_service_provider = ServiceProvider(SomeService)
|
||||||
|
|
||||||
# Trying to create service provider incorrect provided type:
|
# Trying to create service provider an incorrect provided type:
|
||||||
try:
|
try:
|
||||||
some_service_provider = ServiceProvider(object)
|
some_service_provider = ServiceProvider(object)
|
||||||
except errors.Error as exception:
|
except errors.Error as exception:
|
||||||
print(exception)
|
print(exception)
|
||||||
|
# The output is:
|
||||||
# <class '__main__.ServiceProvider'> can provide only
|
# <class '__main__.ServiceProvider'> can provide only
|
||||||
# <class '__main__.BaseService'> instances
|
# <class '__main__.BaseService'> instances
|
||||||
|
|