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
This commit is contained in:
Roman Mogylatov 2020-08-31 21:26:21 -04:00 committed by GitHub
parent f83d8ce143
commit 1ad852d193
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 338 additions and 405 deletions

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@ -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::

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -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

View 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

View File

@ -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

View File

@ -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>

View File

@ -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()

View 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

View File

@ -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

View File

@ -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."""

View File

@ -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]

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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