auto-init 0.0.5
Dependency injection thanks to type hints in Python 3.6+
License: MIT
Requires Python: >=3.6.0
Keywords: dependency injection type hinting typing

=========
auto-init
=========

**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.

Introduction
++++++++++++

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)?

.. code-block:: python

    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!

Simple Example
--------------

.. code-block:: python

    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.


Not So Simple Example
---------------------

.. code-block:: python

    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

Installation
++++++++++++

.. code-block:: shell

    pip install auto-init


API
+++

``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 under ``auto_init/examples/`` .
        If ``factory`` is not supplied, the ``instance_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.


Changelog
+++++++++

v0.0.5
------

* Supports initialising attributes annotated with ``typing.List`` and ``typing.Dict`` as empty lists or dictionaries.
  Other ``typing.*``-annotated attributes are initialised as ``None``.
* 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`` and ``dataclasses``
  dependencies for Python 3.7.
* Travis CI setup.

v0.0.4
------

* 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.

v0.0.3
------

* 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 as ``None``, not as ``0``.

v0.0.2
------

* Non-intrusive auto-init: function ``auto_init`` and method ``AutoInitContext.auto_init`` that initialises instances
  without making any changes to user's classes.
* Added ``AutoInitContext.singleton_types`` -- allows to specify singleton types non-intrusively.