Read only [tool.codespell] from TOML config (#3975)
diff --git a/codespell_lib/_codespell.py b/codespell_lib/_codespell.py
index 8ebb19d..994c5c4 100644
--- a/codespell_lib/_codespell.py
+++ b/codespell_lib/_codespell.py
@@ -712,10 +712,12 @@
if tomllib is not None:
for toml_file in toml_files:
with open(toml_file, "rb") as f:
- data = tomllib.load(f).get("tool", {})
- if "codespell" in data:
- data["codespell"] = _toml_to_parseconfig(data["codespell"])
- config.read_dict(data)
+ data = tomllib.load(f).get("tool", {}).get("codespell")
+ if data is not None:
+ if not isinstance(data, dict):
+ msg = f"{toml_file}: [tool.codespell] must be a table"
+ raise configparser.Error(msg)
+ config.read_dict({"codespell": _toml_to_parseconfig(data)})
# Collect which config files are going to be used
used_cfg_files = []
diff --git a/codespell_lib/tests/test_basic.py b/codespell_lib/tests/test_basic.py
index d1c66a1..930f09f 100644
--- a/codespell_lib/tests/test_basic.py
+++ b/codespell_lib/tests/test_basic.py
@@ -1428,7 +1428,10 @@
assert "ill-formed config file" in stderr
-@pytest.mark.parametrize("kind", ["cfg", "cfg_multiline", "toml", "toml_list"])
+@pytest.mark.parametrize(
+ "kind",
+ ["cfg", "cfg_multiline", "toml", "toml_list", "toml_sibling_array"],
+)
def test_config_toml(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
@@ -1482,14 +1485,24 @@
check-filenames = false
count = true
"""
- else:
- assert kind == "toml_list"
+ elif kind == "toml_list":
text = """\
[tool.codespell]
skip = ['bad.txt', 'whatever.txt']
check-filenames = false
count = true
"""
+ else:
+ assert kind == "toml_sibling_array"
+ text = """\
+[tool.codespell]
+skip = 'bad.txt,whatever.txt'
+check-filenames = false
+count = true
+
+[[tool.dynamic-metadata]]
+provider = 'scikit_build_core.metadata.version'
+"""
tomlfile.write_text(text)
# Should pass when skipping bad.txt or abandonned.txt
@@ -1514,6 +1527,22 @@
assert "abandonned.txt" not in stdout
+def test_config_toml_codespell_array(
+ tmp_path: Path,
+ capsys: pytest.CaptureFixture[str],
+) -> None:
+ if sys.version_info < (3, 11):
+ pytest.importorskip("tomli")
+ tomlfile = tmp_path / "pyproject.toml"
+ tomlfile.write_text("[[tool.codespell]]\nskip = 'bad.txt'\n")
+
+ result = cs.main("--toml", tomlfile, std=True)
+ assert isinstance(result, tuple)
+ code, _, stderr = result
+ assert code == EX_CONFIG
+ assert "[tool.codespell] must be a table" in stderr
+
+
@contextlib.contextmanager
def FakeStdin(text: str) -> Generator[None, None, None]:
oldin = sys.stdin