跳转到主要内容

Asyncio插件、组件、依赖注入和配置

项目描述

该项目受到Pyramid的启发,并满足我快速创建和维护类似微服务应用的日常需求。

插件机制

  • 插件可能依赖于其他插件

  • 插件产生要运行的任务

  • 上下文注册表充当插件创建的应用组件的存储库

  • 依赖注入创建中间组件

  • 配置源映射到插件特定配置,并且可能被环境变量完全覆盖

  • structlog用于json/tty日志的样板代码

  • 分叉进程并共享绑定套接字

  • pytest插件以减少测试样板代码

您可以通过以下方式启动

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()

组件和依赖注入解决方案

依赖注入依赖于注册的适配器,这些适配器可能是一个函数、一个方法、一个类、一个类方法或一个通用类方法。

依赖关系在组件中查找,或可能通过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()

配置源

buvar.config.ConfigSource只是一个dict,它将任意dict合并为一个。它作为应用变异性唯一真相来源。

您可以将配置值的部分加载到您自定义的attrs类实例中。如果存在,ConfigSource将覆盖环境变量中的值。

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"

buvar.config.Config提供了一种上述方法的快捷方式,需要从它派生出一个具有不同section属性的子类。如果添加了一个buvar.config.ConfigSource组件,他将在一次调用中收到映射的配置。

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

只是structlog的基本模板。

import sys

from buvar import log

log_config = log.LogConfig(tty=sys.stdout.isatty(), level="DEBUG")
log_config.setup()

分支进程和共享套接字

您可以对进程进行分支并绑定共享套接字,以利用可用的CPU,例如为aiohttp微服务提供服务。

INT、TERM、HUP等信号被转发到子进程。

import aiohttp.web
from buvar import fork, plugin, di, context
from buvar_aiohttp import AioHttpConfig


async def hello(request):
    return aiohttp.web.Response(body=b"Hello, world")


async def prepare_aiohttp(load: plugin.Loader):
    await load("buvar_aiohttp")

    app = await di.nject(aiohttp.web.Application)
    app.router.add_route("GET", "/", hello)


context.add(AioHttpConfig(host="0.0.0.0", port=5678))

fork.stage(prepare_aiohttp, forks=0, sockets=["tcp://:5678"])

pytest

提供了一些pytest fixture,以便将您的上下文置于合理的状态

buvar_config_source

一个包含任意应用程序设置的dict

buvar_context

基本上下文分阶段操作。

buvar_stage

处理所有插件的实际阶段。

buvar_load

将插件添加到阶段的加载器。

buvar_plugin_context

当插件准备就绪时,所有插件共享的上下文。

以下标记可以应用于测试

buvar_plugins(plugin, ...)

将所有插件加载到插件上下文中。

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)

项目详情


发布历史 发布通知 | RSS源

下载文件

下载适合您平台的应用程序文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源分布

buvar-0.43.10.tar.gz (33.0 kB 查看哈希值)

上传时间

构建分布

buvar-0.43.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl (739.7 kB 查看哈希值)

上传于 CPython 3.12 manylinux: glibc 2.28+ x86-64 manylinux: glibc 2.5+ x86-64

buvar-0.43.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl (727.6 kB 查看哈希值)

上传于 CPython 3.11 manylinux: glibc 2.28+ x86-64 manylinux: glibc 2.5+ x86-64

buvar-0.43.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl (682.2 kB 查看哈希值)

上传于 CPython 3.10 manylinux: glibc 2.28+ x86-64 manylinux: glibc 2.5+ x86-64

由以下机构支持: