adriangb / di

Pythonic dependency injection
https://www.adriangb.com/di/
MIT License
301 stars 13 forks source link

feat: allow access to the Dependant in executors #36

Closed adriangb closed 2 years ago

adriangb commented 2 years ago

The main impetus for this feature is to be able to add a marker to say "this sync dependency {should,should not} be executed in a threadpool". Often sync dependencies are small sans-io things, it's super wasteful to put them in a threadpool. Here is an example of such a marker: https://github.com/index-py/index.py/blob/5d7cb5c2de20ef633473bdca58d06306e62a7fed/indexpy/parameters/field_functions.py#L223

I don't think I would add the marker to the DependantProtocol itself. Instead, it can be a contract between the Dependant implementation and the Executor implementation.

What would need changing, as far as interfaces go, is giving access to the dependent from within the Executor. I can see two ways to do this, one which is backwards compatible and one which is not:

Option 1: add the attribute to the executor's task

from __future__ import annotations

from di.types.dependencies import DependantBase

import sys
from typing import Awaitable, Iterable, Union

if sys.version_info < (3, 8):
    from typing_extensions import Protocol
else:
    from typing import Protocol

class Task(Protocol):
    dependent: DependantBase[Any]
    def __call__(
        self,
    ) -> Union[Awaitable[Union[None, Iterable[Task]]], Union[None, Iterable[Task]]]:
        ...

This would require creating a small wrapper within di/_tasks.py.

Option 2: return a tuple from Task

from __future__ import annotations

from di.types.dependencies import DependantBase

import sys
from typing import Awaitable, Iterable, Union

if sys.version_info < (3, 8):
    from typing_extensions import Protocol
else:
    from typing import Protocol

class Task(Protocol):
    dependent: DependantBase[Any]
    def __call__(
        self,
    ) -> Union[Awaitable[Union[None, Iterable[Tuple[Task, Dependant]]], Union[None, Iterable[Tuple[Task, Dependant]]]:
        ...

I like this less. It's not backwards compatible and is more complicated of an interface (requires implementers to do unpacking).