mode.services

Async I/O services that can be started/stopped/shutdown.

class mode.services.Diag(service: ServiceT)

Service diagnostics.

This can be used to track what your service is doing. For example if your service is a Kafka consumer with a background thread that commits the offset every 30 seconds, you may want to see when this happens:

DIAG_COMMITTING = 'committing'

class Consumer(Service):

    @Service.task
    async def _background_commit(self) -> None:
        while not self.should_stop:
            await self.sleep(30.0)
            self.diag.set_flag(DIAG_COMITTING)
            try:
                await self._consumer.commit()
            finally:
                self.diag.unset_flag(DIAG_COMMITTING)

The above code is setting the flag manually, but you can also use a decorator to accomplish the same thing:

@Service.timer(30.0)
async def _background_commit(self) -> None:
    await self.commit()

@Service.transitions_with(DIAG_COMITTING)
async def commit(self) -> None:
    await self._consumer.commit()
set_flag(flag: str) None
unset_flag(flag: str) None
class mode.services.Service(*, beacon: Optional[NodeT] = None, loop: Optional[AbstractEventLoop] = None)

An asyncio service that can be started/stopped/restarted.

Keyword Arguments:
  • beacon (NodeT) – Beacon used to track services in a graph.

  • loop (AbstractEventLoop) – Event loop object.

class Diag(service: ServiceT)

Service diagnostics.

This can be used to track what your service is doing. For example if your service is a Kafka consumer with a background thread that commits the offset every 30 seconds, you may want to see when this happens:

DIAG_COMMITTING = 'committing'

class Consumer(Service):

    @Service.task
    async def _background_commit(self) -> None:
        while not self.should_stop:
            await self.sleep(30.0)
            self.diag.set_flag(DIAG_COMITTING)
            try:
                await self._consumer.commit()
            finally:
                self.diag.unset_flag(DIAG_COMMITTING)

The above code is setting the flag manually, but you can also use a decorator to accomplish the same thing:

@Service.timer(30.0)
async def _background_commit(self) -> None:
    await self.commit()

@Service.transitions_with(DIAG_COMITTING)
async def commit(self) -> None:
    await self._consumer.commit()
set_flag(flag: str) None
unset_flag(flag: str) None
abstract: ClassVar[bool] = False

Set to True if this service class is abstract-only, meaning it will only be used as a base class.

async add_async_context(context: AsyncContextManager) Any
add_context(context: ContextManager) Any
add_dependency(service: ServiceT) ServiceT

Add dependency to other service.

The service will be started/stopped with this service.

add_future(coro: Awaitable) Future

Add relationship to asyncio.Future.

The future will be joined when this service is stopped.

async add_runtime_dependency(service: ServiceT) ServiceT
async_exit_stack: AsyncExitStack
property beacon: NodeT

Beacon used to track services in a dependency graph.

async crash(reason: BaseException) None

Crash the service and all child services.

property crash_reason: Optional[BaseException]
property crashed: bool
classmethod crontab(cron_format: str, *, timezone: Optional[tzinfo] = None) Callable[[Callable], ServiceTask]

Background timer executing periodic task based on Crontab description.

Example

>>> class S(Service):
...
...     @Service.crontab(cron_format='30 18 * * *',
                         timezone=pytz.timezone('US/Pacific'))
...     async def every_6_30_pm_pacific(self):
...         print('IT IS 6:30pm')
...
...     @Service.crontab(cron_format='30 18 * * *')
...     async def every_6_30_pm(self):
...         print('6:30pm UTC')
diag: DiagT
exit_stack: ExitStack
classmethod from_awaitable(coro: Awaitable, *, name: Optional[str] = None, **kwargs: Any) ServiceT
human_tracebacks() str
async itertimer(interval: ~typing.Union[~datetime.timedelta, int, float, str], *, max_drift_correction: float = 0.1, sleep: ~typing.Optional[~typing.Callable[[...], ~typing.Awaitable]] = None, clock: ~typing.Callable[[], float] = <built-in function perf_counter>, name: str = '') AsyncIterator[float]

Sleep interval seconds for every iteration.

This is an async iterator that takes advantage of Timer() to monitor drift and timer oerlap.

Uses Service.sleep so exits fast when the service is stopped.

Note

Will sleep the full interval seconds before returning from first iteration.

Examples

>>> async for sleep_time in self.itertimer(1.0):
...   print('another second passed, just woke up...')
...   await perform_some_http_request()
async join_services(services: Sequence[ServiceT]) None
property label: str

Label used for graphs.

log: CompositeLogger
logger: logging.Logger = <Logger mode.services (WARNING)>
async maybe_start() bool

Start the service, if it has not already been started.

mundane_level = 'info'

The log level for mundane info such as starting, stopping, etc. Set this to "debug" for less information.

on_init_dependencies() Iterable[ServiceT]

Return list of service dependencies for this service.

async remove_dependency(service: ServiceT) ServiceT

Stop and remove dependency of this service.

async restart() None

Restart this service.

restart_count: int = 0

Current number of times this service instance has been restarted.

service_reset() None
set_shutdown() None

Set the shutdown signal.

Notes

If wait_for_shutdown is set, stopping the service will wait for this flag to be set.

property shortlabel: str

Label used for logging.

property should_stop: bool

Return True if the service must stop.

shutdown_timeout: float = 60.0

Time to wait for shutdown flag set before we give up.

async sleep(n: Union[timedelta, int, float, str]) None

Sleep for n seconds, or until service stopped.

async start() None
property started: bool

Return True if the service was started.

property state: str

Service state - as a human readable string.

async stop() None

Stop the service.

classmethod task(fun: Callable[[Any], Awaitable[None]]) ServiceTask

Decorate function to be used as background task.

Example

>>> class S(Service):
...
...     @Service.task
...     async def background_task(self):
...         while not self.should_stop:
...             await self.sleep(1.0)
...             print('Waking up')
classmethod timer(interval: Union[timedelta, int, float, str], *, exec_first: bool = False, name: Optional[str] = None, max_drift_correction: float = 0.1) Callable[[Callable], ServiceTask]

Background timer executing every n seconds.

Example

>>> class S(Service):
...
...     @Service.timer(1.0)
...     async def background_timer(self):
...         print('Waking up')
tracebacks() Mapping[str, str]
async transition_with(flag: str, fut: Awaitable, *args: Any, **kwargs: Any) Any
classmethod transitions_to(flag: str) Callable

Decorate function to set and reset diagnostic flag.

async wait(*coros: Union[Future, Generator[Any, None, Any], Awaitable, Event], timeout: Optional[Union[timedelta, int, float, str]] = None) WaitResult

Wait for coroutines to complete, or until the service stops.

async wait_first(*coros: Union[Future, Generator[Any, None, Any], Awaitable, Event], timeout: Optional[Union[timedelta, int, float, str]] = None) WaitResults
wait_for_shutdown: bool = False

Set to True if .stop must wait for the shutdown flag to be set.

async wait_for_stopped(*coros: Union[Future, Generator[Any, None, Any], Awaitable, Event], timeout: Optional[Union[timedelta, int, float, str]] = None) bool
async wait_many(coros: Iterable[Union[Future, Generator[Any, None, Any], Awaitable, Event]], *, timeout: Optional[Union[timedelta, int, float, str]] = None) WaitResult
async wait_until_stopped() None

Wait until the service is signalled to stop.

class mode.services.ServiceBase(*, loop: Optional[AbstractEventLoop] = None)

Base class for services.

abstract: ClassVar[bool] = True

Set to True if this service class is abstract-only, meaning it will only be used as a base class.

log: CompositeLogger
logger: Logger = None
property loop: AbstractEventLoop
mode.services.crontab(cron_format: str, *, timezone: Optional[tzinfo] = None) Callable[[Callable], ServiceTask]

Background timer executing periodic task based on Crontab description.

Example

>>> class S(Service):
...
...     @Service.crontab(cron_format='30 18 * * *',
                         timezone=pytz.timezone('US/Pacific'))
...     async def every_6_30_pm_pacific(self):
...         print('IT IS 6:30pm')
...
...     @Service.crontab(cron_format='30 18 * * *')
...     async def every_6_30_pm(self):
...         print('6:30pm UTC')
mode.services.task(fun: Callable[[Any], Awaitable[None]]) ServiceTask

Decorate function to be used as background task.

Example

>>> class S(Service):
...
...     @Service.task
...     async def background_task(self):
...         while not self.should_stop:
...             await self.sleep(1.0)
...             print('Waking up')
mode.services.timer(interval: Union[timedelta, int, float, str], *, exec_first: bool = False, name: Optional[str] = None, max_drift_correction: float = 0.1) Callable[[Callable], ServiceTask]

Background timer executing every n seconds.

Example

>>> class S(Service):
...
...     @Service.timer(1.0)
...     async def background_timer(self):
...         print('Waking up')