Asyncio plugins, components, dependency injection and configs
Project description
This is heavily inspired by Pyramid and my daily needs to fastly create and maintain microservice like applications.
a plugin mechanic
plugin may depend on other plugins
plugins yield tasks to run
a context registry serves as a store for application components created by plugins
a dependency injection creates intermediate components
a config source is mapped to plugin specific configuration and may be fully overridden by environment vars
structlog boilerplate for json/tty logging
fork the process and share bound sockets
pytest fixtures to reduce testing boilerplate
You bootstrap like following:
from buvar import plugin
plugin.stage("some.module.with.prepare")
# some.module.with.prepare
from buvar import context, plugin
class Foo:
...
async def task():
asyncio.sleep(1)
async def server():
my_component = context.get(Foo)
await asyncio.Future()
# there is also plugin.Teardown and plugin.Cancel
async def prepare(load: plugin.Loader):
await load('.another.plugin')
# create some long lasting components
my_component = context.add(Foo())
# you may run simple tasks
yield task()
# you may run server tasks
yield server()
a components and dependency injection solution
Dependency injection relies on registered adapters, which may be a function, a method, a class, a classmethod or a generic classmthod.
Dependencies are looked up in components or may be provided via kwargs.
from buvar import di
class Bar:
pass
class Foo:
def __init__(self, bar: Bar = None):
self.bar = bar
@classmethod
async def adapt(cls, baz: str) -> Foo:
return Foo()
async def adapt(bar: Bar) -> Foo
foo = Foo(bar)
return foo
async def task():
foo = await di.nject(Foo, baz="baz")
assert foo.bar is None
bar = Bar()
foo = await di.nject(Foo, bar=bar)
assert foo.bar is bar
async def prepare():
di.register(Foo.adapt)
di.register(adapt)
yield task()
a config source
buvar.config.ConfigSource
is just a dict
, which merges
arbitrary dicts into one. It serves as the single source of truth for
application variability.
You can load a section of config values into your custom attrs class instance. ConfigSource will override values by environment variables if present.
config.toml
log_level = "DEBUG"
show_warnings = "yes"
[foobar]
some = "value"
export APP_FOOBAR_SOME=thing
import attr
import toml
from buvar import config
@attr.s(auto_attribs=True)
class GeneralConfig:
log_level: str = "INFO"
show_warnings: bool = config.bool_var(False)
@attr.s(auto_attribs=True)
class FoobarConfig:
some: str
source = config.ConfigSource(toml.load('config.toml'), env_prefix="APP")
general_config = source.load(GeneralConfig)
assert general_config == GeneralConfig(log_level="DEBUG", show_warnings=True)
foobar_config = source.load(FoobarConfig, 'foobar')
assert foobar_config.some == "thing"
There is a shortcut to the above approach provided by
buvar.config.Config
, which requires to be subclassed from it with a
distinct section
attribute. If one adds a buvar.config.ConfigSource
component, he will receive the mapped config in one call.
from buvar import config, plugin
@attr.s(auto_attribs=True)
class GeneralConfig(config.Config):
log_level: str = "INFO"
show_warnings: bool = config.bool_var(False)
@attr.s(auto_attribs=True)
class FoobarConfig(config.Config, section="foobar"):
some: str
async def prepare(load: plugin.Loader):
# this would by typically placed in the main CLI entry point
source = context.add(config.ConfigSource(toml.load('config.toml'), env_prefix="APP"))
# to provide the adapter to di, which could also be done in the main entry point
await load(config)
foobar_config = await di.nject(FoobarConfig)
a structlog
Just structlog boilerplate.
import sys
from buvar import log
log_config = log.LogConfig(tty=sys.stdout.isatty(), level="DEBUG")
log_config.setup()
pytest
There are a couple of pytest fixtures provided to get your context into a reasonable state:
buvar_config_source
A
dict
with arbitrary application settings.buvar_context
The basic context staging operates on.
buvar_stage
The actual stage processing all plugins.
buvar_load
The loader to add plugins to the stage.
buvar_plugin_context
The context all plugins share, when they are prepared.
Following markers may be applied to a test
buvar_plugins(plugin, ...)
Load all plugins into plugin context.
import pytest
async def prepare():
from buvar import context
context.add("foobar")
@pytest.mark.asyncio
@pytest.mark.buvar_plugins("tests.test_testing")
async def test_wrapped_stage_context():
from buvar import context, plugin
assert context.get(str) == "foobar"
assert context.get(plugin.Cancel)
@pytest.mark.asyncio
@pytest.mark.buvar_plugins()
async def test_wrapped_stage_context_load(buvar_load):
from buvar import context, plugin
await buvar_load(prepare)
assert context.get(str) == "foobar"
assert context.get(plugin.Cancel)
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 Distributions
Hashes for buvar-0.43.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 61637ffe7b129b5735c5e37deada24fdb346818460e32ebb8710c35d13e36746 |
|
MD5 | d66acc6f88b6cfe62feb25af7bce29d8 |
|
BLAKE2b-256 | 9c0aa5c20e103ad505e27a41041d5df7f5ffccfb268b37f619f7421f2a69177e |
Hashes for buvar-0.43.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | b3d4b672d9087d5c52ab18ef16953f7946ac8dae8e0195d6493339fdf53156cf |
|
MD5 | d485942d26047bde790d8a8997c96512 |
|
BLAKE2b-256 | ff806009f431b7dfe0ae3c813cb0be852fa2b9410335f0a6f4f2cabf87796e0e |
Hashes for buvar-0.43.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 9b2e79ddf090cecf4ef9280495a61c4ce23b06bdf771c8f58e48170a3da42186 |
|
MD5 | 2f3d619bd4cf4b484817e85251aa693c |
|
BLAKE2b-256 | 3ec22a7c5d8bc0625ad98e9ddbd6a0c3712eede946ae63813ddd97681de6e55e |
Hashes for buvar-0.43.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | c2adfd9425535c422f795ced391ea17f5beb49dae57b8509a73e14e397f2323f |
|
MD5 | 355886f1e0e7500693830a20a7bb595d |
|
BLAKE2b-256 | 1d580a8082c252db4892aed9d7b0665c2090c8966b902703a764878e0cd3e748 |
Hashes for buvar-0.43.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | f2ac0aa3c54327f80ee95e6c6402a7eaa1f3b082fb5865c0e93df46a54b62872 |
|
MD5 | d4de4b6e9035413eb8e8e2deede89bdc |
|
BLAKE2b-256 | 2b260a09ab1b8eb79ed94d01e44531ba43914845465470c45be8a393d78f4922 |