A new take on dependency injection in Python

In case anyone's interested, I've put together a DI framework "pylayer" in python that's fairly different from the alternatives I'm aware of (there aren't many). It includes a simple example at the bottom.
https://gist.github.com/johnhungerford/ccb398b666fd72e69f6798921383cb3f

What my project does

It allows you automatically construct dependencies based on their constructors.

The way it works is you define your dependencies as dataclasses inheriting from an Injectable class, where upstream dependencies are declared as dataclass attributes with type hints. Then you can just pass the classes to an Env object, which you can query for any provided type that you want to use. The Env object will construct a value of that type based on the Injectable classes you have provided. If any dependency needed to construct the queried type, it will generate an error message explaining what was missing and why it was needed.

Target audience

This is a POC that might be of interest to anyone who is uses or has wanted to use dependency injection in a Python project.

Comparison

https://python-dependency-injector.ets-labs.org/ is but complicated and unintuitive. pylayer is more automated and less verbose.

https://github.com/google/pinject is not maintained and seems similarly complicated.

https://itnext.io/dependency-injection-in-python-a1e56ab8bdd0 provides an approach similar to the first, but uses annotations to simplify some aspects of it. It's still more verbose and less intuitive, in my opinion, than pylayer.

Unlike all the above, pylayer has a relatively simple, functional mechanism for wiring dependencies. It is able to automate more by using the type introspection and the automated __init__ provided by dataclasses.

For anyone interested, my approach is based on Scala's ZIO library. Like ZIO's ZLayer type, pylayer takes a functional approach that uses memoization to prevent reconstruction of the same values. The main difference between pylayer and ZIO is that wiring and therefore validation is done at runtime. (Obviously compile-time validation isn't possible in Python...)