Source code for runtimepy.commands.server

"""
An entry-point for the 'server' command.
"""

# built-in
from argparse import ArgumentParser as _ArgumentParser
from argparse import Namespace as _Namespace
from typing import Any

# third-party
from vcorelib.args import CommandFunction as _CommandFunction

# internal
from runtimepy.commands.arbiter import arbiter_cmd
from runtimepy.commands.common import FACTORIES, arbiter_args, cmd_with_jit

SSL_PASSTHROUGH = ["cafile", "capath", "cadata", "certfile"]
PASSTHROUGH = SSL_PASSTHROUGH + ["keyfile"]


[docs] def port_name(args: _Namespace, port: str = "port") -> str: """Get the name for a connection factory's port.""" return f"{args.factory}_{'udp' if args.udp else 'tcp'}_{port}"
[docs] def server_data(args: _Namespace) -> dict[str, Any]: """Get server data based on command-line arguments.""" return { "factory": args.factory, "kwargs": get_kwargs(args, port=f"${port_name(args)}", host=args.host), }
[docs] def is_websocket(args: _Namespace) -> bool: """Determine if the specified factory uses WebSocket or not.""" return "websocket" in args.factory.lower()
[docs] def is_ssl(kwargs: dict[str, Any]) -> bool: """Determine if server arugments indicate SSL use.""" return any(x in kwargs for x in SSL_PASSTHROUGH)
[docs] def get_kwargs(args: _Namespace, **kwargs) -> dict[str, Any]: """Get boilerplate kwargs.""" new_kwargs: dict[str, Any] = {**kwargs} # Pass additional arguments through. print(args) for opt in PASSTHROUGH: value = getattr(args, opt, None) if value is not None: new_kwargs[opt] = value return new_kwargs
[docs] def client_data(args: _Namespace) -> dict[str, Any]: """Get client data based on command-line arguments.""" port = f"${port_name(args)}" arg_list: list[Any] = [] kwargs: dict[str, Any] = {} if is_websocket(args): arg_list.append( f"ws{'s' if is_ssl(get_kwargs(args)) else ''}://localhost:{port}" ) elif not args.udp: kwargs["host"] = "localhost" kwargs["port"] = port else: kwargs["remote_addr"] = ["localhost", port] result = { "name": port_name(args, port="client"), "defer": True, "factory": args.factory, } if arg_list: result["args"] = arg_list if kwargs: result["kwargs"] = kwargs return result
[docs] def config_data(args: _Namespace) -> dict[str, Any]: """Get configuration data for the 'server' command.""" servers = [] clients = [] if not args.udp: servers.append(server_data(args)) else: clients.append( { "name": port_name(args, port="server"), "factory": args.factory, "kwargs": get_kwargs( args, local_addr=["0.0.0.0", f"${port_name(args)}"] ), } ) # Add a loopback connection if specified. if args.loopback: clients.append(client_data(args)) return { "includes": [FACTORIES] + args.configs, "clients": clients, "servers": servers, "ports": [ { "name": port_name(args), "port": args.port, "type": "udp" if args.udp else "tcp", } ], }
[docs] def server_cmd(args: _Namespace) -> int: """Execute the server command.""" return cmd_with_jit(arbiter_cmd, args, config_data(args))
[docs] def add_server_cmd(parser: _ArgumentParser) -> _CommandFunction: """Add server-command arguments to its parser.""" with arbiter_args(parser, nargs="*"): for optional in PASSTHROUGH: parser.add_argument( f"--{optional}", help="passed directly to instantiation" ) parser.add_argument( "--host", default="0.0.0.0", help="host address to listen on (default: '%(default)s')", ) parser.add_argument( "-p", "--port", default=0, type=int, help="port to listen on (default: %(default)s)", ) parser.add_argument( "-u", "--udp", action="store_true", help="whether or not this is a UDP-based server " "(otherwise it must be a TCP-based server)", ) parser.add_argument( "-l", "--loopback", action="store_true", help="if true a client of the same connection type is added", ) parser.add_argument( "factory", help="name of connection factory to create server for" ) return server_cmd