From 78589c0c0caab2aa25401f56b09ed8c88aaa1a91 Mon Sep 17 00:00:00 2001 From: webdevsamran Date: Sun, 23 Aug 2026 21:13:50 +0500 Subject: [PATCH 1/2] config: deprecate non-string values for string-typed ini options in ini mode _getini_ini returned any value as-is for type='string' (the default when addini is called without type=), while _getini_toml strictly validates. A TOML array in [tool.pytest.ini_options] therefore silently became a list for string-typed options. Per the maintainer-approved path in #14808, emit a PytestDeprecationWarning in ini mode when a string-typed option receives a non-str value; it will raise TypeError in pytest 10. --- changelog/14808.deprecation.rst | 4 ++++ src/_pytest/config/__init__.py | 10 ++++++++++ testing/test_config.py | 23 +++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 changelog/14808.deprecation.rst diff --git a/changelog/14808.deprecation.rst b/changelog/14808.deprecation.rst new file mode 100644 index 00000000000..18c1417d06d --- /dev/null +++ b/changelog/14808.deprecation.rst @@ -0,0 +1,4 @@ +In ini mode (``.ini`` files and ``[tool.pytest.ini_options]`` in ``pyproject.toml``), +reading a non-string value for a :confval:`string`-typed ini option (including +options registered without an explicit type, which default to ``"string"``) is now +deprecated and will raise a ``TypeError`` in pytest 10. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 7a7573de050..8a9923abca4 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -79,6 +79,7 @@ from _pytest.pathlib import safe_exists from _pytest.stash import Stash from _pytest.warning_types import PytestConfigWarning +from _pytest.warning_types import PytestDeprecationWarning from _pytest.warning_types import warn_explicit_for @@ -1949,6 +1950,15 @@ def _getini_ini( elif type == "bool": return _strtobool(str(value).strip()) elif type == "string": + if not isinstance(value, str): + warnings.warn( + PytestDeprecationWarning( + f"{self.inipath}: config option '{name}' expects a string value, " + f"got {builtins.type(value).__name__}: {value!r}. This will raise " + "a TypeError in pytest 10." + ), + stacklevel=2, + ) return value elif type == "int": if not isinstance(value, str): diff --git a/testing/test_config.py b/testing/test_config.py index 4a290f98ec5..a713301fdd4 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -949,6 +949,29 @@ def pytest_addoption(parser): with pytest.raises(ValueError): config.getini("other") + def test_addini_string_type_toml_list_deprecated(self, pytester: Pytester) -> None: + # https://github.com/pytest-dev/pytest/issues/14808 + pytester.makeconftest( + """ + def pytest_addoption(parser): + parser.addini("mystr", "a string option", type="string") + parser.addini("myuntyped", "an untyped option") + """ + ) + pytester.makepyprojecttoml( + """ + [tool.pytest.ini_options] + mystr = ["a", "b"] + myuntyped = ["c", "d"] + """ + ) + config = pytester.parseconfig() + with pytest.warns(pytest.PytestDeprecationWarning, match="expects a string value"): + assert config.getini("mystr") == ["a", "b"] + # Options registered without an explicit type default to "string". + with pytest.warns(pytest.PytestDeprecationWarning, match="expects a string value"): + assert config.getini("myuntyped") == ["c", "d"] + @pytest.mark.parametrize("config_type", ["ini", "pyproject"]) def test_addini_paths(self, pytester: Pytester, config_type: str) -> None: pytester.makeconftest( From 068e4fc6e46461c5dc6ec7b8b743d4eb6545f6ea Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 23 Aug 2026 16:15:32 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_config.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/testing/test_config.py b/testing/test_config.py index a713301fdd4..04b37f0bf3c 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -966,10 +966,14 @@ def pytest_addoption(parser): """ ) config = pytester.parseconfig() - with pytest.warns(pytest.PytestDeprecationWarning, match="expects a string value"): + with pytest.warns( + pytest.PytestDeprecationWarning, match="expects a string value" + ): assert config.getini("mystr") == ["a", "b"] # Options registered without an explicit type default to "string". - with pytest.warns(pytest.PytestDeprecationWarning, match="expects a string value"): + with pytest.warns( + pytest.PytestDeprecationWarning, match="expects a string value" + ): assert config.getini("myuntyped") == ["c", "d"] @pytest.mark.parametrize("config_type", ["ini", "pyproject"])