Dependency Injector 4.0 - integração simplificada com outras estruturas Python





Olá, Habr! Eu lancei uma nova versão principal do Dependency Injector .



A principal característica desta versão é a fiação. Ele permite que você injete funções e métodos sem arrastá-los para um contêiner.



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


Quando a função é chamada, a main()dependência Serviceé coletada e passada automaticamente.



Durante o teste, ele é chamado container.api_client.override()para substituir o cliente API por uma simulação. Quando chamada, a main()dependência Servicecoletará simulação.



O novo recurso simplifica o uso do Dependency Injector com outras estruturas Python.



Como a vinculação ajuda a se integrar a outras estruturas?



A vinculação permite a injeção precisa, independentemente da estrutura do aplicativo. Ao contrário da versão 3, a injeção de dependência não requer puxar uma função ou classe em um contêiner.



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


Outros exemplos:



  • Django
  • Frasco (exemplo expandido)
  • Aiohttp
  • Sanic


Como funciona a ligação?



Para aplicar a vinculação, você precisa:



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


A vinculação funciona com base na introspecção. Quando chamado, o container.wire(modules=[...], packages=[...])framework percorrerá todas as funções e métodos nesses pacotes e módulos e examinará seus parâmetros padrão. Se o parâmetro padrão for um marcador, essa função ou método será corrigido pelo decorador de injeção de dependência. Este decorador, quando chamado, prepara e injeta dependências em vez de marcadores na função original.



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


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

foo()  # <---  "bar"  

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


Saiba mais sobre vinculação aqui .



Compatibilidade?



A versão 4.0 é compatível com as versões 3.x.



Módulos de integração ext.flaske são ext.aiohttpfixados em favor do empacotamento.

Quando usada, a estrutura exibirá um aviso e recomendará a mudança para a vinculação.



Uma lista completa de mudanças pode ser encontrada aqui .



Qual é o próximo?



  • Confira o Github do projeto
  • Confira a documentação



All Articles