Getting Started

This page shows that how Autowire provides resource management and dependency injection.

Resource

Resource is a implementable resorce definition. It can have default implementation and also can have different implementation for each contexts.

Resource have two type of names. First is name, and second one is namespace name.

from autowire import Resource

basic = Resource("basic", __name__)

The first parameter is name, and second parameter is namespace. Generally you can pass __name__ to second parameter.

Note

Since python uses dot (.) as module separator, name cannot have any dot characters.

The resource will make canonical name with two names

>>> basic.canonical_name
"some.module.name.basic"

You can set default implementation with context manager to resource by contextual()

import contextlib

@basic.contextual()
@contextlib.contextmanager
def implementation():
    print("Enter")
    try:
        yield "Value"
    finally:
        print("Leave")

Or you can use a plain function for implementation by using plain()

@basic.plain()
def implementation():
    return "Hello!"

This is equivalent to

import contextlib

@basic.contextual()
@contextlib.contextmanager
def implementation():
    yield "Hello!"

If you just want provide a constant value for a resource, use set_constant()

basic.set_constant("Hello!")

Container

Context dependency injection & resource implementation provider

You can provide different container for each environment, so that you can configure running environment, testing options, injecting mock-ups.

You can define context like this

from autowire import Container

container = Container()

Each contexts can have parent context.

child_container = Container(container)

Providing Implementation to the container

You can provide an implementation for the resource by using provide() method.

import contextlib

from autowire.implementation import Implementation

class BasicImplementation(Implementation):
    @contextlib.contextmanager
    def reify(self, resource, context):
        yield "some-value"

child_container.provide(basic, BasicImplementation())

Actually, you don’t have to make a subclass of Implementation for each resource. plain(), contextual() provides similar functionalities to Resource

You can replace above example with

@child_container.contextual(basic)
@contextlib.contextmanager
def with_basic():
    yield "some-value"

Almost same with autowire.resource.Resource.contextual() but you have to pass the resource as the first argument.

The Container version of set_constant() is provide_constant().

child_container.provide_constant(basic, "some-value")

Context

To reify your resource implementation, you have to use Context.

You can get a root context by using context()

with container.context() as context:
    value = context.resolve(basic)
    print(value)

The output will be like this

Enter
Value
Leave

Since context() is a context manager, you should use this method with with statement.

When there’s no implementation to be provided, it will raise ResourceNotProvidedError

null = Resource("null", __name__)

with container.context() as context:
    context.resolve(null)  # raise ResourceNotProvidedError

Resource Management

The reason why you should use context object with with statement is resource management.

Every resolved resources will be released on context’s __exit__ call.

But if you want manage the lifecycle manually, you can use drain() for releasing all resources.

try:
    value = context.resolve(basic)
    print(value)
finally:
    context.drain()

is equivalent to

with context:
    value = context.resolve(basic)
    print(value)

Child context

Contexts are nestable. You can hold some resources on your parent context and keep use them.

with container.context() as context:
    pool = context.resolve(global_connection_pool)
    connection = pool.get_connection()

    with context.child() as child:
        # This is same with pool above and retained until parent context be drained
        pool2 = child.resolve(global_connection_pool)

        # But this will be releases on child context be drained
        tx = child.resolve(transaction)

Dependency Inejection

You can inject the dependencies when you using plain(), contextual()

Just pass depending resources to the decorator

hello = Resource("hello", __name__)

@hello.plain(basic)
def get_hello(basic: str):
    return f"Hello, {basic}"