Coder Social home page Coder Social logo

wsrpc-aiohttp's Introduction

WSRPC aiohttp

Github Actions

Coveralls

Latest Version

python wheel

Python Versions

license

Easy to use minimal WebSocket Remote Procedure Call library for aiohttp servers.

See online demo and documentation with examples.

Features

  • Call server functions from the client side;
  • Call client functions from the server (for example to notify clients about events);
  • Async connection protocol: both server or client are able to call several functions and get responses as soon as each response would be ready in any order.
  • Fully async server-side functions;
  • Transfer any exceptions from a client side to the server side and vise versa;
  • Ready-to-use frontend-library without dependencies;
  • Thread-based websocket handler for writing fully-synchronous backend code (for synchronous database drivers etc.)
  • Protected server-side methods (cliens are not able to call methods, starting with underline directly);
  • Signals for introspection

Installation

Install via pip:

pip install wsrpc-aiohttp

You may want to install optional ujson library to speedup message serialization/deserialization:

pip install ujson

Python module provides client js library out of the box. But for pure javascript applications you can install standalone js client library using npm:

npm install @wsrpc/client

Usage

Backend code:

import logging
from time import time

import aiohttp.web
from wsrpc_aiohttp import Route, STATIC_DIR, WebSocketRoute, decorators


log = logging.getLogger(__name__)


# This class can be called by client.
# Connection object will have this class instance after calling route-alias.
class TestRoute(Route):
    # This method will be executed when client calls route-alias
    # for the first time.
    def init(self, **kwargs):
        # Python __init__ must be return "self".
        # This method might return anything.
        return kwargs

    # This method named by camelCase because the client can call it.
    @decorators.proxy
    async def getEpoch(self):

        # You can execute functions on the client side
        await self.do_notify()

        return time()

    # This method calls function on the client side
    @decorators.proxy
    async def do_notify(self):
        awesome = 'Somebody executed test1.getEpoch method!'
        await self.socket.call('notify', result=awesome)


app = aiohttp.web.Application()
app.router.add_route("*", "/ws/", WebSocketAsync)  # Websocket route
app.router.add_static('/js', STATIC_DIR)  # WSRPC js library
app.router.add_static('/', ".")  # Your static files

# Stateful request
# This is the route alias TestRoute as "test1"
WebSocketAsync.add_route('test1', TestRoute)

# Stateless request
WebSocketAsync.add_route('test2', lambda *a, **kw: True)


if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG)
    aiohttp.web.run_app(app, port=8000)

Frontend code:

<script type="text/javascript" src="/js/wsrpc.min.js"></script>
<script>
    var url = (window.location.protocol==="https):"?"wss://":"ws://") + window.location.host + '/ws/';
    RPC = new WSRPC(url, 8000);

    // Configure client API, that can be called from server
    RPC.addRoute('notify', function (data) {
        console.log('Server called client route "notify":', data);
        return data.result;
    });
    RPC.connect();

    // Call stateful route
    // After you call that route, server would execute 'notify' route on the
    // client, that is registered above.
    RPC.call('test1.getEpoch').then(function (data) {
        console.log('Result for calling server route "test1.getEpoch": ', data);
    }, function (error) {
        alert(error);
    });

    // Call stateless method
    RPC.call('test2').then(function (data) {
        console.log('Result for calling server route "test2"', data);
    });
</script>

Build

Just run

```bash
poetry run nox
```

Versioning

This software follows Semantic Versioning

wsrpc-aiohttp's People

Contributors

alvassin avatar alviner avatar dizballanze avatar leenr avatar mosquito avatar pyup-bot avatar sv-ivanov avatar tzoiker avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

wsrpc-aiohttp's Issues

Broadcasting issue

Hello I really like this lib, thanks for it.

I'm trying to use WebSocketBase.broadcast function to call functions on the connected js clients.

I call it like

def notify_story_change(story):
    print("calling")
    WebSocketAsync.broadcast('story_change',story=story)

But it raises the unexpected keyword.

ERROR:wsrpc_aiohttp.websocket.common:create_task() got an unexpected keyword argument 'story'
Traceback (most recent call last):
  File "/Users/ciscer01/Documents/Dev/LiveHacks/show_runner/venv/lib/python3.7/site-packages/wsrpc_aiohttp/websocket/common.py", line 222, in on_message
    method, serial, *args, **kwargs
  File "/Users/ciscer01/Documents/Dev/LiveHacks/show_runner/venv/lib/python3.7/site-packages/wsrpc_aiohttp/websocket/common.py", line 178, in handle_method
    result = await self._executor(partial(callee, *args, **kwargs))
  File "/Users/ciscer01/Documents/Dev/LiveHacks/show_runner/venv/lib/python3.7/site-packages/wsrpc_aiohttp/websocket/handler.py", line 122, in broadcast
    client.call, func, callback, **kwargs
TypeError: create_task() got an unexpected keyword argument 'story'

I think that or I didn't understnad how to call this function (which is quite possible) or
that there is an issue is at
tasks.append(loop.create_task( client.call, func, callback, **kwargs)
in, this doesn't look to me to respect the create_task() signature.

I've solved my issue by creating a modified broadcast function

def broadcast(func, **kwargs):
    """ Call remote function on all connected clients

    :param func: Remote route name
    :param callback: Function which receive responses
    """

    loop = asyncio.get_event_loop()

    tasks = []

    for client in WebSocketAsync.get_clients().values():
        tasks.append(loop.create_task(
            client.call(func, **kwargs)
        ))

    return asyncio.wait(tasks, loop=loop,
                        return_when=asyncio.ALL_COMPLETED)

Thanks again.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.