Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

As a (mainly) Python dev, I'm aware that there are DI frameworks out there, but personally I haven't to date used any of them.

My favourite little hack for simple framework-less DI in Python these days looks something like this:

    # The code that we want to call
    def do_foo(sleep_func = None):
        _sleep_func = sleep_func if sleep_func is not None else time.sleep

        for _ in range(10):
            _sleep_func(1)

    # Calling it in non-test code
    # (we want it to actually take 10 seconds to run)
    def main():
        do_foo()

    # Calling it in test code
    # (we want it to take mere milliseconds to run, but nevertheless we
    # want to test that it sleeps 10 times!)
    def test_do_foo():
        mock_sleep_func = MagicMock()
        do_foo(sleep_func=mock_sleep_func)
        assert mock_sleep_func.call_count == 10


I’ve used this exact pattern.

However, in Python I prefer to use true DI. I mostly like Injector[0] because it’s lightweight and more like a set of primitives than an actual framework. Very easy to build functionality on top of and reuse - I have one set of modules that can be loaded for an API server, CLI, integration tests, offline workers, etc.

That said, I have a few problems with it - 2 features which I feel are bare minimum required, and one that isn’t there but could be powerful. You can’t provide async dependencies natively, which is not usable in 2025 - and it’s keyed purely on type, so if you want to return different string instances with some additional key you need a wrapper type.

Between these problems and missing features (Pydantic-like eval time validation of wiring graphs) I really want to write my own library.

However, as a testament to the flexibility of Injector, I could implement all 3 of these things as a layer on top without modifying its code directly.

[0]: https://pypi.org/project/injector/


Yeah, I use this sometimes too (even though Python makes "monkey patching" easy). However, note that it's simpler and clearer to use a default value for the argument:

  def do_foo(sleep=time.sleep):
    for _ in range(10):
      sleep(1)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: