service manager for asyncio
Project description
Facet
Service manager for asyncio.
Reason
mode
tries to do too much job:
- Messy callbacks (
on_start
,on_started
,on_crashed
, etc.). - Inheritance restrict naming and forces
super()
calls. - Forced logging module and logging configuration.
Features
- Simple (
start
,stop
,dependencies
andadd_task
). - Configurable via inheritance (graceful shutdown timeout).
- Mixin (no
super()
required). - Requires no runner engine (
Worker
,Runner
, etc.) just plainawait
orasync with
.
License
facet
is offered under MIT license.
Requirements
- python 3.6+
Usage
import asyncio
import logging
from facet import ServiceMixin
class B(ServiceMixin):
def __init__(self):
self.value = 0
async def start(self):
self.value += 1
logging.info("b started")
async def stop(self):
self.value -= 1
logging.info("b stopped")
class A(ServiceMixin):
def __init__(self):
self.b = B()
@property
def dependencies(self):
return [self.b]
async def start(self):
logging.info("a started")
async def stop(self):
logging.info("a stopped")
logging.basicConfig(level=logging.DEBUG)
asyncio.run(A().run())
This will produce:
INFO:root:b started
INFO:root:a started
Start and stop order determined by strict rule: dependencies must be started first and stopped last. That is why B
starts before A
. Since A
may use B
in start
routine.
Hit ctrl-c
and you will see:
INFO:root:a stopped
INFO:root:b stopped
Traceback (most recent call last):
...
KeyboardInterrupt
Stop order is reversed, since A
may use B
in stop
routine. Any raised exception propagates to upper context. facet
do not trying to be too smart.
Service can be used as a context manager. Instead of
asyncio.run(A().run())
Code can look like:
async def main():
async with A() as a:
assert a.b.value == 1
await a.wait()
asyncio.run(main())
Another service feature is add_task
method:
class A(ServiceMixin):
async def task(self):
await asyncio.sleep(1)
logging.info("task done")
async def start(self):
self.add_task(self.task())
logging.info("start done")
logging.basicConfig(level=logging.DEBUG)
asyncio.run(A().run())
This will lead to background task creation and handling:
INFO:root:start done
INFO:root:task done
Any non-handled exception on background task will lead the whole service stack crashed. This is also a key feature to fall down fast and loud.
All background tasks will be cancelled and awaited on service stop.
You can manage dependencies start/stop to start sequently, parallel or mixed. Like this:
class A(ServiceMixin):
def __init__(self):
self.b = B()
self.c = C()
self.d = D()
@property
def dependencies(self):
return [
[self.b, self.c],
self.d,
]
This leads to first b
and c
starts parallel, after they successfully started d
will try to start, and then a
itself start will be called. And on stop routine a
stop called first, then d
stop, then both b
and c
stops parallel.
The rule here is first nesting level is sequential, second nesting level is parallel
API
Here is public methods you get on inheritance/mixin:
wait
async def wait(self):
Wait for service stop. Service must be started. This is useful when you use service as a context manager.
run
async def run(self):
Run service and wait until it stop.
graceful_shutdown_timeout
@property
def graceful_shutdown_timeout(self):
return 10
How much total time in seconds wait for stop routines. This property can be overriden with subclass:
class CustomServiceMixin(ServiceMixin):
@property
def graceful_shutdown_timeout(self):
return 60
dependencies
@property
def dependencies(self):
return []
Should return iterable of current service dependencies instances.
running
@property
def running(self) -> bool:
Check if service is running
add_task
def add_task(self, coro) -> asyncio.Task:
Add background task.
start
async def start(self):
pass
Start routine.
stop
async def stop(self):
pass
Stop routine.
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 facet-0.8.0.tar.gz
.
File metadata
- Download URL: facet-0.8.0.tar.gz
- Upload date:
- Size: 4.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.4.1 importlib_metadata/4.6.0 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.61.1 CPython/3.7.1
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 78e34d0a6e30f15cd18cca99acc1fd0c9299858dd3945ca70ae40f90975cdea7 |
|
MD5 | 2f4857cf320af8d6b74cd996302ade92 |
|
BLAKE2b-256 | 7b8fc6a5e6553c6e63533aa8ece86bbb4e92d3cb40cef452e790f013320768be |
File details
Details for the file facet-0.8.0-py3-none-any.whl
.
File metadata
- Download URL: facet-0.8.0-py3-none-any.whl
- Upload date:
- Size: 4.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.4.1 importlib_metadata/4.6.0 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.61.1 CPython/3.7.1
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7a6e7ff2d03119957f0c10ba1095ce94abb0ea463efd90d8b82678e5f330965e |
|
MD5 | 7f5ea5d993d039238444dd387d294c38 |
|
BLAKE2b-256 | c3d689c56418539ce453678e7d0b7cc7be178a7f82906607736d7c52d5e40f86 |