Skip to content

Commit

Permalink
Merge pull request #969 from python-cmd2/subcmd_fix
Browse files Browse the repository at this point in the history
Subcmd fix
  • Loading branch information
kmvanbrunt authored Aug 7, 2020
2 parents 47568c0 + a04fdb5 commit 2674341
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 180 deletions.
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
## 1.3.2 (August 7, 2020)
* Bug Fixes
* Fixed `prog` value of subcommands added with `as_subcommand_to()` decorator.
* Fixed missing settings in subcommand parsers created with `as_subcommand_to()` decorator. These settings
include things like description and epilog text.

## 1.3.1 (August 6, 2020)
* Bug Fixes
* Fixed issue determining whether an argparse completer function required a reference to a containing
CommandSet. Also resolves issues determining the correct CommandSet instance when calling the argparse
argument completer function. Manifested as a TypeError when using `cmd2.Cmd.path_complete` as a completer
for an argparse-based command defined in a CommandSet
CommandSet. Also resolves issues determining the correct CommandSet instance when calling the argparse
argument completer function. Manifested as a TypeError when using `cmd2.Cmd.path_complete` as a completer
for an argparse-based command defined in a CommandSet

## 1.3.0 (August 4, 2020)
* Enhancements
Expand Down
16 changes: 14 additions & 2 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,19 @@
# cmd2 code
cmd2/__init__.py @tleonhardt @kotfu
cmd2/ansi.py @kmvanbrunt @tleonhardt
cmd2/argparse_*.py @kmvanbrunt
cmd2/argparse_*.py @kmvanbrunt @anselor
cmd2/clipboard.py @tleonhardt
cmd2/cmd2.py @tleonhardt @kmvanbrunt @kotfu
cmd2/command_definition.py @anselor
cmd2/constants.py @kotfu
cmd2/decorators.py @kotfu @kmvanbrunt @anselor
cmd2/exceptions.py @kmvanbrunt @anselor
cmd2/history.py @kotfu @tleonhardt
cmd2/parsing.py @kotfu @kmvanbrunt
cmd2/plugin.py @kotfu
cmd2/pyscript_bridge.py @kmvanbrunt
cmd2/py_bridge.py @kmvanbrunt
cmd2/rl_utils.py @kmvanbrunt
cmd2/table_creator.py @kmvanbrunt
cmd2/transcript.py @kotfu
cmd2/utils.py @tleonhardt @kotfu @kmvanbrunt

Expand All @@ -34,6 +39,11 @@ docs/* @tleonhardt @kotfu
examples/async_printing.py @kmvanbrunt
examples/environment.py @kotfu
examples/tab_*.py @kmvanbrunt
examples/modular_*.py @anselor
examples/modular_commands/* @anselor

plugins/template/* @kotfu
plugins/ext_test/* @anselor

# Unit Tests
tests/pyscript/* @kmvanbrunt
Expand All @@ -47,6 +57,8 @@ tests/test_pars*.py @kotfu
tests/test_run_pyscript.py @kmvanbrunt
tests/test_transcript.py @kotfu

tests_isolated/test_commandset/* @anselor

# Top-level project stuff
CONTRIBUTING.md @tleonhardt @kotfu
setup.py @tleonhardt @kotfu
Expand Down
330 changes: 175 additions & 155 deletions cmd2/cmd2.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion cmd2/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
# subcommand attributes for the base command name and the subcommand name
SUBCMD_ATTR_COMMAND = 'parent_command'
SUBCMD_ATTR_NAME = 'subcommand_name'
SUBCMD_ATTR_PARSER_ARGS = 'subcommand_parser_args'
SUBCMD_ATTR_ADD_PARSER_KWARGS = 'subcommand_add_parser_kwargs'

# arpparse attribute linking to command set instance
PARSER_ATTR_COMMANDSET = 'command_set'
25 changes: 14 additions & 11 deletions cmd2/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,37 +326,40 @@ def as_subcommand_to(command: str,
subcommand: str,
parser: argparse.ArgumentParser,
*,
help_text: Optional[str] = None,
help: Optional[str] = None,
aliases: Iterable[str] = None) -> Callable[[argparse.Namespace], Optional[bool]]:
"""
Tag this method as a subcommand to an existing argparse decorated command.
:param command: Command Name. Space-delimited subcommands may optionally be specified
:param subcommand: Subcommand name
:param parser: argparse Parser for this subcommand
:param help_text: Help message for this subcommand
:param aliases: Alternative names for this subcommand
:param help: Help message for this subcommand which displays in the list of subcommands of the command we are adding to.
This is passed as the help argument to ArgumentParser.add_subparser().
:param aliases: Alternative names for this subcommand. This is passed as the alias argument to
ArgumentParser.add_subparser().
:return: Wrapper function that can receive an argparse.Namespace
"""
def arg_decorator(func: Callable):
_set_parser_prog(parser, subcommand)
_set_parser_prog(parser, command + ' ' + subcommand)

# If the description has not been set, then use the method docstring if one exists
if parser.description is None and func.__doc__:
parser.description = func.__doc__

parser.set_defaults(func=func)

# Set some custom attributes for this command
setattr(func, constants.SUBCMD_ATTR_COMMAND, command)
setattr(func, constants.CMD_ATTR_ARGPARSER, parser)
setattr(func, constants.SUBCMD_ATTR_NAME, subcommand)
parser_args = {}
if help_text is not None:
parser_args['help'] = help_text

# Keyword arguments for ArgumentParser.add_subparser()
add_parser_kwargs = dict()
if help is not None:
add_parser_kwargs['help'] = help
if aliases is not None:
parser_args['aliases'] = aliases[:]
setattr(func, constants.SUBCMD_ATTR_PARSER_ARGS, parser_args)
add_parser_kwargs['aliases'] = aliases[:]

setattr(func, constants.SUBCMD_ATTR_ADD_PARSER_KWARGS, add_parser_kwargs)

return func

Expand Down
14 changes: 8 additions & 6 deletions examples/modular_subcommands.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# coding=utf-8
"""A simple example demonstracting modular subcommand loading through CommandSets
"""A simple example demonstrating modular subcommand loading through CommandSets
In this example, there are loadable CommandSets defined. Each CommandSet has 1 subcommand defined that will be
attached to the 'cut' command.
Expand All @@ -23,10 +23,11 @@ def __init__(self):
def do_apple(self, cmd: cmd2.Cmd, _: cmd2.Statement):
cmd.poutput('Apple')

banana_parser = cmd2.Cmd2ArgumentParser(add_help=False)
banana_description = "Cut a banana"
banana_parser = cmd2.Cmd2ArgumentParser(add_help=False, description=banana_description)
banana_parser.add_argument('direction', choices=['discs', 'lengthwise'])

@cmd2.as_subcommand_to('cut', 'banana', banana_parser)
@cmd2.as_subcommand_to('cut', 'banana', banana_parser, help=banana_description.lower())
def cut_banana(self, cmd: cmd2.Cmd, ns: argparse.Namespace):
"""Cut banana"""
cmd.poutput('cutting banana: ' + ns.direction)
Expand All @@ -40,10 +41,11 @@ def __init__(self):
def do_arugula(self, cmd: cmd2.Cmd, _: cmd2.Statement):
cmd.poutput('Arugula')

bokchoy_parser = cmd2.Cmd2ArgumentParser(add_help=False)
bokchoy_description = "Cut some bokchoy"
bokchoy_parser = cmd2.Cmd2ArgumentParser(add_help=False, description=bokchoy_description)
bokchoy_parser.add_argument('style', choices=['quartered', 'diced'])

@cmd2.as_subcommand_to('cut', 'bokchoy', bokchoy_parser)
@cmd2.as_subcommand_to('cut', 'bokchoy', bokchoy_parser, help=bokchoy_description.lower())
def cut_bokchoy(self, cmd: cmd2.Cmd, _: cmd2.Statement):
cmd.poutput('Bok Choy')

Expand Down Expand Up @@ -95,9 +97,9 @@ def do_unload(self, ns: argparse.Namespace):

@with_argparser(cut_parser)
def do_cut(self, ns: argparse.Namespace):
# Call handler for whatever subcommand was selected
handler = ns.get_handler()
if handler is not None:
# Call whatever subcommand function was selected
handler(ns)
else:
# No subcommand was provided, so call help
Expand Down
4 changes: 2 additions & 2 deletions tests_isolated/test_commandset/test_commandset.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ def do_apple(self, cmd: cmd2.Cmd, _: cmd2.Statement):
banana_parser = cmd2.Cmd2ArgumentParser(add_help=False)
banana_parser.add_argument('direction', choices=['discs', 'lengthwise'])

@cmd2.as_subcommand_to('cut', 'banana', banana_parser, help_text='Cut banana', aliases=['bananer'])
@cmd2.as_subcommand_to('cut', 'banana', banana_parser, help='Cut banana', aliases=['bananer'])
def cut_banana(self, cmd: cmd2.Cmd, ns: argparse.Namespace):
"""Cut banana"""
cmd.poutput('cutting banana: ' + ns.direction)
Expand Down Expand Up @@ -545,7 +545,7 @@ def do_cut(self, ns: argparse.Namespace):
banana_parser = cmd2.Cmd2ArgumentParser(add_help=False)
banana_parser.add_argument('direction', choices=['discs', 'lengthwise'])

@cmd2.as_subcommand_to('cut', 'banana', banana_parser, help_text='Cut banana', aliases=['bananer'])
@cmd2.as_subcommand_to('cut', 'banana', banana_parser, help='Cut banana', aliases=['bananer'])
def cut_banana(self, ns: argparse.Namespace):
"""Cut banana"""
self.poutput('cutting banana: ' + ns.direction)
Expand Down

0 comments on commit 2674341

Please sign in to comment.