Dependency Injector4.0-他のPythonフレームワークとの統合の簡素化





こんにちは、Habr!DependencyInjectorの新しいメジャーバージョンをリリースしました



このバージョンの主な機能は配線です。これにより、関数とメソッドをコンテナーにドラッグせずに挿入できます。



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


関数が呼び出されると、main()依存関係Serviceが収集され、自動的に渡されます。



テスト中にcontainer.api_client.override()、APIクライアントをモックに置き換えるために呼び出されます。呼び出されると、main()依存関係Serviceはモックを収集します。



この新機能により、他のPythonフレームワークでDependencyInjectorを簡単に使用できるようになります。



リンクは他のフレームワークとの統合にどのように役立ちますか?



バインディングにより、アプリケーションの構造に関係なく、正確な注入が可能になります。バージョン3とは異なり、依存関係の挿入では、関数またはクラスをコンテナーにプルする必要はありません。



フラスコの例:



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


その他の例:





バインディングはどのように機能しますか?



バインディングを適用するには、次のものが必要です。



  • コードにマーカーを配置します。ビューマーカーはProvide[Container.bar]、関数またはメソッド引数のデフォルト値として指定されます。何をどこに埋め込むかを示すためにマーカーが必要です。
  • コンテナをコード内のマーカーに関連付けます。これを行うには、container.wire(modules = [...]、packages = [...])メソッドを呼び出して、マーカーを持つモジュールまたはパッケージを指定する必要があります。
  • 通常どおり関数とメソッドを使用します。フレームワークは、必要な依存関係を自動的に準備して挿入します。


バインディングは、イントロスペクションに基づいて機能します。呼び出されると、container.wire(modules=[...], packages=[...])フレームワークはこれらのパッケージとモジュールのすべての関数とメソッドを調べ、それらのデフォルトのパラメーターを調べます。デフォルトのパラメータがマーカーの場合、そのような関数またはメソッドは、依存関係インジェクションデコレータによってパッチが適用されます。このデコレータは、呼び出されると、マーカーの代わりに依存関係を準備して元の関数に挿入します。



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


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

foo()  # <---  "bar"  

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


リンクの詳細については、こちらをご覧ください



互換性?



バージョン4.0はバージョン3.xと互換性があります。



統合モジュールext.flaskとはext.aiohttp、バンドルを優先し固定されています。

フレームワークを使用すると、警告が表示され、リンクへの切り替えが推奨されます。



変更の完全なリストはここにあります



次は何ですか?






All Articles