diff --git a/changelog/14773.bugfix.rst b/changelog/14773.bugfix.rst new file mode 100644 index 00000000000..7f6ae597cac --- /dev/null +++ b/changelog/14773.bugfix.rst @@ -0,0 +1 @@ +Fixed a crash in ``Argument.dest`` when accessing an option with an invalid option string (for example, one that starts with a digit). Previously, this would raise an ``AttributeError`` because the underlying ``argparse.Action`` may not have a ``dest`` attribute when initialization fails. diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index d6a97c8928e..5092c2389f9 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -302,7 +302,7 @@ def names(self) -> Sequence[str]: @property def dest(self) -> str: - return self._action.dest + return getattr(self._action, "dest", "") @property def default(self) -> Any: diff --git a/testing/test_config.py b/testing/test_config.py index 14946dde164..1d88f8f9a25 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -2487,6 +2487,21 @@ def test_help_formatter_uses_py_get_terminal_width(monkeypatch: MonkeyPatch) -> assert formatter._width == 42 +def test_argument_dest_does_not_crash_on_invalid_option() -> None: + """``Argument.dest`` should not raise ``AttributeError`` when accessed on an + Action that failed to initialize (e.g. with an invalid option string).""" + from _pytest.config.argparsing import Argument + + # Simulate the crash path: an option name that fails _set_opt_strings + # may result in an Action without a `dest` attribute. + class BrokenAction: + pass + + action = BrokenAction() + arg = Argument(action) # type: ignore[arg-type] + assert arg.dest == "" + + def test_config_does_not_load_blocked_plugin_from_args(pytester: Pytester) -> None: """This tests that pytest's config setup handles "-p no:X".""" p = pytester.makepyfile("def test(capfd): pass")