diff --git a/README.rst b/README.rst index 9604c0ee..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 in implementing the dependency injection principle. +It helps 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 f49ecc8d..00000000 Binary files a/docs/images/internals.png and /dev/null differ diff --git a/docs/index.rst b/docs/index.rst index 168ef255..e1eb64c7 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**. Fast. Written in ``Cython``. +- **Maturity**. Mature and production-ready. Well-tested, documented and supported. -- **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 c2a1756f..3990e08c 100644 --- a/docs/introduction/key_features.rst +++ b/docs/introduction/key_features.rst @@ -3,66 +3,33 @@ 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. +Key features of the ``Dependency Injector``: -``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. +- **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**. Fast. Written in ``Cython``. +- **Maturity**. Mature and production-ready. Well-tested, documented and supported. -It stands on two principles: +The framework 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 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. -``Dependency Injector`` makes a simple contract with you: - -- You tell the framework how to build you code -- 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. - -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 framework is in a simplicity. ``Dependency Injector`` 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 498ab0e3..3f5a0db0 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -7,6 +7,14 @@ that were made in every particular version. From version 0.7.6 *Dependency Injector* framework strictly follows `Semantic versioning`_ +3.37.0 +------ +- 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. + 3.36.0 ------ - Update providers overriding documentation and rework examples. 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:: diff --git a/docs/providers/configuration.rst b/docs/providers/configuration.rst index 9d049e18..496cb69d 100644 --- a/docs/providers/configuration.rst +++ b/docs/providers/configuration.rst @@ -1,3 +1,5 @@ +.. _configuration-provider: + Configuration provider ====================== @@ -14,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". @@ -27,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: @@ -47,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: @@ -81,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 ------------------------------------ @@ -92,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 --------------------------------- @@ -103,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: @@ -122,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: @@ -135,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/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/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/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/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/docs/providers/singleton.rst b/docs/providers/singleton.rst index a3cc2d4d..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 -------------------------- @@ -41,7 +48,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 +71,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/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/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) 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' 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) 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 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..bcf80b71 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 Services(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: 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'), 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 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) 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/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) 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_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 edec0594..48e3cadf 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() - assert users_service2 is not user_service1 + container.user_service_provider.reset() + + user_service2 = container.user_service_provider() + assert user_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. 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 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: ...