Skip to main content

Async-first dependency injection library for Python

Project description

aiodine

python pypi travis black codecov license

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

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

A consumer can also define any extra non-provider parameters. These must be declared after provider parameters in order for aiodine to correctly inject the provided values to the correct parameters. When calling the consumer, extra arguments can be passed as usual.

@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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aiodine-0.1.0.tar.gz (10.2 kB view details)

Uploaded Source

Built Distribution

aiodine-0.1.0-py3-none-any.whl (13.5 kB view details)

Uploaded Python 3

File details

Details for the file aiodine-0.1.0.tar.gz.

File metadata

  • Download URL: aiodine-0.1.0.tar.gz
  • Upload date:
  • Size: 10.2 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

Hashes for aiodine-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1b7811dfb0ee893bbf4b4f0f112ceff02224a6530c53664f31fec98af25181a4
MD5 6a64ea529611198eaf428cc2a78bbde3
BLAKE2b-256 1a2a55ca3a1ff2a82b63cec7cf5b70f2dab2fd11cb477d878a08ed6fffd1fd37

See more details on using hashes here.

File details

Details for the file aiodine-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: aiodine-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 13.5 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

Hashes for aiodine-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 feeb50df4d8a5bcce8571509c9620439d8d2792befd287959ab34492e945bf6d
MD5 0062d525a2c57c7dea97e83e16c548fb
BLAKE2b-256 d0c7e74842f61315de6f3e988ac669887be386fbd0eb7083ee47831eb9f03456

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page