Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ Sylvain Marié
Tadek Teleżyński
Takafumi Arakaki
Takumi Otani
Tammy Hartline
Taneli Hukkinen
Tanvi Mehta
Tanya Agarwal
Expand Down
1 change: 1 addition & 0 deletions changelog/13817.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed ``AttributeError`` in ``Argument.__repr__`` when ``dest`` attribute is not set, which occurred when displaying error messages for invalid option names. The error now shows a helpful message instead of masking the original validation error.
2 changes: 1 addition & 1 deletion src/_pytest/config/argparsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ def __repr__(self) -> str:
args += ["_short_opts: " + repr(self._short_opts)]
if self._long_opts:
args += ["_long_opts: " + repr(self._long_opts)]
args += ["dest: " + repr(self.dest)]
args += ["dest: " + repr(getattr(self, "dest", NOT_SET))]
if hasattr(self, "type"):
args += ["type: " + repr(self.type)]
if hasattr(self, "default"):
Expand Down
79 changes: 79 additions & 0 deletions testing/test_argparsing_repr_fix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Test case for issue #13817: AttributeError with invalid flag in pytest_addoption."""

from __future__ import annotations

from _pytest.config.argparsing import ArgumentError
from _pytest.config.argparsing import Parser
import pytest


# Suppress warning about using private pytest API (we're testing pytest itself)
@pytest.mark.filterwarnings("ignore::pytest.PytestDeprecationWarning")
class TestArgumentReprFix:
"""Test that Argument.__repr__ handles missing dest attribute."""

def test_invalid_option_without_dashes(self) -> None:
"""Test that invalid option names produce helpful error messages."""
parser = Parser()

with pytest.raises(ArgumentError) as exc_info:
parser.addoption("shuffle") # Missing required -- prefix

error_message = str(exc_info.value)
assert "invalid long option string" in error_message
assert "shuffle" in error_message
assert "must start with --" in error_message

# Ensure no AttributeError is mentioned
assert "AttributeError" not in error_message
assert "has no attribute 'dest'" not in error_message

def test_invalid_short_option(self) -> None:
"""Test that invalid short option names produce helpful error messages."""
parser = Parser()

with pytest.raises(ArgumentError) as exc_info:
parser.addoption("-ab") # 3 chars, treated as invalid long option

error_message = str(exc_info.value)
# -ab is treated as invalid long option (3+ chars)
assert (
"invalid long option string" in error_message
or "invalid short option string" in error_message
)

def test_valid_option_works(self) -> None:
"""Test that valid options still work correctly."""
parser = Parser()
parser.addoption("--shuffle", action="store_true", help="Shuffle tests")

options = parser._anonymous.options
assert len(options) > 0
assert "--shuffle" in options[0].names()

def test_repr_with_dest_set(self) -> None:
"""Test that __repr__ works correctly when dest is set."""
parser = Parser()
parser.addoption("--valid-option", dest="valid_dest", help="A valid option")

# Get the argument object and check its repr
option = parser._anonymous.options[0]
repr_str = repr(option)

# Should contain the dest
assert "dest: 'valid_dest'" in repr_str
assert "NOT_SET" not in repr_str

def test_repr_without_dest(self) -> None:
"""Test that __repr__ works when dest is not set due to error."""
from _pytest.config.argparsing import Argument

# Create an Argument that will fail during initialization
# This triggers the code path where dest is not set
try:
Argument("invalid") # No dashes, will fail
except ArgumentError as exc:
# The repr was called during error creation
# Verify it contains NOT_SET representation
assert "dest:" in str(exc)
assert "NOT_SET" in str(exc) or "<notset>" in str(exc)