Plan9 9p2000 protocol¶
The library provides basic asynchronous 9p2000 implementation.
- class pyroute2.plan9.server.Plan9ServerSocket(address=None, use_socket=None)¶
9p2000 server.
Requires either an IP address to listen on, or an open SOCK_STREAM socket to operate. An IP example, suitable to establish IPC between processes in one network:
from pyroute2 import Plan9ClientSocket, Plan9ServerSocket address = ('localhost', 8149) p9server = Plan9ServerSocket(address=address) p9client = Plan9ClientSocket(address=address)
Server/client running on a socketpair() suitable for internal API within one process, or between parent/child processes:
from socket import socketpair from pyroute2 import Plan9ClientSocket, Plan9ServerSocket server, client = socketpair() p9server = Plan9ServerSocket(use_socket=server) p9client = Plan9ClientSocket(use_socket=client)
- register_function(func, inode, loader=<function loads>, dumper=<function Plan9ServerSocket.<lambda>>)¶
Register a function to an file.
The file usage:
write(): write arguments for the call as a json dictionary of keyword arguments to the file data.
- read():
if the arguments were written to the data, call the function and write the result to the file data
read the file data and return to the client
call(): protocol extension, Tcall = 80, Rcall = 81, make this in one turn.
Registering a function:
def communicate(a, b): return a + b def example_register(): fd = p9server.filesystem.create('test_func') p9server.register_function(communicate, fd)
Communication using Twrite/Tread:
import json async def example_write(): fid = await p9client.fid('test_func') await p9client.write( fid, json.dumps({"a": 17, "b": 25}) ) msg = await p9client.read(fid) response = json.loads(msg['data']) assert response == 42
Same, using a command line 9p client from plan9port:
$ echo '{"a": 17, "b": 25}' | 9p -a localhost:8149 write test_func $ 9p -a localhost:8149 read test_func 42
And using a mounted file system via FUSE client from plan9port:
$ 9pfuse localhost:8149 mnt $ echo '{"a": 17, "b": 25}' >mnt/test_func $ cat mnt/test_func 42
And the same, but using Tcall:
async def example_call(): fid = await p9client.fid('test_func') response = await p9client.call(fid, argv=(17, 25)) assert response == 42
And finnaly run this code:
async def main(): server_task = await p9server.async_run() example_register() await p9client.start_session() await example_write() await example_call() server_task.cancel() asyncio.run(main())
- async async_run()¶
Return the server asyncio task.
Using this task one can stop the server:
async def main(): server = Plan9ServerSocket(address=('localhost', 8149)) server_task = await server.async_run() # ... server is running here server_task.cancel() # ... server is stopped asyncio.run(main())
To forcefully close all client connections and stop the server immediately from a registered function, one can pass this task to the function, cancel it, and raise Plan9Exit() exception:
import functools from pyroute2.plan9 import Plan9Exit server_sock, client_sock = socketpair() def test_exit_func(context): if 'server_task' in context: context['server_task'].cancel() raise Plan9Exit('server stopped upon client request') return 'server starting, please wait' async def server(): p9server = Plan9ServerSocket(use_socket=server_sock) context = {} inode = p9server.filesystem.create('stop') p9server.register_function( functools.partial(test_exit_func, context), inode ) context['server_task'] = await p9server.async_run() try: await context['server_task'] except asyncio.exceptions.CancelledError: pass assert context['server_task'].cancelled()
- run()¶
A simple synchronous runner.
Uses event_loop.run_forever().
- class pyroute2.plan9.client.Plan9ClientSocket(address=None, use_socket=None)¶
9p2000 client.
address -- ('address', port) to listen on
use_socket -- alternatively, provide a connected SOCK_STRAM socket
- async start_session()¶
Initiate 9p2000 session.
One must await this routine before running any other requests.
- async version()¶
Tverion request. No arguments required.
- async attach(aname='')¶
Tattach request.
aname (optional) -- aname to attach to
- async walk(path, newfid=None, fid=None)¶
Twalk request.
path -- string path to the file
newfid (optional) -- use this fid to store the info
fid (optional) -- use this fid to walk from, otherwise walk from the current directory for this client session
- async fid(path)¶
Walk the path and return fid to the required file.
path -- string path to the file
- async read(fid, offset=0, count=8192)¶
Tread request.
fid -- fid of the file to read from
offset (optional, default 0) -- read offset
count (optional, default 8192) -- read count
- async write(fid, data, offset=0)¶
Twrite request.
fid -- fid of the file to write to
data -- bytes to write
offset (optional, default 0) -- write offset
- async call(fid, argv=None, kwarg=None, data=b'', data_arg='data', loader=<function loads>)¶
Tcall request.
fid -- fid of the file that represents a registered function
argv (optional) -- positional arguments as an iterable
kwarg (optional) -- keyword arguments as a dictionary
data (opional) -- optional binary data
data_arg (optional) -- name of the argument to use with the binary data
loader (optional, default json.loads) -- loader for the response data