Repository navigation
argparse: Strange behaviour of positional arguments with store_true, store_false, or store_const action #97848
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Oct 4, 2022 - addedtype-featureA feature request or enhancementA feature request or enhancementand removedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Oct 5, 2022 That behavior is weird.
It doesn't seem to have caused problems in practice. If an exception does get added, it should not be backported. Essentially, this is a feature request to provide early warning about an action that doesn't make sense for positional arguments.
Reacted by Krzysiek Karbowiak- addedstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
on Oct 5, 2022 - changed the title
[-]Strange behaviour of positional arguments with `store_true`, `store_false`, or `store_const` action[/-][+]argparse: Strange behaviour of positional arguments with `store_true`, `store_false`, or `store_const` action[/+]on Nov 13, 2022 It's not entirely clear where such a test or warning should be added.
add_argumentdoesn't do a whole lot of testing itself. Based on the absence of prefixchars, it calls_get_positional_kwargsto tweak the args/kwargs mix. Same for optionals. For both, the action is actually created withaction = action_class(**kwargs). The action class may be one of the registered subclasses, or a user defined subclass. No one checks whether that 'action_class' is appropriate for optionals or positionals.This
_get_positional_kwargsis used before it looks for the action class.nargs=0, unless provided by the user, is set by theaction_class. The 'store_const' subclasses are distinct in that they usenargs=0. Subclasses like this don't normally test whether they are creating a positional or optional, though I suppose it could test whether theoption_stringsargument is empty to not (that's how later code distinguishes between the two).Given this calling structure there isn't a whole lot checking of the
kwargs. Wrong or unnecessary kwargs are ignored, or allowed to raise some sort of low level error (such as in the Action subclass). This keeps the overall code simple, without potentially large and complex "checking" routines. It also allows you, the developer, to write custom Action classes without being blocked by some master-parameter-checker.Also errors like this will arise normally during development and testing. That's when the programmer can easily correct things. What we don't want are un-caught errors during production use. There every error should result in a proper call to
self.error, with a message aimed at the final user.Many of these complaints about
add_argumentparameters come from developers who are pushing the bounds of what's documented. They want 'store_true' that accept arguments, or use atypethat isn't a callable, or use some unrealistic combination of kwargs. These problems can usually be tracked down by matching the error message with theargparse.pycode (which is linked in the doc page). It's hard to anticipate this kind of error without imposing a straight-jacket on everyone.Reacted by Douglas SilvaHow on earth am I supposed to do this now with Python 3.14?
parser_help = sub_parser.add_parser("help", help="Print a summary of available commands") parser_help.add_argument( "help", help="Print a summary of available commands", action="store_true", default=False )To get a flag named help=False when anything but
cmd helpis passed?Reacted by Douglas SilvaUse the
destargument withadd_subparsers().Reacted by Gabriel FerreiraMeh, looks terrible, but I guess it works. Thanks for the help.
sub_parser = parser.add_subparsers(dest="subparser") ... sub_parser_actions = list( filter(lambda x: isinstance(x, argparse._SubParsersAction), parser._actions) ) sub_parsers = list(sub_parser_actions[0].choices.keys()) if args.subparser in sub_parsers: sub_parsers.remove(args.subparser) for parser in sub_parsers: setattr(args, parser, False if parser != "build" else []) if args.subparser not in [None, "build"]: setattr(args, args.subparser, "True")Your old parser may produce weird result for
cmd help --help(showing non-existent parameter "HELP").It worked just fine. And now it is ok with Python 3.14 too. 🙂
$ ./ns3 help --help usage: ns3 help [-h] positional arguments: help Print a summary of available commands options: -h, --help show this help message and exit $ ./ns3 --help or $ ./ns3 help ns-3 wrapper for the CMake build system usage: ns3 [-h] [--compile-or-die compile_or_die compile_or_die] [--dry-run] [-j JOBS] [--quiet] [-v] {help,build,configure,clean,distclean,install,uninstall,run,shell,docs,show} ... or: ns3 help [-h] or: ns3 build [-h] [--dry-run] [-j BUILD_JOBS] [--quiet] [-v] [target ...] or: ns3 configure [-h] [-d {debug,default,release,optimized,minsizerel}] [-G G] [--cxx-standard {23,26}] [--enable-asserts] [--... or: ns3 clean [-h] [--dry-run] or: ns3 distclean [-h] [--dry-run] or: ns3 install [-h] or: ns3 uninstall [-h] or: ns3 run [-h] [--no-build] [--command-template COMMAND_TEMPLATE] [--cwd CWD] [--gdb] [--lldb] [-g] [--memray] [--heaptrack] [--perf] [--vis] [--enable-sudo] [--dry-run] [-j RUN_JOBS] [--quiet] [-v] [target] or: ns3 shell [-h] or: ns3 docs [-h] [--dry-run] [-v] {contributing,installation,manual,models,tutorial,sphinx,doxygen-no-build,doxygen,all} or: ns3 show [-h] [--dry-run] [--quiet] {profile,version,config,targets,typeid,all} ... positional arguments: {help,build,configure,clean,distclean,install,uninstall,run,shell,docs,show} help Print a summary of available commands build Accepts a list of targets to build, or builds the entire project if no target is given configure Try "./ns3 configure --help" for more configuration options clean Removes files created by ns3 distclean Removes files created by ns3, tests and documentation install Install ns-3 uninstall Uninstall ns-3 run Try "./ns3 run --help" for more runtime options shell Try "./ns3 shell --help" for more shell options docs Try "./ns3 docs --help" for more documentation options show Try "./ns3 show --help" for more show options options: -h, --help Print a summary of available commands --compile-or-die compile_or_die compile_or_die Build and test each individual commit between a base and an head commits. This is especially useful when preparing MRs or rewriting git history, and ensuring refactor-or-die principle is being followed --dry-run Do not execute the commands. -j, --jobs JOBS Set number of parallel jobs. --quiet Don't print task lines, i.e. messages saying which tasks are being executed. -v, --verbose Print which commands were executed
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDoc issues
Bug report
When
store_true,store_false, orstore_constis set as anactionof positional argument, the behaviour is counterintuitive. Running this small program:outputs
Namespace(pos=True). Changing last line toprint(parser.parse_args(['foo']))gives an error:Your environment
Expected behaviour
I believe there is no good reason to allow setting those actions to positional arguments. Trying to do so should result in raising an exception (as is the case when trying to set
required).Linked PRs