patternpythonMinor
Using argparse module within cmd interface
Viewed 0 times
argparsemodulewithininterfaceusingcmd
Problem
I've created an application that uses a cmd interface. It has multiple levels, and the number of available commands and their complexity is growing. As such, I need to generalise argument parsing - of the
I like argparse - I've previously used it a few times for its 'intended' purpose - so I figured I'd try that. Unfortunately, it doesn't lend itself well to being used in 'unconventional' ways, as many design decisions are forced on you (e.g.
I looked around for solutions and found this. However, that solution relinquishes
I came up with a solution using the decorator below:
It allows a command/function to be defined independently, taking
line parameter. I like argparse - I've previously used it a few times for its 'intended' purpose - so I figured I'd try that. Unfortunately, it doesn't lend itself well to being used in 'unconventional' ways, as many design decisions are forced on you (e.g.
sys.exit() is called on invalid arguments, etc.).I looked around for solutions and found this. However, that solution relinquishes
cmd's listing of available functions, and its help functionality. As well, it requires that all the available commands and their arguments be listed in one place (i.e. __init__), which makes it impractical when there are many commands.I came up with a solution using the decorator below:
class WrapperCmdLineArgParser:
def __init__(self, parser):
"""Init decorator with an argparse parser to be used in parsing cmd-line options"""
self.parser = parser
self.help_msg = ""
def __call__(self, f):
"""Decorate 'f' to parse 'line' and pass options to decorated function"""
if not self.parser: # If no parser was passed to the decorator, get it from 'f'
self.parser = f(None, None, None, True)
def wrapped_f(*args):
line = args[1].split()
try:
parsed = self.parser.parse_args(line)
except SystemExit:
return
f(*args, parsed=parsed)
wrapped_f.__doc__ = self.__get_help(self.parser)
return wrapped_f
@staticmethod
def __get_help(parser):
"""Get and return help message from 'parser.print_help()'"""
f = tempfile.SpooledTemporaryFile(max_size=2048)
parser.print_help(file=f)
f.seek(0)
return f.read().rstrip()It allows a command/function to be defined independently, taking
Solution
Not as much as a code review, but an architecture suggestion. Your code is fine, but I'm weary of the cleverness we tend to add when things grow more and more complex.
Unless you really want to run everything from a single .py module, I'd suggest breaking up the program into multiple pieces. Have your shared pieces inside a common library and split the different groups into different executables, a bit like git and apt do - by calling external utilities. This also allows other people to easily write add-ons for your utility.
Think of
Unless you really want to run everything from a single .py module, I'd suggest breaking up the program into multiple pieces. Have your shared pieces inside a common library and split the different groups into different executables, a bit like git and apt do - by calling external utilities. This also allows other people to easily write add-ons for your utility.
Think of
utility verb --be-awesome as just calling utility-verb --be-awesome under the hood and passing whatever command line parameters it got. As a useful convention, you may want to have parameters that affect the base utility that could be stripped out of the command line when called or just force them to be placed between the utility and the verb parts. All utility-verb commands should be able to rely on the shared functionality on the library.Context
StackExchange Code Review Q#134333, answer score: 3
Revisions (0)
No revisions yet.