diff options
author | Michal Minar <miminar@redhat.com> | 2013-10-11 10:56:45 +0200 |
---|---|---|
committer | Michal Minar <miminar@redhat.com> | 2013-10-11 13:27:37 +0200 |
commit | de3849421b442e7ac923608eae310b74b7cbbe95 (patch) | |
tree | a14cc95cabdc7171ec90c5fc1592c5650341c5d3 /lmi/scripts/common | |
parent | d2c6c4421dc80e7361b072f7360cc03df23412b9 (diff) | |
download | openlmi-scripts-de3849421b442e7ac923608eae310b74b7cbbe95.tar.gz openlmi-scripts-de3849421b442e7ac923608eae310b74b7cbbe95.tar.xz openlmi-scripts-de3849421b442e7ac923608eae310b74b7cbbe95.zip |
allow multiplexer to have a fallback command
Diffstat (limited to 'lmi/scripts/common')
-rw-r--r-- | lmi/scripts/common/command/base.py | 1 | ||||
-rw-r--r-- | lmi/scripts/common/command/helper.py | 16 | ||||
-rw-r--r-- | lmi/scripts/common/command/meta.py | 37 | ||||
-rw-r--r-- | lmi/scripts/common/command/multiplexer.py | 26 | ||||
-rw-r--r-- | lmi/scripts/common/command/session.py | 2 |
5 files changed, 72 insertions, 10 deletions
diff --git a/lmi/scripts/common/command/base.py b/lmi/scripts/common/command/base.py index 21d790e..7949202 100644 --- a/lmi/scripts/common/command/base.py +++ b/lmi/scripts/common/command/base.py @@ -111,7 +111,6 @@ class LmiBaseCommand(object): raise NotImplementedError("child_commands() method must be overriden" " in a subclass") - def __init__(self, app, cmd_name, parent=None): if not isinstance(cmd_name, basestring): raise TypeError('cmd_name must be a string') diff --git a/lmi/scripts/common/command/helper.py b/lmi/scripts/common/command/helper.py index a13c99c..00d842f 100644 --- a/lmi/scripts/common/command/helper.py +++ b/lmi/scripts/common/command/helper.py @@ -69,7 +69,8 @@ def make_list_command(func, props['transform_options'] = transform_func return LmiLister.__metaclass__(name, (LmiLister, ), props) -def register_subcommands(command_name, usage, command_map): +def register_subcommands(command_name, usage, command_map, + fallback_command=None): """ Create a multiplexer command (a node in a tree of commands). @@ -77,13 +78,18 @@ def register_subcommands(command_name, usage, command_map): be given on a command line. :param string usage: Usage string parseable by ``docopt``. :param dictionary command_map: Dictionary of subcommands. Associates - command names to their factories. + command names to their factories. It's assigned to ``COMMANDS`` + property. + :param fallback_command: Command factory used when no command is given + on command line. + :type fallback_command: :py:class:`~.endpoint.LmiEndPointCommand` :returns: Subclass of :py:class:`~.multiplexer.LmiCommandMultiplexer`. :rtype: type """ - props = { 'COMMANDS' : command_map - , 'OWN_USAGE' : True - , '__doc__' : usage } + props = { 'COMMANDS' : command_map + , 'OWN_USAGE' : True + , '__doc__' : usage + , 'FALLBACK_COMMAND' : fallback_command } return LmiCommandMultiplexer.__metaclass__(command_name, (LmiCommandMultiplexer, ), props) diff --git a/lmi/scripts/common/command/meta.py b/lmi/scripts/common/command/meta.py index 8561f83..ad81f53 100644 --- a/lmi/scripts/common/command/meta.py +++ b/lmi/scripts/common/command/meta.py @@ -411,6 +411,38 @@ def _handle_opt_preprocess(name, dcl): dcl['_preprocess_options'] = _new_preprocess_options +def _handle_fallback_command(name, bases, dcl): + """ + Process ``FALLBACK_COMMAND`` property of multiplexer command. It's turned + into a :py:meth:`~.multiplexer.LmiCommandMultiplexer.fallback_command` + class method. It needs to be called after the usage string is handled. + + .. seealso:: + :py:func:`_handle_usage` + """ + fallback = dcl.pop('FALLBACK_COMMAND', None) + if fallback is not None: + if not issubclass(type(fallback), EndPointCommandMetaClass): + raise errors.LmiCommandInvalidProperty(dcl['__module__'], name, + "FALLBACK_COMMAND must be a command class" + " (subclass of LmiEndPointCommand) not %s" % repr(fallback)) + if not fallback.has_own_usage(): + usage_string = dcl.get('__doc__', None) + if not usage_string: + for base_cls in bases: + if not issubclass(base_cls, base.LmiBaseCommand): + continue + cmd = base_cls + while not cmd.has_own_usage() and cmd.parent is not None: + cmd = cmd.parent + usage_string = cmd.__doc__ + if not usage_string: + errors.LmiCommandError(dcl['__module__'], name, + "missing usage string") + fallback.__doc__ = usage_string + fallback.has_own_usage = lambda cls: True + dcl['fallback_command'] = staticmethod(lambda: fallback) + class EndPointCommandMetaClass(abc.ABCMeta): """ End point command does not have any subcommands. It's a leaf of @@ -582,6 +614,10 @@ class MultiplexerMetaClass(abc.ABCMeta): Command names with assigned command classes. Each of them is a direct subcommands of command with this property. Mandatory property. + + ``FALLBACK_COMMAND`` : :py:class:`~.endpoint.LmiEndPointCommand` + Command factory to use in case that no command is passed on command + line. """ def __new__(mcs, name, bases, dcl): @@ -621,5 +657,6 @@ class MultiplexerMetaClass(abc.ABCMeta): dcl['__module__'], name) _handle_usage(name, dcl) + _handle_fallback_command(name, bases, dcl) return super(MultiplexerMetaClass, mcs).__new__(mcs, name, bases, dcl) diff --git a/lmi/scripts/common/command/multiplexer.py b/lmi/scripts/common/command/multiplexer.py index 829d90f..8476999 100644 --- a/lmi/scripts/common/command/multiplexer.py +++ b/lmi/scripts/common/command/multiplexer.py @@ -57,9 +57,12 @@ class LmiCommandMultiplexer(base.LmiBaseCommand): Where ``Subcmd1`` and ``Subcmd2`` are some other ``LmiBaseCommand`` subclasses. Documentation string must be parseable with ``docopt``. - ``COMMANDS`` property will be translated to - :py:meth:`LmiCommandMultiplexer.child_commands` class method by - :py:class:`~.meta.MultiplexerMetaClass`. + Recognized properties: + + ``COMMANDS`` : ``dictionary`` + property will be translated to + :py:meth:`LmiCommandMultiplexer.child_commands` class method by + :py:class:`~.meta.MultiplexerMetaClass`. Using metaclass: :py:class:`.meta.MultiplexerMetaClass`. """ @@ -78,6 +81,18 @@ class LmiCommandMultiplexer(base.LmiBaseCommand): """ raise NotImplementedError("child_commands must be implemented in" " a subclass") + + @classmethod + def fallback_command(cls): + """ + This is overriden by :py:class:`~.meta.MultiplexerMetaClass` when + the ``FALLBACK_COMMAND`` gets processed. + + :returns: Command factory invoked for missing command on command line. + :rtype: :py:class:`~.endpoint.LmiEndPointCommand` + """ + return None + @classmethod def is_end_point(cls): return False @@ -128,4 +143,9 @@ class LmiCommandMultiplexer(base.LmiBaseCommand): if options.pop('--help', False): self.app.stdout.write(self.get_usage()) return 0 + if ( self.fallback_command() is not None + and (not args or args[0] not in self.child_commands())): + cmd_cls = self.fallback_command() + cmd = cmd_cls(self.app, self.cmd_name, self.parent) + return cmd.run(args) return self.run_subcommand(args[0], args[1:]) diff --git a/lmi/scripts/common/command/session.py b/lmi/scripts/common/command/session.py index adba8da..cecc387 100644 --- a/lmi/scripts/common/command/session.py +++ b/lmi/scripts/common/command/session.py @@ -139,7 +139,7 @@ class LmiSessionCommand(LmiEndPointCommand): it just prints errors in a form of list. :param session: Session object. - :type session: :py:class:`lmi.scripts.common.session.Session` + :type session: :py:class:`lmi.scripts.common.command.session.Session` :param dictionary results: Dictionary of form: :: { 'hostname' : (success_flag, result), ... } |