Skip to content

--toml crashes with AttributeError: 'list' object has no attribute 'items' on a pyproject.toml with an array-of-tables directly under [tool] (e.g. [[tool.dynamic-metadata]]) #3969

Description

@zklaus

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:

hte cat

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions