Wiring services together by hand gets messy. A dependency injection container builds objects and their dependencies for you.
Implement a class Container:
register(name, factory, deps=(), singleton=False): factory is called with the resolved dependencies as positional arguments, in orderresolve(name): build the object. Singletons are created once and cached; other registrations produce a new object on every call (but their singleton dependencies are shared). An unknown name raises KeyError. A dependency cycle raises RuntimeError with the path, like "a -> b -> a".override(name, factory): a context manager that temporarily replaces a registration (keeping its deps and singleton flag) and clears that name's cached singleton, restoring both on exit. This is handy in tests.