Metadata-Version: 2.1
Name: buvar
Version: 0.30.1
Summary: Asyncio plugins, components, dependency injection and configs
Home-page: https://gitlab.com/diefans/buvar
Author: Oliver Berger
Author-email: diefans@gmail.com
License: UNKNOWN
Platform: UNKNOWN
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Framework :: AsyncIO
Classifier: License :: OSI Approved :: Apache Software License
Classifier: License :: OSI Approved :: MIT License
Requires-Python: >=3.7,<4.0
Description-Content-Type: text/x-rst
Provides-Extra: tests
Provides-Extra: orjson
Provides-Extra: all
Requires-Dist: attrs (<20.0,>=19.1)
Requires-Dist: cattrs (<2.0,>=1.0)
Requires-Dist: multidict (<5.0,>=4.5)
Requires-Dist: structlog (<20.0,>=19.1)
Requires-Dist: toml (<0.11,>=0.10)
Requires-Dist: tomlkit (<0.6.0,>=0.5.3)
Requires-Dist: typing-inspect (>=0.4.0<0.5)
Requires-Dist: aiofile (<2)
Requires-Dist: cached-property (<2)
Requires-Dist: prance[osv] (<1)
Provides-Extra: all
Requires-Dist: aiohttp; extra == 'all'
Provides-Extra: orjson
Requires-Dist: orjson (<3.0,>=2.5.0); extra == 'orjson'
Provides-Extra: tests
Requires-Dist: pytest (<5.0,>=4.6); extra == 'tests'
Requires-Dist: pytest-cov (<3.0,>=^2.7); extra == 'tests'
Requires-Dist: pytest-asyncio (<0.11,>=0.10.0); extra == 'tests'
Requires-Dist: pytest-benchmark (>=3.2.2<4.0); extra == 'tests'
Requires-Dist: mock (>=3.0<4.0); extra == 'tests'
Requires-Dist: pytest-mock (>=1.10<2.0); extra == 'tests'
Requires-Dist: pytest-watch (>=4.2<5.0); extra == 'tests'
Requires-Dist: pytest-randomly (>=3.1<4.0); extra == 'tests'
Requires-Dist: pytest-doctestplus (>=0.5<1.0); extra == 'tests'
Requires-Dist: pytest-aiohttp; extra == 'tests'
Requires-Dist: pdbpp; extra == 'tests'

Búvár
=====

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 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 needs

- structlog boilerplate for json/tty logging


You bootstrap like following:

.. code-block:: python

    from buvar import plugin

    plugin.stage("some.module.with.plugin.function")


.. code-block:: python

   # some.module.with.plugin.function
   from buvar import context, plugin

   class Foo:
       ...


   async def task():
       asyncio.sleep(1)


   async def server():
       my_component = context.get(Foo)
       await asyncio.Future()


   # you may omit include in arguments
   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, arguments.


.. code-block:: python

   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)



a config source
---------------

`buvar.config.ConfigSource` is just a `dict`, which merges
arbitrary dicts into one. It serves a 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`

.. code-block:: toml

   log_level = "DEBUG"
   show_warnings = "yes"

   [foobar]
   some = "value"


.. code-block:: bash

   export APP_FOOBAR_SOME=thing


.. code-block:: python

   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.

.. code-block:: python

   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 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 inthe main entry point
       await load(config)
       foobar_config = await di.nject(FoobarConfig)


a structlog
-----------

Just `structlog`_ boilerplate.

.. code-block:: python

   import sys

   from buvar import log

   log.setup_logging(sys.stdout.isatty(), general_config.log_level)


.. _Pyramid: https://github.com/Pylons/pyramid
.. _structlog: https://www.structlog.org/en/stable/
.. _attrs: https://www.attrs.org/en/stable/


