Skip to content

Is That-Depends could be used in a Library? #50

Description

@rustam-ashurov-mcx

Hey mates,
On the main page it's said that this lib is good to switch from dependency-injector so I'm hyped already.
But can this library help with wiring FastAPI routers which are defined in a library and should be used eventually in a customer application code?

To elaborate a bit more about my particular example (which fails with other DI libraries):

I want to make a library with some REST API routers for FastAPI application and with some DI bindings to pre-configured so my routers will receive my services:

Here is the LoggignContainer where I have logging-related dependencies:

class LoggingContainer(containers.DeclarativeContainer):

    logger = providers.Singleton(MyLogger)

And here is a main BaseContainer where I aggregated all smaller containers from my library, it then can be used in the client code to register (for me) and access (for client) my services if needed:

class BaseContainer(containers.DeclarativeContainer):

    app = providers.Container(AppContainer)

    logging = providers.Container(LoggingContainer)

    web_api = providers.Container(WebApiContainer, app=app, logging=logging)

Here is my library router which should automate some work, very simple one and which uses services registred in IoC Contaier from my lib, specifically my logger:

router = APIRouter()

tag = "Ping"

@router.get("/ping", tags=[tag], status_code=status.HTTP_200_OK)

@inject

async def ping_async(logger: Logger = Depends(Provide[LoggingContainer.logger])):

    logger.trace("Ping")

    return

In a client code some steps should be done, firstly a client Container to be defined, it has it's own application-related dependencies + inherits from my BaseContainer:

class AppContainer(BaseContainer):

    hello_world_service = providers.Factory(HelloWorldService) <- some application related registration

Now client code need to register the library router (typical registration via FastAPI methods) + wire the router :

if __name__ == "__main__":

    container = Container()

    ... here happens FastAPI code and registration of router there ....

    container.wire(modules=[ping_router])

    container.wire(modules=[__name__])

    configure_services()

    run_app()

My expectations are Logger from my library will be injected in my router but the results is:
"AttributeError: 'Provide' object has no attribute logger"

I tried many combinations and changed code many ways but unless I directly use AppContainer in my library code (what is impossible to expect beyond local examples and testing since I can not know what is App level container in advance) wiring doesn't work

So now I started to check you examples and in the FastAPI example I see such lines of code:

@ROUTER.get("/decks/")
async def list_decks(
    decks_repo: DecksRepository = fastapi.Depends(ioc.IOCContainer.decks_repo),
) -> schemas.Decks:
    objects = await decks_repo.all()
    return typing.cast(schemas.Decks, {"items": objects})

Where ioc.IOCContainer.decks_repo is a reference to the container in the application code and I decided to ask in advance.

Is it possible in your lib to:

  • Define a library Container that will be used or inherited by the Client specific container
  • Wire client and library modules with Client Container
  • And happily use dependencies from library container in library code not knowing about client-level Container in advance?

Thank you 🙂

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions