From f32363d25927f7fad22293dbe602df4f659b9778 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 13:36:19 -0400 Subject: [PATCH 01/21] Edit key features pages --- docs/introduction/key_features.rst | 69 +++++++++--------------------- docs/main/changelog.rst | 4 ++ 2 files changed, 24 insertions(+), 49 deletions(-) diff --git a/docs/introduction/key_features.rst b/docs/introduction/key_features.rst index c2a1756f..bceb6e93 100644 --- a/docs/introduction/key_features.rst +++ b/docs/introduction/key_features.rst @@ -3,66 +3,37 @@ Key features .. meta:: :keywords: Python,DI,Dependency injection,IoC,Inversion of Control - :description: This article describes key features of "Dependency Injector" - framework. It also provides some cases and recommendations - about usage of "Dependency Injector" framework. + :description: This article describes key features of the Dependency Injector + framework. +``Dependency Injector`` is a dependency injection framework for Python. It takes the +responsibility of assembling your objects. -``Dependency Injector`` is a dependency injection framework for Python. -It was designed to be a unified and developer-friendly tool that helps -implement a dependency injection design pattern in a formal, pretty, and -Pythonic way. +Key features of the ``Dependency Injector`` are: + +- **Pythonic design**. Simple & explicit. +- **High performance**. Written in ``Cython``. +- **Maturity and production readiness**. Downloaded over 200.000 times a month. It stands on two principles: -- Explicit is better than implicit (PEP20). -- Do no magic to your code. +- **Explicit is better than implicit (PEP20)**. +- **Do not do any magic to your code**. -How does it different from the other frameworks? +How is the ``Dependency Injector`` different from the other frameworks? - **No autowiring.** The framework does NOT do any autowiring / autoresolving of the dependencies. You need to specify everything explicitly. Because *"Explicit is better than implicit" (PEP20)*. - **Does not pollute your code.** Your application does NOT know and does NOT depend on the framework. No ``@inject`` decorators, annotations, patching or any other magic tricks. -``Dependency Injector`` makes a simple contract with you: +In addition ``Dependency Injector`` is: -- You tell the framework how to build you code -- The framework does it for you +- Tested. +- Documented. +- Supported. +- Semantically versioned. +- Distributed as pre-compiled wheels. -The power of the ``Dependency Injector`` is in its simplicity and straightforwardness. It is a simple tool for the powerful concept. - -The key features of the ``Dependency Injector`` framework are: - -+ Easy, smart, and Pythonic style. -+ Does NOT pollute client code. -+ Obvious and clear structure. -+ Extensibility and flexibility. -+ High performance. -+ Memory efficiency. -+ Thread safety. -+ Documented. -+ Semantically versioned. -+ Distributed as pre-compiled wheels. - -``Dependency Injector`` containers and providers are implemented as C extension -types using ``Cython``. - -``Dependency Injector`` framework can be used in the different application types: - -+ Web applications based on the ``Flask``, ``Django`` or any other web framework. -+ Asynchronous applications ``asyncio``, ``aiohttp``, ``Tornado``, or ``Twisted``. -+ Standalone frameworks and libraries. -+ GUI applications. - -``Dependency Injector`` framework can be integrated on the different project -stages: - -+ It can be used in the beginning of the development of a new application. -+ It can be integrated into application that is on its active development stage. -+ It can be used for refactoring of legacy application. - -Components of ``Dependency Injector`` framework could be used: - -+ In composition with each other. -+ Independently from each other. +The power of the ``Dependency Injector`` is in its straightforwardness. It is a simple tool for +the powerful concept. .. disqus:: diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 498ab0e3..6e5d20f2 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -7,6 +7,10 @@ that were made in every particular version. From version 0.7.6 *Dependency Injector* framework strictly follows `Semantic versioning`_ +Development version +------------------- +- Update "Key Features" documentation page. + 3.36.0 ------ - Update providers overriding documentation and rework examples. From b2b8d2f4cf41a520fc3b35aa75eee8c3ccd9ed1a Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 14:52:16 -0400 Subject: [PATCH 02/21] Add files via upload --- README - coupling cohesion.png | Bin 0 -> 6883 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 README - coupling cohesion.png diff --git a/README - coupling cohesion.png b/README - coupling cohesion.png new file mode 100644 index 0000000000000000000000000000000000000000..3aaed9b69b912da5af67fddb8281186417eafa52 GIT binary patch literal 6883 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 literal 0 HcmV?d00001 From 13286783d0e89918a38e5051c9854c68d5316a67 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 14:52:41 -0400 Subject: [PATCH 03/21] Delete README - coupling cohesion.png --- README - coupling cohesion.png | Bin 6883 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 README - coupling cohesion.png diff --git a/README - coupling cohesion.png b/README - coupling cohesion.png deleted file mode 100644 index 3aaed9b69b912da5af67fddb8281186417eafa52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6883 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 From ca986698e952a4c085139e8156ab3f494e6956a1 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 16:19:40 -0400 Subject: [PATCH 04/21] Update key features page and remove structure page --- README.rst | 2 +- docs/containers/index.rst | 2 ++ docs/images/internals.png | Bin 8260 -> 0 bytes docs/index.rst | 47 +++++++++++++++++++++------ docs/introduction/index.rst | 1 - docs/introduction/key_features.rst | 28 +++++++++------- docs/introduction/structure.rst | 50 ----------------------------- docs/main/changelog.rst | 2 ++ docs/providers/configuration.rst | 2 ++ 9 files changed, 60 insertions(+), 74 deletions(-) delete mode 100644 docs/images/internals.png delete mode 100644 docs/introduction/structure.rst diff --git a/README.rst b/README.rst index 9604c0ee..7128b6b9 100644 --- a/README.rst +++ b/README.rst @@ -52,7 +52,7 @@ What is ``Dependency Injector``? ``Dependency Injector`` is a dependency injection framework for Python. -It helps you in implementing the dependency injection principle. +It helps you implementing the dependency injection principle. What is dependency injection? ----------------------------- diff --git a/docs/containers/index.rst b/docs/containers/index.rst index 1e5d7c33..c1abec16 100644 --- a/docs/containers/index.rst +++ b/docs/containers/index.rst @@ -1,3 +1,5 @@ +.. _containers: + Containers ========== diff --git a/docs/images/internals.png b/docs/images/internals.png deleted file mode 100644 index f49ecc8db6710bd13aebd91cc7d13a665671b343..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8260 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 diff --git a/docs/index.rst b/docs/index.rst index 168ef255..409df690 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -64,22 +64,49 @@ Dependency Injector --- Dependency injection framework for Python ``Dependency Injector`` is a dependency injection framework for Python. -It stands on two principles: +It helps implementing the dependency injection principle. -- Explicit is better than implicit (PEP20). -- Do no magic to your code. +Key features of the ``Dependency Injector``: -How does it different from the other frameworks? +- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``, + ``List``, ``Configuration``, ``Dependency`` and ``Selector`` providers that help assembling your + objects. See :ref:`providers`. +- **Overriding**. Can override any provider by another provider on the fly. This helps in testing + and configuring dev / stage environment to replace API clients with stubs etc. See + :ref:`provider-overriding`. +- **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables + and dictionaries. See :ref:`configuration-provider`. +- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`. +- **Performance**. Written in ``Cython``. +- **Maturity**. Mature and ready for production. -- **No autowiring.** The framework does NOT do any autowiring / autoresolving of the dependencies. You need to specify everything explicitly. Because *"Explicit is better than implicit" (PEP20)*. -- **Does not pollute your code.** Your application does NOT know and does NOT depend on the framework. No ``@inject`` decorators, annotations, patching or any other magic tricks. +.. code-block:: python -``Dependency Injector`` makes a simple contract with you: + from dependency_injector import containers, providers -- You tell the framework how to assemble your objects -- The framework does it for you -The power of the ``Dependency Injector`` is in its simplicity and straightforwardness. It is a simple tool for the powerful concept. + class Container(containers.DeclarativeContainer): + + config = providers.Configuration() + + api_client = providers.Singleton( + ApiClient, + api_key=config.api_key, + timeout=config.timeout.as_int(), + ) + + service = providers.Factory( + Service, + api_client=api_client, + ) + + + if __name__ == '__main__': + container = Container() + container.config.api_key.from_env('API_KEY') + container.config.timeout.from_env('TIMEOUT') + + service = container.service() With the ``Dependency Injector`` you keep **application structure in one place**. This place is called **the container**. You use the container to manage all the components of the diff --git a/docs/introduction/index.rst b/docs/introduction/index.rst index 3e7ce7cc..0c3588de 100644 --- a/docs/introduction/index.rst +++ b/docs/introduction/index.rst @@ -17,4 +17,3 @@ dependency injection pattern, inversion of control principle and what_is_di di_in_python key_features - structure diff --git a/docs/introduction/key_features.rst b/docs/introduction/key_features.rst index bceb6e93..21e97331 100644 --- a/docs/introduction/key_features.rst +++ b/docs/introduction/key_features.rst @@ -6,25 +6,32 @@ Key features :description: This article describes key features of the Dependency Injector framework. -``Dependency Injector`` is a dependency injection framework for Python. It takes the -responsibility of assembling your objects. +Key features of the ``Dependency Injector``: -Key features of the ``Dependency Injector`` are: +- **Providers**. Provides ``Factory``, ``Singleton``, ``Callable``, ``Coroutine``, ``Object``, + ``List``, ``Configuration``, ``Dependency`` and ``Selector`` providers that help assembling your + objects. See :ref:`providers`. +- **Overriding**. Can override any provider by another provider on the fly. This helps in testing + and configuring dev / stage environment to replace API clients with stubs etc. See + :ref:`provider-overriding`. +- **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables + and dictionaries. See :ref:`configuration-provider`. +- **Containers**. Provides declarative and dynamic containers. See :ref:`containers`. +- **Performance**. Written in ``Cython``. +- **Maturity**. Mature and ready for production. -- **Pythonic design**. Simple & explicit. -- **High performance**. Written in ``Cython``. -- **Maturity and production readiness**. Downloaded over 200.000 times a month. - -It stands on two principles: +The framework stands on two principles: - **Explicit is better than implicit (PEP20)**. - **Do not do any magic to your code**. -How is the ``Dependency Injector`` different from the other frameworks? +How is that different from the other frameworks? - **No autowiring.** The framework does NOT do any autowiring / autoresolving of the dependencies. You need to specify everything explicitly. Because *"Explicit is better than implicit" (PEP20)*. - **Does not pollute your code.** Your application does NOT know and does NOT depend on the framework. No ``@inject`` decorators, annotations, patching or any other magic tricks. +The power of the framework is in a simplicity. ``Dependency Injector`` is a simple tool for the powerful concept. + In addition ``Dependency Injector`` is: - Tested. @@ -33,7 +40,4 @@ In addition ``Dependency Injector`` is: - Semantically versioned. - Distributed as pre-compiled wheels. -The power of the ``Dependency Injector`` is in its straightforwardness. It is a simple tool for -the powerful concept. - .. disqus:: diff --git a/docs/introduction/structure.rst b/docs/introduction/structure.rst deleted file mode 100644 index 195b3d39..00000000 --- a/docs/introduction/structure.rst +++ /dev/null @@ -1,50 +0,0 @@ -Structure of Dependency Injector --------------------------------- - -.. meta:: - :keywords: Python,DI,Dependency injection,IoC,Inversion of Control - :description: This article describes "Dependency Injector" framework - components and their interaction between each other. - Providers and containers are the former components of - the framework. - -Current section describes *Dependency Injector* main entities and their -interaction between each other. - -.. image:: /images/internals.png - :width: 100% - :align: center - -There are 2 main entities: providers & containers. - -Providers -~~~~~~~~~ - -Providers are strategies of accessing objects. For example, -:py:class:`dependency_injector.providers.Factory` creates new instance -of provided class every time it is called. -:py:class:`dependency_injector.providers.Singleton` creates provided -instance once and returns it on every next call. Base class is - -:py:class:`dependency_injector.providers.Provider`. - -Providers could be: - -+ Injected into each other. -+ Overridden by each other. -+ Extended. - -Containers -~~~~~~~~~~ - -Containers are collections of providers. They are used for grouping -of providers by some principles. Base class is - -:py:class:`dependency_injector.containers.DeclarativeContainer`. - -Containers could be: - -+ Overridden by each other. -+ Copied from each other. -+ Extended. - - -.. disqus:: diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 6e5d20f2..2dde548e 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -9,7 +9,9 @@ follows `Semantic versioning`_ Development version ------------------- +- Update index documentation page. - Update "Key Features" documentation page. +- Remove "Structure of Dependency Injector" documentation page. 3.36.0 ------ diff --git a/docs/providers/configuration.rst b/docs/providers/configuration.rst index 9d049e18..0ce046e6 100644 --- a/docs/providers/configuration.rst +++ b/docs/providers/configuration.rst @@ -1,3 +1,5 @@ +.. _configuration-provider: + Configuration provider ====================== From 8b13a809e6644af14855f74589da7d6795955e6b Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 16:26:32 -0400 Subject: [PATCH 05/21] Edit feedback documentation page --- docs/main/changelog.rst | 1 + docs/main/feedback.rst | 10 +++------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 2dde548e..0e8b3c58 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -12,6 +12,7 @@ Development version - Update index documentation page. - Update "Key Features" documentation page. - Remove "Structure of Dependency Injector" documentation page. +- Edit "Feedback" documentation page. 3.36.0 ------ diff --git a/docs/main/feedback.rst b/docs/main/feedback.rst index dc51dae2..94b86658 100644 --- a/docs/main/feedback.rst +++ b/docs/main/feedback.rst @@ -1,12 +1,8 @@ Feedback ======== -Feel free to post questions, bugs, feature requests, proposals etc. on -*Dependency Injector* GitHub Issues: - - https://github.com/ets-labs/python-dependency-injector/issues - -Your feedback is quite important! - +To post a question, bug report, a feature proposal or get some help open a +`Github Issue `_ or leave a comment +below. .. disqus:: From 33b4416c2ccf2207b8daa262d1ef068d5b85510d Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 16:28:11 -0400 Subject: [PATCH 06/21] Update readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 7128b6b9..33b1b570 100644 --- a/README.rst +++ b/README.rst @@ -52,7 +52,7 @@ What is ``Dependency Injector``? ``Dependency Injector`` is a dependency injection framework for Python. -It helps you implementing the dependency injection principle. +It helps implementing the dependency injection principle. What is dependency injection? ----------------------------- From a7afa66e40ff8ca8cbb21afff624e5854643f6d0 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 16:46:03 -0400 Subject: [PATCH 07/21] Add container usage for all factory provider examples --- docs/providers/factory.rst | 8 ++-- examples/providers/abstract_factory.py | 24 +++++++----- examples/providers/factory.py | 12 ++++-- examples/providers/factory_aggregate.py | 18 +++++---- examples/providers/factory_delegation.py | 19 ++++++---- examples/providers/factory_init_injections.py | 24 +++++++----- .../factory_init_injections_underlying.py | 37 +++++++++++-------- examples/providers/factory_provided_type.py | 10 +++-- 8 files changed, 91 insertions(+), 61 deletions(-) diff --git a/docs/providers/factory.rst b/docs/providers/factory.rst index 626cda8c..a4e2aa29 100644 --- a/docs/providers/factory.rst +++ b/docs/providers/factory.rst @@ -73,7 +73,7 @@ all the classes and use special double-underscore ``__`` syntax for passing the .. literalinclude:: ../../examples/providers/factory_init_injections_underlying.py :language: python :lines: 3- - :emphasize-lines: 24-35,39,42,45 + :emphasize-lines: 44,49 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. @@ -98,7 +98,7 @@ attribute of the provider that you're going to inject. .. literalinclude:: ../../examples/providers/factory_delegation.py :language: python :lines: 3- - :emphasize-lines: 25 + :emphasize-lines: 28 .. note:: Any provider has a ``.provider`` attribute. @@ -135,7 +135,7 @@ provider with two peculiarities: .. literalinclude:: ../../examples/providers/abstract_factory.py :language: python :lines: 3- - :emphasize-lines: 32 + :emphasize-lines: 34 Factory aggregate ----------------- @@ -155,7 +155,7 @@ rest of the arguments are passed to the delegated ``Factory``. .. literalinclude:: ../../examples/providers/factory_aggregate.py :language: python :lines: 3- - :emphasize-lines: 31-35,43 + :emphasize-lines: 33-37,47 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 diff --git a/examples/providers/abstract_factory.py b/examples/providers/abstract_factory.py index 03ccf4f4..f67a66fb 100644 --- a/examples/providers/abstract_factory.py +++ b/examples/providers/abstract_factory.py @@ -5,7 +5,7 @@ import dataclasses import random from typing import List -from dependency_injector import providers +from dependency_injector import containers, providers class AbstractCacheClient(metaclass=abc.ABCMeta): @@ -31,18 +31,22 @@ class Service: cache: AbstractCacheClient -cache_client_factory = providers.AbstractFactory(AbstractCacheClient) -service_factory = providers.Factory( - Service, - cache=cache_client_factory, -) +class Container(containers.DeclarativeContainer): + + 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]) + container = Container() + cache_type = random.choice(['redis', 'memcached']) if cache_type == 'redis': - cache_client_factory.override( + container.cache_client_factory.override( providers.Factory( RedisCacheClient, host='localhost', @@ -51,7 +55,7 @@ if __name__ == '__main__': ), ) elif cache_type == 'memcached': - cache_client_factory.override( + container.cache_client_factory.override( providers.Factory( MemcachedCacheClient, hosts=['10.0.1.1'], @@ -60,7 +64,7 @@ if __name__ == '__main__': ), ) - service = service_factory() + service = container.service_factory() print(service.cache) # The output depends on cache_type variable value. # diff --git a/examples/providers/factory.py b/examples/providers/factory.py index c83ded02..7fa38386 100644 --- a/examples/providers/factory.py +++ b/examples/providers/factory.py @@ -1,15 +1,19 @@ """`Factory` provider example.""" -from dependency_injector import providers +from dependency_injector import containers, providers class User: ... -users_factory = providers.Factory(User) +class Container(containers.DeclarativeContainer): + + user_factory = providers.Factory(User) if __name__ == '__main__': - user1 = users_factory() - user2 = users_factory() + container = Container() + + user1 = container.user_factory() + user2 = container.user_factory() diff --git a/examples/providers/factory_aggregate.py b/examples/providers/factory_aggregate.py index 7facca65..299e17cc 100644 --- a/examples/providers/factory_aggregate.py +++ b/examples/providers/factory_aggregate.py @@ -3,7 +3,7 @@ import dataclasses import sys -from dependency_injector import providers +from dependency_injector import containers, providers @dataclasses.dataclass @@ -30,11 +30,13 @@ class Ludo(Game): ... -game_factory = providers.FactoryAggregate( - chess=providers.Factory(Chess), - checkers=providers.Factory(Checkers), - ludo=providers.Factory(Ludo), -) +class Container(containers.DeclarativeContainer): + + game_factory = providers.FactoryAggregate( + chess=providers.Factory(Chess), + checkers=providers.Factory(Checkers), + ludo=providers.Factory(Ludo), + ) if __name__ == '__main__': @@ -42,7 +44,9 @@ if __name__ == '__main__': player1 = sys.argv[2].capitalize() player2 = sys.argv[3].capitalize() - selected_game = game_factory(game_type, player1, player2) + container = Container() + + selected_game = container.game_factory(game_type, player1, player2) selected_game.play() # $ python factory_aggregate.py chess John Jane diff --git a/examples/providers/factory_delegation.py b/examples/providers/factory_delegation.py index 00f3fec5..7112ab3a 100644 --- a/examples/providers/factory_delegation.py +++ b/examples/providers/factory_delegation.py @@ -2,7 +2,7 @@ from typing import Callable, List -from dependency_injector import providers +from dependency_injector import containers, providers class User: @@ -21,15 +21,20 @@ class UserRepository: ] -user_factory = providers.Factory(User) -user_repository_factory = providers.Factory( - UserRepository, - user_factory=user_factory.provider, -) +class Container(containers.DeclarativeContainer): + + user_factory = providers.Factory(User) + + user_repository_factory = providers.Factory( + UserRepository, + user_factory=user_factory.provider, + ) if __name__ == '__main__': - user_repository = user_repository_factory() + container = Container() + + user_repository = container.user_repository_factory() user1, user2 = user_repository.get_all() diff --git a/examples/providers/factory_init_injections.py b/examples/providers/factory_init_injections.py index d720edb6..134d5b16 100644 --- a/examples/providers/factory_init_injections.py +++ b/examples/providers/factory_init_injections.py @@ -1,6 +1,6 @@ """`Factory` provider init injections example.""" -from dependency_injector import providers +from dependency_injector import containers, providers class Photo: @@ -13,23 +13,27 @@ class User: self.main_photo = main_photo -photo_factory = providers.Factory(Photo) -user_factory = providers.Factory( - User, - main_photo=photo_factory, -) +class Container(containers.DeclarativeContainer): + + photo_factory = providers.Factory(Photo) + + user_factory = providers.Factory( + User, + main_photo=photo_factory, + ) if __name__ == '__main__': - user1 = user_factory(1) + container = Container() + + user1 = container.user_factory(1) # Same as: # user1 = User(1, main_photo=Photo()) - user2 = user_factory(2) + user2 = container.user_factory(2) # Same as: # user2 = User(2, main_photo=Photo()) - # Context keyword arguments have a priority: another_photo = Photo() - user3 = user_factory( + user3 = container.user_factory( uid=3, main_photo=another_photo, ) diff --git a/examples/providers/factory_init_injections_underlying.py b/examples/providers/factory_init_injections_underlying.py index 5c9f196f..cc291a6a 100644 --- a/examples/providers/factory_init_injections_underlying.py +++ b/examples/providers/factory_init_injections_underlying.py @@ -1,6 +1,6 @@ """`Factory` provider - passing injections to the underlying providers example.""" -from dependency_injector import providers +from dependency_injector import containers, providers class Regularizer: @@ -23,26 +23,31 @@ class Algorithm: self.task = task -algorithm_factory = providers.Factory( - Algorithm, - task=providers.Factory( - ClassificationTask, - loss=providers.Factory( - Loss, - regularizer=providers.Factory( - Regularizer, +class Container(containers.DeclarativeContainer): + + algorithm_factory = providers.Factory( + Algorithm, + task=providers.Factory( + ClassificationTask, + loss=providers.Factory( + Loss, + regularizer=providers.Factory( + Regularizer, + ), ), ), - ), -) + ) if __name__ == '__main__': - algorithm_1 = algorithm_factory(task__loss__regularizer__alpha=0.5) + container = Container() + + algorithm_1 = container.algorithm_factory( + task__loss__regularizer__alpha=0.5, + ) assert algorithm_1.task.loss.regularizer.alpha == 0.5 - algorithm_2 = algorithm_factory(task__loss__regularizer__alpha=0.7) + algorithm_2 = container.algorithm_factory( + task__loss__regularizer__alpha=0.7, + ) assert algorithm_2.task.loss.regularizer.alpha == 0.7 - - algorithm_3 = algorithm_factory(task__loss__regularizer=Regularizer(alpha=0.8)) - assert algorithm_3.task.loss.regularizer.alpha == 0.8 diff --git a/examples/providers/factory_provided_type.py b/examples/providers/factory_provided_type.py index 7e37cfa5..bf4004ce 100644 --- a/examples/providers/factory_provided_type.py +++ b/examples/providers/factory_provided_type.py @@ -1,6 +1,6 @@ """`Factory` specialization with limitation to provided type example.""" -from dependency_injector import providers, errors +from dependency_injector import containers, providers, errors class BaseService: @@ -17,11 +17,15 @@ class ServiceProvider(providers.Factory): # Creating service provider with a correct provided type: -some_service_provider = ServiceProvider(SomeService) +class Container(containers.DeclarativeContainer): + + some_service_provider = ServiceProvider(SomeService) + # Trying to create service provider an incorrect provided type: try: - some_service_provider = ServiceProvider(object) + class Container(containers.DeclarativeContainer): + some_service_provider = ServiceProvider(object) except errors.Error as exception: print(exception) # The output is: From 1eb9020a4e19874f9d3763460f1cd9616a7ef961 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 16:56:01 -0400 Subject: [PATCH 08/21] Add container usage for all singleton provider examples --- docs/providers/singleton.rst | 4 +- examples/providers/singleton.py | 12 ++++-- examples/providers/singleton_resetting.py | 14 ++++--- examples/providers/singleton_scoped.py | 17 +++++--- examples/providers/singleton_thread_locals.py | 42 +++++++++++-------- 5 files changed, 55 insertions(+), 34 deletions(-) diff --git a/docs/providers/singleton.rst b/docs/providers/singleton.rst index a3cc2d4d..c42b6a3e 100644 --- a/docs/providers/singleton.rst +++ b/docs/providers/singleton.rst @@ -41,7 +41,7 @@ provider. .. literalinclude:: ../../examples/providers/singleton_resetting.py :language: python :lines: 3- - :emphasize-lines: 14 + :emphasize-lines: 18 .. note:: Resetting of the memorized object clears the reference to it. Further object's lifecycle is @@ -64,7 +64,7 @@ There are two thread-safe singleton implementations out of the box: .. literalinclude:: ../../examples/providers/singleton_thread_locals.py :language: python :lines: 3- - :emphasize-lines: 11,12 + :emphasize-lines: 13,15 Implementing scopes ------------------- diff --git a/examples/providers/singleton.py b/examples/providers/singleton.py index 9e94b76c..f1083895 100644 --- a/examples/providers/singleton.py +++ b/examples/providers/singleton.py @@ -1,16 +1,20 @@ """`Singleton` provider example.""" -from dependency_injector import providers +from dependency_injector import containers, providers class UserService: ... -user_service_provider = providers.Singleton(UserService) +class Container(containers.DeclarativeContainer): + + user_service_provider = providers.Singleton(UserService) if __name__ == '__main__': - user_service1 = user_service_provider() - user_service2 = user_service_provider() + container = Container() + + user_service1 = container.user_service_provider() + user_service2 = container.user_service_provider() assert user_service1 is user_service2 diff --git a/examples/providers/singleton_resetting.py b/examples/providers/singleton_resetting.py index edec0594..7188744b 100644 --- a/examples/providers/singleton_resetting.py +++ b/examples/providers/singleton_resetting.py @@ -1,19 +1,23 @@ """`Singleton` provider resetting example.""" -from dependency_injector import providers +from dependency_injector import containers, providers class UserService: ... -user_service_provider = providers.Singleton(UserService) +class Container(containers.DeclarativeContainer): + + user_service_provider = providers.Singleton(UserService) if __name__ == '__main__': - user_service1 = user_service_provider() + container = Container() - user_service_provider.reset() + user_service1 = container.user_service_provider() - users_service2 = user_service_provider() + container.user_service_provider.reset() + + users_service2 = container.user_service_provider() assert users_service2 is not user_service1 diff --git a/examples/providers/singleton_scoped.py b/examples/providers/singleton_scoped.py index e9bc2ea6..0d40a6e7 100644 --- a/examples/providers/singleton_scoped.py +++ b/examples/providers/singleton_scoped.py @@ -1,30 +1,35 @@ """`Singleton` - Flask request scope example.""" -from dependency_injector import providers -from flask import Flask +from dependency_injector import containers, providers +from flask import Flask, current_app class Service: ... -service_provider = providers.Singleton(Service) +class Container(containers.DeclarativeContainer): + + service_provider = providers.Singleton(Service) def index_view(): - service_1 = service_provider() - service_2 = service_provider() + service_1 = current_app.container.service_provider() + service_2 = current_app.container.service_provider() assert service_1 is service_2 print(service_1) return 'Hello World!' def teardown_context(request): - service_provider.reset() + current_app.container.service_provider.reset() return request +container = Container() + app = Flask(__name__) +app.container = container app.add_url_rule('/', 'index', view_func=index_view) app.after_request(teardown_context) diff --git a/examples/providers/singleton_thread_locals.py b/examples/providers/singleton_thread_locals.py index 83325559..1255d718 100644 --- a/examples/providers/singleton_thread_locals.py +++ b/examples/providers/singleton_thread_locals.py @@ -3,31 +3,39 @@ import threading import queue -from dependency_injector import providers +from dependency_injector import containers, providers def put_in_queue(example_object, queue_object): queue_object.put(example_object) -thread_local_object = providers.ThreadLocalSingleton(object) -queue_provider = providers.ThreadSafeSingleton(queue.Queue) -put_in_queue = providers.Callable( - put_in_queue, - example_object=thread_local_object, - queue_object=queue_provider, -) -thread_factory = providers.Factory( - threading.Thread, - target=put_in_queue.provider, -) +class Container(containers.DeclarativeContainer): + + thread_local_object = providers.ThreadLocalSingleton(object) + + queue_provider = providers.ThreadSafeSingleton(queue.Queue) + + put_in_queue = providers.Callable( + put_in_queue, + example_object=thread_local_object, + queue_object=queue_provider, + ) + + thread_factory = providers.Factory( + threading.Thread, + target=put_in_queue.provider, + ) if __name__ == '__main__': + container = Container() + + n = 10 threads = [] - for thread_number in range(10): + for thread_number in range(n): threads.append( - thread_factory(name='Thread{0}'.format(thread_number)), + container.thread_factory(name='Thread{0}'.format(thread_number)), ) for thread in threads: thread.start() @@ -35,9 +43,9 @@ if __name__ == '__main__': thread.join() all_objects = set() - while not queue_provider().empty(): - all_objects.add(queue_provider().get()) + while not container.queue_provider().empty(): + all_objects.add(container.queue_provider().get()) - assert len(all_objects) == len(threads) + assert len(all_objects) == len(threads) == n # Queue contains same number of objects as number of threads where # thread-local singleton provider was used. From a497cb2527773ec4c1bc0ebd77e3de56cb9a5d27 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 16:57:35 -0400 Subject: [PATCH 09/21] Add container usage for callable provider example --- examples/providers/callable.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/examples/providers/callable.py b/examples/providers/callable.py index 40e0d363..82d18e38 100644 --- a/examples/providers/callable.py +++ b/examples/providers/callable.py @@ -2,17 +2,22 @@ import passlib.hash -from dependency_injector import providers +from dependency_injector import containers, providers -password_hasher = providers.Callable( - passlib.hash.sha256_crypt.hash, - salt_size=16, - rounds=10000, -) -password_verifier = providers.Callable(passlib.hash.sha256_crypt.verify) +class Container(containers.DeclarativeContainer): + + password_hasher = providers.Callable( + passlib.hash.sha256_crypt.hash, + salt_size=16, + rounds=10000, + ) + + password_verifier = providers.Callable(passlib.hash.sha256_crypt.verify) if __name__ == '__main__': - hashed_password = password_hasher('super secret') - assert password_verifier('super secret', hashed_password) + container = Container() + + hashed_password = container.password_hasher('super secret') + assert container.password_verifier('super secret', hashed_password) From b074d2aeb7276ab21305492abea8bd366bdcf430 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 16:59:11 -0400 Subject: [PATCH 10/21] Add container usage for coroutine provider example --- examples/providers/coroutine.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/providers/coroutine.py b/examples/providers/coroutine.py index 98767295..d42689ba 100644 --- a/examples/providers/coroutine.py +++ b/examples/providers/coroutine.py @@ -2,7 +2,7 @@ import asyncio -from dependency_injector import providers +from dependency_injector import containers, providers async def coroutine(arg1, arg2): @@ -10,10 +10,14 @@ async def coroutine(arg1, arg2): return arg1, arg2 -coroutine_provider = providers.Coroutine(coroutine, arg1=1, arg2=2) +class Container(containers.DeclarativeContainer): + + coroutine_provider = providers.Coroutine(coroutine, arg1=1, arg2=2) if __name__ == '__main__': - arg1, arg2 = asyncio.run(coroutine_provider()) + container = Container() + + arg1, arg2 = asyncio.run(container.coroutine_provider()) assert (arg1, arg2) == (1, 2) - assert asyncio.iscoroutinefunction(coroutine_provider) + assert asyncio.iscoroutinefunction(container.coroutine_provider) From 6528411271f49540140ec951953c2beeaf1570c1 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 17:00:14 -0400 Subject: [PATCH 11/21] Add container usage for object provider example --- examples/providers/object.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/providers/object.py b/examples/providers/object.py index 7da9cb26..d0066153 100644 --- a/examples/providers/object.py +++ b/examples/providers/object.py @@ -1,10 +1,14 @@ """`Object` provider example.""" -from dependency_injector import providers +from dependency_injector import containers, providers -object_provider = providers.Object(1) +class Container(containers.DeclarativeContainer): + + object_provider = providers.Object(1) if __name__ == '__main__': - assert object_provider() == 1 + container = Container() + + assert container.object_provider() == 1 From 2a23f3d2f4b4f41509e57badc2b68a0291599aac Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 17:03:28 -0400 Subject: [PATCH 12/21] Add container usage for list provider example --- docs/providers/list.rst | 2 +- examples/providers/list.py | 24 ++++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/providers/list.rst b/docs/providers/list.rst index afd24a3d..5f2d3924 100644 --- a/docs/providers/list.rst +++ b/docs/providers/list.rst @@ -13,7 +13,7 @@ List provider .. literalinclude:: ../../examples/providers/list.py :language: python :lines: 3- - :emphasize-lines: 19-22 + :emphasize-lines: 21-24 ``List`` provider handles positional arguments the same way as a :ref:`factory-provider`. diff --git a/examples/providers/list.py b/examples/providers/list.py index 1d2eb474..d7096e62 100644 --- a/examples/providers/list.py +++ b/examples/providers/list.py @@ -3,7 +3,7 @@ import dataclasses from typing import List -from dependency_injector import providers +from dependency_injector import containers, providers @dataclasses.dataclass @@ -16,23 +16,27 @@ class Dispatcher: modules: List[Module] -dispatcher_factory = providers.Factory( - Dispatcher, - modules=providers.List( - providers.Factory(Module, name='m1'), - providers.Factory(Module, name='m2'), - ), -) +class Container(containers.DeclarativeContainer): + + dispatcher_factory = providers.Factory( + Dispatcher, + modules=providers.List( + providers.Factory(Module, name='m1'), + providers.Factory(Module, name='m2'), + ), + ) if __name__ == '__main__': - dispatcher = dispatcher_factory() + container = Container() + + dispatcher = container.dispatcher_factory() assert isinstance(dispatcher.modules, list) assert dispatcher.modules[0].name == 'm1' assert dispatcher.modules[1].name == 'm2' - # Call "dispatcher = dispatcher_factory()" is an equivalent for: + # Call "dispatcher = container.dispatcher_factory()" is equivalent to: # dispatcher = Dispatcher( # modules=[ # Module(name='m1'), From e48746d65fb383f9a615029488ea2e9d57923321 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 17:37:03 -0400 Subject: [PATCH 13/21] Add usage of the container to the configuration examples --- docs/providers/configuration.rst | 45 +++++++++----- .../providers/configuration/configuration.py | 23 +++---- .../configuration/configuration_dict.py | 42 ++++++++----- .../configuration/configuration_env.py | 27 +++++---- .../configuration/configuration_ini.py | 28 ++++++--- .../configuration_itemselector.py | 60 +++++++++---------- .../configuration/configuration_multiple.py | 30 +++++++--- .../configuration/configuration_type.py | 24 ++++---- .../configuration_type_custom.py | 20 ++++--- .../configuration/configuration_yaml.py | 28 ++++++--- 10 files changed, 208 insertions(+), 119 deletions(-) diff --git a/docs/providers/configuration.rst b/docs/providers/configuration.rst index 0ce046e6..496cb69d 100644 --- a/docs/providers/configuration.rst +++ b/docs/providers/configuration.rst @@ -16,8 +16,8 @@ Configuration provider .. literalinclude:: ../../examples/providers/configuration/configuration.py :language: python - :emphasize-lines: 4,9-10 - :lines: 4-14 + :emphasize-lines: 7,12-13 + :lines: 3- It implements the principle "use first, define later". @@ -29,8 +29,8 @@ Loading from an INI file .. literalinclude:: ../../examples/providers/configuration/configuration_ini.py :language: python - :lines: 3-5,6- - :emphasize-lines: 6 + :lines: 3- + :emphasize-lines: 12 where ``examples/providers/configuration/config.ini`` is: @@ -49,8 +49,8 @@ Loading from a YAML file .. literalinclude:: ../../examples/providers/configuration/configuration_yaml.py :language: python - :lines: 3-5,6- - :emphasize-lines: 6 + :lines: 3- + :emphasize-lines: 12 where ``examples/providers/configuration/config.yml`` is: @@ -83,8 +83,8 @@ Loading from a dictionary .. literalinclude:: ../../examples/providers/configuration/configuration_dict.py :language: python - :lines: 3-5,6- - :emphasize-lines: 6-13 + :lines: 3- + :emphasize-lines: 12-19 Loading from an environment variable ------------------------------------ @@ -94,8 +94,8 @@ Loading from an environment variable .. literalinclude:: ../../examples/providers/configuration/configuration_env.py :language: python - :lines: 5-7,13-21 - :emphasize-lines: 6-8 + :lines: 3- + :emphasize-lines: 18-20 Loading from the multiple sources --------------------------------- @@ -105,8 +105,8 @@ configuration is merged recursively over the existing configuration. .. literalinclude:: ../../examples/providers/configuration/configuration_multiple.py :language: python - :lines: 3-5,6-14 - :emphasize-lines: 6-7 + :lines: 3- + :emphasize-lines: 12-13 where ``examples/providers/configuration/config.local.yml`` is: @@ -124,7 +124,7 @@ convert it into an ``int`` or a ``float``. .. literalinclude:: ../../examples/providers/configuration/configuration_type.py :language: python :lines: 3- - :emphasize-lines: 17 + :emphasize-lines: 19 ``Configuration`` provider has next helper methods: @@ -137,10 +137,27 @@ The last method ``.as_(callback, *args, **kwargs)`` helps to implement other con .. literalinclude:: ../../examples/providers/configuration/configuration_type_custom.py :language: python :lines: 3- - :emphasize-lines: 16 + :emphasize-lines: 18 With the ``.as_(callback, *args, **kwargs)`` you can specify a function that will be called before the injection. The value from the config will be passed as a first argument. The returned value will be injected. Parameters ``*args`` and ``**kwargs`` are handled as any other injections. +Injecting invariants +-------------------- + +You can inject invariant configuration options based on the value of the other configuration +option. + +To use that you should provide the switch-value as an item of the configuration option that +contains sections ``config.options[config.switch]``: + +- When the value of the ``config.switch`` is ``A``, the ``config.options.A`` is injected +- When the value of the ``config.switch`` is ``B``, the ``config.options.B`` is injected + +.. literalinclude:: ../../examples/providers/configuration/configuration_itemselector.py + :language: python + :lines: 3- + :emphasize-lines: 15,30-31,38 + .. disqus:: diff --git a/examples/providers/configuration/configuration.py b/examples/providers/configuration/configuration.py index a1ea0f44..7a11cbfc 100644 --- a/examples/providers/configuration/configuration.py +++ b/examples/providers/configuration/configuration.py @@ -1,21 +1,24 @@ """`Configuration` provider example.""" import boto3 -from dependency_injector import providers +from dependency_injector import containers, providers -config = providers.Configuration() +class Container(containers.DeclarativeContainer): -s3_client_factory = providers.Factory( - boto3.client, - 's3', - aws_access_key_id=config.aws.access_key_id, - aws_secret_access_key=config.aws.secret_access_key, -) + config = providers.Configuration() + + s3_client_factory = providers.Factory( + boto3.client, + 's3', + aws_access_key_id=config.aws.access_key_id, + aws_secret_access_key=config.aws.secret_access_key, + ) if __name__ == '__main__': - config.from_dict( + container = Container() + container.config.from_dict( { 'aws': { 'access_key_id': 'KEY', @@ -23,4 +26,4 @@ if __name__ == '__main__': }, }, ) - s3_client = s3_client_factory() + s3_client = container.s3_client_factory() diff --git a/examples/providers/configuration/configuration_dict.py b/examples/providers/configuration/configuration_dict.py index 275c2b12..d5669ccc 100644 --- a/examples/providers/configuration/configuration_dict.py +++ b/examples/providers/configuration/configuration_dict.py @@ -1,20 +1,34 @@ """`Configuration` provider values loading example.""" -from dependency_injector import providers +from dependency_injector import containers, providers -config = providers.Configuration() +class Container(containers.DeclarativeContainer): -config.from_dict( - { + config = providers.Configuration() + + +if __name__ == '__main__': + container = Container() + + container.config.from_dict( + { + 'aws': { + 'access_key_id': 'KEY', + 'secret_access_key': 'SECRET', + }, + }, + ) + + assert container.config() == { 'aws': { - 'access_key_id': 'KEY', - 'secret_access_key': 'SECRET', - }, - }, -) - -assert config() == {'aws': {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'}} -assert config.aws() == {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'} -assert config.aws.access_key_id() == 'KEY' -assert config.aws.secret_access_key() == 'SECRET' + 'access_key_id': 'KEY', + 'secret_access_key': 'SECRET', + }, + } + assert container.config.aws() == { + 'access_key_id': 'KEY', + 'secret_access_key': 'SECRET', + } + assert container.config.aws.access_key_id() == 'KEY' + assert container.config.aws.secret_access_key() == 'SECRET' diff --git a/examples/providers/configuration/configuration_env.py b/examples/providers/configuration/configuration_env.py index 52c3e0ca..b9d491bc 100644 --- a/examples/providers/configuration/configuration_env.py +++ b/examples/providers/configuration/configuration_env.py @@ -2,20 +2,25 @@ import os -from dependency_injector import providers +from dependency_injector import containers, providers -# Emulate environment variables -os.environ['AWS_ACCESS_KEY_ID'] = 'KEY' -os.environ['AWS_SECRET_ACCESS_KEY'] = 'SECRET' +class Container(containers.DeclarativeContainer): + + config = providers.Configuration() -config = providers.Configuration() +if __name__ == '__main__': + container = Container() -config.aws.access_key_id.from_env('AWS_ACCESS_KEY_ID') -config.aws.secret_access_key.from_env('AWS_SECRET_ACCESS_KEY') -config.optional.from_env('UNDEFINED', 'default_value') + # Emulate environment variables + os.environ['AWS_ACCESS_KEY_ID'] = 'KEY' + os.environ['AWS_SECRET_ACCESS_KEY'] = 'SECRET' -assert config.aws.access_key_id() == 'KEY' -assert config.aws.secret_access_key() == 'SECRET' -assert config.optional() == 'default_value' + container.config.aws.access_key_id.from_env('AWS_ACCESS_KEY_ID') + container.config.aws.secret_access_key.from_env('AWS_SECRET_ACCESS_KEY') + container.config.optional.from_env('UNDEFINED', 'default_value') + + assert container.config.aws.access_key_id() == 'KEY' + assert container.config.aws.secret_access_key() == 'SECRET' + assert container.config.optional() == 'default_value' diff --git a/examples/providers/configuration/configuration_ini.py b/examples/providers/configuration/configuration_ini.py index 0533a8ae..98783bd0 100644 --- a/examples/providers/configuration/configuration_ini.py +++ b/examples/providers/configuration/configuration_ini.py @@ -1,13 +1,27 @@ """`Configuration` provider values loading example.""" -from dependency_injector import providers +from dependency_injector import containers, providers -config = providers.Configuration() +class Container(containers.DeclarativeContainer): -config.from_ini('examples/providers/configuration/config.ini') + config = providers.Configuration() -assert config() == {'aws': {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'}} -assert config.aws() == {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'} -assert config.aws.access_key_id() == 'KEY' -assert config.aws.secret_access_key() == 'SECRET' + +if __name__ == '__main__': + container = Container() + + container.config.from_ini('examples/providers/configuration/config.ini') + + assert container.config() == { + 'aws': { + 'access_key_id': 'KEY', + 'secret_access_key': 'SECRET', + }, + } + assert container.config.aws() == { + 'access_key_id': 'KEY', + 'secret_access_key': 'SECRET', + } + assert container.config.aws.access_key_id() == 'KEY' + assert container.config.aws.secret_access_key() == 'SECRET' diff --git a/examples/providers/configuration/configuration_itemselector.py b/examples/providers/configuration/configuration_itemselector.py index fd60a616..eb257843 100644 --- a/examples/providers/configuration/configuration_itemselector.py +++ b/examples/providers/configuration/configuration_itemselector.py @@ -1,11 +1,8 @@ -"""`Configuration` provider dynamic item selector. - -Details: https://github.com/ets-labs/python-dependency-injector/issues/274 -""" +"""`Configuration` provider dynamic item selector.""" import dataclasses -from dependency_injector import providers +from dependency_injector import containers, providers @dataclasses.dataclass @@ -14,34 +11,37 @@ class Foo: option2: object -config = providers.Configuration(default={ - 'target': 'A', - 'items': { - 'A': { - 'option1': 60, - 'option2': 80, - }, - 'B': { - 'option1': 10, - 'option2': 20, - }, - }, -}) +class Container(containers.DeclarativeContainer): -foo = providers.Factory( - Foo, - option1=config.items[config.target].option1, - option2=config.items[config.target].option2, -) + config = providers.Configuration(default={ + 'target': 'A', + 'items': { + 'A': { + 'option1': 60, + 'option2': 80, + }, + 'B': { + 'option1': 10, + 'option2': 20, + }, + }, + }) + + foo_factory = providers.Factory( + Foo, + option1=config.items[config.target].option1, + option2=config.items[config.target].option2, + ) if __name__ == '__main__': - config.target.from_env('TARGET') - f = foo() - print(f.option1, f.option2) + container = Container() + container.config.target.from_env('TARGET') + foo = container.foo_factory() + print(foo.option1, foo.option2) -# $ TARGET=A python configuration_itemselector.py -# 60 80 -# $ TARGET=B python configuration_itemselector.py -# 10 20 + # $ TARGET=A python configuration_itemselector.py + # 60 80 + # $ TARGET=B python configuration_itemselector.py + # 10 20 diff --git a/examples/providers/configuration/configuration_multiple.py b/examples/providers/configuration/configuration_multiple.py index 4b85c87b..ac95b3e2 100644 --- a/examples/providers/configuration/configuration_multiple.py +++ b/examples/providers/configuration/configuration_multiple.py @@ -1,14 +1,28 @@ """`Configuration` provider values loading example.""" -from dependency_injector import providers +from dependency_injector import containers, providers -config = providers.Configuration() +class Container(containers.DeclarativeContainer): -config.from_yaml('examples/providers/configuration/config.yml') -config.from_yaml('examples/providers/configuration/config.local.yml') + config = providers.Configuration() -assert config() == {'aws': {'access_key_id': 'LOCAL-KEY', 'secret_access_key': 'LOCAL-SECRET'}} -assert config.aws() == {'access_key_id': 'LOCAL-KEY', 'secret_access_key': 'LOCAL-SECRET'} -assert config.aws.access_key_id() == 'LOCAL-KEY' -assert config.aws.secret_access_key() == 'LOCAL-SECRET' + +if __name__ == '__main__': + container = Container() + + container.config.from_yaml('examples/providers/configuration/config.yml') + container.config.from_yaml('examples/providers/configuration/config.local.yml') + + assert container.config() == { + 'aws': { + 'access_key_id': 'LOCAL-KEY', + 'secret_access_key': 'LOCAL-SECRET', + }, + } + assert container.config.aws() == { + 'access_key_id': 'LOCAL-KEY', + 'secret_access_key': 'LOCAL-SECRET', + } + assert container.config.aws.access_key_id() == 'LOCAL-KEY' + assert container.config.aws.secret_access_key() == 'LOCAL-SECRET' diff --git a/examples/providers/configuration/configuration_type.py b/examples/providers/configuration/configuration_type.py index 213f9f79..8c8e044d 100644 --- a/examples/providers/configuration/configuration_type.py +++ b/examples/providers/configuration/configuration_type.py @@ -2,7 +2,7 @@ import os -from dependency_injector import providers +from dependency_injector import containers, providers class ApiClient: @@ -11,24 +11,28 @@ class ApiClient: self.timeout = timeout -config = providers.Configuration() +class Container(containers.DeclarativeContainer): -api_client_factory = providers.Factory( - ApiClient, - api_key=config.api.key, - timeout=config.api.timeout.as_int(), -) + config = providers.Configuration() + + api_client_factory = providers.Factory( + ApiClient, + api_key=config.api.key, + timeout=config.api.timeout.as_int(), + ) if __name__ == '__main__': + container = Container() + # Emulate environment variables os.environ['API_KEY'] = 'secret' os.environ['API_TIMEOUT'] = '5' - config.api.key.from_env('API_KEY') - config.api.timeout.from_env('API_TIMEOUT') + container.config.api.key.from_env('API_KEY') + container.config.api.timeout.from_env('API_TIMEOUT') - api_client = api_client_factory() + api_client = container.api_client_factory() assert api_client.api_key == 'secret' assert api_client.timeout == 5 diff --git a/examples/providers/configuration/configuration_type_custom.py b/examples/providers/configuration/configuration_type_custom.py index 9b53dafe..6782ab52 100644 --- a/examples/providers/configuration/configuration_type_custom.py +++ b/examples/providers/configuration/configuration_type_custom.py @@ -3,7 +3,7 @@ import os import decimal -from dependency_injector import providers +from dependency_injector import containers, providers class Calculator: @@ -11,20 +11,24 @@ class Calculator: self.pi = pi -config = providers.Configuration() +class Container(containers.DeclarativeContainer): -calculator_factory = providers.Factory( - Calculator, - pi=config.pi.as_(decimal.Decimal), -) + config = providers.Configuration() + + calculator_factory = providers.Factory( + Calculator, + pi=config.pi.as_(decimal.Decimal), + ) if __name__ == '__main__': + container = Container() + # Emulate environment variables os.environ['PI'] = '3.1415926535897932384626433832' - config.pi.from_env('PI') + container.config.pi.from_env('PI') - calculator = calculator_factory() + calculator = container.calculator_factory() assert calculator.pi == decimal.Decimal('3.1415926535897932384626433832') diff --git a/examples/providers/configuration/configuration_yaml.py b/examples/providers/configuration/configuration_yaml.py index 842b4ae8..d281aaf0 100644 --- a/examples/providers/configuration/configuration_yaml.py +++ b/examples/providers/configuration/configuration_yaml.py @@ -1,13 +1,27 @@ """`Configuration` provider values loading example.""" -from dependency_injector import providers +from dependency_injector import containers, providers -config = providers.Configuration() +class Container(containers.DeclarativeContainer): -config.from_yaml('examples/providers/configuration/config.yml') + config = providers.Configuration() -assert config() == {'aws': {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'}} -assert config.aws() == {'access_key_id': 'KEY', 'secret_access_key': 'SECRET'} -assert config.aws.access_key_id() == 'KEY' -assert config.aws.secret_access_key() == 'SECRET' + +if __name__ == '__main__': + container = Container() + + container.config.from_yaml('examples/providers/configuration/config.yml') + + assert container.config() == { + 'aws': { + 'access_key_id': 'KEY', + 'secret_access_key': 'SECRET', + }, + } + assert container.config.aws() == { + 'access_key_id': 'KEY', + 'secret_access_key': 'SECRET', + } + assert container.config.aws.access_key_id() == 'KEY' + assert container.config.aws.secret_access_key() == 'SECRET' From d61281a0b92aaedf32a2015437a84204d8ea965f Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 17:38:52 -0400 Subject: [PATCH 14/21] Add usage of the container to the selector example --- docs/providers/selector.rst | 2 +- examples/providers/selector.py | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/docs/providers/selector.rst b/docs/providers/selector.rst index 6326e051..db3a8be3 100644 --- a/docs/providers/selector.rst +++ b/docs/providers/selector.rst @@ -17,7 +17,7 @@ Selector provider .. literalinclude:: ../../examples/providers/selector.py :language: python :lines: 3- - :emphasize-lines: 14-18 + :emphasize-lines: 16-20 The first argument of the ``Selector`` provider is called ``selector``. It can be an option of a ``Configuration`` provider or any other callable. The ``selector`` callable has to return a diff --git a/examples/providers/selector.py b/examples/providers/selector.py index 7c7c17cd..88166ec4 100644 --- a/examples/providers/selector.py +++ b/examples/providers/selector.py @@ -1,6 +1,6 @@ """`Selector` provider example.""" -from dependency_injector import providers +from dependency_injector import containers, providers class SomeClass: @@ -11,19 +11,24 @@ class SomeOtherClass: ... -config = providers.Configuration() +class Container(containers.DeclarativeContainer): + + config = providers.Configuration() + + selector = providers.Selector( + config.one_or_another, + one=providers.Factory(SomeClass), + another=providers.Factory(SomeOtherClass), + ) -selector = providers.Selector( - config.one_or_another, - one=providers.Factory(SomeClass), - another=providers.Factory(SomeOtherClass), -) if __name__ == '__main__': - config.override({'one_or_another': 'one'}) - instance_1 = selector() + container = Container() + + container.config.override({'one_or_another': 'one'}) + instance_1 = container.selector() assert isinstance(instance_1, SomeClass) - config.override({'one_or_another': 'another'}) - instance_2 = selector() + container.config.override({'one_or_another': 'another'}) + instance_2 = container.selector() assert isinstance(instance_2, SomeOtherClass) From 4f111aae9b0b000d1bc1d2ab01d6052297ca4d1c Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 17:42:15 -0400 Subject: [PATCH 15/21] Update provider overriding example to use container and fix bug --- examples/providers/overriding.py | 36 +++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/examples/providers/overriding.py b/examples/providers/overriding.py index 42c01de5..9627667c 100644 --- a/examples/providers/overriding.py +++ b/examples/providers/overriding.py @@ -1,8 +1,9 @@ """Simple providers overriding example.""" +import dataclasses import unittest.mock -from dependency_injector import providers +from dependency_injector import containers, providers class ApiClient: @@ -13,30 +14,35 @@ class ApiClientStub(ApiClient): ... +@dataclasses.dataclass class Service: - def __init__(self, api_client: ApiClient): - self._api_client = api_client + api_client: ApiClient -api_client_factory = providers.Factory(ApiClient) -service_factory = providers.Factory( - Service, - api_client=api_client_factory, -) +class Container(containers.DeclarativeContainer): + + api_client_factory = providers.Factory(ApiClient) + + service_factory = providers.Factory( + Service, + api_client=api_client_factory, + ) if __name__ == '__main__': + container = Container() + # 1. Use .override() to replace the API client with stub - api_client_factory.override(providers.Factory(ApiClientStub)) - service1 = service_factory() + container.api_client_factory.override(providers.Factory(ApiClientStub)) + service1 = container.service_factory() assert isinstance(service1.api_client, ApiClientStub) # 2. Use .override() as a context manager to mock the API client in testing - with api_client_factory.override(unittest.mock.Mock(ApiClient)): - service3 = service_factory() - assert isinstance(service3.api_client, unittest.mock.Mock) + with container.api_client_factory.override(unittest.mock.Mock(ApiClient)): + service2 = container.service_factory() + assert isinstance(service2.api_client, unittest.mock.Mock) # 3. Use .reset_override() to get back to normal - api_client_factory.reset_override() - service3 = service_factory() + container.api_client_factory.reset_override() + service3 = container.service_factory() assert isinstance(service3.api_client, ApiClient) From d6e4e8fb089a18681c0580d04ef8f755ae6bb9e6 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 17:48:45 -0400 Subject: [PATCH 16/21] Add container usage to provided instance examples --- docs/providers/provided_instance.rst | 4 +- examples/providers/provided_instance.py | 24 ++++++----- .../providers/provided_instance_complex.py | 40 ++++++++++--------- src/dependency_injector/providers.pyi | 4 +- 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/docs/providers/provided_instance.rst b/docs/providers/provided_instance.rst index a53035cf..0bb7d664 100644 --- a/docs/providers/provided_instance.rst +++ b/docs/providers/provided_instance.rst @@ -14,7 +14,7 @@ You can inject provided object attribute, item or result of its method call. .. literalinclude:: ../../examples/providers/provided_instance.py :language: python - :emphasize-lines: 26-32 + :emphasize-lines: 28-34 :lines: 3- To use the feature you should use the ``.provided`` attribute of the injected provider. This @@ -32,7 +32,7 @@ You can do nested constructions: .. literalinclude:: ../../examples/providers/provided_instance_complex.py :language: python - :emphasize-lines: 24-30 + :emphasize-lines: 26-32 :lines: 3- The ``.provided`` attribute is available for the next providers: diff --git a/examples/providers/provided_instance.py b/examples/providers/provided_instance.py index e61f9bb7..bf63bc6f 100644 --- a/examples/providers/provided_instance.py +++ b/examples/providers/provided_instance.py @@ -1,6 +1,6 @@ """Example of the injecting of provided instance attributes and items.""" -from dependency_injector import providers +from dependency_injector import containers, providers class Service: @@ -23,17 +23,21 @@ class Client: self.value4 = value4 -service = providers.Singleton(Service) +class Container(containers.DeclarativeContainer): -client_factory = providers.Factory( - Client, - value1=service.provided[0], - value2=service.provided.value, - value3=service.provided.values[0], - value4=service.provided.get_value.call(), -) + service = providers.Singleton(Service) + + client_factory = providers.Factory( + Client, + value1=service.provided[0], + value2=service.provided.value, + value3=service.provided.values[0], + value4=service.provided.get_value.call(), + ) if __name__ == '__main__': - client = client_factory() + container = Container() + + client = container.client_factory() assert client.value1 == client.value2 == client.value3 == 'foo' diff --git a/examples/providers/provided_instance_complex.py b/examples/providers/provided_instance_complex.py index 20381f9b..108e1ebf 100644 --- a/examples/providers/provided_instance_complex.py +++ b/examples/providers/provided_instance_complex.py @@ -1,6 +1,6 @@ """Complex example of the injecting of provided instance attributes and items.""" -from dependency_injector import providers +from dependency_injector import containers, providers class Service: @@ -12,31 +12,35 @@ class Service: return self.value -service = providers.Singleton(Service, value=42) +class Container(containers.DeclarativeContainer): -dependency = providers.Object( - { - 'foo': { - 'bar': 10, - 'baz': lambda arg: {'arg': arg} + service = providers.Singleton(Service, value=42) + + dependency = providers.Object( + { + 'foo': { + 'bar': 10, + 'baz': lambda arg: {'arg': arg} + }, }, - }, -) + ) -demo_list = providers.List( - dependency.provided['foo']['bar'], - dependency.provided['foo']['baz'].call(22)['arg'], - dependency.provided['foo']['baz'].call(service)['arg'], - dependency.provided['foo']['baz'].call(service)['arg'].value, - dependency.provided['foo']['baz'].call(service)['arg'].get_value.call(), -) + demo_list = providers.List( + dependency.provided['foo']['bar'], + dependency.provided['foo']['baz'].call(22)['arg'], + dependency.provided['foo']['baz'].call(service)['arg'], + dependency.provided['foo']['baz'].call(service)['arg'].value, + dependency.provided['foo']['baz'].call(service)['arg'].get_value.call(), + ) if __name__ == '__main__': - assert demo_list() == [ + container = Container() + + assert container.demo_list() == [ 10, 22, - service(), + container.service(), 42, 42, ] diff --git a/src/dependency_injector/providers.pyi b/src/dependency_injector/providers.pyi index c8d97a70..53052c76 100644 --- a/src/dependency_injector/providers.pyi +++ b/src/dependency_injector/providers.pyi @@ -283,8 +283,8 @@ class Selector(Provider): class ProvidedInstanceFluentInterface: - def __getattr__(self, item: str) -> AttributeGetter: ... - def __getitem__(self, item: str) -> ItemGetter: ... + def __getattr__(self, item: Any) -> AttributeGetter: ... + def __getitem__(self, item: Any) -> ItemGetter: ... def call(self, *args: Injection, **kwargs: Injection) -> MethodCaller: ... From ad260fe709ae5d98ef4980aa4f3b1ad883a69611 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 17:56:54 -0400 Subject: [PATCH 17/21] Add container usage to the custom provider example --- examples/providers/custom_factory.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/providers/custom_factory.py b/examples/providers/custom_factory.py index 706ed04c..c73ab041 100644 --- a/examples/providers/custom_factory.py +++ b/examples/providers/custom_factory.py @@ -1,6 +1,6 @@ """Custom provider example.""" -from dependency_injector import providers +from dependency_injector import containers, providers class CustomFactory(providers.Provider): @@ -29,14 +29,18 @@ class CustomFactory(providers.Provider): return self._factory(*args, **kwargs) -factory = CustomFactory(object) +class Container(containers.DeclarativeContainer): + + factory = CustomFactory(object) if __name__ == '__main__': - object1 = factory() + container = Container() + + object1 = container.factory() assert isinstance(object1, object) - object2 = factory() + object2 = container.factory() assert isinstance(object1, object) assert object1 is not object2 From 441cc66427c244e1a4a2e2239b465cdb7668b255 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 17:57:26 -0400 Subject: [PATCH 18/21] Improve singleton provider docs --- docs/providers/singleton.rst | 7 ++++++ .../singleton_multiple_containers.py | 24 +++++++++++++++++++ examples/providers/singleton_resetting.py | 4 ++-- 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 examples/providers/singleton_multiple_containers.py diff --git a/docs/providers/singleton.rst b/docs/providers/singleton.rst index c42b6a3e..6624777f 100644 --- a/docs/providers/singleton.rst +++ b/docs/providers/singleton.rst @@ -32,6 +32,13 @@ factories: - :ref:`factory-specialize-provided-type` - :ref:`abstract-factory` +``Singleton`` provider scope is tied to the container. Two different containers will provider +two different singleton objects: + +.. literalinclude:: ../../examples/providers/singleton_multiple_containers.py + :language: python + :lines: 3- + Resetting memorized object -------------------------- diff --git a/examples/providers/singleton_multiple_containers.py b/examples/providers/singleton_multiple_containers.py new file mode 100644 index 00000000..7b5e4fab --- /dev/null +++ b/examples/providers/singleton_multiple_containers.py @@ -0,0 +1,24 @@ +"""`Singleton` provider resetting example.""" + +from dependency_injector import containers, providers + + +class UserService: + ... + + +class Container(containers.DeclarativeContainer): + + user_service_provider = providers.Singleton(UserService) + + +if __name__ == '__main__': + container1 = Container() + user_service1 = container1.user_service_provider() + assert user_service1 is container1.user_service_provider() + + container2 = Container() + user_service2 = container2.user_service_provider() + assert user_service2 is container2.user_service_provider() + + assert user_service1 is not user_service2 diff --git a/examples/providers/singleton_resetting.py b/examples/providers/singleton_resetting.py index 7188744b..48e3cadf 100644 --- a/examples/providers/singleton_resetting.py +++ b/examples/providers/singleton_resetting.py @@ -19,5 +19,5 @@ if __name__ == '__main__': container.user_service_provider.reset() - users_service2 = container.user_service_provider() - assert users_service2 is not user_service1 + user_service2 = container.user_service_provider() + assert user_service2 is not user_service1 From 913ce01475d4583288cced9d8cf4a549278fdf9e Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 18:03:21 -0400 Subject: [PATCH 19/21] Update key features --- docs/index.rst | 4 ++-- docs/introduction/key_features.rst | 12 ++---------- docs/main/changelog.rst | 1 + 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 409df690..e1eb64c7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -77,8 +77,8 @@ Key features of the ``Dependency Injector``: - **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables and dictionaries. See :ref:`configuration-provider`. - **Containers**. Provides declarative and dynamic containers. See :ref:`containers`. -- **Performance**. Written in ``Cython``. -- **Maturity**. Mature and ready for production. +- **Performance**. Fast. Written in ``Cython``. +- **Maturity**. Mature and production-ready. Well-tested, documented and supported. .. code-block:: python diff --git a/docs/introduction/key_features.rst b/docs/introduction/key_features.rst index 21e97331..3990e08c 100644 --- a/docs/introduction/key_features.rst +++ b/docs/introduction/key_features.rst @@ -17,8 +17,8 @@ Key features of the ``Dependency Injector``: - **Configuration**. Read configuration from ``yaml`` & ``ini`` files, environment variables and dictionaries. See :ref:`configuration-provider`. - **Containers**. Provides declarative and dynamic containers. See :ref:`containers`. -- **Performance**. Written in ``Cython``. -- **Maturity**. Mature and ready for production. +- **Performance**. Fast. Written in ``Cython``. +- **Maturity**. Mature and production-ready. Well-tested, documented and supported. The framework stands on two principles: @@ -32,12 +32,4 @@ How is that different from the other frameworks? The power of the framework is in a simplicity. ``Dependency Injector`` is a simple tool for the powerful concept. -In addition ``Dependency Injector`` is: - -- Tested. -- Documented. -- Supported. -- Semantically versioned. -- Distributed as pre-compiled wheels. - .. disqus:: diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index 0e8b3c58..a13aefff 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -10,6 +10,7 @@ follows `Semantic versioning`_ Development version ------------------- - Update index documentation page. +- Make multiple improvements and fixes for the providers documentation. - Update "Key Features" documentation page. - Remove "Structure of Dependency Injector" documentation page. - Edit "Feedback" documentation page. From b5b6c1f6800ca8182756f9dfb8bddfcc4979c239 Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 18:05:28 -0400 Subject: [PATCH 20/21] Fix flake8 warning in factory provided type example --- examples/providers/factory_provided_type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/providers/factory_provided_type.py b/examples/providers/factory_provided_type.py index bf4004ce..bcf80b71 100644 --- a/examples/providers/factory_provided_type.py +++ b/examples/providers/factory_provided_type.py @@ -17,7 +17,7 @@ class ServiceProvider(providers.Factory): # Creating service provider with a correct provided type: -class Container(containers.DeclarativeContainer): +class Services(containers.DeclarativeContainer): some_service_provider = ServiceProvider(SomeService) From 5f7d9780122d26bd59e1f95c2e1249689a25328c Mon Sep 17 00:00:00 2001 From: Roman Mogylatov Date: Thu, 3 Sep 2020 18:05:45 -0400 Subject: [PATCH 21/21] Bump version to 3.37.0 --- docs/main/changelog.rst | 4 ++-- src/dependency_injector/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index a13aefff..3f5a0db0 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -7,8 +7,8 @@ that were made in every particular version. From version 0.7.6 *Dependency Injector* framework strictly follows `Semantic versioning`_ -Development version -------------------- +3.37.0 +------ - Update index documentation page. - Make multiple improvements and fixes for the providers documentation. - Update "Key Features" documentation page. diff --git a/src/dependency_injector/__init__.py b/src/dependency_injector/__init__.py index b1aa8bb1..177dd55b 100644 --- a/src/dependency_injector/__init__.py +++ b/src/dependency_injector/__init__.py @@ -1,6 +1,6 @@ """Dependency injector top-level package.""" -__version__ = '3.36.0' +__version__ = '3.37.0' """Version number that follows semantic versioning. :type: str