Source code for nicfit.command

import functools
from collections import OrderedDict, defaultdict
from deprecation import deprecated
from .__about__ import __version__
from ._argparse import ArgumentParser


@deprecated(deprecated_in="0.8", removed_in="0.9", current_version=__version__,
            details="Use the static class methods from nicfit.command.Command "
                    "or subclass thereof.")
def register(CommandSubClass):
    """A class decorator for Command classes to register in the default set."""
    name = CommandSubClass.name()
    if name in Command._all_commands:
        raise ValueError("Command already exists: " + name)
    Command._all_commands[name] = CommandSubClass
    return CommandSubClass


[docs]class CommandError(Exception): """Base error type for nicfit.command.Command errors.""" def __init__(self, msg, exit_status=1): super().__init__(msg) self.exit_status = exit_status
class Command: """Base class for commands.""" # XXX: deprectated, with with global register _all_commands = OrderedDict() # Using partial so subclasses can use as a builder. CommandDict = functools.partial(defaultdict, OrderedDict) _registered_commands = CommandDict() @classmethod def register(Class, CommandSubClass): """A class decorator for Command classes to register.""" for name in [CommandSubClass.name()] + CommandSubClass.aliases(): if name in Class._registered_commands[Class]: raise ValueError("Command already exists: " + name) Class._registered_commands[Class][name] = CommandSubClass return CommandSubClass @classmethod def name(Class): return Class.NAME if hasattr(Class, "NAME") else Class.__name__ @classmethod def help(Class): return Class.HELP if hasattr(Class, "HELP") else None @classmethod def desc(Class): return Class.DESC if hasattr(Class, "DESC") else Class.help() @classmethod def aliases(Class): return Class.ALIASES if hasattr(Class, "ALIASES") else [] def __init__(self, subparsers=None, **kwargs): """Construct a command. Any `kwargs` are added to the class object using ``setattr``. All commands have an ArgumentParser, either constructed here or when ``subparsers`` is given a new parser is created using its ``add_parser`` method. """ for name, value in kwargs.items(): setattr(self, name, value) self.subparsers = subparsers if subparsers: self.parser = self.subparsers.add_parser(self.name(), help=self.help(), description=self.desc(), aliases=self.aliases()) else: self.parser = ArgumentParser(prog=self.name(), description=self.desc(), epilog=self.help()) self.parser.set_defaults(command_func=self.run) self._initArgParser(self.parser) def _initArgParser(self, parser): pass def run(self, args): self.args = args return self._run() def _run(self): raise NotImplementedError("Must implement a _run function") @staticmethod @deprecated(deprecated_in="0.8", removed_in="0.9", details="Use :meth:`Command.loadCommandMap` instead.", current_version=__version__) def initAll(subparsers=None): if not Command._all_commands: raise ValueError("No commands have been registered") seen = set() cmds = [] for Cmd in Command._all_commands.values(): if Cmd not in seen: cmds.append(Cmd(subparsers)) seen.add(Cmd) return cmds @classmethod @deprecated(deprecated_in="0.8", removed_in="0.9", details="Use :meth:`Command.loadCommandMap.values()` instead.", current_version=__version__) def iterCommands(Class): return iter(set(Class._all_commands.values())) @classmethod def loadCommandMap(Class, subparsers=None, instantiate=True, **cmd_kwargs): """Instantiate each registered command to a dict mapping name/alias to instance. Due to aliases, the returned length may be greater there the number of commands, but the unique instance count will match. """ if not Class._registered_commands: raise ValueError("No commands have been registered with {}" .format(Class)) all = {} for Cmd in set(Class._registered_commands[Class].values()): cmd = Cmd(subparsers=subparsers, **cmd_kwargs) \ if instantiate else Cmd for name in [Cmd.name()] + Cmd.aliases(): all[name] = cmd return all class SubCommandCommand(Command): """Like a normal command, but structured as a command with sub-commands, each with its own argument interface (and argument parser). """ SUB_CMDS = [] DEFAULT_CMD = None def __init__(self, title="Sub-commands", subparsers=None, **kwargs): self.title = title self._sub_cmds = [] self._ctor_kwargs = dict(kwargs) super().__init__(subparsers=subparsers) def _run(self): return self.args.command_func(self.args) def _initArgParser(self, parser): def_cmd = None subparsers = parser.add_subparsers(title=self.title, dest="command", prog=self.name(), required=not self.DEFAULT_CMD) for CmdClass in self.SUB_CMDS: cmd = CmdClass(subparsers=subparsers, **self._ctor_kwargs) if CmdClass is self.DEFAULT_CMD: def_cmd = cmd self._sub_cmds.append(cmd) parser.set_defaults(command_func=def_cmd.run if def_cmd else None) __all__ = ["register", "CommandError", "Command", "SubCommandCommand"]