def main(argv: Sequence[str] | None = None) -> int:
"""CLI for managing endpoints on the local system.
!!! note "Usage"
```bash
$ proxystore-endpoint [command] {options}
$ proxystore-endpoint --help
```
"""
argv = argv if argv is not None else sys.argv[1:]
parser = argparse.ArgumentParser(
prog='proxystore-endpoint',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
# https://stackoverflow.com/a/8521644/812183
parser.add_argument(
'-V',
'--version',
action='version',
version=f'%(prog)s {proxystore.__version__}',
)
parser.add_argument(
'--log-level',
choices=['ERROR', 'WARNING', 'INFO', 'DEBUG'],
default='INFO',
help='logging level for CLI and any subprocesses',
)
subparsers = parser.add_subparsers(dest='command')
# Command: configure
parser_configure = subparsers.add_parser(
'configure',
help='configure a new endpoint',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser_configure.add_argument('name', help='name of endpoint')
parser_configure.add_argument(
'--port',
type=int,
default=9753,
help='port the endpoint should listen on',
)
parser_configure.add_argument(
'--server',
default=None,
help='signaling server address for P2P connections',
)
parser_configure.add_argument(
'--max-memory',
default=None,
type=int,
required='--dump-dir' in sys.argv,
help='optional maximum number of bytes to store in memory',
)
parser_configure.add_argument(
'--dump-dir',
default=None,
required='--max-memory' in sys.argv,
help='optional directory to dump objects to if max_memory reached',
)
parser_configure.add_argument(
'--peer-channels',
default=1,
type=int,
help='datachannels per peer connection',
)
# Command: list
subparsers.add_parser(
'list',
help='list all user endpoints',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
# Command: remove
parser_remove = subparsers.add_parser(
'remove',
help='remove an endpoint',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser_remove.add_argument('name', help='name of endpoint')
# Command: start
parser_start = subparsers.add_parser(
'start',
help='start an endpoint',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser_start.add_argument('name', help='name of endpoint')
parser_start.add_argument(
'--no-detach',
action='store_true',
help='do not detach the endpoint process',
)
# Command: stop
parser_stop = subparsers.add_parser(
'stop',
help='stop an endpoint',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser_stop.add_argument('name', help='name of endpoint')
# Source: https://github.com/pre-commit/pre-commit
parser_help = subparsers.add_parser(
'help',
help='show help for a specific command',
)
parser_help.add_argument(
'help_command',
nargs='?',
help='command to show help for',
)
if len(argv) == 0:
argv = ['--help']
# https://stackoverflow.com/questions/46962065
known, unknown = parser.parse_known_args(argv)
args = parser.parse_args(unknown, namespace=known)
if args.command == 'help' and args.help_command is not None:
parser.parse_args([args.help_command, '--help'])
elif args.command == 'help':
parser.parse_args(['--help'])
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(_CLIFormatter())
logging.basicConfig(level=args.log_level, handlers=[handler])
if args.command == 'configure':
return configure_endpoint(
args.name,
port=args.port,
server=args.server,
max_memory=args.max_memory,
dump_dir=args.dump_dir,
peer_channels=args.peer_channels,
)
elif args.command == 'list':
return list_endpoints()
elif args.command == 'remove':
return remove_endpoint(args.name)
elif args.command == 'start':
return start_endpoint(
args.name,
detach=not args.no_detach,
log_level=args.log_level,
)
elif args.command == 'stop':
return stop_endpoint(args.name)
else:
raise NotImplementedError(
f'{args.command} is not a supported command. '
'Use --help for list of commands.',
)
raise AssertionError(f'{args.command} failed to exit with a return code.')