GeezerVison is showing github.com — argparse: optional subparsers · Issue #53499 · python/cpython
argparse: optional subparsers #53499Description
| BPO |
9253 |
| Nosy |
@csernazs, @jwilk, @merwok, @bitdancer, @wking, @cjerdonek, @Julian, @asottile, @Mariatta, @johnnybubonic, @epicfaace |
| Dependencies |
bpo-26510: [argparse] Add required argument to add_subparsers |
| Files |
ed0fce615582.diffcheck.py: Test for optional subparsers vs. "too few arguments"required.patch |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee = None
closed_at = None
created_at = <Date 2010-07-13.21:29:47.745>
labels = ['3.7', 'type-feature', 'library']
title = 'argparse: optional subparsers'
updated_at = <Date 2020-04-03.11:22:15.235>
user = 'https://bugs.python.org/nvie'
bugs.python.org fields:
activity = <Date 2020-04-03.11:22:15.235>
actor = 'bsaner'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Library (Lib)']
creation = <Date 2010-07-13.21:29:47.745>
creator = 'nvie'
dependencies = ['26510']
files = ['24217', '28778', '29793']
hgrepos = ['68', '69', '100', '101', '102']
issue_num = 9253
keywords = ['patch']
message_count = 36.0
messages = ['110231', '110232', '110244', '110309', '113267', '113512', '113558', '113575', '113577', '121250', '121271', '144484', '144512', '149533', '150757', '150784', '150816', '150821', '150953', '153771', '170491', '180229', '181855', '186052', '186387', '186532', '186695', '193956', '215894', '217461', '267692', '300283', '302294', '302296', '302661', '304594']
nosy_count = 26.0
nosy_names = ['csernazs', 'bethard', 'jwilk', 'frispete', 'eric.araujo', 'r.david.murray', 'zzzeek', 'labrat', 'chris.jerdonek', 'nvie', 'DasIch', 'elsdoerfer', 'G2P', 'Julian', 'dsully', 'derks', 'seblu', 'paul.j3', 'bewest', 'bkabrda', 'couplewavylines', 'svilgelm', 'Anthony Sottile', 'Mariatta', 'bsaner', 'epicfaace']
pr_nums = []
priority = 'high'
resolution = None
stage = 'test needed'
status = 'open'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue9253'
versions = ['Python 3.7']
Activitynvie commented on Jul 13, 2010 MannequinAuthor More actions **NOTE**: This is a re-post of http://code.google.com/p/argparse/issues/detail?id=47
What steps will reproduce the problem?
parser = argparse.ArgumentParser()
sub = parser.add_subparsers()
sub.add_parser("info")
parser.add_argument("paths", "+")
parser.parse_args(["foo", "bar"])
What is the expected output? What do you see instead?
Expected behavior is that, failing to match one of the subparser inputs
("info"), the parser checks if the argument matches any of the top-level
arguments, in this case the 'paths' multi-arg. In other words, it should be
possible to make the subparser be optional, such that when the subparser
argument fails to retrieve any valid subparser, the remaining args are
parsed as if no subparser exists. At present, it does not seem possible to
make a subparser be optional at all.
Perhaps this could be exposed to the user as:
parser.add_subparsers(nargs=argparse.OPTIONAL)
or something to that effect. Or, allow a default subparser to be specified.
I.e.,
sub = parser.add_subparsers()
info = sub.add_parser("info")
main = sub.add_parser("main")
sub.default = main
I'm sure the point will come up that the current behavior is correct,
because given a subparser like "info", a user could easily make a mistake
like "myapp ino foo bar" and rather than get a safe error be given
something unexpected. For this reason, I think the default behavior is
usually going to be correct. BUT, it would still be nice if it could be
optional, so that developers could be free to make that call. Sometimes the
potential user errors aren't really an issue, and having to explicitly set
a subparse arg every time can be a nuissance. nvie commented on Jul 13, 2010 MannequinAuthor More actions Changed the title, so it shows that the feature request is for argparse. nvie changed the title [-]optional subparsers[/-] [+]argparse: optional subparsers[/+] on Jul 13, 2010bitdancer commented on Jul 13, 2010 I've added Steven as nosy so he knows this was reposted here. I've also set the priority to low. Personally I'm at least -0 on this, since if I use a command that has subcommands I expect to get an error if I supply an invalid subcommand. As you say, however, the command designer *could* be supplied with the opportunity to shoot themselves in the foot :)
The issue isn't going to go anywhere unless someone proposes a patch, though. nvie commented on Jul 14, 2010 MannequinAuthor More actions Actually, this is a rather common concept. Broadly used tools like for example Git use this kind of subcommand handling.
This command shows all remotes:
git remote (i.e. is like git remote list)
Showing/removing remotes is done using subsubcommands:
git remote show [...]
git remote rm [...]
That, in combination with the explicit design goal that "[argparse] isn't dogmatic about what your command line interface should look like" should be enough reason to be wanting this, in my humble opinion. bitdancer commented on Aug 8, 2010 See also 9540, which has an alternate proposal (that I don't like as much) for how to handle parser arguments supplied after subparsers are declared.
Reviewing this, I'm now +1 on fixing this *somehow*, since clearly there is an ambiguity here that needs to be resolved. bethard commented on Aug 10, 2010 Seems like there's minimally the bug that argparse should currently throw an error if you add an argument after subparsers (since that argument will never be parsed under the current semantics).
I do believe that supporting an optional command like the "git remote" example is useful, but as RDM suggests, this probably won't go anywhere unless someone proposes a patch. elsdoerfer commented on Aug 10, 2010 To expand on my case from bpo-9540, I have a bunch of commands, each of which should enable a specific subset of options only available the individual command, but all of the commands share the same behavior in taking nargs='*' positional arguments:
./script.py --global-option command --command-option arg1 arg2 arg3
For example:
./backups.py -c /etc/tarsnap.conf make --no-expire job1 job2
If no positional arguments are given, all jobs defined in the config file are run. Or, in the above example, only "job1" and "job2" are run.
The positional arguments are the same for *all* commands. Now I can define them separately for each subparser, which is what I'm currently doing, but I kind of like having the global usage instructions (script.py -h) indicating the fact that positional arguments can be passed after the command.
In fact, right now I'm able to sort of achieve this by defining the positional nargs arguments both globally (to have them show in usage) and in each subparser (to have them parsed). This wouldn't be possible anymore if argparse where to throw an error after adding arguments after a subparser, although probably a more correct behavior.
Anyway, while the two issues are clearly related, I don't think that the two are necessarily mutually exclusive. argparse could allow both optional subparsers (if no subparser matches), as well as pass control back to the parent parser once an already matched subparser is no longer able to handle further command line input. Or optionally, support defining subparsers as "options only", so that positional arguments would always be handled by the parent parser.
Now, I can see how this could potentially become messy if we start talking about these positional arguments handled by the parent then being followed by more flags, which would then presumably also be handled by the parent etc. On the other hand, my use case doesn't seem that strange to me. merwok commented on Aug 11, 2010 Stable releases don’t go into stable branches, so I’m editing versions. I also remove 3.3 since it doesn’t exist now, it means “this won’t go in 3.2”. merwok commented on Aug 11, 2010 Wow, it is late. I wanted to write: New features don’t go into stable branches. G2P commented on Nov 15, 2010 Trying to spec this, here is a proposed API:
parser = argparse.ArgumentParser()
sub = parser.add_subparsers(default='show')
sub_show = sub.add_parser('show')
sub_add = sub.add_parser('add')
If default isn't passed, the subcommand isn't optional.
If default is passed, and no explicit subcommand is given,
the default subcommand is picked.
Arguments are given to the top parser; passing arguments
to the subcommand requires naming it explicitly.
As far as motivation, I'd like to change a program that
uses --choice options (that can have a default) to use
more expressive subcommands. Some programs rely on implicit
subcommands a lot; the ip command on linux is a good
example. bethard commented on Nov 16, 2010 I think the proposed API looks fine and should be backwards compatible since add_subparsers will currently throw an exception with a default= argument.
In case someone feels like writing a patch, you'll want to look at _SubParsersAction.__init__, which will need to grow the default= argument, and pass a different nargs= argument on. I think you'll need to define a new nargs type which means you probably also need to look at ArgumentParser._get_nargs_pattern as well. bewest commented on Sep 24, 2011 paulj3 commented on Apr 10, 2014 paulj3 commented on Apr 29, 2014 paulj3 commented on Jun 7, 2016 My answer to
http://stackoverflow.com/questions/23349349/argparse-with-required-subparser
is getting a slow but steady stream of + scores; so the required subparser issue is still bothering people.
This particular question addresses the problem that the error message has when a required subparser is missing - it can't format the error with the default dest - SUPPRESS. The may be a another bug issue that addresses that.
Anyways, due to this continued attention, I'm going to raise the priority for this issue. asottile commented on Aug 15, 2017 I've attempted to address some of the backward/forward compatibility issue with subparsers becoming optional by default (vs required by default in python2) with this pull request: #3027 (would love to get a review as well!) merwok commented on Sep 15, 2017 I am now reviewing the PR added to the other issue by Anthony. This ticket has a lot of discussion; it would be good to check which parts are addressed by the other ticket, and particularly if the problems noted by Mike and others are now fixed. asottile commented on Sep 15, 2017 My patch mainly addresses the regression pointed out by mike bayer (zzzeek)'s comment. merwok commented on Sep 20, 2017 The other PR is now merged in 3.7, and won’t be backported (it changes default behaviour and adds a new param). paulj3 commented on Oct 18, 2017 In a recent stackoverflow question a user wanted this optional-subparsers ability in Python 2.7.
https://stackoverflow.com/questions/46667843/how-to-set-a-default-subparser-using-argparse-module-with-python-2-7
Short of modifying the _parse_known_args method, the best I could suggest was a two stage parsing. That is, one parser without the subparsers. This uses parse_known_args, and if a 'cmd' is provided passes the 'extras' to one that handles subparsers.
---
Another issue which I don't think has been addressed is the 'usage' when subparsers are optional. At least with 3.5, subparsers are displayed with the choices: {'cmd1', 'cmd2', ...}, but no indication of being optional. An optional positional (with ? nargs) would normally be displayed as
prog [-h] [{'one', 'two'}] ...
My guess is that 'usage' adds the [] when positionals nargs='?', without regard to the 'required' attribute (I should verify this from code).
I'm undecided as to whether we want the brackets or not. It's more accurate, but makes the usage messier. And the 'help' grouping for 'optional-positionals' is the subject of other bug/issue(s).
I haven't checked it the patch has changed this behavior. savannahostrowski commented on Jun 5, 2026 I'm going to close this as resolved. Subparsers have been optional since Python 3.3, and the complementary required= keyword for add_subparsers() was added in 3.7 (gh-70697 / #3027).
MetadataMetadataStatusShow more project fields
DevelopmentNo branches or pull requestsIssue actions
|