:Version: 1.3.0
:Web: http://mode.readthedocs.org/
:Download: http://pypi.python.org/pypi/mode
:Source: http://github.com/fauststream/mode
:Keywords: async, service, framework, actors, bootsteps, graph

What is Mode?
=============

Mode is a library for Python AsyncIO, using the new ``async/await`` syntax
in Python 3.6 to define your program as a set of services.

When starting a larger project using ``asyncio``, it immediately became
apparent that we needed a way to manage the different services running in the
program.  Questions such as "how do we shutdown the event loop" is frequently
answered by telling users to "wait for all coroutines in
asyncio.Task.all_tasks", but we needed more control over what services
where stopped, in what order and what services can we safely shutdown without
waiting for current operations to complete.

So for us the answer was to create a generic ``Service`` class that handles
this for us, including creating pretty graphs of active services in the
system, and what they are currently doing.

Heavily inspired by Celery/RabbitMQ bootsteps, you could say it's a less
formal version of that, where the graph is built at runtime.

Creating a Service
==================

To define a service, simply subclass and fill in the methods
to do stuff as the service is started/stopped etc.::

    class MyService(Service):

        async def on_start(self) -> None:
            print('Im starting now')

        async def on_started(self) -> None:
            print('Im ready')

        async def on_stop(self) -> None:
            print('Im stopping now')

To start the service, call ``await service.start()``::

    await service.start()

Or you can use ``mode.Worker`` (or a subclass of this) to start your
services-based asyncio program from the console::

    if __name__ == '__main__':
        imoport mode
        worker = mode.Worker(MyService(), loglevel='INFO', logfile=None)
        worker.execute_from_commandline()

It's a Graph!
=============

Services can start other services, coroutines, and background tasks.

1) Starting other services using ``add_depenency``::

    class MyService(Service):

        def on_init(self) -> None:
           self.add_dependency(OtherService(loop=self.loop))

2) Start a list of services using ``on_init_dependencies``::

    class MyService(Service):

        def on_init_dependencies(self) -> None:
            return [
                ServiceA(loop=self.loop),
                ServiceB(loop=self.loop),
                ServiceC(loop=self.loop),
            ]

3) Start a future/coroutine (that will be waited on to complete on stop)::

    class MyService(Service):

        async def on_start(self) -> None:
            self.add_future(self.my_coro())

        async def my_coro(self) -> None:
            print('Executing coroutine')

4) Start a background task::

    class MyService(Service):

        @Service.task
        async def _my_coro(self) -> None:
            print('Executing coroutine')


5) Start a background task that keeps running::

    class MyService(Service):

        @Service.task
        async def _my_coro(self) -> None:
            while not self.should_stop:
                # NOTE: self.sleep will wait for one second, or
                #       until service stopped/crashed.
                await self.sleep(1.0)
                print('Background thread waking up')
