snake-guice is a simple, lightweight Python dependency injection framework
based on google-guice. The Guice way of doing things is quite a bit different
than the current breed of XML IoC containers.
- Repository: https://github.com/dstanek/snake-guice
- Documentation: https://snake-guice.readthedocs.io/
pip install snake-guiceDeclare dependencies with @inject and type annotations, wire them up in a
module, then let the injector build your object graph:
from snakeguice import create_injector, inject
from snakeguice.interfaces import Binder
class IEngine:
"""Interface for an engine."""
class SmallBlockEngine(IEngine):
pass
class Car:
@inject
def __init__(self, engine: IEngine) -> None:
self.engine = engine
class AutoModule:
def configure(self, binder: Binder) -> None:
binder.bind(IEngine, to=SmallBlockEngine)
injector = create_injector([AutoModule()])
car = injector.get_instance(Car)
assert isinstance(car.engine, SmallBlockEngine)- User Guide — bindings, scopes, child injectors
- Using Providers — per-call construction and lazy resolution
- Multibinding — collecting multiple implementations of one interface
- Private Modules — scoping bindings to a subsystem
scopes.SINGLETONis per-injector: twocreate_injector()calls each get their own cached instance. Acreate_child()injector still shares a parent-defined singleton with its parent (that's deliberate, matching Guice). For an instance shared across independent injectors, construct it yourself and bind it withto_instance=, which bypasses scoping. For per-thread instances, usescopes.THREAD_LOCAL.Injectoris safe to share between threads (and asyncio tasks): resolution state is per-thread/per-task, not shared. Objects it constructs are still your responsibility to make thread-safe. See Threads and Async for how to structure an application around that and its limitations.- Method injection (
@injecton methods other than__init__) runs for any class snake-guice constructs, whether that's implicit construction or an explicitto=binding. annotated_with="left"andannotated_with=Annotation("left")are different keys, not two spellings of the same one - see Guice Basics. An annotated lookup with no matching binding raisesMissingBindingErrorrather than implicitly constructing something unbound.