Bug 1108399 - Introduce decorator for mach sub-commands; r=ahal

With this patch, we now have the ability to declare mach sub-commands.
Subsequent patches will implement dispatching for sub-commands.

--HG--
extra : rebase_source : 32a672ff3ba7a1a1c8c27dff3fe657dec08e3569
extra : histedit_source : 2bb2c27ae0e2dab052a6ef93b0a70f488f8cd56e
This commit is contained in:
Gregory Szorc 2014-12-07 11:40:39 -08:00
parent 66816678e1
commit a263f9b2b2
3 changed files with 78 additions and 2 deletions

View File

@ -23,6 +23,15 @@ The important decorators are as follows:
A method decorator that defines an argument to the command. Its
arguments are essentially proxied to ArgumentParser.add_argument()
:py:func:`SubCommand <mach.decorators.SubCommand`
A method decorator that denotes that the method should be a
sub-command to an existing ``@Command``. The decorator takes the
parent command name as its first argument and the sub-command name
as its second argument.
``@CommandArgument`` can be used on ``@SubCommand`` instances just
like they can on ``@Command`` instances.
Classes with the ``@CommandProvider`` decorator **must** have an
``__init__`` method that accepts 1 or 2 arguments. If it accepts 2
arguments, the 2nd argument will be a

View File

@ -91,11 +91,16 @@ class MethodHandler(object):
# Argument groups added to this command's parser.
'argument_group_names',
# Dict of string to MethodHandler defining sub commands for this
# command.
'subcommand_handlers',
)
def __init__(self, cls, method, name, category=None, description=None,
conditions=None, parser=None, arguments=None,
argument_group_names=None, pass_context=False):
argument_group_names=None, pass_context=False,
subcommand_handlers=None):
self.cls = cls
self.method = method
@ -107,4 +112,4 @@ class MethodHandler(object):
self.arguments = arguments or []
self.argument_group_names = argument_group_names or []
self.pass_context = pass_context
self.subcommand_handlers = subcommand_handlers or {}

View File

@ -91,6 +91,41 @@ def CommandProvider(cls):
Registrar.register_command_handler(handler)
# Now do another pass to get sub-commands. We do this in two passes so
# we can check the parent command existence without having to hold
# state and reconcile after traversal.
for attr in sorted(cls.__dict__.keys()):
value = cls.__dict__[attr]
if not isinstance(value, types.FunctionType):
continue
command, subcommand, description = getattr(value, '_mach_subcommand',
(None, None, None))
if not command:
continue
if command not in Registrar.command_handlers:
raise MachError('Command referenced by sub-command does not '
'exist: %s' % command)
arguments = getattr(value, '_mach_command_args', None)
argument_group_names = getattr(value, '_mach_command_arg_group_names', None)
handler = MethodHandler(cls, attr, subcommand, description=description,
arguments=arguments, argument_group_names=argument_group_names,
pass_context=pass_context)
parent = Registrar.command_handlers[command]
if parent.parser:
raise MachError('cannot declare sub commands against a command '
'that has a parser installed: %s' % command)
if subcommand in parent.subcommand_handlers:
raise MachError('sub-command already defined: %s' % subcommand)
parent.subcommand_handlers[subcommand] = handler
return cls
@ -128,6 +163,33 @@ class Command(object):
return func
class SubCommand(object):
"""Decorator for functions or methods that provide a sub-command.
Mach commands can have sub-commands. e.g. ``mach command foo`` or
``mach command bar``. Each sub-command has its own parser and is
effectively its own mach command.
The decorator accepts arguments that define basic attributes of the
sub command:
command -- The string of the command this sub command should be
attached to.
subcommand -- The string name of the sub command to register.
description -- A textual description for this sub command.
"""
def __init__(self, command, subcommand, description=None):
self._command = command
self._subcommand = subcommand
self._description = description
def __call__(self, func):
func._mach_subcommand = (self._command, self._subcommand,
self._description)
return func
class CommandArgument(object):
"""Decorator for additional arguments to mach subcommands.