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

feat: Only autowire transitive edges between manually wired dependencies #88

Open
adriangb opened this issue Nov 1, 2022 · 1 comment

Comments

@adriangb
Copy link
Owner

adriangb commented Nov 1, 2022

di does auto wiring for dependencies which is super convenient to avoid boilerplate. But let's say you have something like:

class DBConnection:
    def __init__(self, host: str) -> None:
        ...

We (wrongly) assume that this can be constructed like DBConnection(host=str()). This is because we inspect the type annotation and autowire str itself!

We need some way of cutting off how deep we auto-wire. I think a sensible rule would be "all of the leaf dependencies (dependencies with no further dependencies) must be manually wired with:

  • Marker(function_that_accepts_no_arguments)
  • A default value
  • A bind
@mikedmcfarland
Copy link

mikedmcfarland commented Sep 26, 2023

@adriangb some control of this would be nice, since I can imagine creating a class with no dependencies, using it within your project, and expecting it to get wired in for folks. I know I'd love this for builtin types, but maybe not so much my own.

I think a sensible default would be to check if its a leaf and if the type is part of builtins...

import inspect
import builtins
inspect.getmodule(str) == builtins    # True
inspect.getmodule(list) == builtins   # True
inspect.getmodule(dict) == builtins   # True

Here's example BindHook that helps a little bit here:

def match_all_builtins_and_error(
    param: Optional[inspect.Parameter], dependent: DependentBase[Any]
) -> Optional[DependentBase[Any]]:
    if (
        param is not None
        and param.default is param.empty
        and inspect.getmodule(param.annotation) is builtins
    ):
        return Dependent(wire=False, call=lambda: _raise(param=param, dependent=dependent))

    return None

def _raise(param: Optional[inspect.Parameter], dependent: DependentBase[Any]) -> Any:
    raise RuntimeError(
        f"The parameter {param} to {dependent.call} is a builtin which we don't want to autowire"
    )

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

No branches or pull requests

2 participants