From 12a3a827346a7f9ab1a343e2948836d52f5bed18 Mon Sep 17 00:00:00 2001 From: Michal Dulko Date: Fri, 22 Aug 2014 12:14:10 +0200 Subject: [PATCH] Add bash completion to glance client Currently glance client does not support command completion. The intention is to add this functionality to the client blueprint add-bash-completion Change-Id: I725dd308118b101e87182acf0cee6dbfd214e0e4 --- glanceclient/shell.py | 28 ++++++++++++++++++++++++++ tests/test_shell.py | 38 ++++++++++++++++++++++++++++++++++++ tools/glance.bash_completion | 25 ++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 tools/glance.bash_completion diff --git a/glanceclient/shell.py b/glanceclient/shell.py index c529b6e9..99dcc5a4 100644 --- a/glanceclient/shell.py +++ b/glanceclient/shell.py @@ -286,6 +286,8 @@ class OpenStackImagesShell(object): self._find_actions(subparsers, submodule) self._find_actions(subparsers, self) + self._add_bash_completion_subparser(subparsers) + return parser def _find_actions(self, subparsers, actions_module): @@ -312,6 +314,13 @@ class OpenStackImagesShell(object): subparser.add_argument(*args, **kwargs) subparser.set_defaults(func=callback) + def _add_bash_completion_subparser(self, subparsers): + subparser = subparsers.add_parser('bash_completion', + add_help=False, + formatter_class=HelpFormatter) + self.subcommands['bash_completion'] = subparser + subparser.set_defaults(func=self.do_bash_completion) + def _get_image_url(self, args): """Translate the available url-related options into a single string. @@ -567,6 +576,9 @@ class OpenStackImagesShell(object): if args.func == self.do_help: self.do_help(args) return 0 + elif args.func == self.do_bash_completion: + self.do_bash_completion(args) + return 0 LOG = logging.getLogger('glanceclient') LOG.addHandler(logging.StreamHandler()) @@ -605,6 +617,22 @@ class OpenStackImagesShell(object): else: self.parser.print_help() + def do_bash_completion(self, _args): + """ + Prints all of the commands and options to stdout so that the + glance.bash_completion script doesn't have to hard code them. + """ + commands = set() + options = set() + for sc_str, sc in self.subcommands.items(): + commands.add(sc_str) + for option in sc._optionals._option_string_actions.keys(): + options.add(option) + + commands.remove('bash_completion') + commands.remove('bash-completion') + print(' '.join(commands | options)) + class HelpFormatter(argparse.HelpFormatter): def start_section(self, heading): diff --git a/tests/test_shell.py b/tests/test_shell.py index 5c80e493..d4b1ddf5 100644 --- a/tests/test_shell.py +++ b/tests/test_shell.py @@ -16,8 +16,10 @@ import argparse import os +import sys import mock +import six from glanceclient import exc from glanceclient import shell as openstack_shell @@ -79,6 +81,26 @@ class ShellTest(utils.TestCase): global _old_env os.environ = _old_env + def shell(self, argstr, exitcodes=(0,)): + orig = sys.stdout + orig_stderr = sys.stderr + try: + sys.stdout = six.StringIO() + sys.stderr = six.StringIO() + _shell = openstack_shell.OpenStackImagesShell() + _shell.main(argstr.split()) + except SystemExit: + exc_type, exc_value, exc_traceback = sys.exc_info() + self.assertIn(exc_value.code, exitcodes) + finally: + stdout = sys.stdout.getvalue() + sys.stdout.close() + sys.stdout = orig + stderr = sys.stderr.getvalue() + sys.stderr.close() + sys.stderr = orig_stderr + return (stdout, stderr) + def test_help_unknown_command(self): shell = openstack_shell.OpenStackImagesShell() argstr = 'help foofoo' @@ -285,6 +307,22 @@ class ShellTestWithKeystoneV3Auth(ShellTest): glance_shell = openstack_shell.OpenStackImagesShell() self.assertRaises(exc.CommandError, glance_shell.main, args.split()) + def test_bash_completion(self): + stdout, stderr = self.shell('bash_completion') + # just check we have some output + required = [ + '--status', + 'image-create', + 'help', + '--size'] + for r in required: + self.assertIn(r, stdout.split()) + avoided = [ + 'bash_completion', + 'bash-completion'] + for r in avoided: + self.assertNotIn(r, stdout.split()) + class ShellCacheSchemaTest(utils.TestCase): def setUp(self): diff --git a/tools/glance.bash_completion b/tools/glance.bash_completion new file mode 100644 index 00000000..1e7f72cd --- /dev/null +++ b/tools/glance.bash_completion @@ -0,0 +1,25 @@ +_glance_opts="" # lazy init +_glance_flags="" # lazy init +_glance_opts_exp="" # lazy init +_glance() +{ + local cur prev nbc cflags + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + if [ "x$_glance_opts" == "x" ] ; then + nbc="`glance bash-completion | sed -e "s/ *-h */ /" -e "s/ *-i */ /"`" + _glance_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/ */ /g"`" + _glance_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/ */ /g"`" + _glance_opts_exp="`echo "$_glance_opts" | sed 's/^ *//' | tr ' ' '|'`" + fi + + if [[ " ${COMP_WORDS[@]} " =~ " "($_glance_opts_exp)" " && "$prev" != "help" ]] ; then + COMPREPLY=($(compgen -W "${_glance_flags}" -- ${cur})) + else + COMPREPLY=($(compgen -W "${_glance_opts}" -- ${cur})) + fi + return 0 +} +complete -F _glance glance \ No newline at end of file