Plan9 9p2000 protocol — pyroute2 0.9.4 documentation

# Plan9 9p2000 protocol

The library provides basic asynchronous 9p2000 implementation.

class pyroute2.plan9.server.Plan9ServerSocket(address=None, use_socket=None, use_event_loop=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():

        1. if the arguments were written to the data, call the function and write the result to the file data

        2. 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, use_event_loop=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