Sayd#

A performant asynchronous communication protocol in pure Python.

This library was developed with simplicity and performance in mind, with modern practices of Python development, currently in a test state.

Documentation Reference

Install#

Works on Python 3.7.4+.

pip install sayd

Development#

You need to have installed poetry for dependencies management (how to).

git clone https://github.com/lw016/sayd
cd sayd
poetry install

Run tests#

poetry run tox -e tests

Build docs#

poetry run tox -e docs

Features#

  • Client and server implementations

  • Reliable TCP persistent connection

  • Auto reconnection (client)

  • Multiple asynchronous connections (server)

  • Blacklist of clients (server)

  • TLS encryption

  • Proxy Protocol V2 support (server)

  • Data transmitted as dictionaries (json)

  • Broadcast (server)

  • Remote function callbacks

  • Built-in CLI utility to generate self-signed certificates

Roadmap#

  • Add support to Unix socket

  • Implement TLS certificate authentication

CLI#

The built-in CLI utility (sayd) can be used to generate self-signed certificates to encrypt the connection.

sayd --help

Usage example#

Server#

import logging
import asyncio

from sayd import SaydServer


logging.basicConfig(
        format="[%(name)s][%(levelname)s] %(asctime)s - %(message)s",
        datefmt="%Y/%m/%d %H:%M:%S"
        )

logger = logging.getLogger("SERVER")
logger.setLevel(logging.INFO)


server = SaydServer(logger=logger)


@server.callback("message")
async def msg(address: tuple, instance: str, data: dict) -> dict:
    return {"greetings": "Hello from server!"}


async def main() -> None:
    await server.start()


    while True:
        result = await server.call("msg", {"greetings": "Hi!"}) # Broadcast call.
        print(result)

        await asyncio.sleep(1)


    await server.stop()


if __name__ == "__main__":
    asyncio.run(main())

Client#

import logging
import asyncio

from sayd import SaydClient


logging.basicConfig(
        format="[%(name)s][%(levelname)s] %(asctime)s - %(message)s",
        datefmt="%Y/%m/%d %H:%M:%S"
        )

logger = logging.getLogger("CLIENT")
logger.setLevel(logging.INFO)


client = SaydClient(logger=logger)


@client.callback("msg")
async def msg(instance: str, data: dict) -> dict:
    return {"greetings": "Hello from client!"}


async def main() -> None:
    await client.start()


    while True:
        result = await client.call("message", {"greetings": "Hi!"})
        print(result)

        await asyncio.sleep(1)


    await client.stop()


if __name__ == "__main__":
    asyncio.run(main())

Indices and tables#