Package: nameko-injector
injectorbased dependency injection
mechanism for nameko services. Project is similar toflask-injector.ProblemNameko provides a dependency injection mechanism, built-in in the framework.
It works in many cases with the limitations:All the dependencies are injected regardless of whether they are used in the entry-point. For instance, all the dependencies will be injected for/healthHTTP entry point.Dependencies cannot depend on each other.The scope is an implementation detail. Frequency of the dependency creation depends on theDependencyProviderimplementation.Subjectively, implementing a newDependencyProvideris not the easiest
the task for the developers.SolutionThe library provides an alternative dependency injection mechanism to the one
that is built-in in nameko. Several types ofrequestscope can
be used out of the box without special injector module declarations.from nameko.containers.ServiceContainerfrom nameko.containers.WorkerContextwerkzeug.wrappers.Request, in case of HTTP requestsThe library provides 2scopes:nameko_injector.core.request_scopewhere each request has own instance of
the injected type.nameko_injected.core.resource_request_scopeit’s likerequest_scopebut alsoclosemethod is called on each injected value after the request
is processed to free the resources onDependencyProvider.worker_teardowncall.An example of the test that declares service class and configuration provider:importjsonimporttypingastimportinjectorimportpytestfromnameko.containersimportServiceContainerfromnameko.web.handlersimporthttpfromnameko_injector.coreimportNamekoInjectorclassServiceConfig:value:t.Mapping@injector.providerdefprovide_service_config(container:ServiceContainer)->ServiceConfig:returncontainer.configdefconfigure(binder):binder.bind(ServiceConfig,to=provide_service_config,scope=injector.singleton,)INJECTOR=NamekoInjector(configure)@INJECTOR.decorate_serviceclassService:name="service-name"@http("GET","/config")defview_config(self,request,config:ServiceConfig):# 'config' is injected as singleton in each request that specifies it's type in# the view function's signature.returnjson.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 yourconftest.pymodule.pytest_plugins=["nameko_injector.testing.pytest_fixtures",]Graph of fixtures generated from the testpytest--fixture-graphtests/test_http.py::test_http_request_injectedThere are several fixtures that help during the testing. All of the fixtures
havefunctionpytest scope.service_classfixture thatMUSTbe redefined and return a service class under the test.web_servicefixture starts a real HTTP server to make real HTTP requests to the service. It can be used together with nameko’s fixtureweb_sessionthat injects HTTP client that knows a correct port. Seetests/test_injected.pyas an example.injector_in_testfixture gives access to theinjector.Injectorinstance that will resolve the dependencies in the instance ofservice_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 usesworker_contextfixture.container_overridden_dependencies-web_serviceuses this mapping of
nameko dependencies that need to be overridden with the instance values.worker_ctxfixture is used to getinjector_in_testvalue 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
redefineinjector_in_testfixture.@pytest.fixturedefinjector_in_test(injector_in_test,mocked_http_client):injector_in_test.binder.bind(ThirdPartyServiceHttpClient,to=mocked_http_client)# injector_in_test.binder.install(MockedClientModule())returninjector_in_testSophisticated casesIn moresophisticatedcases when we redefine how the server is started withrunner_factoriesmain task is to ensure that the container (service instance
basically) has a valid injector. Seenameko_injector/testing/pytest_fixtures.py:web_servicecode as an example.
Main line there isreplace_dependencies(container, **container_overridden_dependencies).Developmenttox