Added 'alias' decorator

Alias decorator allows you to name a command exposed to cli
to point to a method of a different name.
example:
    @cliutils.alias("import")
    def import_verification...

will result in command:
rally verify import

This is useful for commands using reserved words or
to avoid commands following python function naming
conventions:
'rally task sla_check'
could be
'rally task sla-check'
by using  alias decorator.

Change-Id: I476abbfd57f0f61ba3cccc7562d21c55963551a4
This commit is contained in:
David Paterson 2015-02-20 10:36:29 -05:00
parent 556acb862b
commit 795ddbabde
2 changed files with 25 additions and 7 deletions

View File

@ -215,6 +215,17 @@ def args(*args, **kwargs):
return _decorator return _decorator
def alias(command_name):
"""Allow cli to use alias command name instead of function name.
:param command_name: desired command name
"""
def decorator(func):
func.alias = command_name
return func
return decorator
def deprecated_args(*args, **kwargs): def deprecated_args(*args, **kwargs):
def _decorator(func): def _decorator(func):
func.__dict__.setdefault("args", []).insert(0, (args, kwargs)) func.__dict__.setdefault("args", []).insert(0, (args, kwargs))
@ -254,7 +265,7 @@ def _compose_category_description(category):
sublen = lambda item: len(item[0]) sublen = lambda item: len(item[0])
first_column_len = max(map(sublen, descr_pairs)) + MARGIN first_column_len = max(map(sublen, descr_pairs)) + MARGIN
for item in descr_pairs: for item in descr_pairs:
name = item[0] name = getattr(item[1], "alias", item[0])
if item[1].__doc__: if item[1].__doc__:
doc = utils.parse_docstring( doc = utils.parse_docstring(
item[1].__doc__)["short_description"] item[1].__doc__)["short_description"]
@ -300,15 +311,15 @@ def _add_command_parsers(categories, subparsers):
category_subparsers = parser.add_subparsers(dest="action") category_subparsers = parser.add_subparsers(dest="action")
for action, action_fn in _methods_of(command_object): for method_name, method in _methods_of(command_object):
descr = _compose_action_description(action_fn) descr = _compose_action_description(method)
parser = category_subparsers.add_parser( parser = category_subparsers.add_parser(
action, getattr(method, "alias", method_name),
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
description=descr, help=descr) description=descr, help=descr)
action_kwargs = [] action_kwargs = []
for args, kwargs in getattr(action_fn, "args", []): for args, kwargs in getattr(method, "args", []):
# FIXME(markmc): hack to assume dest is the arg name without # FIXME(markmc): hack to assume dest is the arg name without
# the leading hyphens if no dest is supplied # the leading hyphens if no dest is supplied
kwargs.setdefault("dest", args[0][2:]) kwargs.setdefault("dest", args[0][2:])
@ -316,7 +327,7 @@ def _add_command_parsers(categories, subparsers):
kwargs["dest"] = "action_kwarg_" + kwargs["dest"] kwargs["dest"] = "action_kwarg_" + kwargs["dest"]
parser.add_argument(*args, **kwargs) parser.add_argument(*args, **kwargs)
parser.set_defaults(action_fn=action_fn) parser.set_defaults(action_fn=method)
parser.set_defaults(action_kwargs=action_kwargs) parser.set_defaults(action_kwargs=action_kwargs)
parser.add_argument("action_args", nargs="*") parser.add_argument("action_args", nargs="*")
@ -480,6 +491,7 @@ complete -F _rally rally
completion = [] completion = []
for category, cmds in main.categories.items(): for category, cmds in main.categories.items():
for name, command in _methods_of(cmds): for name, command in _methods_of(cmds):
command_name = getattr(command, "alias", name)
args_list = [] args_list = []
for arg in getattr(command, "args", []): for arg in getattr(command, "args", []):
if getattr(command, "deprecated_args", []): if getattr(command, "deprecated_args", []):
@ -490,5 +502,5 @@ complete -F _rally rally
args = " ".join(args_list) args = " ".join(args_list)
completion.append(""" OPTS["{cat}_{cmd}"]="{args}"\n""".format( completion.append(""" OPTS["{cat}_{cmd}"]="{args}"\n""".format(
cat=category, cmd=name, args=args)) cat=category, cmd=command_name, args=args))
return bash_data % {"data": "".join(sorted(completion))} return bash_data % {"data": "".join(sorted(completion))}

View File

@ -577,3 +577,9 @@ class ValidateArgsTest(test.TestCase):
def test_static_method_with_default_missing_args4(self): def test_static_method_with_default_missing_args4(self):
self.assertRaises(cliutils.MissingArgs, self.assertRaises(cliutils.MissingArgs,
self._test_static_method_with_default, y=2, z=3) self._test_static_method_with_default, y=2, z=3)
def test_alias_decorator(self):
alias_fn = mock.Mock(name="alias_fn")
cmd_name = "test-command"
wrapped = cliutils.alias(cmd_name)
self.assertEqual(wrapped(alias_fn).alias, cmd_name)