What kind of behavior are you expecting from the added subparsers?
sp = parser.add_subparsers(...)
add_subparsers creates a positional argument of _SubParsersAction class, and makes a note of it in a parser._subparsers attribute. It raises that error if that attribute has already been set. sp is that Action object.
Logically it doesn't make sense to have more than one 'subparsers' action. When the main parser encounters of a subparser command, it delegates the parsing to that subparser. Apart from some cleanup error checking the main parser does not resume parsing. So it couldn't handle a second subparser command.
(the terminology for what add_subparsers creates and what add_parser does can be a confusing. One is a positional argument/Action, the other a parser (instance of ArgumentParser).
It should be possible to add new parsers to an existing subparsers Action (I'd have to experiment to find a clean way of doing this). It also possible to add nest subparsers, that is, define add_subparsers for a subparser.
That parents approach bypasses thismultiple subparser arguments test, probably because it doesn't set the parser._subparsers attribute the first time (when copying Actions from the parent). So that in effect creates two subparser arguments. That's what the help shows. But my guess is that the parsing will not be meaningful. It will still expect 'original_subparser' and not look for 'extra' or 'extra2'.
The parents mechanism is not robust. There are straight forward uses of it, but it's easily broken.
In [2]: parser = argparse.ArgumentParser(prog='main')
In [3]: a1 = parser.add_argument('--foo')
In [4]: parser._subparsers # initial value None
In [5]: sp = parser.add_subparsers(dest='cmd')
In [6]: parser._subparsers # new non-None value
Out[6]: <argparse._ArgumentGroup at 0xaf780f0c>
The sp object, an Action subclass:
In [8]: sp
Out[8]: _SubParsersAction(option_strings=[], dest='cmd', nargs='A...', const=None, default=None, type=None, choices=OrderedDict(), help=None, metavar=None)
In [9]: sp1 = sp.add_parser('cmd1')
sp is an Action (positional argument); sp1 is a parser.
In [10]: parser.print_help()
usage: main [-h] [--foo FOO] {cmd1} ...
positional arguments:
{cmd1}
optional arguments:
-h, --help show this help message and exit
--foo FOO
The _actions attribute is a list of actions. Note the help, and foo
In [12]: parser._subparsers._actions
Out[12]:
[_HelpAction(option_strings=['-h', '--help'], dest='help',...),
_StoreAction(option_strings=['--foo'], dest='foo',....),
_SubParsersAction(option_strings=[], dest='cmd', ...]
So the last item in this list is the sp object
In [13]: parser._subparsers._actions[-1]
Out[13]: _SubParsersAction(option_strings=[], dest='cmd', nargs='A...', const=None, default=None, type=None, choices=OrderedDict([('cmd1', ArgumentParser(prog='main cmd1', usage=None, description=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True))]), help=None, metavar=None)
So we can added a new subparser with
In [14]: parser._subparsers._actions[-1].add_parser('extra')
...
In [15]: parser.print_help()
usage: main [-h] [--foo FOO] {cmd1,extra} ...
positional arguments:
{cmd1,extra}
optional arguments:
-h, --help show this help message and exit
--foo FOO
So if you want just add extra and extra2 to original_subparser, this should work.
So as long as we don't try further parser.add_argument(...) grabbing the last _actions item should work. If it isn't the last, we'd have to do a more sophisticate search.
Judging from my rep change, someone found this earlier exploration of the _subparsers attribute:
How to obtain argparse subparsers from a parent parser (to inspect defaults)