user2283347
user2283347

Reputation: 769

argparse subparser implied by other parameters

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?

Upvotes: 1

Views: 1508

Answers (2)

HotPlasma
HotPlasma

Reputation: 1

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.

Upvotes: 0

chepner
chepner

Reputation: 530970

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)

Upvotes: 1

Related Questions