diff --git a/README.rst b/README.rst index e4e7b3e2..91397b28 100644 --- a/README.rst +++ b/README.rst @@ -66,11 +66,11 @@ Coupling and cohesion are about how tough the components are tied. - **High coupling**. If the coupling is high it's like using a superglue or welding. No easy way to disassemble. - **High cohesion**. High cohesion is like using the screws. Very easy to disassemble and - assemble back or assemble a different way. It is an alternative to high coupling. + assemble back or assemble a different way. It is an opposite to high coupling. When the cohesion is high the coupling is low. -High cohesion brings the flexibility. Your code becomes easier to change and test. +Low coupling brings a flexibility. Your code becomes easier to change and test. How to implement dependency injection? -------------------------------------- @@ -124,7 +124,6 @@ After: if __name__ == '__main__': service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT'))) - ``ApiClient`` is decoupled from knowing where the options come from. You can read a key and a timeout from a configuration file or even get them from a database. @@ -133,16 +132,23 @@ stub or other compatible object. Flexibility comes with a price. -Now you need to assemble your objects like this -``Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))``. The assembly code might get -duplicated and it'll become harder to change the application structure. +Now you need to assemble the objects like this:: -What does Dependency Injector do? ---------------------------------- + service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT'))) + +The assembly code might get duplicated and it'll become harder to change the application structure. + +Here comes the ``Dependency Injector``. + +What does the Dependency Injector do? +------------------------------------- + +With the dependency injection pattern objects lose the responsibility of assembling the +dependencies. The ``Dependency Injector`` absorbs that responsibility. ``Dependency Injector`` helps to assemble the objects. -It provides you the container and the providers that help you describe objects assembly. When you +It provides a container and providers that help you with the objects assembly. When you need an object you get it from the container. The rest of the assembly work is done by the framework: @@ -151,19 +157,6 @@ framework: from dependency_injector import containers, providers - class ApiClient: - - def __init__(self, api_key: str, timeout: int): - self.api_key = api_key - self.timeout = timeout - - - class Service: - - def __init__(self, api_client: ApiClient): - self.api_client = api_client - - class Container(containers.DeclarativeContainer): config = providers.Configuration() @@ -187,12 +180,14 @@ framework: service = container.service() -Retrieving of the ``Service`` instance now is done like this ``container.service()``. +Retrieving of the ``Service`` instance now is done like this:: + + service = container.service() Objects assembling is consolidated in the container. When you need to make a change you do it in one place. -When doing the testing you call the ``container.api_client.override()`` to replace the real API +When doing a testing you call the ``container.api_client.override()`` to replace the real API client with a mock: .. code-block:: python @@ -203,10 +198,10 @@ client with a mock: with container.api_client.override(mock.Mock()): service = container.service() -It helps in a testing. Also you can use it for configuring project for the different environments: -replace an API client with a stub on the dev or stage. +You can override any provider by another provider. -`More examples `_ +It also helps you in configuring project for the different environments: replace an API client +with a stub on the dev or stage. Installation ------------ diff --git a/docs/introduction/di_in_python.rst b/docs/introduction/di_in_python.rst index 7802d7e3..7822e964 100644 --- a/docs/introduction/di_in_python.rst +++ b/docs/introduction/di_in_python.rst @@ -1,137 +1,256 @@ Dependency injection and inversion of control in Python -------------------------------------------------------- +======================================================= .. meta:: - :keywords: Python,DI,Dependency injection,IoC,Inversion of Control - :description: This article describes benefits of dependency injection and - inversion of control for Python applications. Also it - contains some Python examples that show how dependency - injection and inversion could be implemented. In addition, it - demonstrates usage of dependency injection framework, - IoC container and such popular design pattern as Factory. + :keywords: Python,DI,Dependency injection,IoC,Inversion of Control,Example + :description: This page describes a usage of the dependency injection and inversion of control + in Python. It contains Python examples that show how to implement dependency + injection. It demonstrates a usage of the dependency injection framework + Dependency Injector, its container, Factory, Singleton and Configuration + providers. The example show how to use Dependency Injector providers overriding + feature for testing or configuring project in different environments and explains + why it's better then monkey-patching. -History -~~~~~~~ +Originally dependency injection pattern got popular in the languages with a static typing, +like Java. Dependency injection is a principle that helps to achieve an inversion of control. +Dependency injection framework can significantly improve a flexibility of the language +with a static typing. Implementation of a dependency injection framework for a language +with a static typing is not something that one can do quickly. It will be a quite complex thing +to be done well. And will take time. -Originally, dependency injection pattern got popular in languages with static -typing, like Java. Dependency injection framework can -significantly improve flexibility of the language with static typing. Also, -implementation of dependency injection framework for language with static -typing is not something that one can do shortly, it could be quite complex -thing to be done well. +Python is an interpreted language with a dynamic typing. There is an opinion that dependency +injection doesn't work for it as well as it does for Java. A lot of the flexibility is already +built in. Also there is an opinion that a dependency injection framework is something that +Python developer rarely needs. Python developers say that dependency injection can be implemented +easily using language fundamentals. -While Python is very flexible interpreted language with dynamic typing, there -is a meaning that dependency injection doesn't work for it as well, as it does -for Java. Also there is a meaning that dependency injection framework is -something that Python developer would not ever need, cause dependency injection -could be implemented easily using language fundamentals. +This page describes the advantages of the dependency injection usage in Python. It +contains Python examples that show how to implement dependency injection. It demonstrates a usage +of the dependency injection framework ``Dependency Injector``, its container, ``Factory``, +``Singleton`` and ``Configuration`` providers. The example shows how to use ``Dependency Injector`` +providers overriding feature for testing or configuring project in different environments and +explains why it's better then monkey-patching. -Discussion -~~~~~~~~~~ +What is dependency injection? +----------------------------- -It is true. +Let's see what the dependency injection is. -Partly. +Dependency injection is a principle that helps to decrease coupling and increase cohesion. -Dependency injection, as a software design pattern, has number of -advantages that are common for each language (including Python): +.. image:: images/coupling-cohesion.png -+ Dependency Injection decreases coupling between a class and its dependency. -+ Because dependency injection doesn't require any change in code behavior it - can be applied to legacy code as a refactoring. The result is clients that - are more independent and that are easier to unit test in isolation using - stubs or mock objects that simulate other objects not under test. This ease - of testing is often the first benefit noticed when using dependency - injection. -+ Dependency injection can be used to externalize a system's configuration - details into configuration files allowing the system to be reconfigured - without recompilation (rebuilding). Separate configurations can be written - for different situations that require different implementations of - components. This includes, but is not limited to, testing. -+ Reduction of boilerplate code in the application objects since all work to - initialize or set up dependencies is handled by a provider component. -+ Dependency injection allows a client to remove all knowledge of a concrete - implementation that it needs to use. This helps isolate the client from the - impact of design changes and defects. It promotes reusability, testability - and maintainability. -+ Dependency injection allows a client the flexibility of being configurable. - Only the client's behavior is fixed. The client may act on anything that - supports the intrinsic interface the client expects. +What is coupling and cohesion? -.. note:: +Coupling and cohesion are about how tough the components are tied. - While improved testability is one the first benefits of using dependency - injection, it could be easily overwhelmed by monkey-patching technique, - that works absolutely great in Python (you can monkey-patch anything, - anytime). At the same time, monkey-patching has nothing similar with - other advantages defined above. Also monkey-patching technique is - something that could be considered like too dirty to be used in production. +- **High coupling**. If the coupling is high it's like using a superglue or welding. No easy way + to disassemble. +- **High cohesion**. High cohesion is like using the screws. Very easy to disassemble and + assemble back or assemble a different way. It is an opposite to high coupling. -The complexity of dependency injection pattern implementation in Python is -definitely quite lower than in other languages (even with dynamic typing). +When the cohesion is high the coupling is low. -.. note:: +Low coupling brings a flexibility. Your code becomes easier to change and test. - Low complexity of dependency injection pattern implementation in Python - still means that some code should be written, reviewed, tested and - supported. +How to implement the dependency injection? -Talking about inversion of control, it is a software design principle that -also works for each programming language, not depending on its typing type. +Objects do not create each other anymore. They provide a way to inject the dependencies instead. -Inversion of control is used to increase modularity of the program and make -it extensible. +Before: -Main design purposes of using inversion of control are: +.. code-block:: python -+ To decouple the execution of a task from implementation. -+ To focus a module on the task it is designed for. -+ To free modules from assumptions about how other systems do what they do and - instead rely on contracts. -+ To prevent side effects when replacing a module. + import os -Example -~~~~~~~ -Let's go through next example: + class ApiClient: -.. image:: /images/miniapps/engines_cars/diagram.png - :width: 100% - :align: center + def __init__(self): + self.api_key = os.getenv('API_KEY') # <-- the dependency + self.timeout = os.getenv('TIMEOUT') # <-- the dependency -Listing of ``example.engines`` module: -.. literalinclude:: ../../examples/miniapps/engines_cars/example/engines.py - :language: python + class Service: -Listing of ``example.cars`` module: + def __init__(self): + self.api_client = ApiClient() # <-- the dependency -.. literalinclude:: ../../examples/miniapps/engines_cars/example/cars.py - :language: python -Next example demonstrates creation of several cars with different engines: + if __name__ == '__main__': + service = Service() -.. literalinclude:: ../../examples/miniapps/engines_cars/example_di.py - :language: python -While previous example demonstrates advantages of dependency injection, there -is a disadvantage demonstration as well - creation of car requires additional -code for specification of dependencies. Nevertheless, this disadvantage could -be easily avoided by using a dependency injection framework for creation of -inversion of control container (IoC container). +After: -Example of creation of several inversion of control containers (IoC containers) -using :doc:`Dependency Injector <../index>`: +.. code-block:: python -.. literalinclude:: ../../examples/miniapps/engines_cars/example_ioc_containers.py - :language: python + import os + + + class ApiClient: + + def __init__(self, api_key: str, timeout: int): + self.api_key = api_key # <-- the dependency is injected + self.timeout = timeout # <-- the dependency is injected + + + class Service: + + def __init__(self, api_client: ApiClient): + self.api_client = api_client # <-- the dependency is injected + + + if __name__ == '__main__': + service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT'))) + +``ApiClient`` is decoupled from knowing where the options come from. You can read a key and a +timeout from a configuration file or even get them from a database. + +``Service`` is decoupled from the ``ApiClient``. It does not create it anymore. You can provide a +stub or other compatible object. + +Flexibility comes with a price. + +Now you need to assemble the objects like this:: + + service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT'))) + +The assembly code might get duplicated and it'll become harder to change the application structure. + +Here comes the ``Dependency Injector``. + +What does the Dependency Injector do? +------------------------------------- + +With the dependency injection pattern objects lose the responsibility of assembling the +dependencies. The ``Dependency Injector`` absorbs that responsibility. + +``Dependency Injector`` helps to assemble the objects. + +It provides a container and providers that help you with the objects assembly. When you +need an object you get it from the container. The rest of the assembly work is done by the +framework: + +.. code-block:: python + + from dependency_injector import containers, providers + + + 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() + +Retrieving of the ``Service`` instance now is done like this:: + + service = container.service() + +Objects assembling is consolidated in the container. When you need to make a change you do it in +one place. + +When doing a testing you call the ``container.api_client.override()`` to replace the real API +client with a mock: + +.. code-block:: python + + from unittest import mock + + + with container.api_client.override(mock.Mock()): + service = container.service() + +You can override any provider by another provider. + +It also helps you in configuring project for the different environments: replace an API client +with a stub on the dev or stage. + +Testing, Monkey-patching and dependency injection +------------------------------------------------- + +The testability benefit is opposed to a monkey-patching. + +In Python you can monkey-patch +anything, anytime. The problem with a monkey-patching is that it's too fragile. The reason is that +when you monkey-patch you do something that wasn't intended to be done. You monkey-patch the +implementation details. When implementation changes the monkey-patching is broken. + +With a dependency injection you patch the interface, not an implementation. This is a way more +stable approach. + +Also monkey-patching is a way too dirty to be used outside of the testing code for +reconfiguring the project for the different environments. + +Conclusion +---------- + +Dependency injection brings you 3 advantages: + +- **Flexibility**. The components are loosely coupled. You can easily extend or change a + functionality of the system by combining the components different way. You even can do it on + the fly. +- **Testability**. Testing is easy because you can easily inject mocks instead of real objects + that use API or database, etc. +- **Clearness and maintainability**. Dependency injection helps you reveal the dependencies. + Implicit becomes explicit. And "Explicit is better than implicit" (PEP20 - The Zen of Python). + You have all the components and dependencies defined explicitly in the container. This + provides an overview and control on the application structure. It is easy to understand and + change it. + +Is it worth to use a dependency injection in Python? + +It depends on what you build. The advantages above are not too important if you use Python as a +scripting language. The picture is different when you use Python to create an application. The +larger the application the more significant is the benefit. + +Is it worth to use a framework for the dependency injection? + +The complexity of the dependency injection pattern implementation in Python is +lower than in the other languages but it's still in place. It doesn't mean you have to use a +framework but using a framework is beneficial because the framework is: + +- Already implemented +- Tested on all platforms and versions of Python +- Documented +- Supported +- Known to the other engineers + +Few advices at last: + +- **Give it a try**. Dependency injection is counter-intuitive. Our nature is that + when we need something the first thought that comes to our mind is to go and get it. Dependency + injection is just like "Wait, I need to state a need instead of getting something right now". + It's like a little investment that will pay-off later. The advice is to just give it a try for + two weeks. This time will be enough for getting your own impression. If you don't like it you + won't lose too much. +- **Common sense first**. Use a common sense when apply dependency injection. It is a good + principle, but not a silver bullet. If you do it too much you will reveal too much of the + implementation details. Experience comes with practice and time. What's next? -~~~~~~~~~~~~ +------------ Choose one of the following as a next step: -- Look at application examples: +- Look at the application examples: - :ref:`application-single-container` - :ref:`application-multiple-containers` - :ref:`decoupled-packages` @@ -140,11 +259,12 @@ Choose one of the following as a next step: - :ref:`aiohttp-tutorial` - :ref:`asyncio-daemon-tutorial` - :ref:`cli-tutorial` +- Know more about the ``Dependency Injector`` :ref:`key-features` - Know more about the :ref:`providers` - Go to the :ref:`contents` Useful links -~~~~~~~~~~~~ +------------ There are some useful links related to dependency injection design pattern that could be used for further reading: diff --git a/docs/introduction/index.rst b/docs/introduction/index.rst index 2a4c77ff..b67039df 100644 --- a/docs/introduction/index.rst +++ b/docs/introduction/index.rst @@ -3,18 +3,16 @@ Introduction .. meta:: :keywords: Python,DI,Dependency injection,IoC,Inversion of Control - :description: Current section of documentation is designed to give some - overview about dependency injection pattern, inversion of - control principle and "Dependency Injector" framework. + :description: Current section of the documentation is provides an + overview of the dependency injection, inversion of + control and Dependency Injector framework. -Current section of documentation is designed to give some overview about -dependency injection pattern, inversion of control principle and -*Dependency Injector* framework. +Current section of the documentation provides an overview of the +dependency injection, inversion of control and the ``Dependency Injector`` framework. .. toctree:: :maxdepth: 2 - what_is_di di_in_python key_features installation diff --git a/docs/introduction/key_features.rst b/docs/introduction/key_features.rst index 3990e08c..fb8ae3d9 100644 --- a/docs/introduction/key_features.rst +++ b/docs/introduction/key_features.rst @@ -1,3 +1,5 @@ +.. _key-features: + Key features ------------ diff --git a/docs/introduction/what_is_di.rst b/docs/introduction/what_is_di.rst deleted file mode 100644 index aa64c07c..00000000 --- a/docs/introduction/what_is_di.rst +++ /dev/null @@ -1,195 +0,0 @@ -What is dependency injection? ------------------------------ - -.. meta:: - :keywords: Python,DI,Dependency injection,Low coupling,High cohesion - :description: This page provides a Python example of what is dependency injection. It tells - about benefits of coupling and high cohesion. - -Dependency injection is a principle that helps to decrease coupling and increase cohesion. - -.. image:: images/coupling-cohesion.png - -What is coupling and cohesion? - -Coupling and cohesion are about how tough the components are tied. - -- **High coupling**. If the coupling is high it's like using a superglue or welding. No easy way - to disassemble. -- **High cohesion**. High cohesion is like using the screws. Very easy to disassemble and - assemble back or assemble a different way. It is an alternative to high coupling. - -When the cohesion is high the coupling is low. - -High cohesion brings the flexibility. Your code becomes easier to change and test. - -The example -~~~~~~~~~~~ - -How does dependency injection helps to achieve high cohesion? - -Objects do not create each other anymore. They provide a way to inject the dependencies instead. - -Before: - -.. code-block:: python - - import os - - - class ApiClient: - - def __init__(self): - self.api_key = os.getenv('API_KEY') # <-- the dependency - self.timeout = os.getenv('TIMEOUT') # <-- the dependency - - - class Service: - - def __init__(self): - self.api_client = ApiClient() # <-- the dependency - - - if __name__ == '__main__': - service = Service() - - -After: - -.. code-block:: python - - import os - - - class ApiClient: - - def __init__(self, api_key: str, timeout: int): - self.api_key = api_key # <-- the dependency is injected - self.timeout = timeout # <-- the dependency is injected - - - class Service: - - def __init__(self, api_client: ApiClient): - self.api_client = api_client # <-- the dependency is injected - - - if __name__ == '__main__': - service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT'))) - -``ApiClient`` is decoupled from knowing where the options come from. You can read a key and a -timeout from a configuration file or even get them from a database. - -``Service`` is decoupled from the ``ApiClient``. It does not create it anymore. You can provide a -stub or other compatible object. - -Flexibility comes with a price. - -Now you need to assemble your objects like this -``Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))``. The assembly code might get -duplicated and it'll become harder to change the application structure. - -Here comes the ``Dependency Injector``. - -``Dependency Injector`` helps to assemble the objects. - -It provides you the container and the providers that help you describe objects assembly. When you -need an object you get it from the container. The rest of the assembly work is done by the -framework: - -.. code-block:: python - - from dependency_injector import containers, providers - - - class ApiClient: - - def __init__(self, api_key: str, timeout: int): - self.api_key = api_key - self.timeout = timeout - - - class Service: - - def __init__(self, api_client: ApiClient): - self.api_client = api_client - - - 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() - -Retrieving of the ``Service`` instance now is done like this ``container.service()``. - -Objects assembling is consolidated in the container. When you need to make a change you do it in -one place. - -When doing the testing you call the ``container.api_client.override()`` to replace the real API -client with a mock: - -.. code-block:: python - - from unittest import mock - - - with container.api_client.override(mock.Mock()): - service = container.service() - -How to explain dependency injection to a 5-year-old? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Some time ago `user198313`_ posted this `question`_ on the `StackOverflow`_. - -`John Munsch`_ provided a great answer: - - *When you go and get things out of the refrigerator for yourself, you can - cause problems. You might leave the door open, you might get something - Mommy or Daddy doesn't want you to have. You might even be looking for - something we don't even have or which has expired.* - - *What you should be doing is stating a need, "I need something to drink - with lunch," and then we will make sure you have something when you sit - down to eat.* - -What's next? -~~~~~~~~~~~~ - -Choose one of the following as a next step: - -- Look at application examples: - - :ref:`application-single-container` - - :ref:`application-multiple-containers` - - :ref:`decoupled-packages` -- Pass the tutorials: - - :ref:`flask-tutorial` - - :ref:`aiohttp-tutorial` - - :ref:`asyncio-daemon-tutorial` - - :ref:`cli-tutorial` -- Know more about the :ref:`providers` -- Go to the :ref:`contents` - -.. disqus:: - -.. _StackOverflow: http://stackoverflow.com/ -.. _question: http://stackoverflow.com/questions/1638919/how-to-explain-dependency-injection-to-a-5-year-old/1639186 -.. _user198313: http://stackoverflow.com/users/198313/user198313 -.. _John Munsch: http://stackoverflow.com/users/31899/john-munsch diff --git a/docs/main/changelog.rst b/docs/main/changelog.rst index ac1f6c85..f88e33ff 100644 --- a/docs/main/changelog.rst +++ b/docs/main/changelog.rst @@ -7,6 +7,13 @@ that were made in every particular version. From version 0.7.6 *Dependency Injector* framework strictly follows `Semantic versioning`_ +3.42.0 +------ +- Update "DI in Python" documentation page. +- Delete "What is DI?" documentation page. +- Delete "engines cars" example mini app. +- Update README. + 3.41.0 ------ - Refactor "use cases" example. diff --git a/examples/miniapps/engines_cars/README.rst b/examples/miniapps/engines_cars/README.rst deleted file mode 100644 index b36b02b6..00000000 --- a/examples/miniapps/engines_cars/README.rst +++ /dev/null @@ -1,9 +0,0 @@ -Engines & Cars Dependency Injection Example -=========================================== - -Instructions for running: - -.. code-block:: bash - - python example_di.py - python example_ioc_containers.py diff --git a/examples/miniapps/engines_cars/example/__init__.py b/examples/miniapps/engines_cars/example/__init__.py deleted file mode 100644 index bfa99aa2..00000000 --- a/examples/miniapps/engines_cars/example/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Example top-level package.""" diff --git a/examples/miniapps/engines_cars/example/cars.py b/examples/miniapps/engines_cars/example/cars.py deleted file mode 100644 index c82361f1..00000000 --- a/examples/miniapps/engines_cars/example/cars.py +++ /dev/null @@ -1,9 +0,0 @@ -"""Dependency injection example, cars module.""" - - -class Car: - """Example car.""" - - def __init__(self, engine): - """Initialize instance.""" - self._engine = engine # Engine is injected diff --git a/examples/miniapps/engines_cars/example/engines.py b/examples/miniapps/engines_cars/example/engines.py deleted file mode 100644 index 191f9e88..00000000 --- a/examples/miniapps/engines_cars/example/engines.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Dependency injection example, engines module.""" - - -class Engine: - """Example engine base class. - - Engine is a heart of every car. Engine is a very common term and - could be implemented in very different ways. - """ - - -class GasolineEngine(Engine): - """Gasoline engine.""" - - -class DieselEngine(Engine): - """Diesel engine.""" - - -class ElectricEngine(Engine): - """Electric engine.""" diff --git a/examples/miniapps/engines_cars/example_di.py b/examples/miniapps/engines_cars/example_di.py deleted file mode 100644 index fe5912d8..00000000 --- a/examples/miniapps/engines_cars/example_di.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Dependency injection example, Cars & Engines.""" - -import example.cars -import example.engines - - -if __name__ == '__main__': - gasoline_car = example.cars.Car(example.engines.GasolineEngine()) - diesel_car = example.cars.Car(example.engines.DieselEngine()) - electric_car = example.cars.Car(example.engines.ElectricEngine()) diff --git a/examples/miniapps/engines_cars/example_ioc_containers.py b/examples/miniapps/engines_cars/example_ioc_containers.py deleted file mode 100644 index 7edda6d2..00000000 --- a/examples/miniapps/engines_cars/example_ioc_containers.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Dependency injection example, Cars & Engines IoC containers.""" - -import example.cars -import example.engines - -import dependency_injector.containers as containers -import dependency_injector.providers as providers - - -class Engines(containers.DeclarativeContainer): - """IoC container of engine providers.""" - - gasoline = providers.Factory(example.engines.GasolineEngine) - - diesel = providers.Factory(example.engines.DieselEngine) - - electric = providers.Factory(example.engines.ElectricEngine) - - -class Cars(containers.DeclarativeContainer): - """IoC container of car providers.""" - - gasoline = providers.Factory(example.cars.Car, - engine=Engines.gasoline) - - diesel = providers.Factory(example.cars.Car, - engine=Engines.diesel) - - electric = providers.Factory(example.cars.Car, - engine=Engines.electric) - - -if __name__ == '__main__': - gasoline_car = Cars.gasoline() - diesel_car = Cars.diesel() - electric_car = Cars.electric() diff --git a/src/dependency_injector/__init__.py b/src/dependency_injector/__init__.py index 1b7b4e85..30966826 100644 --- a/src/dependency_injector/__init__.py +++ b/src/dependency_injector/__init__.py @@ -1,6 +1,6 @@ """Dependency injector top-level package.""" -__version__ = '3.41.0' +__version__ = '3.42.0' """Version number that follows semantic versioning. :type: str