Metadata-Version: 1.1
Name: nameko-injector
Version: 1.1.1
Summary: Injector support in nameko
Home-page: https://github.com/signalpillar/nameko-injector
Author: Volodymyr Vitvitskyi
Author-email: UNKNOWN
License: MIT
Description: nameko-injector
        ===============
        
        `injector <https://pypi.org/project/injector/>`_ based dependency injection
        mechanism for nameko services. Project is similar to `flask-injector <https://pypi.org/project/Flask-Injector/>`_.
        
        Problem
        -------
        
        Nameko provides a dependency injection mechanism, built-in in the framework.
        It works in many cases with the limitations:
        
        1. All the dependencies are injected regardless of whether they are used in the entry-point. For instance, all the dependencies will be injected for ``/health`` HTTP entry point.
        2. Dependencies cannot depend on each other.
        3. The scope is an implementation detail. Frequency of the dependency creation depends on the ``DependencyProvider`` implementation.
        4. Subjectively, implementing a new ``DependencyProvider`` is not the easiest
           the task for the developers.
        
        Solution
        --------
        
        The library provides an alternative dependency injection mechanism to the one
        that is built-in in nameko. Several types of `request` scope can
        be used out of the box without special injector module declarations.
        
        - ``from nameko.containers.ServiceContainer``
        - ``from nameko.containers.WorkerContext``
        - ``werkzeug.wrappers.Request``, in case of HTTP requests
        
        The library provides 2 **scopes**:
        
        - ``nameko_injector.core.request_scope`` where each request has own instance of
          the injected type.
        - ``nameko_injected.core.resource_request_scope`` it's like ``request_scope``
          but also ``close`` method is called on each injected value after the request
          is processed to free the resources on ``DependencyProvider.worker_teardown`` call.
        
        An example of the test that declares service class and configuration provider:
        
        .. code:: python
        
            import json
            import typing as t
        
            import injector
            import pytest
            from nameko.containers import ServiceContainer
            from nameko.web.handlers import http
            from nameko_injector.core import NamekoInjector
        
        
            class ServiceConfig:
                value: t.Mapping
        
        
            @injector.provider
            def provide_service_config(container: ServiceContainer) -> ServiceConfig:
                return container.config
        
        
            def configure(binder):
                binder.bind(
                    ServiceConfig,
                    to=provide_service_config,
                    scope=injector.singleton,
                )
        
        
            INJECTOR = NamekoInjector(configure)
        
        
            @INJECTOR.decorate_service
            class Service:
        
                name = "service-name"
        
                @http("GET", "/config")
                def view_config(self, request, config: ServiceConfig):
                    # 'config' is injected as singleton in each request that specifies it's type in
                    # the view function's signature.
                    return json.dumps(config)
        
        Testing with library (pytest)
        -----------------------------
        The library provides a plugin for pytest with some basic fixtures.
        To enable the plugin, add the following line in your ``conftest.py`` module.
        
        .. code:: python
        
          pytest_plugins = [
              "nameko_injector.testing.pytest_fixtures",
          ]
        
        Graph of fixtures generated from the test
        
        .. code:: bash
        
           pytest --fixture-graph tests/test_http.py::test_http_request_injected
        
        .. image:: assets/fixtures_graph.png
        
        There are several fixtures that help during the testing. All of the fixtures
        have ``function`` pytest scope.
        
        - ``service_class`` fixture that **MUST** be redefined and return a service class under the test.
        
        - ``web_service`` fixture starts a real HTTP server to make real HTTP requests to the service. It can be used together with nameko's fixture ``web_session`` that injects HTTP client that knows a correct port. See ``tests/test_injected.py`` as an example.
        
        - ``injector_in_test`` fixture gives access to the ``injector.Injector`` instance that will resolve the dependencies in the instance of ``service_class``.
          The fixture uses a child injector from the one that decorates the service that provides isolation between the test cases with the same class under the test.
          By default, it uses ``worker_context`` fixture.
        
        - ``container_overridden_dependencies`` - ``web_service`` uses this mapping of
          nameko dependencies that need to be overridden with the instance values.
        
        - ``worker_ctx`` fixture is used to get ``injector_in_test`` value but it's a mock
          and might be redefined in your tests.
        
        How to redefine dependency?
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~
        Let's assume that service depends on an HTTP client for some 3rd-party service.
        In our test, we would like to use a mocked version of it. In that case, we need to
        redefine ``injector_in_test`` fixture.
        
        .. code:: python
        
          @pytest.fixture
          def injector_in_test(injector_in_test, mocked_http_client):
              injector_in_test.binder.bind(ThirdPartyServiceHttpClient, to=mocked_http_client)
              # injector_in_test.binder.install(MockedClientModule())
              return injector_in_test
        
        Sophisticated cases
        ~~~~~~~~~~~~~~~~~~~
        In more *sophisticated* cases when we redefine how the server is started with
        ``runner_factories`` main task is to ensure that the container (service instance
        basically) has a valid injector. See
        ``nameko_injector/testing/pytest_fixtures.py:web_service`` code as an example.
        Main line there is ``replace_dependencies(container, **container_overridden_dependencies)``.
        
        Development
        -----------
        `tox`
        
Platform: linux
Platform: osx
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Utilities
Classifier: Programming Language :: Python
