Issue
codespell --toml pyproject.toml ... raises
AttributeError: 'list' object has no attribute 'items'
when the pyproject.toml contains a TOML array of tables that is a direct child of [tool], such as [[tool.dynamic-metadata]].
The cause is in parse_options, which loads the entire [tool] table and hands all of it to configparser.read_dict:
data = tomllib.load(f).get("tool", {})
if "codespell" in data:
data["codespell"] = _toml_to_parseconfig(data["codespell"])
config.read_dict(data)
read_dict expects a {section: {key: value}} mapping, but a [[tool.dynamic-metadata]] entry makes tool.dynamic-metadata a list, so read_dict fails when it does keys.items() on that value. codespell should only read the [tool.codespell] section, not its sibling tables.
This is distinct from #3852: there the user passed --config (INI-only) instead of --toml, and the array was [[tool.uv.index]], which is nested under tool.uv (a dict) and so does not trip read_dict. Here the correct --toml flag is used and the array sits directly under [tool].
Affected versions
2.4.1, 2.4.2 (latest), and current main — the config.read_dict(data) line in parse_options is unchanged across all of them.
Steps to reproduce
pyproject.toml:
[tool.codespell]
skip = "*.lock"
[[tool.dynamic-metadata]]
provider = "scikit_build_core.metadata.version"
hello.txt:
Run:
$ codespell --toml pyproject.toml hello.txt
(codespell also auto-reads ./pyproject.toml, so plain codespell hello.txt from the project directory crashes the same way, even without --toml.)
Actual behavior
Traceback (most recent call last):
...
File ".../codespell_lib/_codespell.py", line 664, in parse_options
config.read_dict(data)
File ".../configparser.py", line 763, in read_dict
for key, value in keys.items():
AttributeError: 'list' object has no attribute 'items'
Expected behavior
codespell reads the [tool.codespell] settings and reports the misspelling (hte -> the), ignoring the unrelated [[tool.dynamic-metadata]] table.
Why this matters now
[[tool.dynamic-metadata]] is emitted by every scikit-build-core >= 1.0 project (the standard dynamic-metadata table). Any such project that runs codespell against its pyproject.toml — the common case — hits this. We ran into it while migrating PyTorch's build system to scikit-build-core 1.0.
Suggested fix
In parse_options, pass only the codespell section to read_dict rather than the whole [tool] table:
data = tomllib.load(f).get("tool", {})
if "codespell" in data:
config.read_dict({"codespell": _toml_to_parseconfig(data["codespell"])})
so sibling tables (arrays of tables or otherwise) are never fed to configparser.
Issue
codespell --toml pyproject.toml ...raiseswhen the
pyproject.tomlcontains a TOML array of tables that is a direct child of[tool], such as[[tool.dynamic-metadata]].The cause is in
parse_options, which loads the entire[tool]table and hands all of it toconfigparser.read_dict:read_dictexpects a{section: {key: value}}mapping, but a[[tool.dynamic-metadata]]entry makestool.dynamic-metadataa list, soread_dictfails when it doeskeys.items()on that value. codespell should only read the[tool.codespell]section, not its sibling tables.This is distinct from #3852: there the user passed
--config(INI-only) instead of--toml, and the array was[[tool.uv.index]], which is nested undertool.uv(a dict) and so does not tripread_dict. Here the correct--tomlflag is used and the array sits directly under[tool].Affected versions
2.4.1,2.4.2(latest), and currentmain— theconfig.read_dict(data)line inparse_optionsis unchanged across all of them.Steps to reproduce
pyproject.toml:hello.txt:Run:
$ codespell --toml pyproject.toml hello.txt(codespell also auto-reads
./pyproject.toml, so plaincodespell hello.txtfrom the project directory crashes the same way, even without--toml.)Actual behavior
Expected behavior
codespell reads the
[tool.codespell]settings and reports the misspelling (hte->the), ignoring the unrelated[[tool.dynamic-metadata]]table.Why this matters now
[[tool.dynamic-metadata]]is emitted by every scikit-build-core >= 1.0 project (the standard dynamic-metadata table). Any such project that runs codespell against itspyproject.toml— the common case — hits this. We ran into it while migrating PyTorch's build system to scikit-build-core 1.0.Suggested fix
In
parse_options, pass only the codespell section toread_dictrather than the whole[tool]table:so sibling tables (arrays of tables or otherwise) are never fed to
configparser.