auto-init is a dependency injection tool that works in Python 3.6+ thanks to type hints. If you write nice object oriented code and separate interfaces from implementations then you could benefit from this.
Did you know that if you provide a type hint for an attribute in a class body and do not set a value then the attribute isn't actually initialised (neither in class, nor in instances)?
class Point:
x: int
y: int
z: int = None
For p
, an instance of Point
, only p.z
is set. This is great because we can build a dependency
injection mechanism on top of this!
from auto_init import AutoInitContext
ctx = AutoInitContext()
p: Point = ctx.get_instance(Point)
assert p.x == 0
assert p.y == 0
assert p.z is None
Note that the Point
class could also be a dataclass and it would work too.
import logging
from auto_init import AutoInitContext
class Worker:
enterprise: "Enterprise"
log: logging.Logger
class Reporter:
enterprise: "Enterprise"
log: logging.Logger
class Enterprise:
worker: Worker
reporter: Reporter
ctx = AutoInitContext()
ctx.register_instance(logging.getLogger(__name__))
ctx.register_singleton(Enterprise)
enterprise: Enterprise = ctx.get_instance(Enterprise)
assert enterprise.worker.log is enterprise.reporter.log
assert enterprise.worker.enterprise is enterprise
assert enterprise.reporter.enterprise is enterprise
pip install auto-init
AutoInitContext()
Create a new auto-initialisation context.
register_singleton(instance_type: Type, factory: Callable=None)
- Register a singleton type. This is different from
register_instance
in that here auto-init is responsible for the creation as well as initialisation of the singleton instance. This should be used when the singleton itself has dependencies that need to be injected. See the enterprise.py example underauto_init/examples/
. Iffactory
is not supplied, theinstance_type
is used to create the instance. register_factory(instance_type: Type, factory: Callable)
- Register a callable which is called to create a new instance of the specified type when on is requested.
register_instance(instance, instance_type: Type=None)
- Register an instance that should always be returned when an instance of the specified type is requested.
get_instance(instance_type: Type) -> Any
- Get an instance of the specified type.
init_instance(instance)
- Initialise any unitialised attributes of the instance.
- Supports initialising attributes annotated with
typing.List
andtyping.Dict
as empty lists or dictionaries. Othertyping.*
-annotated attributes are initialised asNone
. - Do not attempt to get type hints from
typing.*
classes, it raises exceptions in Python 3.7 and does not return anything useful in Python 3.6 anyway. - Fixes dependencies for Python 3.6, removes unnecessary
contextvars
anddataclasses
dependencies for Python 3.7. - Travis CI setup.
- Complete rewrite to deal with circular references. Intrusive approach is no good.
- Forward references in type hints don't really work in Python 3.7 even with futures. Let's use Python 3.6.
- Added
AutoInitContext.explicit_only
-- allows marking the context as only initialising the types with specified providers and leaving all others untouched. - If a type hint includes a default value (declares a class attribute) then the corresponding instance attribute will
be a copy by reference of the class attribute unless there is an explicit provider specified in the context.
This means that
x: int = None
will be initialised asNone
, not as0
.
- Non-intrusive auto-init: function
auto_init
and methodAutoInitContext.auto_init
that initialises instances without making any changes to user's classes. - Added
AutoInitContext.singleton_types
-- allows to specify singleton types non-intrusively.