Dependency Injector 4.0 - Vereinfachte Integration mit anderen Python-Frameworks





Hallo Habr! Ich habe eine neue Hauptversion von Dependency Injector veröffentlicht .



Das Hauptmerkmal dieser Version ist die Verkabelung. Sie können Funktionen und Methoden einfügen, ohne sie in einen Container zu ziehen.



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()  # <--    


Beim Aufruf der Funktion wird die main()Abhängigkeit Serviceerfasst und automatisch übergeben.



Während des Testens wird aufgerufen container.api_client.override(), den API-Client durch einen Mock zu ersetzen. Beim Aufruf sammelt die main()Abhängigkeit ServiceMock.



Die neue Funktion erleichtert die Verwendung des Abhängigkeitsinjektors mit anderen Python-Frameworks.



Wie hilft die Verknüpfung bei der Integration in andere Frameworks?



Die Bindung ermöglicht eine präzise Injektion unabhängig von der Anwendungsstruktur. Im Gegensatz zu Version 3 muss für die Abhängigkeitsinjektion keine Funktion oder Klasse in einen Container gezogen werden.



Beispiel mit Kolben:



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


Andere Beispiele:





Wie funktioniert das Binden?



Um eine Bindung anzuwenden, benötigen Sie:



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


Die Bindung funktioniert auf der Grundlage der Selbstbeobachtung. Beim Aufruf container.wire(modules=[...], packages=[...])durchläuft das Framework alle Funktionen und Methoden in diesen Paketen und Modulen und überprüft deren Standardparameter. Wenn der Standardparameter ein Marker ist, wird eine solche Funktion oder Methode vom Dekorator für die Abhängigkeitsinjektion gepatcht. Wenn dieser Dekorator aufgerufen wird, bereitet er Abhängigkeiten anstelle von Markern vor und fügt sie in die ursprüngliche Funktion ein.



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


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

foo()  # <---  "bar"  

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


Weitere Informationen zum Verknüpfen hier .



Kompatibilität?



Version 4.0 ist kompatibel mit Version 3.x.



Integrationsmodule ext.flaskund sind ext.aiohttpzugunsten der Bündelung fixiert.

Bei Verwendung zeigt das Framework eine Warnung an und empfiehlt, zur Verknüpfung zu wechseln.



Eine vollständige Liste der Änderungen finden Sie hier .



Was weiter?






All Articles