Dependency Injector - Wiring (연결)

  • dependency injection
  • inversion of control
  • python
  • v4.41.0
About 2 min

원문 : https://python-dependency-injector.ets-labs.org/wiring.htmlopen in new window

Wiring 기능은 컨테이너 Provider를 함수나 메소드에 주입시키는 방법을 제공합니다.

Wiring을 사용할 때 필요한 것:

  • @inject 데코레이터를 사용 : 데코레이터 @inject가 의존성을 주입합니다.
  • 마커를 사용 : Wiring 마커는 어떤 의존성(예, Provide[Container.bar])을 주입할지 지정합니다. 이것은 컨테이너가 주입을 찾는데 도움을 줍니다.
  • 코드의 마커로 컨테이너를 연결 : 연결하고자 하는 특정 모듈과 패키지로 container.wire()를 호출합니다.
  • 평소처럼 함수와 클래스를 사용 : 프레임워크가 지정된 주입을 제공합니다.
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject


class Service:
    ...


class Container(containers.DeclarativeContainer):

    service = providers.Factory(Service)


@inject
def main(service: Service = Provide[Container.service]) -> None:
    ...


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

    main()

데코레이터 @inject

데코레이터 @inject는 의존성을 주입합니다. 주입하기위해 모든 함수와 메소드에 데코레이트할 수 있습니다.

from dependency_injector.wiring import inject, Provide


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

데코레이터 @inject는 적절한 연결동작이 확실하도록 함수의 가장 첫번째 데코레이터로 지정되어야 합니다. 또한 연결 프로세스의 성능에 기여합니다.

from dependency_injector.wiring import inject, Provide


@decorator_etc
@decorator_2
@decorator_1
@inject
def foo(bar: Bar = Provide[Container.bar]):
    ...

또한 FastAPI나 유사하게 데코레이터를 사용하는 다른 프레임워크, 클로저, 주입이 있는 사용자 지정의 데코레이터 유형에 @inject를 첫번째 데코레이터로 지정하는게 중요합니다.

FastAPI 예제는 다음과 같습니다.

app = FastAPI()


@app.api_route("/")
@inject
async def index(service: Service = Depends(Provide[Container.service])):
    value = await service.process()
    return {"result": value}

데코레이터 예제는 다음과 같습니다.

def decorator1(func):
    @functools.wraps(func)
    @inject
    def wrapper(value1: int = Provide[Container.config.value1]):
        result = func()
        return result + value1
    return wrapper


def decorator2(func):
    @functools.wraps(func)
    @inject
    def wrapper(value2: int = Provide[Container.config.value2]):
        result = func()
        return result + value2
    return wrapper

@decorator1
@decorator2
def sample():
    ...

함께보기

이슈 #404open in new window에서 @inject 데코레이터에 대해 좀더 자세하게 설명합니다.

마커

Wiring 기능은 주입을 만들기 위해 마커를 사용합니다. 주입 마커는 함수나 메소드 인자의 값으로 명시됩니다.

from dependency_injector.wiring import inject, Provide


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

어노테이션 명시는 선택적입니다.

Provider 자체를 주입하려면 Provide[foo.provider]를 사용합니다.

from dependency_injector.providers import Factory
from dependency_injector.wiring import inject, Provide


@inject
def foo(bar_provider: Factory[Bar] = Provide[Container.bar.provider]):
    bar = bar_provider(argument="baz")
    ...

또한 Provide[foo] 를 사용하여 Provider 자체를 주입할 수 있습니다.

from dependency_injector.providers import Factory
from dependency_injector.wiring import inject, Provider


@inject
def foo(bar_provider: Factory[Bar] = Provider[Container.bar]):
    bar = bar_provider(argument="baz")
    ...

평소하는 것 처럼 구성, 제공되는 인스턴스, 하위 컨테이너 Provider를 사용하면 됩니다.

@inject
def foo(token: str = Provide[Container.config.api_token]):
    ...


@inject
def foo(timeout: int = Provide[Container.config.timeout.as_(int)]):
    ...


@inject
def foo(baz: Baz = Provide[Container.bar.provided.baz]):
    ...


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

함수단위 실행범위를 구현하기 위해 Wiring과 Resource Provider를 조합할 수 있습니다. 상세한 내용은 Resource와 Wiring, 함수단위 실행 범위open in new window에서 볼 수 있습니다.

또한 컨테이너를 주입하기 위해 Provide를 사용할 수 있습니다.

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


class Service:
    ...


class Container(containers.DeclarativeContainer):

    service = providers.Factory(Service)


@inject
def main(container: Container = Provide[Container]):
    service = container.service()
    ...


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

    main()













 
 
 
 







문자열 식별자

문자열 식별자를 사용해서 연결할 수 있습니다. 문자열 식별자는 컨테이너에 Provider 이름과 일치해야합니다.

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


class Service:
    ...


class Container(containers.DeclarativeContainer):

    service = providers.Factory(Service)


@inject
def main(service: Service = Provide["service"]) -> None:
    ...


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

    main()













 
 
 







문자열 식별자를 사용하면 주입을 지정하기 위해 컨테이너를 사용할 필요가 없습니다.

중첩된 컨테이너에서 주입을 지정하려면 점 .을 구분자로 사용합니다.

@inject
def foo(service: UserService = Provide["services.user"]) -> None:
    ...

또한 주입 수정자를 사용할 수 있습니다.

from dependency_injector.wiring import (
    inject,
    Provide,
    as_int,
    as_float,
    as_,
    required,
    invariant,
    provided,
)


@inject
def foo(value: int = Provide["config.option", as_int()]) -> None:
    ...


@inject
def foo(value: float = Provide["config.option", as_float()]) -> None:
    ...


@inject
def foo(value: Decimal = Provide["config.option", as_(Decimal)]) -> None:
    ...

@inject
def foo(value: str = Provide["config.option", required()]) -> None:
    ...

@inject
def foo(value: int = Provide["config.option", required().as_int()]) -> None:
    ...


@inject
def foo(value: int = Provide["config.option", invariant("config.switch")]) -> None:
    ...

@inject
def foo(value: int = Provide["service", provided().foo["bar"].call()]) -> None:
    ...

컨테이너를 주입할 때는 특수 식별자인 <container>를 사용합니다.

@inject
def foo(container: Container = Provide["<container>"]) -> None:
    ...
Loading...