123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- import shlex
- from argparse import Namespace
- from typing import Callable
-
- from .clock import Clock
- from .errors import DuplicatedCommandError, ArgumentParserError
- from .parser import CommandParser
-
- class Command(object):
-
- _commands = dict()
-
- def __init__(self, name:str, run_fun:Callable[[Clock, Namespace], str],
- **argument_parser_kwargs:dict):
- """ Register a new command
- @param name : The command
- @param run_fun : The callable handling the command (taking two
- arguments, the Clock instance and parsed args)
- @throws DuplicatedCommandError on duplicated name
- """
- if name in self.__class__._commands:
- msg = 'A command named %r allready exists' % name
- raise DuplicatedCommandError(msg)
-
- self.__name = name
- self.__parser = CommandParser(prog=name, **argument_parser_kwargs)
- self.__run = run_fun
-
- self.__class__._commands[self.__name] = self
-
- @property
- def name(self) -> str:
- """ The command name """
- return self.__name
-
- @property
- def description(self) -> str:
- """ The command description """
- return self.__parser.description
-
- @property
- def usage(self) -> str:
- """ The command usage """
- return self.__parser.format_usage().strip()
-
- @property
- def help(self) -> str:
- """ The command help text """
- return self.__parser.format_help()
-
- @staticmethod
- def fmt_ok(message:str='') -> str:
- """ Format a success command result message """
- return 'OK:%s' % message
-
- @staticmethod
- def fmt_err(reason:str) -> str:
- """ Format an error command result message """
- return 'ERR:%s' % reason
-
- @classmethod
- def list(cls) -> list[str]:
- """ Return the list of all command names """
- return list(cls._commands.keys())
-
- @classmethod
- def all(cls) -> dict:
- """ Return a dict associating command name and Command instance """
- return {k:v for k,v in cls._commands.items()}
-
- @classmethod
- def run_command(cls, clock:Clock, command:str) -> str:
- """ Run a command on the given Clock instance
- @param clock : The Clock instance
- @param command : A string with the command name and its arguments
- """
- try:
- argv = shlex.split(command)
- except ValueError as expt:
- return cls.fmt_err(expt)
-
- if len(argv) == 0:
- return cls.fmt_err('Empty command')
-
- if argv[0] not in cls._commands:
- return cls.fmt_err('Unknown command %r' % argv[0])
-
- return cls._commands[argv[0]]._run(clock, argv)
-
- def add_argument(self, name:str, *args, **kwargs):
- """ Add an argument to the CommandParser instance.
-
- Same signature than argparse.ArgumentParser.add_argument()
- """
- return self.__parser.add_argument(name, *args, **kwargs)
-
- def add_subparsers(self, *args,**kwargs):
- """ Add a subparser to the command's ArgumentParser """
- return self.__parser.add_subparsers(*args, **kwargs)
-
- def _run(self, clock:Clock, argv:list) -> str:
- """ Run the command on the given Clock instance
- @param clock : The Clock instance
- @param message : The command and its arguments
- @return Command result as text (formatted by one of the
- Command.fmt_ok() or Command.fmt_err() commands)
- """
- try:
- args = self.__parser.parse_args(argv[1:])
- except ArgumentParserError as expt:
- return self.fmt_err(expt)
- return self.__run(clock, args)
|