Closed proboscis closed 2 months ago
We can have "__del__" suffix to specify that the binding is a destructor. And then finally we can inspect the values in graph and gather all __del__ instances and await it.
Another option would be to make design hold bindings with destructors. This is less hacky
Adding a destructor to IBind is possible. But, what do you do with the Injected?
We need to accumulate the destructors and call them appropriately at the end.
This changes the semantics of provider function to return a destructor as well.
class IResource(Generic[T], ABC):
value: T
async def close(self):
pass
class IBind(Generic[T], ABC):
"""
hold a provider and metadata
"""
@abc.abstractmethod
async def provide(self, cxt: ProvideContext, deps: Dict[IBindKey, Any]) -> IResource[T]:
pass
I realized that an IBind would return an arbitrary functor object rather than IResource[T]. It can be Future[T] or anything. Our IBind is async, this means it's already holding Awaitable as the functor. If we add IResource, then this IBind becomes
IBind[IResource[Awaitable[T]]]
This means we can define the IBind to be
class IBind(Generic[M,T],ABC):
monad:Monad[M]
def provide(...)->M[T]
This way, we can add arbitrary monadic context to the resource without modifying IBind class. While IBind itself is a Monad (called Reader Monad), what we are doing is actually building the tree of Monad: IBind[Awaitable[IResource[T]]]
Actually, IBind cannot be a monad since dependencies cannot be known until runtime.
Now, how do I eval the tree of Awaitable[Resource]?
Just do
res = await eval(target)
... do anything with the res.value
await res.close
Now, how should an Injected hold a destructor?
I think I should separate the implementation of injected
So,, adding resource cleaner to IBind and Injected involves too much work.
Currently, there are several kind of resources.
We aim to support cleaning the resources for case 2. Such services are often defined in global design definition. The question now is, how to register the destructor to a IBindKey.
Examples:
# case1
design = providers(
resource = get_resource
resource__del__ = destruct_resource # async def resource__del__(dep,/,tgt)
)
#case 2:
design = providers(
resource = closable(
constructor=get_resource,
destructor=destruct_resource
)
)
#case3:
design = providers(
resource = get_resource
) + destructors(
resource = destruct_resource
)
lets go from 3 to 2.
We need to be able to register deconstructors.