summaryrefslogtreecommitdiff
path: root/cmd2/argparse_custom.py
diff options
context:
space:
mode:
Diffstat (limited to 'cmd2/argparse_custom.py')
-rw-r--r--cmd2/argparse_custom.py84
1 files changed, 83 insertions, 1 deletions
diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py
index 461b4bba..44e7a90b 100644
--- a/cmd2/argparse_custom.py
+++ b/cmd2/argparse_custom.py
@@ -240,6 +240,7 @@ from typing import (
NoReturn,
Optional,
Sequence,
+ Set,
Tuple,
Type,
Union,
@@ -641,9 +642,74 @@ 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
@@ -753,6 +819,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)
@@ -767,6 +841,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
@@ -1243,6 +1322,9 @@ DEFAULT_ARGUMENT_PARSER: Type[argparse.ArgumentParser] = Cmd2ArgumentParser
def set_default_argument_parser(parser: Type[argparse.ArgumentParser]) -> None:
- """Set the default ArgumentParser class for a cmd2 app"""
+ """
+ Set the default ArgumentParser class for a cmd2 app. This must be called prior to loading cmd2.py if
+ you want to override the parser for cmd2's built-in commands. See examples/override_parser.py.
+ """
global DEFAULT_ARGUMENT_PARSER
DEFAULT_ARGUMENT_PARSER = parser