Today I discovered that Python 3.14 contains an irritating, incompatible change to historical argparse behavior, which I can best illustrate with a little piece of code.
import argparse
def demo():
p = argparse.ArgumentParser()
p.add_argument("-n", dest="anumber", type=int,
action='store', metavar="NUM",
help="A number, default %(default)d")
p.set_defaults(anumber = 1)
o = p.parse_args()
print("Parsed.")
if __name__ == "__main__":
demo()
Before Python 3.14, this program would run successfully (including with '--help', which will report that the default number is '1'). In Python 3.14 and later, this program will fail with a Python exception from argparse in
_check_help()
that runs more or less:
[...] TypeError: %d format: a real number is required, not NoneType [...] ValueError: badly formed help string
This change is not in the 3.14 release notes for argparse , but with digging you can find that it's from gh-124899 aka gh-65865 , which is described as "Raise early errors for invalid help strings in argparse". This is a perfectly good change with good intentions, but it has a problem.
The problem is that through Python 3.13, it was perfectly valid to refer to things like '%(default)' in your help text but not have the default value set until later, in a following
.set_defaults()
call that set the default value of a block of related options (or all of them). After gh-124899 , this will fail if you use any formatting option for '%(default)' that can't be satisfied with
None
, because the help text is formatted immediately and so has the unspecified default value of
None
. This doesn't happen if you use '%(default)s' for everything, because a
None
can be formatted as a string.
Currently this restriction isn't documented for
help=
, but all of the examples use string formatting, ie '%(default)s', even for something that's an int, and also the example sets a
default=
in the
add_argument()
call.
Unfortunately I don't really see an easy and clean way to change this situation. If argparse doesn't check the help text immediately, there's no guarantee that it will have a chance to do so later, before you parse arguments (you might not even call
.set_defaults()
). In theory argparse could make up a temporary default value when checking the help string (if you use '%(default)' in it), but in practice this is at least somewhat complex and might hide errors if the default is never set. In a way the cleanest fix would be to make all uses of '%(default)' with an unset default be an error (and document this), but that would be a clear API break.
(Since Python 3.14 has shipped with this issue and it's survived through 3.14.7 as far as I know, the overall change to check help strings early probably isn't going to be reverted any time soon. I'm probably a highly unusual person in combining
.set_defaults()
with using '%(default)d' instead of formatting all default values as strings.)