Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic dependencies #9

Open
Tishka17 opened this issue Jan 24, 2024 · 7 comments
Open

Dynamic dependencies #9

Tishka17 opened this issue Jan 24, 2024 · 7 comments
Labels
enhancement New feature or request
Milestone

Comments

@Tishka17
Copy link
Collaborator

Create something depending on, at least, request data

@Tishka17
Copy link
Collaborator Author

Need to add a way to understand what dependencies can be accessed during dynamic selection

@Tishka17 Tishka17 added the enhancement New feature or request label Feb 17, 2024
@Skorpyon
Copy link
Contributor

Skorpyon commented Feb 19, 2024

import abc
import asyncio
from dishka import Provider, provide, Scope, make_async_container


class DepMeta(abc.ABCMeta):
    registered_deps: list[type] = []

    def __new__(mcs, *args, **kwargs) -> type:
        dep_class = super().__new__(mcs, *args, **kwargs)
        DepMeta.registered_deps.append(dep_class)
        return dep_class


class AbstractDep(metaclass=DepMeta):
    def __init__(self):
        self.dep_id = id(self)

    def __str__(self) -> str:
        return f'{self.__class__.__name__}({self.dep_id})'


class Dependency1(AbstractDep):
    ...


class Dependency2(AbstractDep):
    ...


class Dependency3(AbstractDep):
    ...


class MyProvider(Provider):
    def __init__(self, scope=None):
        super().__init__(scope)

        for dep in DepMeta.registered_deps:
            setattr(self, str(dep.__name__), provide(dep, scope=Scope.REQUEST))

        self._init_dependency_sources()


async def main():
    async with make_async_container(MyProvider()) as app_container:
        async with app_container() as container:
            print(await container.get(Dependency1))
            print(await container.get(Dependency2))
            print(await container.get(Dependency3))

        async with app_container() as container:
            print(await container.get(Dependency1))
            print(await container.get(Dependency2))
            print(await container.get(Dependency3))


if __name__ == '__main__':
    asyncio.run(main())

Right now the only way is do some like that. It may be very useful for automated dependencies registration, such Repositories, Services that collected in AbstractRegistryMetadata etc.

@Tishka17
Copy link
Collaborator Author

@Skorpyon I guess this will help: #61
It is not released but I hope to do it pretty soon

@Tishka17
Copy link
Collaborator Author

Version 0.4.0 released

@Skorpyon
Copy link
Contributor

Absolutely amazing.

@Tishka17
Copy link
Collaborator Author

As an Idea for dynamic dependencies: dynamic alias

@alias(provides=MyProto)
def foo(self, request: Request) -> Type[TypeA] | Type[TypeB]:
   if ...:
      return TypeA
   else:
      return TypeB

It will work the same as:

@provide
def foo(self, request: Request, container: Container) -> MyProto:
   if ...:
      return container.get(TypeA)
   else:
      return container.get(TypeB)

We can use return type annotations for graph validation

@Tishka17
Copy link
Collaborator Author

Another idea:

@provide
def xxx(self, a: Deferred[XxxA], b: Deferred[XxxB], r: Request) -> XxxProto:
    if r.something:
       return a.ensure()
    else:
       return b.ensure()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants