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

import contextlib
from autowire import impl

@impl.implementation
@contextlib.contextmanager
def implementaion(resource, context):
    print('Enter')
    try:
        yield 'Value'
    finally:
        print('Leave')

basic.implement(implementation)

The implementation should be an instance of Implementation implementation() decorator converts contextmanager function to instance of Implementation.

You can also create resource with implementation at once.

import contextlib
from autowire import resource

@resource.create
@impl.implementation
@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

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

@impl.implementation
@contextlib.contextmanager
def context_implementation(resource, context):
    yield 'Custom Value'

child_context.provide(basic, context_implementation)

provide() also can be used as a decorator

@child_context.provide(basic)
@impl.implementation
@contextlib.contextmanager
def context_implementation(resource, context):
    yield 'Custom Value'

Resolving Resource

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

Functional Interface

You don’t always need neither resource or context for your implementation. And also you don’t need any resource management for providing implementation.

There are some useful utilities for these use cases.

First one is contextual(). It converts simple context manager function to implementation.

import contextlib
from autowire import impl

@res.implement
@impl.contexual
@contextlib.contextmanager
def res_impl():
    value = build_resource()
    try:
        yield value
    finally:
        destroy_resource(value)

Second one is contextmanager(), which is a shortcut for above.

from autowire import impl

@res.implement
@impl.contextmanager
def res_impl():
    value = build_resource()
    try:
        yield value
    finally:
        destroy_resource(value)

Last one is plain(). It converts plain function to implementation.

from autowire import impl

@res.implement
@impl.plain
def get_impl():
    return "Implementation!"

All functional interface decorators preserve decorated function’s interface.

>>> get_impl()
"Implementation!"

Dependency Inejection

Basically, you can resolve some other resources from the context in impementations.

@impl.implementation
@contextlib.contextmanager
def some_implementation(resource, 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 autowired() decorator to do this simply.

@other_resource.implement
@impl.autowired('basic', basic)
@impl.plain
def with_other_resource(basic)
    reutnr 'Hello, {}'.format(basic)

First argument is a name that be used for keyword argument. Second one is resource to be resolved. When the name is not provided, The name property of resource will be used by default.

autowired() decorator can be only used for functional interface implementations.