From 5be07a22eb8197d805af7954316f8c29e3a66c3f Mon Sep 17 00:00:00 2001 From: Terry Howe Date: Fri, 25 Oct 2013 21:37:51 -0600 Subject: add bash complete --- cliff/complete.py | 196 +++++++++++++++++++++++++++++++++++++++++++ cliff/tests/test_complete.py | 139 ++++++++++++++++++++++++++++++ docs/source/complete.rst | 45 ++++++++++ docs/source/index.rst | 1 + 4 files changed, 381 insertions(+) create mode 100644 cliff/complete.py create mode 100644 cliff/tests/test_complete.py create mode 100644 docs/source/complete.rst diff --git a/cliff/complete.py b/cliff/complete.py new file mode 100644 index 0000000..24cb7e4 --- /dev/null +++ b/cliff/complete.py @@ -0,0 +1,196 @@ + +"""Bash completion for the CLI. +""" + +import logging + +from cliff import command + + +class CompleteDictionary: + """dictionary for bash completion + """ + + def __init__(self): + self._dictionary = {} + + def add_command(self, command, actions): + optstr = "" + dicto = self._dictionary + for action in actions: + for opt in action.option_strings: + if optstr: + optstr += " " + opt + else: + optstr += opt + last = None + lastsubcmd = None + for subcmd in command: + if subcmd not in dicto: + dicto[subcmd] = {} + last = dicto + lastsubcmd = subcmd + dicto = dicto[subcmd] + last[lastsubcmd] = optstr + + def get_commands(self): + cmdo = "" + keys = sorted(self._dictionary.keys()) + for cmd in keys: + if cmdo == "": + cmdo += cmd + else: + cmdo += " " + cmd + return cmdo + + def _get_data_recurse(self, dictionary, path): + ray = [] + keys = sorted(dictionary.keys()) + for cmd in keys: + if path == "": + name = str(cmd) + else: + name = path + "_" + str(cmd) + value = dictionary[cmd] + if isinstance(value, str): + ray.append((name, value)) + else: + cmdlist = ' '.join(sorted(value.keys())) + ray.append((name, cmdlist)) + ray += self._get_data_recurse(value, name) + return ray + + def get_data(self): + return sorted(self._get_data_recurse(self._dictionary, "")) + + +class CompleteNoCode: + """completion with no code + """ + def __init__(self, name): + self.name = name + + def get_header(self): + return '' + + def get_trailer(self): + return '' + + +class CompleteBash: + """completion for bash + """ + def __init__(self, name): + self.name = str(name) + + def get_header(self): + return ('_' + self.name + '()\n\ +{\n\ + local cur prev words\n\ + COMPREPLY=()\n\ + _get_comp_words_by_ref -n : cur prev words\n\ +\n\ + # Command data:\n') + + def get_trailer(self): + return ('\ +\n\ + cmd=""\n\ + words[0]=""\n\ + completed="${cmds}" \n\ + for var in "${words[@]:1}"\n\ + do\n\ + if [[ ${var} == -* ]] ; then\n\ + break\n\ + fi\n\ + if [ -z "${cmd}" ] ; then\n\ + proposed="${var}"\n\ + else\n\ + proposed="${cmd}_${var}"\n\ + fi\n\ + local i="cmds_${proposed}"\n\ + local comp="${!i}"\n\ + if [ -z "${comp}" ] ; then\n\ + break\n\ + fi\n\ + if [[ ${comp} == -* ]] ; then\n\ + if [[ ${cur} != -* ]] ; then\n\ + completed=""\n\ + break\n\ + fi\n\ + fi\n\ + cmd="${proposed}"\n\ + completed="${comp}"\n\ + done\n\ +\n\ + if [ -z "${completed}" ] ; then\n\ + COMPREPLY=( $( compgen -f -- "$cur" ) $( compgen -d -- "$cur" ) )\n\ + else\n\ + COMPREPLY=( $(compgen -W "${completed}" -- ${cur}) )\n\ + fi\n\ + return 0\n\ +}\n\ +complete -F _' + self.name + ' ' + self.name + '\n') + + +class CompleteCommand(command.Command): + """print bash completion command + """ + + log = logging.getLogger(__name__ + '.CompleteCommand') + + def get_parser(self, prog_name): + parser = super(CompleteCommand, self).get_parser(prog_name) + parser.add_argument( + "--name", + default=None, + metavar='', + help="Command name to support with command completion" + ) + parser.add_argument( + "--shell", + default='bash', + metavar='', + choices=['bash', 'none'], + help="Shell being used. Use none for data only (default: bash)" + ) + return parser + + def get_actions(self, command): + the_cmd = self.app.command_manager.find_command(command) + cmd_factory, cmd_name, search_args = the_cmd + cmd = cmd_factory(self.app, search_args) + if self.app.interactive_mode: + full_name = (cmd_name) + else: + full_name = (' '.join([self.app.NAME, cmd_name])) + cmd_parser = cmd.get_parser(full_name) + return cmd_parser._get_optional_actions() + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + if parsed_args.name: + name = parsed_args.name + else: + name = self.app.NAME + if parsed_args.shell == "none": + shell = CompleteNoCode(name) + else: + shell = CompleteBash(name) + + self.app.stdout.write(shell.get_header()) + + dicto = CompleteDictionary() + for cmd in self.app.command_manager: + command = cmd[0].split() + dicto.add_command(command, self.get_actions(command)) + + cmdo = dicto.get_commands() + self.app.stdout.write(" cmds='{0}'\n".format(cmdo)) + for datum in dicto.get_data(): + self.app.stdout.write(' cmds_{0}=\'{1}\'\n'.format(*datum)) + + self.app.stdout.write(shell.get_trailer()) + + return 0 diff --git a/cliff/tests/test_complete.py b/cliff/tests/test_complete.py new file mode 100644 index 0000000..23775c0 --- /dev/null +++ b/cliff/tests/test_complete.py @@ -0,0 +1,139 @@ +"""Bash completion tests +""" + +import mock +import os + +from cliff import complete + + +def test_add_command(): + sot = complete.CompleteDictionary() + sot.add_command("image delete".split(), + [mock.Mock(option_strings=["1"])]) + sot.add_command("image list".split(), + [mock.Mock(option_strings=["2"])]) + sot.add_command("image create".split(), + [mock.Mock(option_strings=["3"])]) + sot.add_command("volume type create".split(), + [mock.Mock(option_strings=["4"])]) + sot.add_command("volume type delete".split(), + [mock.Mock(option_strings=["5"])]) + assert "image volume" == sot.get_commands() + result = sot.get_data() + assert "image" == result[0][0] + assert "create delete list" == result[0][1] + assert "image_create" == result[1][0] + assert "3" == result[1][1] + assert "image_delete" == result[2][0] + assert "1" == result[2][1] + assert "image_list" == result[3][0] + assert "2" == result[3][1] + +class FakeStdout: + def __init__(self): + self.content = [] + + def write(self, text): + self.content.append(text) + + def make_string(self): + result = '' + for line in self.content: + result = result + line + return result + +def create_complete_command_mocks(): + app = mock.Mock(name="app") + app_args = mock.Mock(name="app_args") + # actions + action_one = mock.Mock(name="action_one") + action_one.option_strings = ["Eolus"] + action_two = mock.Mock(name="action_two") + action_two.option_strings = ["Wilson", "Sunlight"] + actions = [action_one, action_two] + # get_optional_actions + get_optional_actions = mock.Mock(name="get_optional_actions") + get_optional_actions.return_value = actions + # cmd_parser + cmd_parser = mock.Mock(name="cmd_parser") + cmd_parser._get_optional_actions = get_optional_actions + # get_parser + get_parser = mock.Mock(name="get_parser") + get_parser.return_value = cmd_parser + # cmd_factory_init + cmd_factory_init = mock.Mock("cmd_factory_init") + cmd_factory_init.get_parser = get_parser + # cmd_factory + cmd_factory = mock.Mock(name="cmd_factory") + cmd_factory.return_value = cmd_factory_init + # find_command + cmd_name = "yale" + search_args = "search_args" + find_command = mock.Mock(name="find_command") + find_command.return_value = (cmd_factory, cmd_name, search_args) + # command_manager + commands = [["image create"], ["server meta delete"], ["server ssh"]] + command_manager = mock.MagicMock() + command_manager.__iter__.return_value = iter(commands) + command_manager.find_command = find_command + app.command_manager = command_manager + app.NAME = "openstack" + app.interactive_mode = False + app.stdout = FakeStdout() + return (complete.CompleteCommand(app, app_args), app, actions, cmd_factory_init) + +def check_parser(cmd, args, verify_args): + cmd_parser = cmd.get_parser('check_parser') + parsed_args = cmd_parser.parse_args(args) + for av in verify_args: + attr, value = av + if attr: + assert attr in parsed_args + assert getattr(parsed_args, attr) == value + +def test_parser_nothing(): + sot, app, actions, cmd_factory_init = create_complete_command_mocks() + check_parser(sot, [], [('name', None), ('shell', 'bash')]) + +def test_parser_no_code(): + sot, app, actions, cmd_factory_init = create_complete_command_mocks() + check_parser(sot, ["--shell", "none", "--name", "foo"], + [('name', 'foo'), ('shell', 'none')]) + +def test_get_actions(): + sot, app, actions, cmd_factory_init = create_complete_command_mocks() + result = sot.get_actions("yale") + cmd_factory_init.get_parser.assert_called_with('openstack yale') + assert actions == result + +def test_get_actions_interactive(): + sot, app, actions, cmd_factory_init = create_complete_command_mocks() + app.interactive_mode = True + result = sot.get_actions("yale") + cmd_factory_init.get_parser.assert_called_with('yale') + assert actions == result + +def verify_data(content): + assert " cmds='image server'\n" in content + assert " cmds_image='create'\n" in content + assert " cmds_image_create='Eolus Wilson Sunlight'\n" in content + assert " cmds_server='meta ssh'\n" in content + assert " cmds_server_meta_delete='Eolus Wilson Sunlight'\n" in content + assert " cmds_server_ssh='Eolus Wilson Sunlight'\n" in content + +def test_take_action_nocode(): + sot, app, actions, cmd_factory_init = create_complete_command_mocks() + parsed_args = mock.Mock() + parsed_args.shell = "none" + sot.take_action(parsed_args) + verify_data(app.stdout.content) + +def test_take_action_code(): + sot, app, actions, cmd_factory_init = create_complete_command_mocks() + parsed_args = mock.Mock() + parsed_args.name = "openstack" + sot.take_action(parsed_args) + verify_data(app.stdout.content) + assert "_openstack()\n" in app.stdout.content[0] + assert "complete -F _openstack openstack\n" in app.stdout.content[-1] diff --git a/docs/source/complete.rst b/docs/source/complete.rst new file mode 100644 index 0000000..2a08098 --- /dev/null +++ b/docs/source/complete.rst @@ -0,0 +1,45 @@ +==================== + Command Completion +==================== + +A generic command completion command is available to generate a +bash-completion script. Currently, the command will generate a script +for bash versions 3 or 4. There is also a mode that generates only +data that can be used in your own script. The command completion script +is generated based on the commands and options that you have specified +in cliff. + +Usage +===== + +In order for your command to support command completions, you need to +add the `cliff.complete.CompleteCommand` class to your command manager. + +:: + + self.command_manager.add_command('complete', cliff.complete.CompleteCommand) + +When you run the command, it will generate a bash-completion script: + +:: + + (.venv)$ mycmd complete + _mycmd() + { + local cur prev words + COMPREPLY=() + _get_comp_words_by_ref -n : cur prev words + + # Command data: + cmds='agent aggregate backup' + cmds_agent='--name' + ... + if [ -z "${completed}" ] ; then + COMPREPLY=( $( compgen -f -- "$cur" ) $( compgen -d -- "$cur" ) ) + else + COMPREPLY=( $(compgen -W "${completed}" -- ${cur}) ) + fi + return 0 + } + complete -F _mycmd mycmd + diff --git a/docs/source/index.rst b/docs/source/index.rst index 0a13664..b474208 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -15,6 +15,7 @@ Contents: demoapp list_commands show_commands + complete interactive_mode classes install -- cgit v1.2.1 From 5d33c97404e5661bca62b1e9907740ee83cec3c6 Mon Sep 17 00:00:00 2001 From: Terry Howe Date: Wed, 30 Oct 2013 17:25:59 -0600 Subject: various python code optimizations; shuffle I/O to shell classes --- cliff/complete.py | 163 +++++++++++++++++++++---------------------- cliff/tests/test_complete.py | 156 +++++++++++++++++++---------------------- 2 files changed, 150 insertions(+), 169 deletions(-) diff --git a/cliff/complete.py b/cliff/complete.py index 24cb7e4..c31c659 100644 --- a/cliff/complete.py +++ b/cliff/complete.py @@ -15,42 +15,26 @@ class CompleteDictionary: self._dictionary = {} def add_command(self, command, actions): - optstr = "" + optstr = ' '.join(opt for action in actions + for opt in action.option_strings) dicto = self._dictionary - for action in actions: - for opt in action.option_strings: - if optstr: - optstr += " " + opt - else: - optstr += opt last = None lastsubcmd = None - for subcmd in command: - if subcmd not in dicto: - dicto[subcmd] = {} - last = dicto - lastsubcmd = subcmd - dicto = dicto[subcmd] - last[lastsubcmd] = optstr + for subcmd in command[:-1]: + dicto = dicto.setdefault(subcmd, {}) + dicto[command[-1]] = optstr def get_commands(self): - cmdo = "" - keys = sorted(self._dictionary.keys()) - for cmd in keys: - if cmdo == "": - cmdo += cmd - else: - cmdo += " " + cmd - return cmdo + return ' '.join(k for k in sorted(self._dictionary.keys())) def _get_data_recurse(self, dictionary, path): ray = [] keys = sorted(dictionary.keys()) for cmd in keys: if path == "": - name = str(cmd) + name = cmd else: - name = path + "_" + str(cmd) + name = path + "_" + cmd value = dictionary[cmd] if isinstance(value, str): ray.append((name, value)) @@ -64,11 +48,25 @@ class CompleteDictionary: return sorted(self._get_data_recurse(self._dictionary, "")) -class CompleteNoCode: +class CompleteShellBase(object): + """ base class for bash completion generation + """ + def __init__(self, name, output): + self.name = str(name) + self.output = output + + def write(self, cmdo, data): + self.output.write(self.get_header()) + self.output.write(" cmds='{0}'\n".format(cmdo)) + for datum in data: + self.output.write(' cmds_{0}=\'{1}\'\n'.format(*datum)) + self.output.write(self.get_trailer()) + +class CompleteNoCode(CompleteShellBase): """completion with no code """ - def __init__(self, name): - self.name = name + def __init__(self, name, output): + super(CompleteNoCode, self).__init__(name, output) def get_header(self): return '' @@ -77,60 +75,62 @@ class CompleteNoCode: return '' -class CompleteBash: +class CompleteBash(CompleteShellBase): """completion for bash """ - def __init__(self, name): - self.name = str(name) + def __init__(self, name, output): + super(CompleteBash, self).__init__(name, output) def get_header(self): - return ('_' + self.name + '()\n\ -{\n\ - local cur prev words\n\ - COMPREPLY=()\n\ - _get_comp_words_by_ref -n : cur prev words\n\ -\n\ - # Command data:\n') + return ('_' + self.name + """() +{ + local cur prev words + COMPREPLY=() + _get_comp_words_by_ref -n : cur prev words + + # Command data: +""") def get_trailer(self): - return ('\ -\n\ - cmd=""\n\ - words[0]=""\n\ - completed="${cmds}" \n\ - for var in "${words[@]:1}"\n\ - do\n\ - if [[ ${var} == -* ]] ; then\n\ - break\n\ - fi\n\ - if [ -z "${cmd}" ] ; then\n\ - proposed="${var}"\n\ - else\n\ - proposed="${cmd}_${var}"\n\ - fi\n\ - local i="cmds_${proposed}"\n\ - local comp="${!i}"\n\ - if [ -z "${comp}" ] ; then\n\ - break\n\ - fi\n\ - if [[ ${comp} == -* ]] ; then\n\ - if [[ ${cur} != -* ]] ; then\n\ - completed=""\n\ - break\n\ - fi\n\ - fi\n\ - cmd="${proposed}"\n\ - completed="${comp}"\n\ - done\n\ -\n\ - if [ -z "${completed}" ] ; then\n\ - COMPREPLY=( $( compgen -f -- "$cur" ) $( compgen -d -- "$cur" ) )\n\ - else\n\ - COMPREPLY=( $(compgen -W "${completed}" -- ${cur}) )\n\ - fi\n\ - return 0\n\ -}\n\ -complete -F _' + self.name + ' ' + self.name + '\n') + return ( +""" + + cmd="" + words[0]="" + completed="${cmds}" + for var in "${words[@]:1}" + do + if [[ ${var} == -* ]] ; then + break + fi + if [ -z "${cmd}" ] ; then + proposed="${var}" + else + proposed="${cmd}_${var}" + fi + local i="cmds_${proposed}" + local comp="${!i}" + if [ -z "${comp}" ] ; then + break + fi + if [[ ${comp} == -* ]] ; then + if [[ ${cur} != -* ]] ; then + completed="" + break + fi + fi + cmd="${proposed}" + completed="${comp}" + done + + if [ -z "${completed}" ] ; then + COMPREPLY=( $( compgen -f -- "$cur" ) $( compgen -d -- "$cur" ) ) + else + COMPREPLY=( $(compgen -W "${completed}" -- ${cur}) ) + fi + return 0 +} +complete -F _""" + self.name + ' ' + self.name + '\n') class CompleteCommand(command.Command): @@ -175,22 +175,15 @@ class CompleteCommand(command.Command): else: name = self.app.NAME if parsed_args.shell == "none": - shell = CompleteNoCode(name) + shell = CompleteNoCode(name, self.app.stdout) else: - shell = CompleteBash(name) - - self.app.stdout.write(shell.get_header()) + shell = CompleteBash(name, self.app.stdout) dicto = CompleteDictionary() for cmd in self.app.command_manager: command = cmd[0].split() dicto.add_command(command, self.get_actions(command)) - cmdo = dicto.get_commands() - self.app.stdout.write(" cmds='{0}'\n".format(cmdo)) - for datum in dicto.get_data(): - self.app.stdout.write(' cmds_{0}=\'{1}\'\n'.format(*datum)) - - self.app.stdout.write(shell.get_trailer()) + shell.write(dicto.get_commands(), dicto.get_data()) return 0 diff --git a/cliff/tests/test_complete.py b/cliff/tests/test_complete.py index 23775c0..eecf48c 100644 --- a/cliff/tests/test_complete.py +++ b/cliff/tests/test_complete.py @@ -2,12 +2,14 @@ """ import mock -import os +from cliff.app import App +from cliff.command import Command +from cliff.commandmanager import CommandManager from cliff import complete -def test_add_command(): +def test_complete_dictionary(): sot = complete.CompleteDictionary() sot.add_command("image delete".split(), [mock.Mock(option_strings=["1"])]) @@ -43,48 +45,38 @@ class FakeStdout: result = result + line return result -def create_complete_command_mocks(): - app = mock.Mock(name="app") - app_args = mock.Mock(name="app_args") - # actions - action_one = mock.Mock(name="action_one") - action_one.option_strings = ["Eolus"] - action_two = mock.Mock(name="action_two") - action_two.option_strings = ["Wilson", "Sunlight"] - actions = [action_one, action_two] - # get_optional_actions - get_optional_actions = mock.Mock(name="get_optional_actions") - get_optional_actions.return_value = actions - # cmd_parser - cmd_parser = mock.Mock(name="cmd_parser") - cmd_parser._get_optional_actions = get_optional_actions - # get_parser - get_parser = mock.Mock(name="get_parser") - get_parser.return_value = cmd_parser - # cmd_factory_init - cmd_factory_init = mock.Mock("cmd_factory_init") - cmd_factory_init.get_parser = get_parser - # cmd_factory - cmd_factory = mock.Mock(name="cmd_factory") - cmd_factory.return_value = cmd_factory_init - # find_command - cmd_name = "yale" - search_args = "search_args" - find_command = mock.Mock(name="find_command") - find_command.return_value = (cmd_factory, cmd_name, search_args) - # command_manager - commands = [["image create"], ["server meta delete"], ["server ssh"]] - command_manager = mock.MagicMock() - command_manager.__iter__.return_value = iter(commands) - command_manager.find_command = find_command - app.command_manager = command_manager - app.NAME = "openstack" - app.interactive_mode = False - app.stdout = FakeStdout() - return (complete.CompleteCommand(app, app_args), app, actions, cmd_factory_init) +def given_cmdo_data(): + cmdo = "image server" + data = [("image", "create"), + ("image_create", "--eolus"), + ("server", "meta ssh"), + ("server_meta_delete", "--wilson"), + ("server_ssh", "--sunlight")] + return cmdo, data -def check_parser(cmd, args, verify_args): - cmd_parser = cmd.get_parser('check_parser') +def then_data(content): + assert " cmds='image server'\n" in content + assert " cmds_image='create'\n" in content + assert " cmds_image_create='--eolus'\n" in content + assert " cmds_server='meta ssh'\n" in content + assert " cmds_server_meta_delete='--wilson'\n" in content + assert " cmds_server_ssh='--sunlight'\n" in content + +def test_complete_no_code(): + output = FakeStdout() + sot = complete.CompleteNoCode("doesNotMatter", output) + sot.write(*given_cmdo_data()) + then_data(output.content) + +def test_complete_bash(): + output = FakeStdout() + sot = complete.CompleteBash("openstack", output) + sot.write(*given_cmdo_data()) + then_data(output.content) + assert "_openstack()\n" in output.content[0] + assert "complete -F _openstack openstack\n" in output.content[-1] + +def then_parser(cmd, args, verify_args): parsed_args = cmd_parser.parse_args(args) for av in verify_args: attr, value = av @@ -92,48 +84,44 @@ def check_parser(cmd, args, verify_args): assert attr in parsed_args assert getattr(parsed_args, attr) == value -def test_parser_nothing(): - sot, app, actions, cmd_factory_init = create_complete_command_mocks() - check_parser(sot, [], [('name', None), ('shell', 'bash')]) - -def test_parser_no_code(): - sot, app, actions, cmd_factory_init = create_complete_command_mocks() - check_parser(sot, ["--shell", "none", "--name", "foo"], - [('name', 'foo'), ('shell', 'none')]) - -def test_get_actions(): - sot, app, actions, cmd_factory_init = create_complete_command_mocks() - result = sot.get_actions("yale") - cmd_factory_init.get_parser.assert_called_with('openstack yale') - assert actions == result +def test_complete_command_parser(): + sot = complete.CompleteCommand(mock.Mock(), mock.Mock()) + parser = sot.get_parser('nothing') + assert "nothing" == parser.prog + assert "print bash completion command\n " == parser.description + +def given_complete_command(): + cmd_mgr = CommandManager('cliff.tests') + app = App('testing', '1', cmd_mgr, stdout=FakeStdout()) + sot = complete.CompleteCommand(app, mock.Mock()) + cmd_mgr.add_command('complete', complete.CompleteCommand) + return sot, app, cmd_mgr + +def then_actions_equal(actions): + optstr = ' '.join(opt for action in actions + for opt in action.option_strings) + assert '-h --help --name --shell' == optstr + +def test_complete_command_get_actions(): + sot, app, cmd_mgr = given_complete_command() + app.interactive_mode = False + actions = sot.get_actions(["complete"]) + then_actions_equal(actions) -def test_get_actions_interactive(): - sot, app, actions, cmd_factory_init = create_complete_command_mocks() +def test_complete_command_get_actions_interactive(): + sot, app, cmd_mgr = given_complete_command() app.interactive_mode = True - result = sot.get_actions("yale") - cmd_factory_init.get_parser.assert_called_with('yale') - assert actions == result - -def verify_data(content): - assert " cmds='image server'\n" in content - assert " cmds_image='create'\n" in content - assert " cmds_image_create='Eolus Wilson Sunlight'\n" in content - assert " cmds_server='meta ssh'\n" in content - assert " cmds_server_meta_delete='Eolus Wilson Sunlight'\n" in content - assert " cmds_server_ssh='Eolus Wilson Sunlight'\n" in content - -def test_take_action_nocode(): - sot, app, actions, cmd_factory_init = create_complete_command_mocks() - parsed_args = mock.Mock() - parsed_args.shell = "none" - sot.take_action(parsed_args) - verify_data(app.stdout.content) + actions = sot.get_actions(["complete"]) + then_actions_equal(actions) -def test_take_action_code(): - sot, app, actions, cmd_factory_init = create_complete_command_mocks() +def test_complete_command_take_action(): + sot, app, cmd_mgr = given_complete_command() parsed_args = mock.Mock() - parsed_args.name = "openstack" - sot.take_action(parsed_args) - verify_data(app.stdout.content) - assert "_openstack()\n" in app.stdout.content[0] - assert "complete -F _openstack openstack\n" in app.stdout.content[-1] + parsed_args.name = "test_take" + content = app.stdout.content + assert 0 == sot.take_action(parsed_args) + assert "_test_take()\n" in content[0] + assert "complete -F _test_take test_take\n" in content[-1] + assert " cmds='complete help'\n" in content + assert " cmds_complete='-h --help --name --shell'\n" in content + assert " cmds_help='-h --help'\n" in content -- cgit v1.2.1 From 59e1ee96651db8f12cea133c49dcc2d487b3e421 Mon Sep 17 00:00:00 2001 From: Terry Howe Date: Thu, 31 Oct 2013 09:42:35 -0600 Subject: code style fixes --- cliff/complete.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cliff/complete.py b/cliff/complete.py index c31c659..b469e84 100644 --- a/cliff/complete.py +++ b/cliff/complete.py @@ -16,10 +16,8 @@ class CompleteDictionary: def add_command(self, command, actions): optstr = ' '.join(opt for action in actions - for opt in action.option_strings) + for opt in action.option_strings) dicto = self._dictionary - last = None - lastsubcmd = None for subcmd in command[:-1]: dicto = dicto.setdefault(subcmd, {}) dicto[command[-1]] = optstr @@ -49,7 +47,7 @@ class CompleteDictionary: class CompleteShellBase(object): - """ base class for bash completion generation + """base class for bash completion generation """ def __init__(self, name, output): self.name = str(name) @@ -62,6 +60,7 @@ class CompleteShellBase(object): self.output.write(' cmds_{0}=\'{1}\'\n'.format(*datum)) self.output.write(self.get_trailer()) + class CompleteNoCode(CompleteShellBase): """completion with no code """ @@ -92,9 +91,7 @@ class CompleteBash(CompleteShellBase): """) def get_trailer(self): - return ( -""" - + return (""" cmd="" words[0]="" completed="${cmds}" -- cgit v1.2.1 From 9a64d4fa683321e0ec8a76aeba064ff04ca5e0c5 Mon Sep 17 00:00:00 2001 From: Terry Howe Date: Thu, 31 Oct 2013 11:47:33 -0600 Subject: code style fixes --- cliff/tests/test_complete.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/cliff/tests/test_complete.py b/cliff/tests/test_complete.py index eecf48c..7a650d6 100644 --- a/cliff/tests/test_complete.py +++ b/cliff/tests/test_complete.py @@ -4,7 +4,6 @@ import mock from cliff.app import App -from cliff.command import Command from cliff.commandmanager import CommandManager from cliff import complete @@ -12,15 +11,15 @@ from cliff import complete def test_complete_dictionary(): sot = complete.CompleteDictionary() sot.add_command("image delete".split(), - [mock.Mock(option_strings=["1"])]) + [mock.Mock(option_strings=["1"])]) sot.add_command("image list".split(), - [mock.Mock(option_strings=["2"])]) + [mock.Mock(option_strings=["2"])]) sot.add_command("image create".split(), - [mock.Mock(option_strings=["3"])]) + [mock.Mock(option_strings=["3"])]) sot.add_command("volume type create".split(), - [mock.Mock(option_strings=["4"])]) + [mock.Mock(option_strings=["4"])]) sot.add_command("volume type delete".split(), - [mock.Mock(option_strings=["5"])]) + [mock.Mock(option_strings=["5"])]) assert "image volume" == sot.get_commands() result = sot.get_data() assert "image" == result[0][0] @@ -32,6 +31,7 @@ def test_complete_dictionary(): assert "image_list" == result[3][0] assert "2" == result[3][1] + class FakeStdout: def __init__(self): self.content = [] @@ -45,6 +45,7 @@ class FakeStdout: result = result + line return result + def given_cmdo_data(): cmdo = "image server" data = [("image", "create"), @@ -54,6 +55,7 @@ def given_cmdo_data(): ("server_ssh", "--sunlight")] return cmdo, data + def then_data(content): assert " cmds='image server'\n" in content assert " cmds_image='create'\n" in content @@ -62,12 +64,14 @@ def then_data(content): assert " cmds_server_meta_delete='--wilson'\n" in content assert " cmds_server_ssh='--sunlight'\n" in content + def test_complete_no_code(): output = FakeStdout() sot = complete.CompleteNoCode("doesNotMatter", output) sot.write(*given_cmdo_data()) then_data(output.content) + def test_complete_bash(): output = FakeStdout() sot = complete.CompleteBash("openstack", output) @@ -76,13 +80,6 @@ def test_complete_bash(): assert "_openstack()\n" in output.content[0] assert "complete -F _openstack openstack\n" in output.content[-1] -def then_parser(cmd, args, verify_args): - parsed_args = cmd_parser.parse_args(args) - for av in verify_args: - attr, value = av - if attr: - assert attr in parsed_args - assert getattr(parsed_args, attr) == value def test_complete_command_parser(): sot = complete.CompleteCommand(mock.Mock(), mock.Mock()) @@ -90,6 +87,7 @@ def test_complete_command_parser(): assert "nothing" == parser.prog assert "print bash completion command\n " == parser.description + def given_complete_command(): cmd_mgr = CommandManager('cliff.tests') app = App('testing', '1', cmd_mgr, stdout=FakeStdout()) @@ -97,23 +95,27 @@ def given_complete_command(): cmd_mgr.add_command('complete', complete.CompleteCommand) return sot, app, cmd_mgr + def then_actions_equal(actions): optstr = ' '.join(opt for action in actions - for opt in action.option_strings) + for opt in action.option_strings) assert '-h --help --name --shell' == optstr + def test_complete_command_get_actions(): sot, app, cmd_mgr = given_complete_command() app.interactive_mode = False actions = sot.get_actions(["complete"]) then_actions_equal(actions) + def test_complete_command_get_actions_interactive(): sot, app, cmd_mgr = given_complete_command() app.interactive_mode = True actions = sot.get_actions(["complete"]) then_actions_equal(actions) + def test_complete_command_take_action(): sot, app, cmd_mgr = given_complete_command() parsed_args = mock.Mock() -- cgit v1.2.1