Skip to content

Header equality raises TypeError for non-iterable operands #214

Open

Description

@wynneyin

Comparing a public event's headers with a non-iterable raises TypeError instead of following Python's equality fallback. This affects ordinary sentinel comparisons and prevents unittest.mock.ANY from handling a reflected comparison.

Reproduced on master at 62c5068c971579d61fa1b55373390e12f25fd856, macOS arm64, Python 3.12.7:

from unittest.mock import ANY
import h11

headers = h11.Request(
    method="GET", target="/", headers=[("Host", "example.com")]
).headers

print(headers == None)  # TypeError: 'NoneType' object is not iterable
print(headers == ANY)   # TypeError: '_ANY' object is not iterable

Expected results are False and True, respectively. Headers.__eq__ currently calls list(other) for every operand. An unsupported operand should allow Python to try the other operand's comparison method before falling back to identity.

A focused repair checks for Python's iterator or legacy sequence protocol and returns NotImplemented when neither is present. Existing list, tuple, iterator, Headers, and __getitem__-only comparisons retain their behavior. Exceptions raised by implemented iteration protocols must still propagate, including an internal TypeError from __iter__.

The original implementation fails four new regression cases, while six tests pass. The prepared repair passes the complete 86-test suite on Python 3.10.19, 3.11.14, 3.12.7, 3.13.9 and 3.14.0. Black, isort and strict mypy pass. Original and repaired statement/branch coverage both report 98.2% with the same 19 uncovered statements; the new branches are covered. Python 3.8, 3.9 and PyPy were not tested locally.

AI disclosure: Claude Code assisted with investigation, implementation, testing and this report. A separate automated reviewer identified an exception-propagation gap in an initial draft; a failing regression was added and the final revision was reviewed without a remaining blocker. No maintainer acceptance is claimed.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions