diff --git a/README.rst b/README.rst index 264553e5..a7347028 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 implement the dependency injection principle. +It helps you in implementing the dependency injection principle. What is dependency injection? ----------------------------- @@ -70,6 +70,9 @@ Before: .. code-block:: python + import os + + class ApiClient: def __init__(self): @@ -82,10 +85,18 @@ Before: def __init__(self): self.api_client = ApiClient() + + if __name__ == '__main__': + service = Service() + + After: .. code-block:: python + import os + + class ApiClient: def __init__(self, api_key: str, timeout: int): @@ -98,48 +109,82 @@ After: def __init__(self, api_client: ApiClient): self.api_client = api_client -Who creates the objects now? Look at the next section. + + if __name__ == '__main__': + service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT'))) + + +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. What does Dependency Injector do? --------------------------------- -``Dependency Injector`` provides you with the container and the providers that help you build -your application objects when you apply dependency injection principle: +``Dependency Injector`` helps you 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 - from unittest import mock - - from .example_di import ApiClient, Service + from dependency_injector import containers, providers - class Container(containers.DeclarativeContainer): + class ApiClient: - config = providers.Configuration() - - api_client = providers.Singleton( - ApiClient, - api_key=config.api_key, - timeout=config.timeout, - ) - - service = providers.Factory( - Service, - api_client=api_client, - ) + def __init__(self, api_key: str, timeout: int): + self.api_key = api_key + self.timeout = timeout - if __name__ == '__main__': - container = Container() - container.config.from_yaml('config.yml') + class Service: - service = container.service() - assert isinstance(service.api_client, ApiClient) + def __init__(self, api_client: ApiClient): + self.api_client = api_client - with container.api_client.override(mock.Mock()): - service = container.service() - assert isinstance(service.api_client, mock.Mock) + + class Container(containers.DeclarativeContainer): + + config = providers.Configuration() + + api_client = providers.Singleton( + ApiClient, + api_key=config.api_key, + timeout=config.timeout, + ) + + 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() + assert isinstance(service.api_client, ApiClient) + +Retrieving of the ``Service`` instance now is done like this ``container.service()``. + +Also ``Dependency Injector`` provides a bonus in overriding any of the providers with the +``.override()`` method: + +.. code-block:: python + + from unittest import mock + + + with container.api_client.override(mock.Mock()): + service = container.service() + assert isinstance(service.api_client, mock.Mock) + +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. `More examples `_ diff --git a/examples/di_demo2/config.yml b/examples/di_demo2/config.yml deleted file mode 100644 index b6f36405..00000000 --- a/examples/di_demo2/config.yml +++ /dev/null @@ -1,2 +0,0 @@ -api_key: test-key -timeout: 5 \ No newline at end of file diff --git a/examples/di_demo2/demo.py b/examples/di_demo2/demo.py index d0e2cb9a..02d49f09 100644 --- a/examples/di_demo2/demo.py +++ b/examples/di_demo2/demo.py @@ -1,7 +1,17 @@ from dependency_injector import containers, providers -from unittest import mock -from .example_di import ApiClient, Service + +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): @@ -22,11 +32,8 @@ class Container(containers.DeclarativeContainer): if __name__ == '__main__': container = Container() - container.config.from_yaml('config.yml') + container.config.api_key.from_env('API_KEY') + container.config.timeout.from_env('TIMEOUT') service = container.service() assert isinstance(service.api_client, ApiClient) - - with container.api_client.override(mock.Mock()): - service = container.service() - assert isinstance(service.api_client, mock.Mock) diff --git a/examples/di_demo2/example_di.py b/examples/di_demo2/example_di.py index 2a4bb423..db85c237 100644 --- a/examples/di_demo2/example_di.py +++ b/examples/di_demo2/example_di.py @@ -1,3 +1,4 @@ +import os class ApiClient: @@ -11,3 +12,7 @@ class Service: def __init__(self, api_client: ApiClient): self.api_client = api_client + + +if __name__ == '__main__': + service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT'))) diff --git a/examples/di_demo2/example_no_di.py b/examples/di_demo2/example_no_di.py index 28ca034e..c567ebd6 100644 --- a/examples/di_demo2/example_no_di.py +++ b/examples/di_demo2/example_no_di.py @@ -12,3 +12,7 @@ class Service: def __init__(self): self.api_client = ApiClient() + + +if __name__ == '__main__': + service = Service() diff --git a/examples/di_demo2/test.py b/examples/di_demo2/test.py new file mode 100644 index 00000000..56310989 --- /dev/null +++ b/examples/di_demo2/test.py @@ -0,0 +1,11 @@ +from unittest import mock + +from demo import Container + + +if __name__ == '__main__': + container = Container() + + with container.api_client.override(mock.Mock()): + service = container.service() + assert isinstance(service.api_client, mock.Mock)