0

I wanted to create an interface with argparse for my script with subcommands; so, if my script is script.py, I want to call it like python script.py command --foo bar, where command is one of the N possible custom commands.

The problem is, I already tried looking for a solution here on StackOverflow, but it seems like everything I tried is useless.

What I have currently is this:

parser = argparse.ArgumentParser()

parser.add_argument("-x", required=True)
parser.add_argument("-y", required=True)
parser.add_argument("-f", "--files", nargs="+", required=True)

# subparsers for commands
subparsers = parser.add_subparsers(title="Commands", dest="command")
subparsers.required = True

summary_parser = subparsers.add_parser("summary", help="help of summary command")
  • If I try to run it with:
args = parser.parse_args("-x 1 -y 2 -f a/path another/path".split())

I got this error, as it should be: script.py: error: the following arguments are required: command.

  • If, however, I run this command:
args = parser.parse_args("summary -x 1 -y 2 -f a/path another/path".split())

I got this error, that I shouldn't have: script.py: error: the following arguments are required: -x, -y, -f/--files.

  • If I put the command at the end, changing also the order of arguments because of -f, it works.
args = parser.parse_args("-x 1 -f a/path another/path -y 2 summary".split())
  • If I add the parents keyword in subparser, so substitute the summary_parser line with summary_parser = subparsers.add_parser("summary", help=HELP_CMD_SUMMARY, parents=[parser], add_help=False), then I got:

    • script.py summary: error: the following arguments are required: command when summary is in front of every other argument;
    • script.py summary: error: the following arguments are required: -x, -y, -f/--files, command when summary is at the end of the args.

My question is, how I have to setup the parsers to have the behaviour script.py <command> <args>? Every command shares the same args, because they are needed to create certain objects, but at the same time every command can needs other arguments too.

crissal
  • 2,547
  • 7
  • 25
  • You have specified a bunch of required flagged arguments for `parser`. `subparsers` is a special kind of `positional` argument. When given 'summary' it passes the parsing task to `summary_parser`. That parser only recognizes flags and arguments that are defined for it; it does nothing with `parser`'s` flags. – hpaulj Jul 31 '21 at 17:47

1 Answers1

0

Creating another parser helped me getting what I wanted. The root parser should add all the optional arguments - and also have add_help=False, to avoid an help message conflict -, then another parser - parser2, with a lot of fantasy - will be created.

The second parser will have subparsers, and they all needs to specify as parents the root parser.

parser = argparse.ArgumentParser(add_help=False)

parser.add_argument() ...

parser2 = argparse.ArgumentParser()
subparsers = parser2.add_subparsers(title="Commands", dest="command")
subparsers.required = True

summary_parser = subparsers.add_parser("summary", parents=[parser])
summary_parser.add_argument("v", "--verbose", action="store_true")

# parse args
args = parser2.parse_args()

Now the output will be this:

usage: script.py [-h] {summary} ...

optional arguments:
  -h, --help  show this help message and exit

Commands:
  {summary}
    summary   For each report print its summary, then exit
usage: script.py summary [-h] -x X -y Y -f FILES [FILES ...] [-v]

optional arguments:
  -h, --help            show this help message and exit
  -x X
  -y Y
  -f FILES [FILES ...], --files FILES [FILES ...]
  -v, --verbose         
crissal
  • 2,547
  • 7
  • 25
  • Yes, mixing the main and `parents` only adds confusion. `parents` does not add parsing functionality. It's just a convenient (in some cases) way of defining arguments. You could just as well write a utility function that adds a repeated set of arguments to a parser. – hpaulj Jul 31 '21 at 17:50
  • You're right, but [`parents` is used to share same arguments between parsers](https://docs.python.org/3/library/argparse.html#parents), so I think it's the better way to use is. The only "problem" is that I cannot assign this arguments to `parser2`, so I cannot display an help of shared arguments. – crissal Jul 31 '21 at 20:00
  • Parents does copy a reference to the child, but I don't see any advantage to that. Sometimes it's even problem – hpaulj Jul 31 '21 at 23:17
  • A longer answer re. subparsers and parents. https://stackoverflow.com/questions/45806664/python-argparse-override-help-from-parent/45806753#45806753. Also https://stackoverflow.com/questions/56979728/is-it-possible-to-inherit-required-options-in-argparse-subparsers/56979954#56979954 – hpaulj Aug 01 '21 at 01:51
  • But I see in [your answer](https://stackoverflow.com/a/56979954/11647025) that you did exactly the same thing I did - if I had searched harder, I would have saved myself a lot of time. I don't understand quite well one thing, that is, why it's a problem if arguments are copied by reference? [This is a valid answer](https://stackoverflow.com/a/24666998/11647025) to this question? – crissal Aug 01 '21 at 12:32