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}"