1

I would like to have a program with subparsers that handles specific arguments while also keep some positional and optional arguments to the previous parsers (In fact what I really want is only one option, I mean, a valid subparser OR a valid local argument).

Example of something I wish to have: Program [{sectionName [{a,b}]}] [{c,d}]. Being c/d incompatible if sectionName was provided and viceversa.

However, the best I could achieve is this test.py [-h] {sectionName} ... [{c,d}]. This means, argparse don't allow me to use the positional arguments c or d without specifying a valid sectionName.

Here is the code:

import argparse

mainparser = argparse.ArgumentParser()
# Subparser
subparser = mainparser.add_subparsers(title="section", required=False)
subparser_parser = subparser.add_parser("sectionName")
subparser_parser.add_argument("attribute", choices=['a', 'b'], nargs='?')
# Main parser positional and optional attributes
mainparser.add_argument("attribute", choices=['c', 'd'], nargs='?')

mainparser.parse_args()

I'm getting crazy with this. Any help would be much appreciated!

Edit: I'm using Python 3.8

Ralequi
  • 306
  • 2
  • 12

1 Answers1

0

The subparser object is actually a positional Action, one that takes choices - in this case {'sectionName'}. positinal arguments are filled in the order that they are defined, using the nargs pattern to allocate strings.

Once the main parser gets the 'sectionName' it passes the parsing to subparser_parser. Than handles the rest of the input, such as the {'a','b'} positional. Anything it can't handle is put on the 'unrecognized' list, and control returns main for final processing. main does not do any further argument processing. Thus your attribute argument is ignored.

You could put define a attribute positional before the add_subparsers, but I wouldn't try to make it nargs='?'.

So it's best to define all main arguments before the subparsers, and to use optionals. This will give the cleanest and most reliable parsing.

hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • Thank you hpaulj for your reply! However, what I want is to keep that positionals arguments optional. I mean, my real purpose is to use this under a python cmd2 which uses argparse internally. For this, commands should have some syntax like: `show interface` or `show interface interfaceName`, so they can have positionals (or not) with multiple subparsing levels. Some commands require to have optional arguments at the same time it may have some subparse for "complex" positionals. Possible example: `show interface interfaceName onDevice xxx` where `onDevice` is a subparser that handles devs – Ralequi Sep 25 '20 at 11:37
  • Also, I've tried reordering the commands with different `require` and `nargs` options but none of them gets close to what I whish :-( – Ralequi Sep 25 '20 at 11:40
  • Keep in mind that when parsing positionals, `argparse` works strictly by position, using the `nargs` to do pattern matching. It does not check values. The `choices` testing is done after allocation. In your example it does not look for a 'd' or 'b' or 'sectionName' string. Use `optionals` to look for particular flag values. – hpaulj Sep 25 '20 at 18:13
  • I think I understand your point. It's not the answer I expected, but sometimes there are limitations hard to overcome. Thank you so much @hpaulj for your time. – Ralequi Sep 28 '20 at 09:12