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 to resource by autowire.impl.implement() decorator

import contextlib
from autowire import impl

@impl.implement(basic)
@contextlib.contextmanager
def implementaion(context):
    print('Enter')
    try:
        yield 'Value'
    finally:
        print('Leave')

The implementation should be a function that takes BaseContext as parameter and returns ContextManager. ((BaseContext) -> ContextManager)

You can also create resource with implementation at once.

import contextlib
from autowire import resource

@resource.implement()
@contextlib.contextmanager
def basic(context):
    print('Enter')
    try:
        yield('Value')
    finally:
        print('Leave')

The function ``basic`` will be a resource and also be a function.

Context

Context is a resource implementation container.

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

You can define context like this

from autowire import Context

context = Context()

Each contexts can have parent context.

child_context = Context(context)

The root parent of all contexts is root_context.

Providing Implementation to context

Providing implementaion for resource in context is almost same with providing to resource.

Simply wrap resource like context(resource) and pass it to implementer.

@impl.implement(child_context(basic))
@contextlib.contextmanager
def context_implementation(context):
    yield 'Custom Value'

Resolving Resource

So, resources can have different implementations for each contexts. This is how to resolve implementation of them.

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

with child_context.resolve(basic) as value:
    print(value)

The output will be like this

Enter
Value
Leave
Custom Value

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

null = Resource('null', __name__)

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

Dependency Inejection

Basically, you can resolve some other resources in impementation from context in arguments.

other_resource = Resource('other_resource', __name__)

@impl.implement(other_resource)
@contextlib.contextmanager
def implement_other(context):
    with context.resolve(basic) as value:
        yield 'Hello, {}'.format(value)

By the above code, we injected basic resource to implementation of other_resource.

This is clear, but little boilerplateful.

So we provide some convinience utils.

First one is autowire.impl.contextual()

This decorator convert any functions that returns ContextManager to implementation type.

@impl.contextual(other_resource, basic)
@contextlib.contextmanager
def with_other_resource(basic)
    yield 'Hello, {}'.format(basic)

But it doesn’t change interface of with_other_resource so you can still use it like

with with_other_resource('Basic Mockup') as message:
    print(message)

When you don’t even want a context management, you can use autowire.impl.plain() which means plain function.

@impl.plain(other_resource, basic)
def get_other_resource(basic):
    return 'Hello, {}'.format(basic)

You can surely use this as plain function

print(get_other_resource('Basic Mockup'))

Since contextual() and plain() don’t change original function’s interface, you have care about decorators for implementation.

When you want to apply decoration to a original function, just use that normally

@impl.plain(other_resource, basic)
@decorator
def get_other_resource(basic):
    return 'Hello, {}'.format(basic)

But, when you want to apply them to actual implementation, add them to keyword-only argument decorators.

@impl.plain(other_resource, basic, decorators=[decorator])
def get_other_resource(basic):
    return 'Hello, {}'.format(basic)