1

The usually way to define a subparser is to do

master_parser = argparse.ArgumentParser()
subparsers = master_parser.add_subparsers()
parser = subparsers.add_parser('sub')
parser.add_argument('--subopt')

and the subparser would be called with

command sub --subopt

I am implementing a package that calls a number of converters. If I use the usual subparser approach, I would have to do

convert ext1_to_ext2 file.ext1 file.ext2 --args

which is both repetitive and error prone because users might call

convert ext1_to_ext3 file.ext1 file.ext2 --args

I would much prefer that the subparser is automatically determined from the master parser so users can use command

convert file.ext1 file.ext2 EXTRA

and argparse would determine subparser ext1_to_ext2 from file.ext1 and file.ext2 and call the subparser ext1_to_ext2 to parse EXTRA. Of course EXTRA here is subparser specific.

I tried to use groups of parameters for each converter (add_argument_group) but the parameters in argument groups cannot overlap and I got a messy list of combined arguments from all parsers, so using subparser seems to be the way to go.

I tried to use parse_known_args with two positional arguments, determine and use the appropriate subparser to parse the remaining args, but it is difficult to provide users a list of converters and their arguments from help message.

Is there a way to do this?

asked Dec 12, 2016 at 16:24
3
  • Could you use parse_known_args? That would let you parse the first part of the arguments, evaluate the proper subparser yourself, then pass the remaining arguments to the proper subparser. Commented Dec 12, 2016 at 16:30
  • This is the approach I am taking right now, but I am struggling on how to provide help message for users. It seems that I need to hijack convert file.ext1 file.ext2 -h to print help message from another parser, but still I cannot list all available converters from convert -h (perhaps an epilog could work). Commented Dec 12, 2016 at 16:38
  • The kind of logic that you are asking for is unreasonable for argparse. Either parse sys.argv directly or get the values as simple strings via argparse, and then deduce what action you should take. Commented Dec 12, 2016 at 18:51

2 Answers 2

1

Inferring the subparser to use is tricky, since it requires reimplementing a lot of the logic used by argparse itself while you are examining each of the following arguments.

A simpler approach is to take the subparser command, which subsquently allows you to "typecheck" the following arguments to ensure they use the correct argument. For example

# This allows a file name ending with any of the given extensions,
# or allows "file.1" in place of "file.1.jpg"
def jpeg_file(s):
 for ext in ("jpg", "jpeg"):
 if s.endswith(ext) or os.path.exists("%s.%s" % (s, ext)):
 return s
 raise argparse.ArgumentTypeError()
def tiff_file(s):
 # similar to jpeg_file
master_parser = argparse.ArgumentParser()
subparsers = master_parser.add_subparsers()
jpg_to_tiff_parser = subparsers.add_parser('sub')
jpg_to_tiff_parser = parse.add_argument('jpg', type=jpg_file)
jpg_to_tiff_parser = parse.add_argument('tiff', type=tiff_file)
answered Dec 12, 2016 at 16:34
Sign up to request clarification or add additional context in comments.

2 Comments

But the command line would still be convert sub file.jpg file.tiff. I just had a wonderful (?) idea, how about hijack sys.argv? I can check sys.argv[1] and sys.argv[2], insert appropriate subparser name before sys.argv[1], and call the master_parser with all the subparsers. The -h message would be a bit off though.
No obvious problems to that approach are coming to mind, although the first two positional arguments are not necessarily sys.argv[1] and sys.argv[2].
0

This gives you a little more control in my opinion. It's along the way towards what your asking for. Just add file extension checking for your needs.

#prog.py
topParser=argparse.ArgumentParser()
subParsers = topParser.add_subparsers(
 title='SubCommands', 
 description='Use "prog.py <subCommand> (-h | --help)" to learn more about each subcommand', 
 help='I can do stuff')
subParser1 = subParsers.add_parser('use1', help="Use1 does one thing")
subParser2 = subParsers.add_parser('use2', help='Use2 does another thing')
subParser1.add_argument(
 '-f','--first-arg-for-use1',
 help="A text file",
 required=True
 )
subParser1.add_argument(
 '-s','--second-arg-for-use1',
 help="An encoding",
 required=True
 )
subParser2.add_argument(
 '-f','--first-arg-for-use2',
 help="An image format",
 required=True
 )
args = topParser.parse_args()
print(args)

If nothing else, it shows how to handle the help text for the different layers.

answered Nov 2, 2017 at 17:06

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.