Skip to content

argparse: optional subparsers #53499

Description

@nvie
mannequin
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.diff
  • check.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']

    Activity

    1. nvie commented on Jul 13, 2010

      nviemannequin
      MannequinAuthor

      **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.

    2. added
      stdlibStandard Library Python modules in the Lib/ directory
      type-featureA feature request or enhancement
      on Jul 13, 2010
    3. nvie commented on Jul 13, 2010

      nviemannequin
      MannequinAuthor

      Changed the title, so it shows that the feature request is for argparse.

    4. changed the title [-]optional subparsers[/-] [+]argparse: optional subparsers[/+] on Jul 13, 2010
    5. bitdancer commented on Jul 13, 2010

      @bitdancer
      Member

      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.

    6. nvie commented on Jul 14, 2010

      nviemannequin
      MannequinAuthor

      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.

    7. bitdancer commented on Aug 8, 2010

      @bitdancer
      Member

      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.

    8. bethard commented on Aug 10, 2010

      bethardmannequin
      Mannequin

      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.

    9. elsdoerfer commented on Aug 10, 2010

      elsdoerfermannequin
      Mannequin

      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.

    10. merwok commented on Aug 11, 2010

      @merwok
      Member

      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”.

    11. merwok commented on Aug 11, 2010

      @merwok
      Member

      Wow, it is late. I wanted to write: New features don’t go into stable branches.

    12. G2P commented on Nov 15, 2010

      G2Pmannequin
      Mannequin

      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.

    13. bethard commented on Nov 16, 2010

      bethardmannequin
      Mannequin

      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.

    14. bewest commented on Sep 24, 2011

      bewestmannequin
      Mannequin

      I spent some time looking at this, as I was interested in
      using this pattern to simulate what git and hg do. I
      considered a few modifications and then found this bug. I
      think the default keyword passed to
      _SubParsersAction.__init__ makes sense.

      I started on a patch, that looks promising, but I'm having
      trouble getting the regexp right.

      Here's a changeset higlighting where I think the
      problematic regexp is:
      https://bitbucket.org/bewest/argparse/changeset/938e1e91ddd0

      https://gist.github.com/1202975#file_test_opt_subcommand.py
      Is the meager little test I put together.

    15. 16 remaining items

    16. paulj3 commented on Apr 10, 2014

      paulj3mannequin
      Mannequin

      http://stackoverflow.com/questions/22990977/why-does-this-argparse-code-behave-differently-between-python-2-and-3

      is answered by this change in how required arguments are tested, and how subparsers fell through the cracks.

    17. paulj3 commented on Apr 29, 2014

      paulj3mannequin
      Mannequin

      Another Stackoverflow question triggered by this issue

      http://stackoverflow.com/questions/23349349/argparse-with-required-subparser

    18. paulj3 commented on Jun 7, 2016

      paulj3mannequin
      Mannequin

      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.

    19. asottile commented on Aug 15, 2017

      asottilemannequin
      Mannequin

      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!)

    20. merwok commented on Sep 15, 2017

      @merwok
      Member

      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.

    21. asottile commented on Sep 15, 2017

      asottilemannequin
      Mannequin

      My patch mainly addresses the regression pointed out by mike bayer (zzzeek)'s comment.

    22. merwok commented on Sep 20, 2017

      @merwok
      Member

      The other PR is now merged in 3.7, and won’t be backported (it changes default behaviour and adds a new param).

    23. paulj3 commented on Oct 18, 2017

      paulj3mannequin
      Mannequin

      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.

    24. transferred this issue fromon Apr 10, 2022
    25. savannahostrowski commented on Jun 5, 2026

      @savannahostrowski
      Member

      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).

    26. moved this from Features to Doc issues in Argparse issueson Jun 5, 2026
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      3.7 (EOL)end of lifestdlibStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancement

      Projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions