diff --git a/docs/containers/index.rst b/docs/containers/index.rst index 510d4d02..1e5d7c33 100644 --- a/docs/containers/index.rst +++ b/docs/containers/index.rst @@ -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 diff --git a/docs/images/providers/factory.png b/docs/images/providers/factory.png deleted file mode 100644 index ff79ee5e..00000000 Binary files a/docs/images/providers/factory.png and /dev/null differ diff --git a/docs/images/providers/factory_delegation.png b/docs/images/providers/factory_delegation.png deleted file mode 100644 index e9a741dc..00000000 Binary files a/docs/images/providers/factory_delegation.png and /dev/null differ diff --git a/docs/images/providers/factory_init_injections.png b/docs/images/providers/factory_init_injections.png deleted file mode 100644 index f39b44d7..00000000 Binary files a/docs/images/providers/factory_init_injections.png and /dev/null differ diff --git a/docs/providers/factory.rst b/docs/providers/factory.rst index 2aa7f3ab..7708f25e 100644 --- a/docs/providers/factory.rst +++ b/docs/providers/factory.rst @@ -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:: + + __= + +If ```` is found the underlying provider will receive the +``=`` 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:: diff --git a/docs/providers/images/abstract_factory.png b/docs/providers/images/abstract_factory.png new file mode 100644 index 00000000..172c7ef0 Binary files /dev/null and b/docs/providers/images/abstract_factory.png differ diff --git a/docs/providers/images/factory_aggregate.png b/docs/providers/images/factory_aggregate.png new file mode 100644 index 00000000..0b563372 Binary files /dev/null and b/docs/providers/images/factory_aggregate.png differ diff --git a/docs/providers/images/factory_delegation.png b/docs/providers/images/factory_delegation.png new file mode 100644 index 00000000..4def0d08 Binary files /dev/null and b/docs/providers/images/factory_delegation.png differ diff --git a/docs/providers/images/factory_init_injections.png b/docs/providers/images/factory_init_injections.png new file mode 100644 index 00000000..f176cc8a Binary files /dev/null and b/docs/providers/images/factory_init_injections.png differ diff --git a/docs/providers/images/factory_init_injections_underlying.png b/docs/providers/images/factory_init_injections_underlying.png new file mode 100644 index 00000000..f6e79408 Binary files /dev/null and b/docs/providers/images/factory_init_injections_underlying.png differ diff --git a/docs/providers/index.rst b/docs/providers/index.rst index 15e2eccb..bd5a4745 100644 --- a/docs/providers/index.rst +++ b/docs/providers/index.rst @@ -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 diff --git a/examples/providers/abstract_factory.py b/examples/providers/abstract_factory.py new file mode 100644 index 00000000..03ccf4f4 --- /dev/null +++ b/examples/providers/abstract_factory.py @@ -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() must be + # overridden before calling diff --git a/examples/providers/abstract_factory/cache.py b/examples/providers/abstract_factory/cache.py deleted file mode 100644 index 51cdc3c2..00000000 --- a/examples/providers/abstract_factory/cache.py +++ /dev/null @@ -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 diff --git a/examples/providers/abstract_factory/example.py b/examples/providers/abstract_factory/example.py deleted file mode 100644 index 46382afa..00000000 --- a/examples/providers/abstract_factory/example.py +++ /dev/null @@ -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) - # - - # 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) - # diff --git a/examples/providers/factory.py b/examples/providers/factory.py index 17b1c9ec..33453dd3 100644 --- a/examples/providers/factory.py +++ b/examples/providers/factory.py @@ -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() diff --git a/examples/providers/factory_aggregate.py b/examples/providers/factory_aggregate.py new file mode 100644 index 00000000..7facca65 --- /dev/null +++ b/examples/providers/factory_aggregate.py @@ -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 diff --git a/examples/providers/factory_aggregate/example.py b/examples/providers/factory_aggregate/example.py deleted file mode 100644 index 906c8484..00000000 --- a/examples/providers/factory_aggregate/example.py +++ /dev/null @@ -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 diff --git a/examples/providers/factory_aggregate/games.py b/examples/providers/factory_aggregate/games.py deleted file mode 100644 index aa9e7a8a..00000000 --- a/examples/providers/factory_aggregate/games.py +++ /dev/null @@ -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.""" diff --git a/examples/providers/factory_aggregate/prototype.py b/examples/providers/factory_aggregate/prototype.py deleted file mode 100644 index 930a3159..00000000 --- a/examples/providers/factory_aggregate/prototype.py +++ /dev/null @@ -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] diff --git a/examples/providers/factory_delegation.py b/examples/providers/factory_delegation.py index 0aee54d3..f6bc02d1 100644 --- a/examples/providers/factory_delegation.py +++ b/examples/providers/factory_delegation.py @@ -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 diff --git a/examples/providers/factory_init_injections.py b/examples/providers/factory_init_injections.py index 2bdb0b24..682ace80 100644 --- a/examples/providers/factory_init_injections.py +++ b/examples/providers/factory_init_injections.py @@ -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) diff --git a/examples/providers/factory_deep_init_injections.py b/examples/providers/factory_init_injections_underlying.py similarity index 83% rename from examples/providers/factory_deep_init_injections.py rename to examples/providers/factory_init_injections_underlying.py index 3791b906..cf49388e 100644 --- a/examples/providers/factory_deep_init_injections.py +++ b/examples/providers/factory_init_injections_underlying.py @@ -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 diff --git a/examples/providers/factory_provided_type.py b/examples/providers/factory_provided_type.py index 8009300f..7e37cfa5 100644 --- a/examples/providers/factory_provided_type.py +++ b/examples/providers/factory_provided_type.py @@ -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: # can provide only # instances