Skip to content

Fix Argument.__repr__ crash when _action is not initialized#14429

Merged
nicoddemus merged 5 commits intopytest-dev:mainfrom
EternalRights:fix-argparse-attrerror
May 7, 2026
Merged

Fix Argument.__repr__ crash when _action is not initialized#14429
nicoddemus merged 5 commits intopytest-dev:mainfrom
EternalRights:fix-argparse-attrerror

Conversation

@EternalRights
Copy link
Copy Markdown
Contributor

@EternalRights EternalRights commented Apr 29, 2026

Ran into this while looking at #13817. When Argument.__init__ doesn't complete (e.g. if argparse.Action raises during construction), calling repr() on the partially-initialized Argument triggers a secondary AttributeError because self._action doesn't exist yet. This masks the original error message.

The fix uses getattr(self, "_action", None) in __repr__ and returns "Argument(<uninitialized>)" when _action is missing, as RonnyPfannschmidt suggested. This way the original exception message gets through instead of being buried under a secondary crash.

@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Apr 29, 2026
@EternalRights EternalRights force-pushed the fix-argparse-attrerror branch from 4f863ed to 4cd4ca7 Compare April 29, 2026 17:25
Comment thread testing/test_parseopt.py Outdated
Comment on lines +353 to +368
def test_argument_repr_initialized(parser: parseopt.Parser) -> None:
"""Argument.__repr__ works normally when properly initialized."""
parser.addoption("--myflag", dest="myflag", help="test flag")
option = parser._anonymous.options[-1]
result = repr(option)
assert "opts:" in result
assert "dest:" in result


def test_argument_repr_with_type(parser: parseopt.Parser) -> None:
"""Argument.__repr__ includes type when set."""
parser.addoption("--count", type=int, dest="count", help="count")
option = parser._anonymous.options[-1]
result = repr(option)
assert "type:" in result
assert "int" in result
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.

Why two separate tests for this? Wouldn't make sense to also test the type in test_argument_repr_initialized? For that matter, test_argument_repr_initialized could check the entire string, instead of relying on in checks for a few keywords.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review! You're right on all points.

  1. I kept test_argument_repr_uninitialized separate because it tests a fundamentally different construction path (bare __new__ vs proper initialization through parser.addoption). This follows the same pattern as test_saferepr.py where error paths are separate from normal ones. However, I agree the initialized and with_type cases should be merged as they test the same construction path.

  2. Agreed — the type branch should be tested within test_argument_repr_initialized, not as a separate test. I'll merge them.

  3. Agreed — I'll switch to exact string matching with ==. I see test_saferepr.py consistently uses exact comparison.

I'll push the updates shortly.

EternalRights and others added 3 commits May 7, 2026 23:03
… string matching

Per nicoddemus review feedback:
- Merge test_argument_repr_with_type into test_argument_repr_initialized
  as they test the same construction path
- Switch from 'in' checks to exact == string matching
- Cover both 'without type' and 'with type' branches in one test
Copy link
Copy Markdown
Member

@nicoddemus nicoddemus left a comment

Choose a reason for hiding this comment

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

Awesome, thanks!

@nicoddemus nicoddemus added the backport 9.0.x apply to PRs at any point; backports the changes to the 9.0.x branch label May 7, 2026
@nicoddemus nicoddemus merged commit 84ae27e into pytest-dev:main May 7, 2026
33 checks passed
@patchback
Copy link
Copy Markdown

patchback Bot commented May 7, 2026

Backport to 9.0.x: 💔 cherry-picking failed — conflicts found

❌ Failed to cleanly apply 84ae27e on top of patchback/backports/9.0.x/84ae27e4710af45cc307f8c0c25259e917090219/pr-14429

Backporting merged PR #14429 into main

  1. Ensure you have a local repo clone of your fork. Unless you cloned it
    from the upstream, this would be your origin remote.
  2. Make sure you have an upstream repo added as a remote too. In these
    instructions you'll refer to it by the name upstream. If you don't
    have it, here's how you can add it:
    $ git remote add upstream https://github.com/pytest-dev/pytest.git
  3. Ensure you have the latest copy of upstream and prepare a branch
    that will hold the backported code:
    $ git fetch upstream
    $ git checkout -b patchback/backports/9.0.x/84ae27e4710af45cc307f8c0c25259e917090219/pr-14429 upstream/9.0.x
  4. Now, cherry-pick PR Fix Argument.__repr__ crash when _action is not initialized #14429 contents into that branch:
    $ git cherry-pick -x 84ae27e4710af45cc307f8c0c25259e917090219
    If it'll yell at you with something like fatal: Commit 84ae27e4710af45cc307f8c0c25259e917090219 is a merge but no -m option was given., add -m 1 as follows instead:
    $ git cherry-pick -m1 -x 84ae27e4710af45cc307f8c0c25259e917090219
  5. At this point, you'll probably encounter some merge conflicts. You must
    resolve them in to preserve the patch from PR Fix Argument.__repr__ crash when _action is not initialized #14429 as close to the
    original as possible.
  6. Push this branch to your fork on GitHub:
    $ git push origin patchback/backports/9.0.x/84ae27e4710af45cc307f8c0c25259e917090219/pr-14429
  7. Create a PR, ensure that the CI is green. If it's not — update it so that
    the tests and any other checks pass. This is it!
    Now relax and wait for the maintainers to process your pull request
    when they have some cycles to do reviews. Don't worry — they'll tell you if
    any improvements are necessary when the time comes!

🤖 @patchback
I'm built with octomachinery and
my source is open — https://github.com/sanitizers/patchback-github-app.

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

Labels

backport 9.0.x apply to PRs at any point; backports the changes to the 9.0.x branch bot:chronographer:provided (automation) changelog entry is part of PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants