summaryrefslogtreecommitdiff
path: root/cmd2
diff options
context:
space:
mode:
authorEric Lin <anselor@gmail.com>2021-08-19 17:03:42 -0400
committeranselor <anselor@gmail.com>2021-08-23 14:17:12 -0400
commit49805324772ec5fcd06217f4f0a241a9a8d07960 (patch)
treea1647a18085df118bbe12dc37c67104f2817024b /cmd2
parent6d771e96c0507d9ef4ad3eaaf4bc83669396e591 (diff)
downloadcmd2-git-49805324772ec5fcd06217f4f0a241a9a8d07960.tar.gz
* New function `set_default_command_completer_type()` allows developer to extend and modify the
behavior of `ArgparseCompleter`. * New function `register_argparse_argument_parameter()` allows developers to specify custom parameters to be passed to the argparse parser's `add_argument()` method. These parameters will become accessible in the resulting argparse Action object when modifying `ArgparseCompleter` behavior.
Diffstat (limited to 'cmd2')
-rw-r--r--cmd2/__init__.py12
-rw-r--r--cmd2/argparse_completer.py45
-rw-r--r--cmd2/argparse_custom.py78
-rw-r--r--cmd2/cmd2.py66
-rw-r--r--cmd2/constants.py1
-rw-r--r--cmd2/decorators.py7
6 files changed, 169 insertions, 40 deletions
diff --git a/cmd2/__init__.py b/cmd2/__init__.py
index e545f394..776e783c 100644
--- a/cmd2/__init__.py
+++ b/cmd2/__init__.py
@@ -20,7 +20,13 @@ except importlib_metadata.PackageNotFoundError: # pragma: no cover
from typing import List
from .ansi import style, fg, bg
-from .argparse_custom import Cmd2ArgumentParser, Cmd2AttributeWrapper, CompletionItem, set_default_argument_parser
+from .argparse_custom import (
+ Cmd2ArgumentParser,
+ Cmd2AttributeWrapper,
+ CompletionItem,
+ register_argparse_argument_parameter,
+ set_default_argument_parser,
+)
# Check if user has defined a module that sets a custom value for argparse_custom.DEFAULT_ARGUMENT_PARSER
import argparse
@@ -31,6 +37,7 @@ if cmd2_parser_module is not None:
importlib.import_module(cmd2_parser_module)
+from .argparse_completer import DEFAULT_COMMAND_COMPLETER, set_default_command_completer_type
# Get the current value for argparse_custom.DEFAULT_ARGUMENT_PARSER
from .argparse_custom import DEFAULT_ARGUMENT_PARSER
from .cmd2 import Cmd
@@ -47,6 +54,7 @@ from .utils import categorize, CompletionMode, CustomCompletionSettings, Settabl
__all__: List[str] = [
'COMMAND_NAME',
'DEFAULT_ARGUMENT_PARSER',
+ 'DEFAULT_COMMAND_COMPLETER',
'DEFAULT_SHORTCUTS',
# ANSI Style exports
'bg',
@@ -56,7 +64,9 @@ __all__: List[str] = [
'Cmd2ArgumentParser',
'Cmd2AttributeWrapper',
'CompletionItem',
+ 'register_argparse_argument_parameter',
'set_default_argument_parser',
+ 'set_default_command_completer_type',
# Cmd2
'Cmd',
'CommandResult',
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py
index a244941c..584eb74e 100644
--- a/cmd2/argparse_completer.py
+++ b/cmd2/argparse_completer.py
@@ -13,18 +13,28 @@ from collections import (
deque,
)
from typing import (
+ TYPE_CHECKING,
Dict,
List,
Optional,
+ Type,
Union,
cast,
)
-from . import (
- ansi,
- cmd2,
- constants,
+from .ansi import (
+ style_aware_wcswidth,
+ widest_line,
)
+from .constants import (
+ INFINITY,
+)
+
+if TYPE_CHECKING:
+ from .cmd2 import (
+ Cmd,
+ )
+
from .argparse_custom import (
ChoicesCallable,
ChoicesProviderFuncWithTokens,
@@ -124,10 +134,10 @@ class _ArgumentState:
self.max = 1
elif self.action.nargs == argparse.ZERO_OR_MORE or self.action.nargs == argparse.REMAINDER:
self.min = 0
- self.max = constants.INFINITY
+ self.max = INFINITY
elif self.action.nargs == argparse.ONE_OR_MORE:
self.min = 1
- self.max = constants.INFINITY
+ self.max = INFINITY
else:
self.min = self.action.nargs
self.max = self.action.nargs
@@ -165,7 +175,7 @@ class ArgparseCompleter:
"""Automatic command line tab completion based on argparse parameters"""
def __init__(
- self, parser: argparse.ArgumentParser, cmd2_app: cmd2.Cmd, *, parent_tokens: Optional[Dict[str, List[str]]] = None
+ self, parser: argparse.ArgumentParser, cmd2_app: 'Cmd', *, parent_tokens: Optional[Dict[str, List[str]]] = None
) -> None:
"""
Create an ArgparseCompleter
@@ -564,15 +574,15 @@ class ArgparseCompleter:
desc_header = desc_header.replace('\t', four_spaces)
# Calculate needed widths for the token and description columns of the table
- token_width = ansi.style_aware_wcswidth(destination)
- desc_width = ansi.widest_line(desc_header)
+ token_width = style_aware_wcswidth(destination)
+ desc_width = widest_line(desc_header)
for item in completion_items:
- token_width = max(ansi.style_aware_wcswidth(item), token_width)
+ token_width = max(style_aware_wcswidth(item), token_width)
# Replace tabs with 4 spaces so we can calculate width
item.description = item.description.replace('\t', four_spaces)
- desc_width = max(ansi.widest_line(item.description), desc_width)
+ desc_width = max(widest_line(item.description), desc_width)
cols = list()
cols.append(Column(destination.upper(), width=token_width))
@@ -728,3 +738,16 @@ class ArgparseCompleter:
return []
return self._format_completions(arg_state, results)
+
+
+DEFAULT_COMMAND_COMPLETER: Type[ArgparseCompleter] = ArgparseCompleter
+
+
+def set_default_command_completer_type(completer_type: Type[ArgparseCompleter]) -> None:
+ """
+ Set the default command completer type. It must be a sub-class of the ArgparseCompleter.
+
+ :param completer_type: Type that is a subclass of ArgparseCompleter.
+ """
+ global DEFAULT_COMMAND_COMPLETER
+ DEFAULT_COMMAND_COMPLETER = completer_type
diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py
index 770b1e5c..2bcb0af9 100644
--- a/cmd2/argparse_custom.py
+++ b/cmd2/argparse_custom.py
@@ -238,6 +238,7 @@ from typing import (
NoReturn,
Optional,
Sequence,
+ Set,
Tuple,
Type,
Union,
@@ -639,9 +640,73 @@ setattr(argparse.Action, 'set_suppress_tab_hint', _action_set_suppress_tab_hint)
############################################################################################################
+# Allow developers to add custom action attributes
+############################################################################################################
+
+CUSTOM_ACTION_ATTRIBS: Set[str] = set()
+_CUSTOM_ATTRIB_PFX = '_attr_'
+
+
+def register_argparse_argument_parameter(param_name: str, param_type: Optional[Type[Any]]) -> None:
+ """
+ Registers a custom argparse argument parameter.
+
+ The registered name will then be a recognized keyword parameter to the parser's `add_argument()` function.
+
+ An accessor functions will be added to the parameter's Action object in the form of: ``get_{param_name}()``
+ and ``set_{param_name}(value)``.
+
+ :param param_name: Name of the parameter to add.
+ """
+ attr_name = f'{_CUSTOM_ATTRIB_PFX}{param_name}'
+ if param_name in CUSTOM_ACTION_ATTRIBS or hasattr(argparse.Action, attr_name):
+ raise KeyError(f'Custom parameter {param_name} already exists')
+ if not re.search('^[A-Za-z_][A-Za-z0-9_]*$', param_name):
+ raise KeyError(f'Invalid parameter name {param_name} - cannot be used as a python identifier')
+
+ getter_name = f'get_{param_name}'
+
+ def _action_get_custom_parameter(self: argparse.Action) -> Any:
+ f"""
+ Get the custom {param_name} attribute of an argparse Action.
+
+ This function is added by cmd2 as a method called ``{getter_name}()`` to ``argparse.Action`` class.
+
+ To call: ``action.{getter_name}()``
+
+ :param self: argparse Action being queried
+ :return: The value of {param_name} or None if attribute does not exist
+ """
+ return getattr(self, attr_name, None)
+
+ setattr(argparse.Action, getter_name, _action_get_custom_parameter)
+
+ setter_name = f'set_{param_name}'
+
+ def _action_set_custom_parameter(self: argparse.Action, value: Any) -> None:
+ f"""
+ Set the custom {param_name} attribute of an argparse Action.
+
+ This function is added by cmd2 as a method called ``{setter_name}()`` to ``argparse.Action`` class.
+
+ To call: ``action.{setter_name}({param_name})``
+
+ :param self: argparse Action being updated
+ :param value: value being assigned
+ """
+ if param_type and not isinstance(value, param_type):
+ raise TypeError(f'{param_name} must be of type {param_type}, got: {value} ({type(value)})')
+ setattr(self, attr_name, value)
+
+ setattr(argparse.Action, setter_name, _action_set_custom_parameter)
+
+ CUSTOM_ACTION_ATTRIBS.add(param_name)
+
+############################################################################################################
# Patch _ActionsContainer.add_argument with our wrapper to support more arguments
############################################################################################################
+
# Save original _ActionsContainer.add_argument so we can call it in our wrapper
# noinspection PyProtectedMember
orig_actions_container_add_argument = argparse._ActionsContainer.add_argument
@@ -751,6 +816,14 @@ def _add_argument_wrapper(
# Add the argparse-recognized version of nargs to kwargs
kwargs['nargs'] = nargs_adjusted
+ # Extract registered custom keyword arguments
+ custom_attribs: Dict[str, Any] = {}
+ for keyword, value in kwargs.items():
+ if keyword in CUSTOM_ACTION_ATTRIBS:
+ custom_attribs[keyword] = value
+ for keyword in custom_attribs:
+ del kwargs[keyword]
+
# Create the argument using the original add_argument function
new_arg = orig_actions_container_add_argument(self, *args, **kwargs)
@@ -765,6 +838,11 @@ def _add_argument_wrapper(
new_arg.set_suppress_tab_hint(suppress_tab_hint) # type: ignore[attr-defined]
new_arg.set_descriptive_header(descriptive_header) # type: ignore[attr-defined]
+ for keyword, value in custom_attribs.items():
+ attr_setter = getattr(new_arg, f'set_{keyword}', None)
+ if attr_setter is not None:
+ attr_setter(value)
+
return new_arg
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index df4d1dc2..a1707aee 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -71,12 +71,13 @@ from typing import (
from . import (
ansi,
+ argparse_completer,
+ argparse_custom,
constants,
plugin,
utils,
)
from .argparse_custom import (
- DEFAULT_ARGUMENT_PARSER,
ChoicesProviderFunc,
CompleterFunc,
CompletionItem,
@@ -405,7 +406,7 @@ class Cmd(cmd.Cmd):
# Check for command line args
if allow_cli_args:
- parser = DEFAULT_ARGUMENT_PARSER()
+ parser = argparse_custom.DEFAULT_ARGUMENT_PARSER()
parser.add_argument('-t', '--test', action="store_true", help='Test against transcript(s) in FILE (wildcards OK)')
callopts, callargs = parser.parse_known_args()
@@ -1900,10 +1901,16 @@ class Cmd(cmd.Cmd):
# There's no completer function, next see if the command uses argparse
func = self.cmd_func(command)
argparser = getattr(func, constants.CMD_ATTR_ARGPARSER, None)
+ completer_type = getattr(func, constants.CMD_ATTR_COMPLETER, argparse_completer.DEFAULT_COMMAND_COMPLETER)
+ if completer_type is None:
+ completer_type = argparse_completer.DEFAULT_COMMAND_COMPLETER
if func is not None and argparser is not None:
cmd_set = self._cmd_to_command_sets[command] if command in self._cmd_to_command_sets else None
- completer = ArgparseCompleter(argparser, self)
+ if completer_type is not None:
+ completer = completer_type(argparser, self)
+ else:
+ completer = ArgparseCompleter(argparser, self)
preserve_quotes = getattr(func, constants.CMD_ATTR_PRESERVE_QUOTES)
completer_func = functools.partial(
@@ -2072,7 +2079,7 @@ class Cmd(cmd.Cmd):
break
else:
# No shortcut was found. Complete the command token.
- parser = DEFAULT_ARGUMENT_PARSER(add_help=False)
+ parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(add_help=False)
parser.add_argument(
'command',
metavar="COMMAND",
@@ -2879,7 +2886,7 @@ class Cmd(cmd.Cmd):
# Set custom completion settings
else:
if parser is None:
- parser = DEFAULT_ARGUMENT_PARSER(add_help=False)
+ parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(add_help=False)
parser.add_argument(
'arg',
suppress_tab_hint=True,
@@ -3081,7 +3088,7 @@ class Cmd(cmd.Cmd):
# Top-level parser for alias
alias_description = "Manage aliases\n" "\n" "An alias is a command that enables replacement of a word by another string."
alias_epilog = "See also:\n" " macro"
- alias_parser = DEFAULT_ARGUMENT_PARSER(description=alias_description, epilog=alias_epilog)
+ alias_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=alias_description, epilog=alias_epilog)
alias_subparsers = alias_parser.add_subparsers(dest='subcommand', metavar='SUBCOMMAND')
alias_subparsers.required = True
@@ -3110,7 +3117,8 @@ class Cmd(cmd.Cmd):
" alias create save_results print_results \">\" out.txt\n"
)
- alias_create_parser = DEFAULT_ARGUMENT_PARSER(description=alias_create_description, epilog=alias_create_epilog)
+ alias_create_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=alias_create_description,
+ epilog=alias_create_epilog)
alias_create_parser.add_argument('name', help='name of this alias')
alias_create_parser.add_argument(
'command', help='what the alias resolves to', choices_provider=_get_commands_aliases_and_macros_for_completion
@@ -3156,7 +3164,7 @@ class Cmd(cmd.Cmd):
alias_delete_help = "delete aliases"
alias_delete_description = "Delete specified aliases or all aliases if --all is used"
- alias_delete_parser = DEFAULT_ARGUMENT_PARSER(description=alias_delete_description)
+ alias_delete_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=alias_delete_description)
alias_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all aliases")
alias_delete_parser.add_argument(
'names',
@@ -3191,7 +3199,7 @@ class Cmd(cmd.Cmd):
"Without arguments, all aliases will be listed."
)
- alias_list_parser = DEFAULT_ARGUMENT_PARSER(description=alias_list_description)
+ alias_list_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=alias_list_description)
alias_list_parser.add_argument(
'names',
nargs=argparse.ZERO_OR_MORE,
@@ -3239,7 +3247,7 @@ class Cmd(cmd.Cmd):
# Top-level parser for macro
macro_description = "Manage macros\n" "\n" "A macro is similar to an alias, but it can contain argument placeholders."
macro_epilog = "See also:\n" " alias"
- macro_parser = DEFAULT_ARGUMENT_PARSER(description=macro_description, epilog=macro_epilog)
+ macro_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=macro_description, epilog=macro_epilog)
macro_subparsers = macro_parser.add_subparsers(dest='subcommand', metavar='SUBCOMMAND')
macro_subparsers.required = True
@@ -3292,7 +3300,8 @@ class Cmd(cmd.Cmd):
" will only complete paths while typing a macro."
)
- macro_create_parser = DEFAULT_ARGUMENT_PARSER(description=macro_create_description, epilog=macro_create_epilog)
+ macro_create_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=macro_create_description,
+ epilog=macro_create_epilog)
macro_create_parser.add_argument('name', help='name of this macro')
macro_create_parser.add_argument(
'command', help='what the macro resolves to', choices_provider=_get_commands_aliases_and_macros_for_completion
@@ -3382,7 +3391,7 @@ class Cmd(cmd.Cmd):
# macro -> delete
macro_delete_help = "delete macros"
macro_delete_description = "Delete specified macros or all macros if --all is used"
- macro_delete_parser = DEFAULT_ARGUMENT_PARSER(description=macro_delete_description)
+ macro_delete_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=macro_delete_description)
macro_delete_parser.add_argument('-a', '--all', action='store_true', help="delete all macros")
macro_delete_parser.add_argument(
'names',
@@ -3417,7 +3426,7 @@ class Cmd(cmd.Cmd):
"Without arguments, all macros will be listed."
)
- macro_list_parser = DEFAULT_ARGUMENT_PARSER(description=macro_list_description)
+ macro_list_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=macro_list_description)
macro_list_parser.add_argument(
'names',
nargs=argparse.ZERO_OR_MORE,
@@ -3490,7 +3499,7 @@ class Cmd(cmd.Cmd):
completer = ArgparseCompleter(argparser, self)
return completer.complete_subcommand_help(text, line, begidx, endidx, arg_tokens['subcommands'])
- help_parser = DEFAULT_ARGUMENT_PARSER(
+ help_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(
description="List available commands or provide " "detailed help for a specific command"
)
help_parser.add_argument(
@@ -3670,7 +3679,7 @@ class Cmd(cmd.Cmd):
command = ''
self.poutput()
- shortcuts_parser = DEFAULT_ARGUMENT_PARSER(description="List available shortcuts")
+ shortcuts_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="List available shortcuts")
@with_argparser(shortcuts_parser)
def do_shortcuts(self, _: argparse.Namespace) -> None:
@@ -3680,7 +3689,8 @@ class Cmd(cmd.Cmd):
result = "\n".join('{}: {}'.format(sc[0], sc[1]) for sc in sorted_shortcuts)
self.poutput(f"Shortcuts for other commands:\n{result}")
- eof_parser = DEFAULT_ARGUMENT_PARSER(description="Called when Ctrl-D is pressed", epilog=INTERNAL_COMMAND_EPILOG)
+ eof_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="Called when Ctrl-D is pressed",
+ epilog=INTERNAL_COMMAND_EPILOG)
@with_argparser(eof_parser)
def do_eof(self, _: argparse.Namespace) -> Optional[bool]:
@@ -3693,7 +3703,7 @@ class Cmd(cmd.Cmd):
# noinspection PyTypeChecker
return self.do_quit('')
- quit_parser = DEFAULT_ARGUMENT_PARSER(description="Exit this application")
+ quit_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="Exit this application")
@with_argparser(quit_parser)
def do_quit(self, _: argparse.Namespace) -> Optional[bool]:
@@ -3761,7 +3771,7 @@ class Cmd(cmd.Cmd):
raise CompletionError(param + " is not a settable parameter")
# Create a parser with a value field based on this settable
- settable_parser = DEFAULT_ARGUMENT_PARSER(parents=[Cmd.set_parser_parent])
+ settable_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(parents=[Cmd.set_parser_parent])
# Settables with choices list the values of those choices instead of the arg name
# in help text and this shows in tab completion hints. Set metavar to avoid this.
@@ -3792,7 +3802,7 @@ class Cmd(cmd.Cmd):
"Call without arguments for a list of all settable parameters with their values.\n"
"Call with just param to view that parameter's value."
)
- set_parser_parent = DEFAULT_ARGUMENT_PARSER(description=set_description, add_help=False)
+ set_parser_parent = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=set_description, add_help=False)
set_parser_parent.add_argument(
'-v', '--verbose', action='store_true', help='include description of parameters when viewing'
)
@@ -3805,7 +3815,7 @@ class Cmd(cmd.Cmd):
)
# Create the parser for the set command
- set_parser = DEFAULT_ARGUMENT_PARSER(parents=[set_parser_parent])
+ set_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(parents=[set_parser_parent])
set_parser.add_argument(
'value', nargs=argparse.OPTIONAL, help='new value for settable', completer=complete_set_value, suppress_tab_hint=True
)
@@ -3859,7 +3869,7 @@ class Cmd(cmd.Cmd):
else:
self.poutput(result_str)
- shell_parser = DEFAULT_ARGUMENT_PARSER(description="Execute a command as if at the OS prompt")
+ shell_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="Execute a command as if at the OS prompt")
shell_parser.add_argument('command', help='the command to run', completer=shell_cmd_complete)
shell_parser.add_argument(
'command_args', nargs=argparse.REMAINDER, help='arguments to pass to command', completer=path_complete
@@ -4167,7 +4177,7 @@ class Cmd(cmd.Cmd):
return py_bridge.stop
- py_parser = DEFAULT_ARGUMENT_PARSER(description="Run an interactive Python shell")
+ py_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="Run an interactive Python shell")
@with_argparser(py_parser)
def do_py(self, _: argparse.Namespace) -> Optional[bool]:
@@ -4177,7 +4187,7 @@ class Cmd(cmd.Cmd):
"""
return self._run_python()
- run_pyscript_parser = DEFAULT_ARGUMENT_PARSER(description="Run a Python script file inside the console")
+ run_pyscript_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="Run a Python script file inside the console")
run_pyscript_parser.add_argument('script_path', help='path to the script file', completer=path_complete)
run_pyscript_parser.add_argument(
'script_arguments', nargs=argparse.REMAINDER, help='arguments to pass to script', completer=path_complete
@@ -4214,7 +4224,7 @@ class Cmd(cmd.Cmd):
return py_return
- ipython_parser = DEFAULT_ARGUMENT_PARSER(description="Run an interactive IPython shell")
+ ipython_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description="Run an interactive IPython shell")
# noinspection PyPackageRequirements
@with_argparser(ipython_parser)
@@ -4284,7 +4294,7 @@ class Cmd(cmd.Cmd):
history_description = "View, run, edit, save, or clear previously entered commands"
- history_parser = DEFAULT_ARGUMENT_PARSER(description=history_description)
+ history_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=history_description)
history_action_group = history_parser.add_mutually_exclusive_group()
history_action_group.add_argument('-r', '--run', action='store_true', help='run selected history items')
history_action_group.add_argument('-e', '--edit', action='store_true', help='edit and then run selected history items')
@@ -4621,7 +4631,7 @@ class Cmd(cmd.Cmd):
" set editor (program-name)"
)
- edit_parser = DEFAULT_ARGUMENT_PARSER(description=edit_description)
+ edit_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=edit_description)
edit_parser.add_argument(
'file_path', nargs=argparse.OPTIONAL, help="optional path to a file to open in editor", completer=path_complete
)
@@ -4666,7 +4676,7 @@ class Cmd(cmd.Cmd):
"the output of the script commands to a transcript for testing purposes.\n"
)
- run_script_parser = DEFAULT_ARGUMENT_PARSER(description=run_script_description)
+ run_script_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(description=run_script_description)
run_script_parser.add_argument(
'-t',
'--transcript',
@@ -4735,7 +4745,7 @@ class Cmd(cmd.Cmd):
relative_run_script_epilog = "Notes:\n" " This command is intended to only be used within text file scripts."
- relative_run_script_parser = DEFAULT_ARGUMENT_PARSER(
+ relative_run_script_parser = argparse_custom.DEFAULT_ARGUMENT_PARSER(
description=relative_run_script_description, epilog=relative_run_script_epilog
)
relative_run_script_parser.add_argument('file_path', help='a file path pointing to a script')
diff --git a/cmd2/constants.py b/cmd2/constants.py
index 9f29be86..7656ae58 100644
--- a/cmd2/constants.py
+++ b/cmd2/constants.py
@@ -43,6 +43,7 @@ CLASS_ATTR_DEFAULT_HELP_CATEGORY = 'cmd2_default_help_category'
# The argparse parser for the command
CMD_ATTR_ARGPARSER = 'argparser'
+CMD_ATTR_COMPLETER = 'command_completer'
# Whether or not tokens are unquoted before sending to argparse
CMD_ATTR_PRESERVE_QUOTES = 'preserve_quotes'
diff --git a/cmd2/decorators.py b/cmd2/decorators.py
index 1ff0bdbe..644a8add 100644
--- a/cmd2/decorators.py
+++ b/cmd2/decorators.py
@@ -10,12 +10,16 @@ from typing import (
Optional,
Sequence,
Tuple,
+ Type,
Union,
)
from . import (
constants,
)
+from .argparse_completer import (
+ ArgparseCompleter,
+)
from .argparse_custom import (
Cmd2AttributeWrapper,
)
@@ -271,6 +275,7 @@ def with_argparser(
ns_provider: Optional[Callable[..., argparse.Namespace]] = None,
preserve_quotes: bool = False,
with_unknown_args: bool = False,
+ completer: Optional[Type[ArgparseCompleter]] = None,
) -> Callable[[ArgparseCommandFunc], RawCommandFuncOptionalBoolReturn]:
"""A decorator to alter a cmd2 method to populate its ``args`` argument by parsing arguments
with the given instance of argparse.ArgumentParser.
@@ -281,6 +286,7 @@ def with_argparser(
state data that affects parsing.
:param preserve_quotes: if ``True``, then arguments passed to argparse maintain their quotes
:param with_unknown_args: if true, then capture unknown args
+ :param completer: CommandCompleter type. Defaults to ArgparseCompleter if unspecified.
:return: function that gets passed argparse-parsed args in a ``Namespace``
A :class:`cmd2.argparse_custom.Cmd2AttributeWrapper` called ``cmd2_statement`` is included
in the ``Namespace`` to provide access to the :class:`cmd2.Statement` object that was created when
@@ -391,6 +397,7 @@ def with_argparser(
# Set some custom attributes for this command
setattr(cmd_wrapper, constants.CMD_ATTR_ARGPARSER, parser)
+ setattr(cmd_wrapper, constants.CMD_ATTR_COMPLETER, completer)
setattr(cmd_wrapper, constants.CMD_ATTR_PRESERVE_QUOTES, preserve_quotes)
return cmd_wrapper