I have quite a big program which has a CLI interaction based on argparse
, with several sub parsers. The list of supported choices for the subparsers arguments are determined based on DB queries, parsing different xml files, making different calculations etc, so it is quite IO intensive and time consuming.
The problem is that argparse seems to fetch choices
for all sub parser when I run the script, which adds a considerable and annoying startup delay.
Is there a way to make argparse
only fetch and validate choices
for the currently used sub parser?
One solution could be to move all the validation logic deeper inside the code but that would mean quite a lot of work which I would like to avoid, if possible.
Thank you
To delay the fetching of choices, you could parse the command-line in two stages: In the first stage, you find only the subparser, and in the second stage, the subparser is used to parse the rest of the arguments:
It could be used like this:
Note that the top-level help message will be less friendly, since it can only tell you about the subparser choices:
To find information about the choices in each subparser, the user would have to select the subparser and pass the
-h
to it:All arguments after the
--
are considered non-options (toscript.py
) and are thus parsed by thebar_parser
.This is a script that tests the idea of delaying the creation of a subparser until it is actually needed. In theory it might save start up time, by only creating the subparser that's actually needed.
I use the
nargs=argparse.PARSER
to replicate the subparser behavior in the main parser.help
behavior is similar.test runs with different values of
mode
(and N=5)N
has to be much larger to start seeing a effect.I have solved the issue by creating a simple
ArgumentParser
subclass:Then I can use it as following:
This will really initialize a sub-parser only when parent parser tries to parse arguments for the sub-parser. So if you do
program -h
it will not initialize the sub-parser, but if you doprogram do-something -h
it will.Here's a quick and dirty example of a 'lazy' choices. In this case choices are a range of integers. I think a case that requires expensive DB lookups could implemented in a similar fashion.
Implementing the testing part (
__contains__
) is easy. The help/usage can be customized withhelp
andmetavar
attributes. Customizing the error message is harder. http://bugs.python.org/issue16468 discusses alternatives whenchoices
are not iterable. (also on long list choices: http://bugs.python.org/issue16418)I've also shown how the
type
can be changed after the initial setup. That doesn't solve the problem of setting type based on subparser choice. But it isn't hard to write a customtype
, one that does some sort of Db lookup. All atype
function needs to do is take a string, return the correct converted value, and raiseValueError
if there's a problem.