Dependency Injector 4.0 - Intégration simplifiée avec d'autres frameworks Python





Bonjour, Habr! J'ai publié une nouvelle version majeure de Dependency Injector .



La principale caractéristique de cette version est le câblage. Il vous permet d'injecter des fonctions et des méthodes sans les faire glisser dans un conteneur.



from dependency_injector import containers, providers
from dependency_injector.wiring import Provide


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,
    )


def main(service: Service = Provide[Container.service]):
    ...


if __name__ == '__main__':
    container = Container()
    container.config.api_key.from_env('API_KEY')
    container.config.timeout.from_env('TIMEOUT')
    container.wire(modules=[sys.modules[__name__]])

    main()  # <--   

    with container.api_client.override(mock.Mock()):
        main()  # <--    


Lorsque la fonction est appelée, la main()dépendance Serviceest collectée et transmise automatiquement.



Pendant le test, il est appelé container.api_client.override()pour remplacer le client API par un simulacre. Lorsqu'elle est appelée, la main()dépendance Servicecollectera des simulacres.



La nouvelle fonctionnalité simplifie l'utilisation de l'injecteur de dépendances avec d'autres frameworks Python.



Comment la liaison aide-t-elle à s'intégrer à d'autres cadres?



La liaison permet une injection précise quelle que soit la structure de l'application. Contrairement à la version 3, l'injection de dépendances ne nécessite pas l'extraction d'une fonction ou d'une classe dans un conteneur.



Exemple avec Flask:



import sys

from dependency_injector import containers, providers
from dependency_injector.wiring import Provide
from flask import Flask, json


class Service:
    ...


class Container(containers.DeclarativeContainer):

    service = providers.Factory(Service)


def index_view(service: Service = Provide[Container.service]) -> str:
    return json.dumps({'service_id': id(service)})


if __name__ == '__main__':
    container = Container()
    container.wire(modules=[sys.modules[__name__]])

    app = Flask(__name__)
    app.add_url_rule('/', 'index', index_view)
    app.run()


Autres exemples:





Comment fonctionne la reliure?



Pour appliquer la liaison, vous avez besoin:



  • . Provide[Container.bar] . .
  • . container.wire(modules=[...], packages=[...]) , .
  • . .


La reliure fonctionne sur la base de l'introspection. Lorsqu'il est appelé, le container.wire(modules=[...], packages=[...])framework passera en revue toutes les fonctions et méthodes de ces packages et modules et examinera leurs paramètres par défaut. Si le paramètre par défaut est un marqueur, une telle fonction ou méthode sera corrigée par le décorateur d'injection de dépendances. Ce décorateur, lorsqu'il est appelé, prépare et injecte des dépendances au lieu de marqueurs dans la fonction d'origine.



def foo(bar: Bar = Provide[Container.bar]):
    ...


container = Container()
container.wire(modules=[sys.modules[__name__]])

foo()  # <---  "bar"  

#    :
foo(bar=container.bar())


En savoir plus sur les liens ici .



Compatibilité?



La version 4.0 est compatible avec les versions 3.x.



Les modules d'intégration ext.flasket sont ext.aiohttpépinglés en faveur du regroupement.

Lorsqu'il est utilisé, le framework affichera un avertissement et recommandera de passer à la liaison.



Une liste complète des modifications peut être trouvée ici .



Et après?






All Articles