0

Using python version 3.9.5 I have a problem with the following code snippet:

import argparse


parser = argparse.ArgumentParser()

subparsers = parser.add_subparsers(required=False)
sub_parser = subparsers.add_parser("sub")

parser.add_argument("global-argument")

args = parser.parse_args()

Running the python example without arguments, results into the following error message:

$ python3 main.py
usage: main.py [-h] {sub} ... global-argument
main.py: error: the following arguments are required: global-argument

The error message shows that only the global-argument is required and the subcommand is optional.

But running the python example with the global argument, results into the following error message:

$ python3 main.py global
usage: main.py [-h] {sub} ... global-argument
main.py: error: invalid choice: 'global' (choose from 'sub')

So it's impossible to provide the global-argument without the subcommand (which should be optional)?

  • Good find! Seems to me like a bug/design decision in `argparse`. What should be the logic if you say want provide `global-argument` with value `sub` - how do you tell `argparse` that it is global positional argument and not valid sub-parser command name? I'd expect them to support `--` but that doesn't work either. Best to ask in Python bug tracker. It should be at least documented (which is not) if intended. – blami Sep 18 '21 at 16:04
  • 2
    @blami, as a general rule, main parser arguments should all be defined before creating the `subparsers`. You can get away with adding an `optional` after, but not another `positional`. See my answer for more details. – hpaulj Sep 18 '21 at 16:54

1 Answers1

2

To parser. subparsers is a positional argument, as is global-argument. positionals are parsed in the order they are defined. But since parsing is passed to the subparser when it's encountered, and not passed back to main, 'global-argument' can be handled in any case.

An optional could be defined for parser after, but that's because optionals are handled in to order that the user provides them.

To parser. subparsers is just a specialized kind of positional, one that passes the parsing task on to another parser. sub_parser when invoked handles the remaining strings, and returns a namespace which is incorporated in the main namespace. parser does not resume parsing.

So the main parser's arguments should all be defined first, and used in the command line first. The subparser(s) and arguments are defined and used after that.

Originally subparsers was not optional, as is normal for positionals (without special nargs). But as an unintended side effect of changing how 'required' arguments are tested and flagged, subparsers became, by default, not-required. We've been dealing with the fall out from that for a while.

My top voted answer deals with this; Argparse with required subparser

Also it's a good idea to include a dest, both to better identify which subparser was used, and to prevent problems in the error message.

subparsers = parser.add_subparsers(dest='cmd')
hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • first sorry for contacting you this way. I admire your numpy skills. I am trying to learn it. Any good tutorial, courses you know of? – wwnde Sep 27 '21 at 01:02
  • 1
    @wwnde, I haven't looked at tutorials. Usually I just encourage people to read the basic docs, e.g. https://numpy.org/doc/stable/user/absolute_beginners.html. It's hard to separate what I learned from the original 2006 book, https://web.mit.edu/dvp/Public/numpybook.pdf, and what I've picked up from use and SO. – hpaulj Sep 27 '21 at 01:21
  • nice one, I will certainly look into the docs as advised. Many thanks – wwnde Sep 27 '21 at 03:50