diff options
| author | Eric Lin <anselor@gmail.com> | 2021-08-19 17:03:42 -0400 |
|---|---|---|
| committer | anselor <anselor@gmail.com> | 2021-08-23 14:17:12 -0400 |
| commit | 49805324772ec5fcd06217f4f0a241a9a8d07960 (patch) | |
| tree | a1647a18085df118bbe12dc37c67104f2817024b /cmd2/argparse_custom.py | |
| parent | 6d771e96c0507d9ef4ad3eaaf4bc83669396e591 (diff) | |
| download | cmd2-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/argparse_custom.py')
| -rw-r--r-- | cmd2/argparse_custom.py | 78 |
1 files changed, 78 insertions, 0 deletions
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 |
