Skip to content

Reject empty type and field names in namedutils - #462

Merged
mahmoud merged 1 commit into
mahmoud:masterfrom
avalyset:fix/namedutils-empty-name-validation
Sep 7, 2026
Merged

mahmoud merged 1 commit into
mahmoud:masterfrom
avalyset:fix/namedutils-empty-name-validation

Conversation

@avalyset

Copy link
Copy Markdown
Contributor

The validation loop in namedtuple and namedlist checks names with:

if not all(c.isalnum() or c == "_" for c in name):

all() returns True for an empty iterable, so an empty name passes this check. It then passes _iskeyword("") as well, and reaches name[0].isdigit() — which raises IndexError instead of the intended ValueError:

=== boltons.namedutils.namedtuple ===
  empty field name ["x", ""]     -> IndexError: string index out of range
  empty typename ""              -> IndexError: string index out of range
  control: invalid char ["x-y"]  -> ValueError: Type names and field names can only contain
                                    alphanumeric characters and underscores: "x-y"
  control: valid ["x", "y"]      -> accepted
=== boltons.namedutils.namedlist ===
  empty field name ["x", ""]     -> IndexError: string index out of range
  empty typename ""              -> IndexError: string index out of range

Both factories are affected, and both an empty field name and an empty typename trigger it.

Why this looks like an oversight rather than a decision

The rename branch seven lines above the validation loop already guards for this exact case:

if (not all(c.isalnum() or c == "_" for c in name)
    or _iskeyword(name)
    or not name                     # <-- here
    or name[0].isdigit()
    ...

So the guard exists in the same function; it is just missing from the loop that raises. CPythons collections.namedtuple, which this module is a variant of, rejects the same input properly:

=== CPython collections.namedtuple ===
  empty field name ["x", ""]     -> ValueError: Type names and field names must be valid identifiers: ""
  empty typename ""              -> ValueError: Type names and field names must be valid identifiers: ""

The change

Adds or not name to the validation loop in both functions, matching the form the rename branch already uses.

I also extended the message. The existing wording — "can only contain alphanumeric characters and underscores" — is vacuously true of the empty string: it states a rule the empty string does not actually break, so it would not explain the failure. The new wording is "must be non-empty and can only contain alphanumeric characters and underscores". No test asserted on the old text. Happy to drop that part and keep the one-word condition change if you would rather leave the message alone.

The rename=True branches are untouched.

Tests

Added to tests/test_namedutils.py, parametrized over both factories: empty field name, empty typename, an invalid character (still ValueError), and a valid pair (still accepted).

Verified the tests pin the behaviour rather than just passing — against an unmodified namedutils.py the four empty-name cases fail with the IndexError above:

FAILED tests/test_namedutils.py::test_empty_field_name_raises_value_error[namedtuple]
FAILED tests/test_namedutils.py::test_empty_field_name_raises_value_error[namedlist]
FAILED tests/test_namedutils.py::test_empty_type_name_raises_value_error[namedtuple]
FAILED tests/test_namedutils.py::test_empty_type_name_raises_value_error[namedlist]
4 failed, 7 passed

With the fix, and using the command from tox.ini:

pytest --doctest-modules boltons/namedutils.py tests/test_namedutils.py
13 passed

pytest tests
480 passed

Conflicts

#382 also touches boltons/namedutils.py, but its hunks are at lines 45, 120-122 and 279-281, while this change is at 163 and 322 — no overlap.

`all()` returns True for the empty string, so an empty name passed both
ValueError checks in the validation loop and reached `name[0].isdigit()`,
raising IndexError instead of the intended ValueError. Both namedtuple and
namedlist were affected, for an empty field name and an empty typename.

The rename branch a few lines above already guards with `or not name`; add
the same guard to the validation loop and extend the message, which was
vacuously true for the empty string.
@mahmoud
mahmoud merged commit 970051b into mahmoud:master Sep 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants