Register a custom parameter for argparse.Action and add accessors to the Action class. :param param_name: Name of the parameter. This must be a valid Python identifier. :param validator: Optional function to validate and/or transform the parameter value. It accepts the
(
param_name: str,
*,
validator: Callable[[argparse.Action, Any], Any] | None = None,
)
| 294 | |
| 295 | |
| 296 | def register_argparse_argument_parameter( |
| 297 | param_name: str, |
| 298 | *, |
| 299 | validator: Callable[[argparse.Action, Any], Any] | None = None, |
| 300 | ) -> None: |
| 301 | """Register a custom parameter for argparse.Action and add accessors to the Action class. |
| 302 | |
| 303 | :param param_name: Name of the parameter. This must be a valid Python identifier. |
| 304 | :param validator: Optional function to validate and/or transform the parameter value. |
| 305 | It accepts the Action instance and the value as arguments. |
| 306 | :raises ValueError: if the parameter name is invalid |
| 307 | :raises KeyError: if the new parameter collides with any existing attributes |
| 308 | """ |
| 309 | if not param_name.isidentifier(): |
| 310 | raise ValueError(f"Invalid parameter name '{param_name}': must be a valid Python identifier") |
| 311 | |
| 312 | if param_name in _CUSTOM_ACTION_ATTRIBS: |
| 313 | raise KeyError(f"Custom parameter '{param_name}' is already registered") |
| 314 | |
| 315 | # Ensure we don't hijack standard argparse.Action attributes or existing methods |
| 316 | if hasattr(argparse.Action, param_name): |
| 317 | raise KeyError(f"'{param_name}' conflicts with an existing attribute on argparse.Action") |
| 318 | |
| 319 | # Check if accessors already exist (e.g., from manual patching or previous registration) |
| 320 | getter_name = f"get_{param_name}" |
| 321 | setter_name = f"set_{param_name}" |
| 322 | if hasattr(argparse.Action, getter_name) or hasattr(argparse.Action, setter_name): |
| 323 | raise KeyError(f"Accessor methods for '{param_name}' already exist on argparse.Action") |
| 324 | |
| 325 | # Check for the prefixed internal attribute name collision (e.g., _cmd2_<param_name>) |
| 326 | attr_name = constants.cmd2_private_attr_name(param_name) |
| 327 | if hasattr(argparse.Action, attr_name): |
| 328 | raise KeyError(f"The internal attribute '{attr_name}' already exists on argparse.Action") |
| 329 | |
| 330 | def _action_get_custom_parameter(self: argparse.Action) -> Any: |
| 331 | """Get the custom attribute of an argparse Action.""" |
| 332 | return getattr(self, attr_name, None) |
| 333 | |
| 334 | setattr(argparse.Action, getter_name, _action_get_custom_parameter) |
| 335 | |
| 336 | def _action_set_custom_parameter(self: argparse.Action, value: Any) -> None: |
| 337 | """Set the custom attribute of an argparse Action.""" |
| 338 | if validator is not None: |
| 339 | value = validator(self, value) |
| 340 | |
| 341 | setattr(self, attr_name, value) |
| 342 | |
| 343 | setattr(argparse.Action, setter_name, _action_set_custom_parameter) |
| 344 | |
| 345 | _CUSTOM_ACTION_ATTRIBS.add(param_name) |
| 346 | |
| 347 | |
| 348 | def _validate_completion_callable(self: argparse.Action, value: Any) -> Any: |
searching dependent graphs…