Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -135,6 +135,7 @@ David Peled
David Szotten
David Vierra
Daw-Ran Liou
Debaditya Hait
Debi Mishra
Denis Cherednichenko
Denis Kirisov
Expand Down
2 changes: 2 additions & 0 deletions changelog/14705.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Custom TOML configuration files selected with :option:`-c` now read options from
the ``[pytest]`` table instead of ``[tool.pytest]``.
3 changes: 3 additions & 0 deletions doc/en/reference/customize.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ pytest.toml

Alternatively, the hidden version ``.pytest.toml`` can be used.

A TOML configuration file with a custom name can be loaded using :option:`-c`;
like ``pytest.toml``, it uses the ``[pytest]`` table.

.. tab:: toml

.. code-block:: toml
Expand Down
8 changes: 4 additions & 4 deletions src/_pytest/config/findpaths.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def load_config_dict_from_file(

# '.toml' files are considered if they contain a [tool.pytest] table (toml mode)
# or [tool.pytest.ini_options] table (ini mode) for pyproject.toml,
# or [pytest] table (toml mode) for pytest.toml/.pytest.toml.
# or [pytest] table (toml mode) for other TOML configuration files.
elif filepath.suffix == ".toml":
if sys.version_info >= (3, 11):
import tomllib
Expand All @@ -105,8 +105,8 @@ def load_config_dict_from_file(
except tomllib.TOMLDecodeError as exc:
raise UsageError(f"{filepath}: {exc}") from exc

# pytest.toml and .pytest.toml use [pytest] table directly.
if filepath.name in ("pytest.toml", ".pytest.toml"):
# TOML configuration files other than pyproject.toml use [pytest] directly.
if filepath.name != "pyproject.toml":
if "pytest" in config:
# TOML mode - preserve native TOML types.
return {
Expand All @@ -122,7 +122,7 @@ def load_config_dict_from_file(
f"[pytest] table (found top-level options: "
f"{', '.join(top_level_options)})"
)
# "pytest.toml" files are always the source of configuration, even if empty.
# These files are always the source of configuration, even if empty.
return {}

# pyproject.toml uses [tool.pytest] or [tool.pytest.ini_options].
Expand Down
27 changes: 25 additions & 2 deletions testing/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,8 +766,8 @@ def pytest_addoption(parser):
pytester.makefile(
".toml",
custom="""
[tool.pytest.ini_options]
custom = 1
[pytest]
custom = "1"
value = [
] # this is here on purpose, as it makes this an invalid '.ini' file
""",
Expand All @@ -777,6 +777,29 @@ def pytest_addoption(parser):
config = pytester.parseconfig("--config-file", "custom.toml")
assert config.getini("custom") == "1"

def test_custom_toml_config_file_is_loaded(self, pytester: Pytester) -> None:
pytester.makepyfile(
bench_example="""
def bench_example():
pass
"""
)
pytester.makefile(
".toml",
pytest_benchmark="""
[pytest]
python_files = ["bench_*.py"]
python_functions = ["bench_*"]
""",
)

result = pytester.runpytest(
"-c", "pytest_benchmark.toml", "--collect-only", "-q"
)

assert result.ret == ExitCode.OK
result.stdout.fnmatch_lines(["bench_example.py::bench_example"])

def test_absolute_win32_path(self, pytester: Pytester) -> None:
temp_ini_file = pytester.makeini("[pytest]")
from os.path import normpath
Expand Down
41 changes: 34 additions & 7 deletions testing/test_findpaths.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def test_invalid_toml_file(self, tmp_path: Path) -> None:
load_config_dict_from_file(fn)

def test_custom_toml_file(self, tmp_path: Path) -> None:
""".toml files without [tool.pytest] are not considered for configuration."""
"""Custom .toml files without [pytest] have no configuration values."""
fn = tmp_path / "myconfig.toml"
fn.write_text(
dedent(
Expand All @@ -84,12 +84,11 @@ def test_custom_toml_file(self, tmp_path: Path) -> None:
),
encoding="utf-8",
)
assert load_config_dict_from_file(fn) is None
assert load_config_dict_from_file(fn) == {}

def test_valid_toml_file(self, tmp_path: Path) -> None:
""".toml files with [tool.pytest.ini_options] are read correctly, including changing
data types to str/list for compatibility with other configuration options."""
fn = tmp_path / "myconfig.toml"
def test_pyproject_toml_ini_options(self, tmp_path: Path) -> None:
"""[tool.pytest.ini_options] values are converted for INI compatibility."""
fn = tmp_path / "pyproject.toml"
fn.write_text(
dedent(
"""
Expand All @@ -111,7 +110,35 @@ def test_valid_toml_file(self, tmp_path: Path) -> None:
"heterogeneous_array": ConfigValue([1, "str"], origin="file", mode="ini"),
}

def test_native_toml_config(self, tmp_path: Path) -> None:
def test_custom_toml_native_config(self, tmp_path: Path) -> None:
"""Custom .toml files read native configuration from [pytest]."""
fn = tmp_path / "myconfig.toml"
fn.write_text(
dedent(
"""
[pytest]
xfail_strict = true
testpaths = ["tests", "integration"]
"""
),
encoding="utf-8",
)

assert load_config_dict_from_file(fn) == {
"xfail_strict": ConfigValue(True, origin="file", mode="toml"),
"testpaths": ConfigValue(
["tests", "integration"], origin="file", mode="toml"
),
}

def test_custom_toml_ignores_pyproject_table(self, tmp_path: Path) -> None:
"""Custom .toml files do not read configuration from [tool.pytest]."""
fn = tmp_path / "myconfig.toml"
fn.write_text("[tool.pytest]\nxfail_strict = true", encoding="utf-8")

assert load_config_dict_from_file(fn) == {}

def test_pyproject_native_toml_config(self, tmp_path: Path) -> None:
"""[tool.pytest] sections with native types are parsed correctly without coercion."""
fn = tmp_path / "pyproject.toml"
fn.write_text(
Expand Down
Loading