Dependency Injector - Wiring (연결)
원문 : https://python-dependency-injector.ets-labs.org/wiring.html
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():
...
함께보기
이슈 #404에서 @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, 함수단위 실행 범위에서 볼 수 있습니다.
또한 컨테이너를 주입하기 위해 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:
...