Tutorial / Examples

DOCUMENTATION COMING SOON

1. Hello World

Download example code from [here]

import asyncio, logging
import traceback

from task_thread import TaskThread, reCreate, reSchedule,\
    delete, verbose, signals


class MessageSignal(signals.Signal):
    """A generic message message signal, carrying a python object
    """
    def __init__(self, origin, message):
        self.origin = origin
        self.message = message

    def __str__(self):
        return "<MessageSignal from %s>" % (str(self.origin.getId()))

    def getMessage(self):
        return self.message

Some rtf comments here

class MyThread(TaskThread):

    # *** recurrent tasks that define the functionality of this thread ***

    async def helloTask__(self):
        """A task that prints hello & re-schedules itself
        """
        try:
            # *** define your recurrent task's functionality here ***
            await asyncio.sleep(1)
            self.logger.info("Hello from helloTask__ at %s", self.getId())
            self.tasks.hello_task = await reSchedule(self.helloTask__); return

        except asyncio.CancelledError:
            self.logger.critical("helloTask__: cancelling")

        except Exception as e:
            self.logger.info("helloTask__: failed with '%s', traceback will follow", e)
            traceback.print_exc()

    # *** class ctor ***

    def __init__(self, my_id = 0, parent = None):
        super().__init__(parent = parent)
        self.my_id = my_id


    # *** reimplemented virtual methods: ***

    def initVars__(self):
        """Create & initialize here your tasks with none & create your locks
        """
        # tasks
        self.tasks.hello = None  # self.helloTask__


    @verbose
    async def enter__(self):
        """Everything starts from here.  This cofunction is awaited (i.e. not scheduled as a task)

        - Await for something critical
        - Shedule the re-scheduling tasks
        """
        self.logger.info("enter__ :")
        self.tasks.hello = await reCreate(self.tasks.hello, self.helloTask__)


    @verbose
    async def exit__(self):
        """Close sockets, databases, etc.  Overwrite in child class.
        """
        self.tasks.hello = await delete(self.tasks.hello)
        self.logger.info("exit__ : finished")


    @verbose
    async def signalHandler__(self, signal):
        """Handles a signal that was sent to this thread using insertSignal

        - Return True if thread should keep on running
        - Return False if thread should exit (say, in the case of some fatal error)

        Do subclass this.
        """
        self.logger.info("signalHandler__ : got signal %s", signal)


    def getId(self):
        """Do subclass this.

        Return a unique id for this VirtualThread.
        """
        return self.my_id


    def getInfo(self):
        """Do subclass this

        Returns information string about this thread
        """
        return "<MyThread "+str(self.my_id)+">"



if __name__ == "__main__":
    loglev = logging.DEBUG

    logger = logging.getLogger("MyThread")
    logger.setLevel(loglev)

    thread = MyThread(my_id = "main_thread", parent = None) # no parent, this is the main thread
    loop = asyncio.get_event_loop()
    loop.run_until_complete(thread.run())

2. Parent/Child Structure

Download example code from [here]

import asyncio, logging
import traceback

from task_thread import TaskThread, reCreate, reSchedule,\
    delete, verbose, signals


class MessageSignal(signals.Signal):
    """A generic message message signal, carrying a python object
    """
    def __init__(self, origin, message):
        self.origin = origin
        self.message = message

    def __str__(self):
        return "<MessageSignal from %s>" % (str(self.origin.getId()))

    def getMessage(self):
        return self.message



class MyThread(TaskThread):

    # *** recurrent tasks that define the functionality of this thread ***

    async def helloTask__(self):
        """A task that prints hello & re-schedules itself
        """
        try:
            # *** define your recurrent task's functionality here ***
            await asyncio.sleep(1)
            self.logger.info("Hello from helloTask__ at %s", self.getId())
            self.tasks.hello_task = await reSchedule(self.helloTask__); return

        except asyncio.CancelledError:
            self.logger.critical("helloTask__: cancelling")

        except Exception as e:
            self.logger.info("helloTask__: failed with '%s', traceback will follow", e)
            traceback.print_exc()

    # *** class ctor ***

    def __init__(self, my_id = 0, parent = None):
        super().__init__(parent = parent)
        self.my_id = my_id


    # *** reimplemented virtual methods: ***

    def initVars__(self):
        """Create & initialize here your tasks with none & create your locks
        """
        # tasks
        self.tasks.hello = None  # self.helloTask__


    @verbose
    async def enter__(self):
        """Everything starts from here.  This cofunction is awaited (i.e. not scheduled as a task)

        - Await for something critical
        - Shedule the re-scheduling tasks
        """
        self.logger.info("entry point")
        self.tasks.hello = await reCreate(self.tasks.hello, self.helloTask__)


    @verbose
    async def exit__(self):
        """Close sockets, databases, etc.  Overwrite in child class.
        """
        self.tasks.hello = await delete(self.tasks.hello)
        self.logger.info("exit__ : finished")


    @verbose
    async def signalHandler__(self, signal):
        """Handles a signal that was sent to this thread using insertSignal

        - Return True if thread should keep on running
        - Return False if thread should exit (say, in the case of some fatal error)

        Do subclass this.
        """
        self.logger.info("signalHandler__ : got signal %s", signal)


    def getId(self):
        """Do subclass this.

        Return a unique id for this VirtualThread.
        """
        return self.my_id


    def getInfo(self):
        """Do subclass this

        Returns information string about this thread
        """
        return "<MyThread "+str(self.my_id)+">"


class ChildThread(MyThread):

    async def messageTask__(self):
        """A task that sends a message to its parent thread each 5 seconds
        """
        try:
            # *** define your recurrent task's functionality here ***
            await asyncio.sleep(5)
            self.logger.info("messageTask__: sending a message to parent")

            await self.sigFromChildToParent__(
                MessageSignal(origin = self,
                message = "hello from child " + str(self.getId())))
            self.tasks.message_task = await reSchedule(self.messageTask__); return

        except asyncio.CancelledError:
            self.logger.critical("messageTask__: cancelling")

        except Exception as e:
            self.logger.info("messageTask__: failed with '%s', traceback will follow", e)
            traceback.print_exc()


    def initVars__(self):
        """Create & initialize here your tasks with none & create your locks
        """
        # tasks
        self.tasks.hello   = None  # self.helloTask__
        self.tasks.message = None  # self.messageTask__
        # locks
        self.locks.message_lock = asyncio.Lock()


    @verbose
    async def enter__(self):
        """Everything starts from here.  This cofunction is awaited (i.e. not scheduled as a task)

        - Await for something critical
        - Shedule the re-scheduling tasks
        """
        self.logger.info("entry point")
        self.tasks.hello = await reCreate(self.tasks.hello, self.helloTask__)
        self.tasks.message = await reCreate(self.tasks.message, self.messageTask__)


    @verbose
    async def exit__(self):
        """Close sockets, databases, etc.  Overwrite in child class.
        """
        self.tasks.hello = await delete(self.tasks.hello)
        self.tasks.message = await delete(self.tasks.message)
        self.logger.info("exit__ : finished")



class ParentThread(MyThread):

    @verbose
    async def enter__(self):
        """Everything starts from here.  This cofunction is awaited (i.e. not scheduled as a task)

        - Await for something critical
        - Shedule the re-scheduling tasks
        """
        self.logger.info("entry point")
        self.tasks.hello = await reCreate(self.tasks.hello, self.helloTask__)

        """A child thread is added right away when this thread is started.

        However, child threads can be created and started in any part of your code, for example,
        based on some action coming from other child threads, etc.
        """
        child_thread = ChildThread(my_id = "subthread", parent = self)
        await child_thread.run()
        await self.addChild(child_thread)


    @verbose
    async def childsignalHandler__(self, signal, child):
        """How to handle a certain signal from children?
        """
        self.logger.debug("childsignalHandler__ : got signal %s from child %s", signal, child.getId())
        if isinstance(signal, MessageSignal):
            self.logger.info("Got message %s from child with id %s",
            signal.getMessage(), child.getId())
        else:
            pass


if __name__ == "__main__":
    loglev = logging.DEBUG

    logger = logging.getLogger("ParentThread")
    logger.setLevel(loglev)

    logger = logging.getLogger("ChildThread")
    logger.setLevel(loglev)

    thread = ParentThread(my_id = "parent_thread", parent = None) # no parent, this is the main thread
    loop = asyncio.get_event_loop()
    loop.run_until_complete(thread.run())

3. TCP Server

  • Download server example code from [here]

  • Download client side code from [here]

import asyncio, logging
import traceback

from task_thread import TaskThread, reCreate, reSchedule,\
    delete, MessageSignal, verbose, signals


class TCPConnectionThread(TaskThread):

    def __init__(self, parent = None, reader = None, writer = None):
        super().__init__(parent = parent)
        self.reader = reader
        self.writer = writer
        self.peername, self.peerport = self.writer.get_extra_info("peername")

    def getId(self):
        return str(id(self))

    def getInfo(self):
        return "<TCPConnectionThread %s connected to %s>" % (self.getId(), self.peername)


    def initVars__(self):
        self.tasks.read_socket = None
        self.tasks.write_socket = None


    @verbose
    async def enter__(self):
        self.logger.info("enter__ : %s", self.getInfo())
        self.tasks.read_socket = await reCreate(self.tasks.read_socket, self.readSocket__)


    @verbose
    async def exit__(self):
        self.tasks.read_socket = await delete(self.tasks.read_socket)
        self.tasks.write_socket = await delete(self.tasks.write_socket)
        self.logger.debug("exit__: bye!")


    async def readSocket__(self):
        try:
            try:
                packet = await self.reader.read()
            except Exception as e:
                self.logger.warning("readSocket__ : reading failed : '%s'", e)
                self.logger.info("readSocket__: tcp connection %s terminated", self.getInfo())
                await self.terminate()
            else:
                if len(packet) > 0:
                    # all good!  keep on reading = reschedule this
                    self.logger.info("readSocket__: got packet %s", packet)
                    self.tasks.read_socket = await reSchedule(self.readSocket__); return
                else:
                    self.logger.info("readSocket__: client at %s closed connection", self.peername)
                    await self.terminate()

        except asyncio.CancelledError:
            self.logger.info("readSocket__ : cancelling %s", self.getInfo())

        except Exception as e:
            self.logger.warning("readSocket__: failed with '%s'", e)
            await self.terminate()


    async def writeSocket__(self, packet):
        try:
            self.writer.write(packet)
            # await self.writer.drain() # this would flush
        except Exception as e:
            self.logger.warning("writeSocket__ : writing failed : '%s'", e)
            await self.terminate()


class MasterTCPServerThread(TaskThread):


    def __init__(self, parent = None, name = "thread", pause = 10, port = 5002, max_clients = 10):
        super().__init__(parent = parent)
        self.pause = pause
        self.port = port
        self.max_clients = max_clients
        self.name = name


    def initVars__(self):
        self.server = None
        self.tasks.tcp_server = None


    def getId(self):
        return str(id(self))


    def getInfo(self):
        return "<MasterTCPServerThread %s>" % (self.name)


    @verbose
    async def enter__(self):
        """Everything starts from here.  This cofunction is awaited (i.e. not scheduled as a task)

        - Await for something critical
        - Shedule the re-scheduling tasks
        """
        self.logger.info("entry point")
        self.tasks.tcp_server = await reCreate(self.tasks.tcp_server, self.tcpServer__)
        # now the tasks runs independently


    @verbose
    async def exit__(self):
        self.tasks.tcp_server = await delete(self.tasks.tcp_server)
        self.logger.debug("exit__: bye!")


    @verbose
    async def childsignalHandler__(self, signal, child):
        """How to handle a certain signal from children?
        """
        self.logger.debug("childsignalHandler__ : got signal %s from child %s", signal, child)
        if isinstance(signal, MessageSignal):
            self.logger.info("Got message %s from child with id %s", signal.getMessage(), child.getId())
        else:
            pass

    # *** custom recurrent tasks ***

    async def tcpServer__(self):
        try:
            self.server = await asyncio.start_server(self.handler__, "", self.port)

        except asyncio.CancelledError:
            self.logger.info("tcpServer__ %i: cancel")
            self.server = None

        except Exception as e:
            self.logger.warning("tcpServer__: failed with '%s'", str(e))
            self.logger.warning("tcpServer__: will try again in %s secs", self.pause)
            await asyncio.sleep(self.pause)
            self.tasks.tcp_server = await reSchedule(self.tcpServer__)

        else:
            self.logger.debug("tcpServer__ : new server waiting")


    # *** internal ***

    @verbose
    async def handler__(self, reader, writer):
        self.logger.debug("handler__ : new connection for %s", self.getInfo())

        if len(self.children)>self.max_clients:
            self.logger.warning("handler__ : max number of connections is %s", self.max_clients)
            return

        child_connection = TCPConnectionThread(reader = reader, writer = writer, parent = self)
        await child_connection.run()
        await self.addChild(child_connection)


if __name__ == "__main__":
    loglev = logging.DEBUG

    logger = logging.getLogger("MasterTCPServerThread")
    logger.setLevel(loglev)
    logger = logging.getLogger("TCPConnectionThread")
    logger.setLevel(loglev)

    thread = MasterTCPServerThread(
        parent = None,
        name = "TCPServer"
        )
    loop = asyncio.get_event_loop()
    loop.run_until_complete(thread.run())

4. Data Streaming TCP Server

  • Download example code from [here]

  • Download client side code from [here]

import asyncio, logging, json
import numpy as np
import traceback

from task_thread import TaskThread, reCreate, reSchedule,\
    delete, verbose, signals

INT_NBYTES = 4


def json2bytes(dic):
    """Turn dic into json bytes & append the bytes with the json bytes length
    """
    bytes_ = json.dumps(dic).encode("utf-8")
    le = len(bytes_)
    bytes_ = le.to_bytes(INT_NBYTES, "big") + bytes_
    return bytes_


class PayloadSignal(signals.Signal):
    """A generic message message signal, carrying a python object
    """
    def __init__(self, payload):
        self.payload = payload

    def __str__(self):
        return "<PayloadSignal with %i bytes>" % (len(self.payload))

    def getData(self):
        return self.payload



class TCPConnectionThread(TaskThread):

    def __init__(self, parent = None, reader = None, writer = None):
        super().__init__(parent = parent)
        self.reader = reader
        self.writer = writer
        self.peername, self.peerport = self.writer.get_extra_info("peername")

    def getId(self):
        return str(id(self))

    def getInfo(self):
        return "<TCPConnectionThread %s connected to %s>" % (self.getId(), self.peername)


    def initVars__(self):
        self.tasks.read_socket = None
        self.tasks.write_socket = None


    @verbose
    async def enter__(self):
        self.logger.info("enter__ : %s", self.getInfo())
        self.tasks.write_socket = await reCreate(self.tasks.write_socket, self.writeSocket__)


    @verbose
    async def exit__(self):
        self.tasks.read_socket = await delete(self.tasks.read_socket)
        self.tasks.write_socket = await delete(self.tasks.write_socket)
        self.logger.debug("exit__: bye!")


    async def readSocket__(self):
        try:
            try:
                packet = await self.reader.read(1500)
            except Exception as e:
                self.logger.warning("readSocket__ : reading failed : '%s'", e)
                self.logger.info("readSocket__: tcp connection %s terminated", self.getInfo())
                await self.terminate()
            else:
                if len(packet) > 0:
                    # all good!  keep on reading = reschedule this
                    self.logger.debug("readSocket__: got packet with size %s", len(packet))
                    # send the payload to parent:
                    await self.handlePacket__(packet)
                    self.tasks.read_socket = await reSchedule(self.readSocket__); return
                else:
                    self.logger.info("readSocket__: client at %s closed connection", self.peername)
                    await self.terminate()

        except asyncio.CancelledError:
            self.logger.info("readSocket__ : cancelling %s", self.getInfo())

        except Exception as e:
            self.logger.warning("readSocket__: failed with '%s'", e)
            await self.terminate()


    def resetPacketState__(self, clearbuf = False):
        self.left = INT_NBYTES
        self.len = 0
        self.header = True
        if clearbuf:
            self.buf = bytes(0)


    async def handlePacket__(self, packet):
        """packet reconstructions into blobs of certain length
        """
        if packet is not None:
            self.buf += packet
        if self.header:
            if len(self.buf) >= INT_NBYTES:
                self.len = int.from_bytes(self.buf[0:INT_NBYTES], "big")
                self.header = False # we got the header info (length)
                if len(self.buf) > INT_NBYTES:
                    # sort out the remaining stuff
                    await self.handlePacket__(None)
        else:
            if len(self.buf) >= (INT_NBYTES + self.len):
                # correct amount of bytes have been obtained
                payload = np.frombuffer(self.buf[INT_NBYTES:INT_NBYTES + self.len], dtype=np.uint8)
                await self.sigFromChildToParent__(PayloadSignal(
                    payload = payload))
                # prepare state for next blob
                if len(self.buf) > (INT_NBYTES + self.len):
                    # there's some leftover here for the next blob..
                    self.buf = self.buf[INT_NBYTES + self.len:]
                    self.resetPacketState__()
                    # .. so let's handle that leftover
                    await self.handlePacket__(None)
                else:
                    # blob ends exactly
                    self.resetPacketState__(clearbuf=True)


    async def writeSocket__(self):
        """Send one-time info message
        """
        try:
            packet = json2bytes(
                {
                    "message" : "hello!"
                })
            try:
                self.writer.write(packet)
                # await self.writer.drain() # this would maybe flush
            except Exception as e:
                self.logger.warning("writeSocket__ : writing failed : '%s'", e)
                await self.terminate()
            else:
                """sending info to the client was succesfull, let's start reading
                payload from the client"""
                self.resetPacketState__(clearbuf = True)
                self.tasks.read_socket = await reCreate(self.tasks.read_socket, self.readSocket__)

        except asyncio.CancelledError:
            self.logger.info("writeSocket__ : cancelling %s", self.getInfo())

        except Exception as e:
            self.logger.warning("writeSocket__: failed with '%s'", e)
            await self.terminate()


class MasterTCPServerThread(TaskThread):

    def __init__(self, parent = None, name = "thread", pause = 10, port = 5002, max_clients = 10):
        super().__init__(parent = parent)
        self.pause = pause
        self.port = port
        self.max_clients = max_clients
        self.name = name


    def initVars__(self):
        self.server = None
        self.tasks.tcp_server = None


    def getId(self):
        return str(id(self))


    def getInfo(self):
        return "<MasterTCPServerThread %s>" % (self.name)


    @verbose
    async def enter__(self):
        """Everything starts from here.  This cofunction is awaited (i.e. not scheduled as a task)

        - Await for something critical
        - Shedule the re-scheduling tasks
        """
        self.logger.info("entry point")
        self.tasks.tcp_server = await reCreate(self.tasks.tcp_server, self.tcpServer__)
        # now the tasks runs independently


    @verbose
    async def exit__(self):
        self.tasks.tcp_server = await delete(self.tasks.tcp_server)
        self.logger.debug("exit__: bye!")


    @verbose
    async def childsignalHandler__(self, signal, child):
        """How to handle a certain signal from children?
        """
        self.logger.debug("childsignalHandler__ : got signal %s from child %s", signal, child)
        if isinstance(signal, PayloadSignal):
            self.logger.info("Got %s from child with id %s", str(signal), child.getId())
        else:
            pass

    # *** custom recurrent tasks ***

    async def tcpServer__(self):
        try:
            self.server = await asyncio.start_server(self.handler__, "", self.port)

        except asyncio.CancelledError:
            self.logger.info("tcpServer__ %i: cancel")
            self.server = None

        except Exception as e:
            self.logger.warning("tcpServer__: failed with '%s'", str(e))
            self.logger.warning("tcpServer__: will try again in %s secs", self.pause)
            await asyncio.sleep(self.pause)
            self.tasks.tcp_server = await reSchedule(self.tcpServer__)

        else:
            self.logger.debug("tcpServer__ : new server waiting")


    # *** internal ***

    @verbose
    async def handler__(self, reader, writer):
        self.logger.debug("handler__ : new connection for %s", self.getInfo())

        if len(self.children)>self.max_clients:
            self.logger.warning("handler__ : max number of connections is %s", self.max_clients)
            return

        child_connection = TCPConnectionThread(reader = reader, writer = writer, parent = self)
        await child_connection.run()
        await self.addChild(child_connection)


if __name__ == "__main__":
    # loglev = logging.DEBUG
    loglev = logging.INFO

    logger = logging.getLogger("MasterTCPServerThread")
    logger.setLevel(loglev)
    logger = logging.getLogger("TCPConnectionThread")
    logger.setLevel(loglev)

    thread = MasterTCPServerThread(
        parent = None,
        name = "TCPServer"
        )
    loop = asyncio.get_event_loop()
    loop.run_until_complete(thread.run())