as-rpc


Nameas-rpc JSON
Version 1.0.2 PyPI version JSON
download
home_pagehttps://github.com/bowen-xu/aio_rpc
SummaryRemote Procedure Caller (RPC) based-on Asynchronous IO and Socket
upload_time2024-08-12 16:12:26
maintainerNone
docs_urlNone
authorBowen Xu
requires_python>=3.7.0
licenseMIT
keywords asyncio rpc socket remote procedure call
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # as_rpc

`as_rpc` is a python package for Remote Procedure Call (RPC) based-on *Asynchronous IO* and *Socket*.

It supports bidirectional calls, and it has very high efficiency.

## Quick Start

To install `as-rpc`, run the command

```
pip install as-rpc
```

Let's run the program first, and then some more details on how to use `as-rpc` will be given.

Suppose we have two files, one for server and the other for client.

Server Example (see also `test_server.py`)
```Python
from as_rpc import AioRpcServer, rpc
import asyncio

server = AioRpcServer()

@rpc(server, "test")
def print_test(content):
    print("hello world!", content)
    return str(content)


@rpc(server, 0)
def test0(*args):
    return 1

@rpc(server, 1)
def test1():
    s = "this is test1."
    print(s)
    return s

@rpc(server, 2)
async def test2():
    s = "this is test2."
    print(s)
    return s

@rpc(server, 'Foo')
class Foo:
    @rpc(server, 3)
    async def test3(self):
        s = "this is test3."
        print(s)
        return s
    
    @rpc(server, 4)
    def test4(self):
        s = "this is test4."
        print(s)
        return s

@rpc(server, "call_client")
async def call_client(client_id):
    print("call client")
    msg = await server.async_call_func(client_id, "test", "msg from server")
    print(msg)

server.run()
```

Client Example (see also `test_client.py`)
```Python
import asyncio
from as_rpc import AioRpcClient, RpcServerObject, rpc
from time import time

def callback(data):
    print(data)

client = AioRpcClient()

client.init()


def main():
    print('main')
    client.call_func(1, callback)
    client.call_func("test", callback, RpcServerObject(client.id))

async def main2():
    s = await client.async_call_func(1)
    print('async', s)

async def main3():
    foo = await client.async_instantiate('Foo')
    print(foo)
    print(await client.async_call_async_method(foo, 3))
    print(await client.async_call_method(foo, 4))

@rpc(client, "test")
def test(msg):
    print(msg)
    return "return from client"

loop = asyncio.get_event_loop()
loop.call_soon(main)
loop.run_until_complete(main2())
loop.run_until_complete(main3())
loop.run_until_complete(client.async_call_async_func("call_client", client.id))
```

In one terminal, run the command
```
python test_server.py
```

In another terminal, run the command
```
python test_client.py
```

You will see the outputs from the server

```
this is test1.
hello world! None
this is test1.
this is test3.
this is test4.
call client
return from client
```
and the outputs from the client
```
main
('this is test1.', None)
('None', None)
async this is test1.
-8991251603859186137
this is test3.
this is test4.
msg from server
```

In the client, the program calls some functions in the server, gets the return values, and prints them.

### Create and Run a Server/Client

To create a server, first import related objects
```Python
from as_rpc import AioRpcServer, rpc
```
and then instantiate a server by
```Python
server = AioRpcServer()
```

To run the server, you can either call `server.init()` and then start the event-loop of `asyncio`
```Python
server.init()
loop = asyncio.get_event_loop()
loop.run_forever()
```
or just call `server.run()` which packs the code above
```Python
server.run()
```

To create a client, first import related objects
```Python
from as_rpc import AioRpcClient, rpc
```
and then instantiate a client by
```Python
client = AioRpcClient()
```

To run the client, call `client.init()` and then start the event-loop of `asyncio`
```Python
client.init()
loop = asyncio.get_event_loop()
loop.run_forever()
```

### Register Functions

You can register a function or async function by adding a *decorator* on a function, for example
```Python
@rpc(server, 0)
def test0(*args):
    return 0

@rpc(server, "test1")
async def test1(*args):
    return 1
```
In `@rpc(server, name)`, the first parameter is the server instance, while the second parameter is the name denoting the corresponding function. Client can call this function by the name defined here. For example, in client
```Python
s = await client.async_call_async_func("test1")
```
There are four types of calling a function: `call_func()`, `async_call_func()`, `call_async_func()`, and `async_call_async_func()`. The meaning of them will be illustrated later (see the section "**Call Functions**").

In client programe, functions can also be registered in the same way.
```Python
@rpc(client, "test")
def test(msg):
    print(msg)
    return "return from client"
```

### Call Functions

For a client, there are four types of calling a function:
- `call_func(name_func, callback, *args)`: call a normal function directly in the remote. The first parameter is the function name in the remote (server or client); the second parameter `callback` is a function which would be called after the result from the remote is returned. It could be `None` if the returned value is not concerned. The subsequent `args` is a list of arguments passed to the remote.
- `async_call_func(name_func, *args)`: call a normal function asynchronously in the remote. You can use `await async_call_func(...)` to get the returned value, so there is no need to claim the callback function.
- `call_async_func(name_func, callback, *args)`: call an async function directly in the remote. The function in the remote is asynchronous, defined with keyword `async`. The parameters are the same as `call_func()`.
- `async_call_async_func(name_func, *args)`: call an async function asynchronously in the remote. The parameters are the same as `call_async_func()`.

For a server, there are also four types of calling a function, with the same names as the above. The difference is that there is an additional parameter in the first postion of each of the four, i.e., `client_id`. 
- `call_func(client_id, name_func, callback, *args)`,
- `async_call_func(client_id, name_func, *args)`,
- `call_async_func(client_id, name_func, callback, *args)`,
- `async_call_async_func(client_id, name_func, *args)`.

The parameter `client_id` determines which client to call.

An example of calling a function by server is
```Python
@rpc(server, "call_client")
async def call_client(client_id):
    print("call client")
    msg = await server.async_call_func(client_id, "test", "msg from server")
    print(msg)
```

All the clients' ids are avaible through `server.clients.keys()`.

### Instantiate Classes and Call Methods

For a client, you can create an instance of class in the remote by `instantiate(<Remote-Class-Marker>)` or `async_instantiate(<Remote-Class-Marker>)`. The returned value is the handler of the remote instance.

To call a method of it, you can use the four
- `call_method(self, name_self, name_method, callback, *args)`,
- `async_call_method(self, name_self, name_method, *args)`,
- `call_async_method(self, name_self, name_method, callback, *args)`,
- `async_call_async_method(self, name_self, name_method, *args)`.

The difference among the four types is similar to the previous.

An example to instantiate a class and to call methods is
```Python
foo = await client.async_instantiate('Foo')
print(foo)
print(await client.async_call_async_method(foo, 3))
print(await client.async_call_method(foo, 4))
```

For a server, you can do similar things, except that you need to pass a parameter `client_id` for each calling.

That's all about the usage of as-rpc. You can raise issues in the github repo if you have questions, want to report bugs, or need more features.

## Benchmark

| Framework | Speed (req/s) |
| ---- | ----- |
| [zero (v0.4.1)](https://github.com/Ananto30/zero) | 2173 |
| as-rpc (this repo) | *10244* |

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/bowen-xu/aio_rpc",
    "name": "as-rpc",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.7.0",
    "maintainer_email": null,
    "keywords": "asyncio, rpc, socket, remote procedure call",
    "author": "Bowen Xu",
    "author_email": "xubowen@pku.edu.cn",
    "download_url": "https://files.pythonhosted.org/packages/f8/85/b2383946c2e11da779f2d4f1ab43fd9ec6ab518d39d5034d97ffc61f94c5/as-rpc-1.0.2.tar.gz",
    "platform": null,
    "description": "# as_rpc\n\n`as_rpc` is a python package for Remote Procedure Call (RPC) based-on *Asynchronous IO* and *Socket*.\n\nIt supports bidirectional calls, and it has very high efficiency.\n\n## Quick Start\n\nTo install `as-rpc`, run the command\n\n```\npip install as-rpc\n```\n\nLet's run the program first, and then some more details on how to use `as-rpc` will be given.\n\nSuppose we have two files, one for server and the other for client.\n\nServer Example (see also `test_server.py`)\n```Python\nfrom as_rpc import AioRpcServer, rpc\nimport asyncio\n\nserver = AioRpcServer()\n\n@rpc(server, \"test\")\ndef print_test(content):\n    print(\"hello world!\", content)\n    return str(content)\n\n\n@rpc(server, 0)\ndef test0(*args):\n    return 1\n\n@rpc(server, 1)\ndef test1():\n    s = \"this is test1.\"\n    print(s)\n    return s\n\n@rpc(server, 2)\nasync def test2():\n    s = \"this is test2.\"\n    print(s)\n    return s\n\n@rpc(server, 'Foo')\nclass Foo:\n    @rpc(server, 3)\n    async def test3(self):\n        s = \"this is test3.\"\n        print(s)\n        return s\n    \n    @rpc(server, 4)\n    def test4(self):\n        s = \"this is test4.\"\n        print(s)\n        return s\n\n@rpc(server, \"call_client\")\nasync def call_client(client_id):\n    print(\"call client\")\n    msg = await server.async_call_func(client_id, \"test\", \"msg from server\")\n    print(msg)\n\nserver.run()\n```\n\nClient Example (see also `test_client.py`)\n```Python\nimport asyncio\nfrom as_rpc import AioRpcClient, RpcServerObject, rpc\nfrom time import time\n\ndef callback(data):\n    print(data)\n\nclient = AioRpcClient()\n\nclient.init()\n\n\ndef main():\n    print('main')\n    client.call_func(1, callback)\n    client.call_func(\"test\", callback, RpcServerObject(client.id))\n\nasync def main2():\n    s = await client.async_call_func(1)\n    print('async', s)\n\nasync def main3():\n    foo = await client.async_instantiate('Foo')\n    print(foo)\n    print(await client.async_call_async_method(foo, 3))\n    print(await client.async_call_method(foo, 4))\n\n@rpc(client, \"test\")\ndef test(msg):\n    print(msg)\n    return \"return from client\"\n\nloop = asyncio.get_event_loop()\nloop.call_soon(main)\nloop.run_until_complete(main2())\nloop.run_until_complete(main3())\nloop.run_until_complete(client.async_call_async_func(\"call_client\", client.id))\n```\n\nIn one terminal, run the command\n```\npython test_server.py\n```\n\nIn another terminal, run the command\n```\npython test_client.py\n```\n\nYou will see the outputs from the server\n\n```\nthis is test1.\nhello world! None\nthis is test1.\nthis is test3.\nthis is test4.\ncall client\nreturn from client\n```\nand the outputs from the client\n```\nmain\n('this is test1.', None)\n('None', None)\nasync this is test1.\n-8991251603859186137\nthis is test3.\nthis is test4.\nmsg from server\n```\n\nIn the client, the program calls some functions in the server, gets the return values, and prints them.\n\n### Create and Run a Server/Client\n\nTo create a server, first import related objects\n```Python\nfrom as_rpc import AioRpcServer, rpc\n```\nand then instantiate a server by\n```Python\nserver = AioRpcServer()\n```\n\nTo run the server, you can either call `server.init()` and then start the event-loop of `asyncio`\n```Python\nserver.init()\nloop = asyncio.get_event_loop()\nloop.run_forever()\n```\nor just call `server.run()` which packs the code above\n```Python\nserver.run()\n```\n\nTo create a client, first import related objects\n```Python\nfrom as_rpc import AioRpcClient, rpc\n```\nand then instantiate a client by\n```Python\nclient = AioRpcClient()\n```\n\nTo run the client, call `client.init()` and then start the event-loop of `asyncio`\n```Python\nclient.init()\nloop = asyncio.get_event_loop()\nloop.run_forever()\n```\n\n### Register Functions\n\nYou can register a function or async function by adding a *decorator* on a function, for example\n```Python\n@rpc(server, 0)\ndef test0(*args):\n    return 0\n\n@rpc(server, \"test1\")\nasync def test1(*args):\n    return 1\n```\nIn `@rpc(server, name)`, the first parameter is the server instance, while the second parameter is the name denoting the corresponding function. Client can call this function by the name defined here. For example, in client\n```Python\ns = await client.async_call_async_func(\"test1\")\n```\nThere are four types of calling a function: `call_func()`, `async_call_func()`, `call_async_func()`, and `async_call_async_func()`. The meaning of them will be illustrated later (see the section \"**Call Functions**\").\n\nIn client programe, functions can also be registered in the same way.\n```Python\n@rpc(client, \"test\")\ndef test(msg):\n    print(msg)\n    return \"return from client\"\n```\n\n### Call Functions\n\nFor a client, there are four types of calling a function:\n- `call_func(name_func, callback, *args)`: call a normal function directly in the remote. The first parameter is the function name in the remote (server or client); the second parameter `callback` is a function which would be called after the result from the remote is returned. It could be `None` if the returned value is not concerned. The subsequent `args` is a list of arguments passed to the remote.\n- `async_call_func(name_func, *args)`: call a normal function asynchronously in the remote. You can use `await async_call_func(...)` to get the returned value, so there is no need to claim the callback function.\n- `call_async_func(name_func, callback, *args)`: call an async function directly in the remote. The function in the remote is asynchronous, defined with keyword `async`. The parameters are the same as `call_func()`.\n- `async_call_async_func(name_func, *args)`: call an async function asynchronously in the remote. The parameters are the same as `call_async_func()`.\n\nFor a server, there are also four types of calling a function, with the same names as the above. The difference is that there is an additional parameter in the first postion of each of the four, i.e., `client_id`. \n- `call_func(client_id, name_func, callback, *args)`,\n- `async_call_func(client_id, name_func, *args)`,\n- `call_async_func(client_id, name_func, callback, *args)`,\n- `async_call_async_func(client_id, name_func, *args)`.\n\nThe parameter `client_id` determines which client to call.\n\nAn example of calling a function by server is\n```Python\n@rpc(server, \"call_client\")\nasync def call_client(client_id):\n    print(\"call client\")\n    msg = await server.async_call_func(client_id, \"test\", \"msg from server\")\n    print(msg)\n```\n\nAll the clients' ids are avaible through `server.clients.keys()`.\n\n### Instantiate Classes and Call Methods\n\nFor a client, you can create an instance of class in the remote by `instantiate(<Remote-Class-Marker>)` or `async_instantiate(<Remote-Class-Marker>)`. The returned value is the handler of the remote instance.\n\nTo call a method of it, you can use the four\n- `call_method(self, name_self, name_method, callback, *args)`,\n- `async_call_method(self, name_self, name_method, *args)`,\n- `call_async_method(self, name_self, name_method, callback, *args)`,\n- `async_call_async_method(self, name_self, name_method, *args)`.\n\nThe difference among the four types is similar to the previous.\n\nAn example to instantiate a class and to call methods is\n```Python\nfoo = await client.async_instantiate('Foo')\nprint(foo)\nprint(await client.async_call_async_method(foo, 3))\nprint(await client.async_call_method(foo, 4))\n```\n\nFor a server, you can do similar things, except that you need to pass a parameter `client_id` for each calling.\n\nThat's all about the usage of as-rpc. You can raise issues in the github repo if you have questions, want to report bugs, or need more features.\n\n## Benchmark\n\n| Framework | Speed (req/s) |\n| ---- | ----- |\n| [zero (v0.4.1)](https://github.com/Ananto30/zero) | 2173 |\n| as-rpc (this repo) | *10244* |\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Remote Procedure Caller (RPC) based-on Asynchronous IO and Socket",
    "version": "1.0.2",
    "project_urls": {
        "Homepage": "https://github.com/bowen-xu/aio_rpc"
    },
    "split_keywords": [
        "asyncio",
        " rpc",
        " socket",
        " remote procedure call"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "f885b2383946c2e11da779f2d4f1ab43fd9ec6ab518d39d5034d97ffc61f94c5",
                "md5": "f8772f66f626164110b1347703bac256",
                "sha256": "8b94f44a129adba46eeb6d41b7f1998439d715b0e80828b7b0c375345a3dc096"
            },
            "downloads": -1,
            "filename": "as-rpc-1.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "f8772f66f626164110b1347703bac256",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7.0",
            "size": 12560,
            "upload_time": "2024-08-12T16:12:26",
            "upload_time_iso_8601": "2024-08-12T16:12:26.575669Z",
            "url": "https://files.pythonhosted.org/packages/f8/85/b2383946c2e11da779f2d4f1ab43fd9ec6ab518d39d5034d97ffc61f94c5/as-rpc-1.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-12 16:12:26",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "bowen-xu",
    "github_project": "aio_rpc",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [],
    "lcname": "as-rpc"
}
        
Elapsed time: 0.38281s