Allow customization of the default argument parser used by cmd2
See original GitHub issueWe have the ability to modify the argument parser used by do_XX commands with @with_parser decorator.
The parsers used by the inbuilt cmd2 commands such as macro, alias etc are hardcoded to use Cmd2ArgumentParser
https://github.com/python-cmd2/cmd2/blob/master/cmd2/cmd2.py#L2384
Since I have my custom argument parser for all my do_XX commands (which inherits from Cmd2ArgumentParser), the output of my custom commands while parsing commands (color/format) doesn’t match with the output of the default commands like alias, macro.
Currently, I have a class similar to,
class CustomCmd2(cmd2.Cmd):
def __init__(self, connection, *args, **kwargs):
# Dynamically add all do_XX functions from the package 'modules' since it is cumbersome
# to write every method in the class.
for module_name in modules.__all__:
module = importlib.import_module(modules.__name__ + "." + module_name)
predicate = lambda obj: inspect.isfunction(obj) and obj.__name__.startswith("do_")
functions = inspect.getmembers(module, predicate)
for function_name, function in functions:
setattr(self, function_name, types.MethodType(function, self))
cmd2.Cmd.__init__(self, *args, **kwargs)
# Modify the parsers after initialization.
# Cast the parser to my custom parser for added functionality.
def cast(obj, req_type, obj_type = None):
if obj_type:
assert isinstance(self.alias_parser, obj_type), "The object type is different. Dangerous to cast."
obj.__class__ = req_type
cast(self.alias_parser, ColoredArgParser, cmd2.Cmd2ArgumentParser)
.........
def get_names(self):
"""
The default implementation from cmd (cpython implementation) looks like,
def get_names(self):
return dir(self.__class__)
Since we are modifying the class instance so that functions are added at runtime, we have to return dir(self)
This fixes the issue of the commands not registering in do_help().
"""
return dir(self)
To change the default parser to my custom ColoredArgParser, I am hacking the class with,
def obj_cast(obj, req_type, obj_type = None):
if obj_type:
assert isinstance(self.alias_parser, obj_type), "The object type is different. Dangerous to cast."
obj.__class__ = req_type
obj_cast(self.alias_parser, ColoredArgParser, cmd2.Cmd2ArgumentParser)
This seems very hackish, and casting is not pythonic. I believe there should be a way to specify the default argument parser used.
This would also match the API used in the rest of the project. We have the ability to modify the HelpFormatter in cmd2 https://github.com/python-cmd2/cmd2/blob/master/cmd2/argparse_custom.py#L694 There must also be a way to modify the default argument parser. Maybe in a similar fashion while constructing the object?
If there is a simpler way to achieve this, it would be helpful to know the method. I am willing to contribute if necessary.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:7 (6 by maintainers)

Top Related StackOverflow Question
@teto There isn’t any problem in inheriting from the cmd2 parser.
This discussion is about possibly giving a way to retrofit the built-in cmd2 commands to use a custom parser for the sake of consistency in help output formatting.
I think this is an excellent suggestion for an enhancement.
I suspect a significant number of larger projects using
cmd2might implement a customCmd2ArgumentParser. The whole reason we ported all of the built-in commands withincmd2to use@with_argparser(even when they don’t need any arguments/options) was for consistency in the help formatting and style.As to implementation, I’m not sure what the best approach would be. But I suspect we could keep it relatively simple.