Skip to content

argparse: Allow nargs=0 for positional arguments with custom actions #157922

Description

@alpernebbi

Bug report

Bug description:

Python 3.14 raises an error when nargs=0 is given for a positional argument, as implemented in #124839 (gh-85935: Check for nargs=0 for positional arguments in argparse). Before it became an error, I could use such a construction with a custom action (for other reasons) to extend the help message for a multi-purpose positional argument like <a|b|c>. A small reproducer for what I mean:

#!/usr/bin/env python3

import sys
import argparse

parser = argparse.ArgumentParser(
    prog="bless",
    usage="%(prog)s <DISK|PARTITION|IMAGE>",
    add_help=True,
)

# Actual argument, but I want to break help into different lines
parser.add_argument(
    dest="target",
    help=argparse.SUPPRESS,
    metavar="<DISK|PARTITION|IMAGE>",
    nargs=1,
)

# These don't consume command line arguments, but show up in --help
class SomeAction(argparse.Action):
    def __init__(self, option_strings=None, dest=None, nargs=None,
                 const=None, default=None, type=None, choices=None,
                 required=False, help=None, metavar=None):
        return super().__init__(option_strings, dest=dest, nargs=nargs,
                                const=const, default=default, type=type,
                                choices=choices, required=required,
                                help=help, metavar=metavar)

    def __call__(self, parser, namespace, values, option_string=None):
        print(f"SomeAction ({self.metavar}):\t",
              f"{namespace=}, {values=}, {option_string=}")
        # Can even validate/manipulate namespace here

parser.add_argument(action=SomeAction, nargs=0, metavar="DISK",
                    dest=argparse.SUPPRESS, help="Path to disk device")
parser.add_argument(action=SomeAction, nargs=0, metavar="PARTITION",
                    dest=argparse.SUPPRESS, help="Path to parititon device")
parser.add_argument(action=SomeAction, nargs=0, metavar="IMAGE",
                    dest=argparse.SUPPRESS, help="Path to image file")

if __name__ == "__main__":
    print(parser.parse_args(sys.argv[1:]))

Running this with python3.13 shows the following help message, which I like better than one entry trying to explain three different things:

$ python3.13 test.py --help
usage: bless <DISK|PARTITION|IMAGE>

positional arguments:
  DISK        Path to disk device
  PARTITION   Path to parititon device
  IMAGE       Path to image file

options:
  -h, --help  show this help message and exit

Coincidentally, the custom action was always being run, which could be useful for validating or post-processing the namespace as part of the parse_args() call, although I expect it will not be that important to you since those can usually be handled afterwards anyway:

$ python3.13 test.py a
SomeAction (DISK):	 namespace=Namespace(target=['a']), values=[], option_string=None
SomeAction (PARTITION):	 namespace=Namespace(target=['a']), values=[], option_string=None
SomeAction (IMAGE):	 namespace=Namespace(target=['a']), values=[], option_string=None
Namespace(target=['a'])

I don't see how these can be achieved with optional arguments. For the help message, I think I might have to write a custom help formatter (bad idea), which might not even be enough. Advice welcome. Anyway, I'm rather late reporting this, but I wanted to mention this use case at least as a side note.

CPython versions tested on:

3.14

Operating systems tested on:

Linux

Activity

  1. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Sep 21, 2026
  2. skv0zsneg commented on Sep 21, 2026

    @skv0zsneg
    Contributor

    Looks like not quite clean solution imo to use SomeAction like yours just for pretty printing.

    Maybe this bug can be transfer to feature by extend help arg like this

    parser.add_argument(
        dest="target",
        metavar="<DISK|PARTITION|IMAGE>",
        nargs=1,
        help=(
            "Path to disk device",
            "Path to parititon device",
            "Path to image file",
       ),
    )

    Here help it sequence where help[0] is for "DISK", help[1] for "PARTITION" etc.

    But I guess it needs approve @savannahostrowski @serhiy-storchaka

  3. added
    type-featureA feature request or enhancement
    and removed
    type-bugAn unexpected behavior, bug, or error
    on Sep 22, 2026
  4. picnixz commented on Sep 22, 2026

    @picnixz
    Member

    This is not a bug IMO and possibly a feature but I would say it is too niche.

  5. picnixz commented on Sep 22, 2026

    @picnixz
    Member

    It is also better to describe the parameter in the same paragraph. I do not think the stdlib should have this extension (this is not what most common CLIs would do).

  6. jonathandung commented on Sep 22, 2026

    @jonathandung
    Contributor

    A custom formatter class might help. Alternatively, you may subclass argparse.ArgumentParser and override relevant methods (print_help?) directly.

  7. skv0zsneg commented on Sep 22, 2026

    @skv0zsneg
    Contributor

    I guess issue can be closed as not planned? @picnixz

  8. picnixz commented on Sep 22, 2026

    @picnixz
    Member

    I want to hear it from Savannah or Serhiy. I can see some reason to allow it (where a positional argument becomes an optional value and where all other options are specified by --) but I can also see it as a niche. Using it to document multiple arguments is however hacky IMO

  9. savannahostrowski commented on Sep 25, 2026

    @savannahostrowski
    Member

    I don’t think we should re-enable nargs=0 positionals to create help-only entries; that’s a hacky use of parser actions. The layout you want can be produced with the built-in RawDescriptionHelpFormatter and a formatted description.

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

    pendingThe issue will be closed if no feedback is providedstdlibStandard 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