Dependency Injector 4.0 - Integración simplificada con otros marcos de Python





¡Hola, Habr! He lanzado una nueva versión principal de Dependency Injector .



La característica principal de esta versión es el cableado. Le permite inyectar funciones y métodos sin arrastrarlos a un contenedor.



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


Cuando se llama a la función, la main()dependencia Servicese recopila y se pasa automáticamente.



Durante la prueba, se llama container.api_client.override()para reemplazar el cliente API con un simulacro. Cuando se llame, la main()dependencia Servicerecopilará mock.



La nueva función facilita el uso de Dependency Injector con otros marcos de Python.



¿Cómo ayuda la vinculación a integrarse con otros marcos?



La unión permite una inyección precisa independientemente de la estructura de la aplicación. A diferencia de la versión 3, la inyección de dependencias no requiere extraer una función o clase en un contenedor.



Ejemplo con matraz:



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


Otros ejemplos:





¿Cómo funciona la encuadernación?



Para aplicar la encuadernación necesita:



  • Coloque marcadores en el código. El marcador de vista se Provide[Container.bar]especifica como el valor predeterminado de la función o el argumento del método. Se necesitan marcadores para indicar qué y dónde insertar.
  • Asocia un contenedor con marcadores en código. Para hacer esto, necesita llamar al método container.wire (modules = [...], packages = [...]) y especificar los módulos o paquetes que tienen marcadores.
  • Utilice funciones y métodos como de costumbre. El marco preparará e inyectará las dependencias necesarias automáticamente.


La vinculación funciona sobre la base de la introspección. Cuando se llama, el container.wire(modules=[...], packages=[...])marco revisará todas las funciones y métodos de estos paquetes y módulos y examinará sus parámetros predeterminados. Si el parámetro predeterminado es un marcador, el decorador de inyección de dependencia parcheará dicha función o método. Este decorador, cuando se llama, prepara e inyecta dependencias en lugar de marcadores en la función original.



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


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

foo()  # <---  "bar"  

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


Obtenga más información sobre cómo vincular aquí .



¿Compatibilidad?



La versión 4.0 es compatible con las versiones 3.x.



Los módulos de integración ext.flasky están ext.aiohttpanclados a favor de la agrupación.

Cuando se usa, el marco mostrará una advertencia y recomendará cambiar a la vinculación.



Puede encontrar una lista completa de cambios aquí .



¿Que sigue?






All Articles