Skip to content

Conversation

@anselor
Copy link
Contributor

@anselor anselor commented Apr 22, 2018

If a command uses argparse and doesn't define a completion function, cmd2 now provides a default implementation that uses AutoCompleter.

Added support for decorating individual argparse actions with a completion collection or function. This should make it unnecessary to implement a completion function if a command uses argparse.

This closes #349

@anselor anselor requested a review from tleonhardt as a code owner April 22, 2018 03:01
@codecov
Copy link

codecov bot commented Apr 22, 2018

Codecov Report

Merging #366 into master will decrease coverage by 0.13%.
The diff coverage is 84.21%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master    #366      +/-   ##
=========================================
- Coverage   91.23%   91.1%   -0.14%     
=========================================
  Files           3       3              
  Lines        2271    2236      -35     
=========================================
- Hits         2072    2037      -35     
  Misses        199     199
Impacted Files Coverage Δ
cmd2/cmd2.py 90.97% <84%> (-0.3%) ⬇️
cmd2/argparse_completer.py 91.15% <84.61%> (+0.5%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a0a46f9...cbb94bf. Read the comment docs.

tleonhardt
tleonhardt previously approved these changes Apr 22, 2018
Copy link
Member

@tleonhardt tleonhardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything looks good to me.

Consider updating the tab_completion.py example, even if it is just to add a comment mentioning that there is a newer and better way of doing things shown in the tab_autocompletion.py example.

* All ``cmd2`` code should be ported to use the new ``argparse``-based decorators
* See the [Argument Processing](http://cmd2.readthedocs.io/en/latest/argument_processing.html) section of the documentation for more information on these decorators
* Alternatively, see the [argparse_example.py](https://github.com/python-cmd2/cmd2/blob/master/examples/argparse_example.py)
* Deleted ``cmd_with_subs_completer``, ``get_subcommands``, and ``get_subcommand_completer``
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this. It makes it easier for the vast majority of cases.

Also, a convenience function called ``cmd_with_subs_completer`` is available to easily add tab completion to functions
that implement subcommands. By setting this as the completer of the base command function, the correct completer for
the chosen subcommand will be called.
You may add multiple layers of sub-commands for your command. Cmd2 will automatically traverse and tab-complete
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating the docs

parser_bar.add_argument('z', help='string')
parser_bar.set_defaults(func=base_bar)

bar_subparsers = parser_bar.add_subparsers(title='layer3', help='help for 3rd layer of commands')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And thanks for updating the relevant examples

@tleonhardt tleonhardt requested a review from kmvanbrunt April 22, 2018 03:57
@tleonhardt
Copy link
Member

@kmvanbrunt If you aren't too busy I would appreciate your review of this one since you are much more familiar with the idiosyncrasies of readline tab completion than I am.

cmd2/cmd2.py Outdated
@@ -268,21 +269,7 @@ def cmd_wrapper(instance, cmdline):

# Mark this function as having an argparse ArgumentParser (used by do_help)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this isn't just used by do_help anymore, maybe update this comment to say something like "Used to tab complete this command and its subcommands"
This comment appears twice in the file so update both.

compfunc = functools.partial(self._autocomplete_default,
argparser=argparser)
else:
compfunc = self.completedefault
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can has_parser ever be False if the command uses argparse? If not, can that attribute be removed and this code simplified to be:

try:
    compfunc = getattr(self, 'complete_' + command)
except AttributeError:
    # There's no completer function, next see if we should switch to the default argparse completer
    try:
        cmd_func = getattr(self, 'do_' + command)
        argparser = getattr(cmd_func, 'argparser')
        compfunc = functools.partial(self._autocomplete_default, argparser=argparser)
    except AttributeError:
        compfunc = self.completedefault

cmd2/cmd2.py Outdated
cmd_func = getattr(self, 'do_' + command)
parser = getattr(cmd_func, 'argparser')
completer = AutoCompleter(parser)
matches = completer.complete_command_help(tokens[1:], text, line, begidx, endidx)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming has_parser can be removed since it appears to always be True in an argparse command, simplify the code to something like

elif index >= subcmd_index:
    # If this command uses an argparser, then complete its subcommands if any exist
    try:
        cmd_func = getattr(self, 'do_' + tokens[cmd_index])
        parser = getattr(cmd_func, 'argparser')
        completer = AutoCompleter(parser)
        matches = completer.complete_command_help(tokens[cmd_index:], text, line, begidx, endidx)
    except AttributeError:
        pass

index_dict = {1: self.shell_cmd_complete}
return self.index_based_complete(text, line, begidx, endidx, index_dict, self.path_complete)

def cmd_with_subs_completer(self, text, line, begidx, endidx):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this function had the shortest lifespan of anything I've ever written!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it makes you feel any better - with the revelation that I can just tag the completer on each argparse argument action object I think can remove all of the complicated nested dictionary stuff that I did to pass that over to the AutoCompleter.

return completion_results

def complete_command_help(self, tokens: List[str], text: str, line: str, begidx: int, endidx: int) -> List[str]:
"""Supports the completion of sub-commands for commands thhrough the cmd2 help command."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix spelling of "through"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

argparse tab autocompleter

4 participants