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)