9

I am new to flask and python. I want to implement a Dependency injection container and access the dependencies inside different modules. My first try looks something like:

class AppModule(Module):
    def __init__(self, app):
        self.app = app

    """Configure the application."""
    def configure(self, binder):
        client = self.configure_cosmos_client()
        binder.bind(CosmosClient, to=client, scope=singleton)
        binder.bind(Dao, to=Dao, scope=singleton)

    def configure_cosmos_client(self) -> CosmosClient:
        return CosmosClient(
            url_connection=self.app.config.get('ENDPOINT'),
            auth={'masterKey': self.app.config.get('PRIMARYKEY')}
        )

app = Flask(__name__)

injector = Injector([AppModule(app)])
FlaskInjector(app=app, injector=injector)

app.run()

and further inside a module, I want to get the CosmosClient dependency something like:

class Dao:
    cosmos_client = None

    def __init__(self):
        self.cosmos_client = DI.get(CosmosClient)

Is there any way to achieve this? Please note "DI.get" is just an example since I could not find how to access these dependencies apart from injecting the dependencies into the route.

Rajat Arora
  • 582
  • 1
  • 6
  • 18
  • 1
    here is an [example](https://stackoverflow.com/questions/52587523/how-can-i-manage-extra-modules-in-app-factory-pattern/52644503#52644503). also you can use [serum](https://pypi.org/project/serum/) or [dependency-injector](https://pypi.org/project/dependency-injector/). I used [inject](https://pypi.org/project/Inject/) for more than 3 years(with `Flask`, `Falcon`, `Airflow`). It is really good tool. – Danila Ganchar Oct 29 '19 at 19:43
  • @DanilaGanchar Looks good. thanks a lot. I will follow it. – Rajat Arora Oct 30 '19 at 08:34
  • thank you. good luck ;) – Danila Ganchar Oct 30 '19 at 15:07

2 Answers2

8

Try Dependency Injector. It has with Flask tutorial. Your container will look something like:

from dependency_injector import containers, providers
from dependency_injector.ext import flask
from flask import Flask
from flask_bootstrap import Bootstrap
from github import Github

from . import views, services


class ApplicationContainer(containers.DeclarativeContainer):
    """Application container."""

    app = flask.Application(Flask, __name__)

    bootstrap = flask.Extension(Bootstrap)

    config = providers.Configuration()

    github_client = providers.Factory(
        Github,
        login_or_token=config.github.auth_token,
        timeout=config.github.request_timeout,
    )

    search_service = providers.Factory(
        services.SearchService,
        github_client=github_client,
    )

    index_view = flask.View(
        views.index,
        search_service=search_service,
        default_query=config.search.default_query,
        default_limit=config.search.default_limit,
    )

To run the app you need to:

from .containers import ApplicationContainer


def create_app():
    """Create and return Flask application."""
    container = ApplicationContainer()
    container.config.from_yaml('config.yml')
    container.config.github.auth_token.from_env('GITHUB_TOKEN')

    app = container.app()
    app.container = container

    bootstrap = container.bootstrap()
    bootstrap.init_app(app)

    app.add_url_rule('/', view_func=container.index_view.as_view())

    return app

And testing will look like:

from unittest import mock

import pytest
from github import Github
from flask import url_for

from .application import create_app


@pytest.fixture
def app():
    return create_app()


def test_index(client, app):
    github_client_mock = mock.Mock(spec=Github)
    # Configure mock

    with app.container.github_client.override(github_client_mock):
        response = client.get(url_for('index'))

    assert response.status_code == 200
    # Do more asserts
Roman Mogylatov
  • 516
  • 4
  • 10
-8

I'm not sure what you're trying to achieve exactly, but don't code in python with a java mindset.

If you're trying to add the cosmos_client to the app and access it from somewhere else you might want to save it as a config attribute to the flask app ?

m0etaz
  • 953
  • 1
  • 8
  • 21
  • 1
    Thank you. I want to insitantiate the "CosmosClient" class and bind it using flask DI. Further access it from somewhere else in the app. So basically like a global dependency container which holds dependencies required at different places in the app. – Rajat Arora Oct 29 '19 at 17:26
  • 5
    Instead of saying "dont code in Python with a Java mindset" explain why using DI is nto a good idea in this use-case. IOC and DI should be language agnostic and work to solve the same issues. Why do you think in this case it is not a good solution? – miquelvir Jul 24 '21 at 21:35