Async-first dependency injection library for Python
Project description
aiodine
aiodine provides async-first dependency injection in the style of Pytest fixtures for Python 3.6+.
Installation
pip install aiodine
Usage
Note: this section is under construction.
aiodine revolves around two concepts: providers and consumers.
Providers are:
- Explicit: once a provider is defined, a consumer can use it by declaring it as a function parameter.
- Modular: a provider can itself use other provider.
- Flexible: providers are reusable within the scope of a function or a whole session, and support a variety of syntaxes (asynchronous or synchronous, function or generator) to make provisioning resources fun again.
Providers
Providers make a resource available to consumers within a certain scope. They are created by decorating a provider function with @aiodine.provider
.
Here's a "hello world" provider:
import aiodine
@aiodine.provider
async def hello():
return "Hello, aiodine!"
Tip: synchronous provider functions are also supported!
Providers are available in two scopes:
function
: the provider's value is re-computed everytime it is consumed.session
: the provider's value is computed only once (the first time it is consumed) and is reused in subsequent calls.
By default, providers are function-scoped.
Consumers
Once a provider has been declared, it can be used by consumers. A consumer is built by decoratinga consumer function with @aiodine.consumer
. A consumer can declare a provider as one of its parameters and aiodine will inject it at runtime.
Here's an example consumer:
@aiodine.consumer
async def show_friendly_message(hello):
print(hello)
Tip: synchronous consumer functions are also supported!
All aiodine consumers are asynchronous, so you'll need to run them in an asynchronous context:
from asyncio import run
async def main():
await show_friendly_message()
run(main()) # "Hello, aiodine!"
Of course, a consumer can declare non-provider parameters too. These are then regular parameters and will have to be passed when calling the consumer.
@aiodine.consumer
async def show_friendly_message(hello, repeat=1):
for _ in range(repeat):
print(hello)
async def main():
await show_friendly_message(repeat=10)
Providers consuming other providers
Providers can also consume other providers. To do so, providers need to be frozen so that the dependency graph can be correctly resolved:
import aiodine
@aiodine.provider
async def email():
return "user@example.net"
@aiodine.provider
async def send_email(email):
print(f"Sending email to {email}…")
aiodine.freeze() # <- Ensures that `send_email` has resolved `email`.
A context manager is also available:
import aiodine
with aiodine.exit_freeze():
@aiodine.provider
async def email():
return "user@example.net"
@aiodine.provider
async def send_email(email):
print(f"Sending email to {email}…")
Note: thanks to this, providers can be declared in any order.
Generator providers
Generator providers can be used to perform cleanup operations after a provider has gone out of scope.
import os
import aiodine
@aiodine.provider(scope="session")
async def testing():
initial = os.getenv("APP_ENV")
os.environ["APP_ENV"] = "TESTING"
try:
yield
finally:
os.environ.pop("APP_ENV")
if initial is not None:
os.environ["APP_ENV"] = initial
Tip: synchronous generator providers are also supported!
Lazy async providers
When the provider function is asynchronous, its return value is awaited before being injected into the consumer. In other words, providers are eager by default.
You can mark a provider as lazy in order to defer awaiting the provided value to the consumer. This is useful when the provider needs to be conditionally evaluated.
from asyncio import sleep
import aiodine
@aiodine.provider(lazy=True)
async def expensive_computation():
await sleep(10)
return 42
@aiodine.consumer
async def compute(expensive_computation, cache=None):
if cache:
return cache
return await expensive_computation
FAQ
Why "aiodine"?
aiodine contains aio as in asyncio, and di as in Dependency Injection The last two letters are only there to make this library's name pronounce like iodine, the chemical element.
Changelog
See CHANGELOG.md.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file aiodine-0.1.3.tar.gz
.
File metadata
- Download URL: aiodine-0.1.3.tar.gz
- Upload date:
- Size: 10.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.3
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 1b4ceff63341536eaaa933626866e7a856e7a465fc3f415b9cd62b1601e2b260 |
|
MD5 | 85a2d437b23163fd70ee56f861943d76 |
|
BLAKE2b-256 | 972d2a2cf977b6a2fed730a6254086f6c153b8d0cbff5915aa43079bdc1a3674 |
File details
Details for the file aiodine-0.1.3-py3-none-any.whl
.
File metadata
- Download URL: aiodine-0.1.3-py3-none-any.whl
- Upload date:
- Size: 14.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.3
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | dda1d99bd34438cdef7a958f413fca795b1e26f159f39ce3b16c07088b787cc7 |
|
MD5 | 591697220180380e1e6e3ac4d2de8b01 |
|
BLAKE2b-256 | 096514defe01c7680b114bf5cbe7dafe85d55dade2c3bbc03520254719264a14 |