1

I'm using argparse in python3. In my script, I have some subparsers, a positional argument and some optional arguments. I have an optional argument to pass in any number of file paths, and it's using nargs='*'. The usage message for my script displays like this:

usage: myprog.py subparser1 [-h] [--dir DIR] 
                            [--files [FILES [FILES ...]]]
                            positional_arg

But if you actually put the positional_arg after the --files flag as suggested by this usage message, I think the parser ends up consuming it as a file path instead (because of the nargs='*'), and then throws an error since it doesn't find the required positional_arg.

I think the way the usage message is printed is misleading. Since I have multiple subparsers, I would like to find a way to change all the usage messages (without typing them all by hand using the usage= argument) just so that the positional argument is displayed first, hopefully clearing up the confusion.

So my main question is how do I change the way the arguments are ordered inside the usage portion of the argparse message?

EDIT to address one possible solution that doesn't work for me.

I don't want to add the --files flag as a positional argument with nargs='*'. Firstly, because that makes it now shows up in the "positional arguments" section of the help message instead of in the "optional arguments" section with the rest of my optional arguments so it makes it look like it is required, even though you could pass it 0 arguments.

Secondly, because because if I wanted to have another flag that takes any number of arguments in addition to the --files (e.g. --folders), and if I made them both positional arguments, I'd run into the same problem where the first one would keep consuming arguments until the end. If I made one positional and one optional then the misleading usage message is still a problem.

SaidbakR
  • 13,303
  • 20
  • 101
  • 195
KaleidoEscape
  • 115
  • 1
  • 13
  • Does it really make sense to specify `--files` with 0 arguments? You might want to use `nargs='+'` instead. – chepner May 13 '15 at 20:32
  • @chepner Thanks, that's a good point, I'm going to do that! But I don't think that would resolve the usage message problem. – KaleidoEscape May 13 '15 at 20:37
  • Well, "positional" doesn't mean "required". You can also add a `help` tag to the `files` argument that mentions it's optional. – chepner May 13 '15 at 22:33
  • Two options or arguments with `nargs='*'` is always going to cause a problem. They can't both come last in the list. – chepner May 13 '15 at 22:37

2 Answers2

2

There are a couple of issues here - how arguments are parsed, and how the usage is formatted.

A couple of quick fixes:

Use -- to mark the end of the optionals list, and the start of the positional(s).

Another possibility is to turn your positional into a optional. That is, give it a simple flag, and required parameter if needed. Then it can be placed anywhere in the subparser's input.


https://stackoverflow.com/a/26986546/901925 addresses this issue of a positional following a * optional. It makes the same quick suggestions. It also shows how the help formatter can be changed to not move the positional to the end of the usage. But it doesn't address the multi-line usage case.

Usage order is also discussed in a more recent SO question, https://stackoverflow.com/a/29619171/901925


If you don't like the default argument groupings (positionals and optionals), you can use argument_groups with different titles and descriptions. With nargs like '?' and '*', and 'required' parameters, names like 'optionals' and 'positionals' can be confusing. I'm trying to use 'flagged' for 'optionals', in the hope that it is less confusing.

Community
  • 1
  • 1
hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • Thank you for the nice summary! I like your answer at http://stackoverflow.com/a/26986546/901925, and I think this will work best for my specific situation, but I might also consider turning the positional into a flag or take another look at the `argument_group`. Thanks! – KaleidoEscape May 13 '15 at 23:48
0

Because argparse doesn't put any restrictions on where an option is used in the command line, it doesn't really make sense for an option to take an arbitrary number of arguments. Positional arguments, on the other hand, do have an enforced order. I'd do something like

add_argument("positional_arg")
add_argument("files", nargs='*', help="Optional, one or more files")

instead, converting the --files option to a list of purely optional positional arguments. Then there is no confusion, in either the help message or in actual use, about which arguments are meant to be part of the file list.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Thanks, but I don't think this is a good solution for the confusing usage messages -- I've edited my question to elaborate. – KaleidoEscape May 13 '15 at 22:00
  • `argparse` usage puts the positional argument at the end, regardless of the definition order. – hpaulj May 13 '15 at 23:07