Skip to content

gh-110131: Document how a non-empty default works with append and extend - #158155

Open
v0ropaev wants to merge 5 commits into
python:mainfrom
v0ropaev:gh-110131-argparse-append-default
Open

v0ropaev wants to merge 5 commits into
python:mainfrom
v0ropaev:gh-110131-argparse-append-default

Conversation

@v0ropaev

@v0ropaev v0ropaev commented Sep 25, 2026 •

Copy link
Copy Markdown

Closes #110131.

@serhiy-storchaka already settled what this is:

the behavior is correct and pretty straightforward. Reclassified this issue as the documentation issue.

So this only touches the docs. Two things were off.

The append section explains that a non-empty default is kept and command-line values are appended after it, but it says "the parsed value" without saying whose, which reads as if it were about the list being appended to rather than the option's final value. That now says "the parsed value for the option".

The extend section had no such note at all, even though it behaves identically:

>>> p.add_argument('--foo', action='extend', nargs='+', default=['0'])
>>> p.parse_args(['--foo', '1', '2'])
Namespace(foo=['0', '1', '2'])

Same shape as append, and the surprise in the issue is the same one, so the same note now appears there.

A test pins both, since the whole point is that the documented behaviour is the real one and nobody had written it down.

./python.exe -m test test_argparse is 1,978 passing. Docs plus one test, no behaviour change, so no news entry.

…efault

The 'append' bullet says that a non-empty list default is kept and that
command-line values land after it.  'extend' works the same way for an
option, but the docs say nothing about it, which is the confusion
reported in pythongh-110131.

The sentence is scoped to the option deliberately.  For a positional with
nargs='*' or '?' and no command-line values, _get_values hands the default
back as the parsed value while the same default is already in the
namespace, so the action applies it twice and extend(default=['X'])
yields ['X', 'X'].  The unqualified wording would be wrong there.
TestOptionalsActionAppendWithDefault covers this for 'append'; there was
no equivalent for 'extend', so nothing pinned the behavior the docs now
describe.  The coverage is limited to an option, matching the scope of
the new sentence.
…end docs

Before pythonGH-131389 the bullet read "the default elements will be present in
the parsed value for the option".  That copyedit rewrote the sentence and
added an example, and the qualifier went with it, which left the bullet
claiming something that does not hold for positionals: append with
nargs='*' or '?' and default=['X'] gives ['X', ['X']] on empty input,
because the default is applied twice.

pythongh-110131 is filed against both 'append' and 'extend', so put the scope
back rather than leave the two bullets disagreeing about it.
@bedevere-app

bedevere-app Bot commented Sep 25, 2026

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@read-the-docs-community

read-the-docs-community Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Comment thread Lib/test/test_argparse.py Outdated
Comment thread Doc/library/argparse.rst Outdated
Comment on lines 826 to 829

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably want to update this to show the behaviour with a non-empty default, since that's what we're documenting here.

@bedevere-app

bedevere-app Bot commented Sep 25, 2026

Copy link
Copy Markdown

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@savannahostrowski savannahostrowski added needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes needs backport to 3.15 pre-release feature fixes, bugs and security fixes skip news and removed tests Tests in the Lib/test dir labels Sep 25, 2026
@v0ropaev

Copy link
Copy Markdown
Author

Both done. Test is gone, and the extend example now has a second block with a non-empty default.

>>> parser.add_argument("--foo", action="extend", nargs="+", type=str,
...                     default=["d1"])
>>> parser.parse_args(["--foo", "f1", "f2"])
Namespace(foo=['d1', 'f1', 'f2'])

Docs only now.

@savannahostrowski

Copy link
Copy Markdown
Member

Can we keep it to one example for extend? I think the new one you've added covers our bases and we can remove the old one.

@v0ropaev

Copy link
Copy Markdown
Author

Done, one example now. I folded the repeated --foo from the old one into it so that part isn't lost.

>>> parser.add_argument("--foo", action="extend", nargs="+", type=str,
...                     default=["d1"])
>>> parser.parse_args(["--foo", "f1", "--foo", "f2", "f3", "f4"])
Namespace(foo=['d1', 'f1', 'f2', 'f3', 'f4'])

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting changes needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes needs backport to 3.15 pre-release feature fixes, bugs and security fixes skip news

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Argparse: "Default" interacts incorrectly/non-intuitively with action='append' and action='extend'

2 participants