Skip to content

gh-161433: Identify the failing item when hashing tuples - #161496

Open
localzet wants to merge 2 commits into
python:mainfrom
localzet:gh-161433-upstream
Open

localzet wants to merge 2 commits into
python:mainfrom
localzet:gh-161433-upstream

Conversation

@localzet

@localzet localzet commented Nov 6, 2026

Copy link
Copy Markdown

Summary

When hashing a tuple fails with TypeError, add an exception note identifying the index of the tuple item whose hash operation failed. Nested tuples accumulate one note per level. Preserve the original exception, message, existing notes, and traceback.

For example, a direct call to hash(("alice", [])) reports the existing unhashable type: 'list' error with the added note when hashing tuple item 1.

This is an implementation of gh-161433, based on the proposal by @WakaTaira.

Rationale and compatibility

Tests

Five focused tests cover a direct unhashable item, nested tuples, a custom TypeError, existing notes, and a non-TypeError exception.

A CPython --with-pydebug build completed successfully in the fork and test_tuple, test_dict, test_set, and test_traceback all passed:
https://github.com/localzet/cpython/actions/runs/42240703650

The fork's lint checks passed on the subsequent change adding the NEWS fragment:
https://github.com/localzet/cpython/actions/runs/42241501515

This is a draft to allow upstream feedback and the submitter's review before marking ready.

AI assistance

The initial patch, tests, and PR description were prepared with ChatGPT assistance. The implementation is available for human review and maintenance.

@python-cla-bot

python-cla-bot Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

@vstinner

vstinner commented Nov 6, 2026

Copy link
Copy Markdown
Member

@localzet: Can you please sign the CLA? See previous comment.

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dict and set catchs TypeError on hashing to write a hint on the error message: dict_unhashable_type() and set_unhashable_type(). The current TypeError exception is replaced with a new TypeError: notes are discarded. Would it be interesting to copy notes in this case?

Example:

key = ("alice", [])
# hash(key)
d = dict()
d[key] = 1

Output:

Traceback (most recent call last):
  File "/home/vstinner/python/main/x.py", line 4, in <module>
    d[key] = 1
    ~^^^^^
TypeError: cannot use 'tuple' as a dict key (unhashable type: 'list')

If I uncomment the hash() call, I see the new note when hashing tuple item 1:

Traceback (most recent call last):
  File "/home/vstinner/python/main/x.py", line 2, in <module>
    hash(key)
    ~~~~^^^^^
TypeError: unhashable type: 'list'
when hashing tuple item 1

localzet commented Nov 6, 2026 •

Copy link
Copy Markdown
Author

Thanks for reviewing this, Victor! I agree that keeping the tuple-item note visible when the tuple is used as a dict key or set element is important.

The exception replacement is already tracked in gh-149313, with an existing implementation in PR #149505 (which chains the original exception). I intentionally kept this PR limited to tuple_hash() to avoid duplicating that work.

There is a useful distinction between the options: copying __notes__ to the replacement TypeError preserves the concise single-error presentation, while chaining the old exception also retains its traceback and cause but can duplicate the inner error in the common case (as noted in gh-149313).

Update: I implemented a small note-copying proof of concept in my fork: localzet#2. It preserves the concise outer dict/set message and includes regression tests for tuple-item notes and custom __hash__() notes. A debug build and focused test_tuple, test_dict, test_set, and test_traceback suite all passed: https://github.com/localzet/cpython/actions/runs/42277233728.

I have not folded those changes into this upstream PR yet to avoid competing with #149505 without coordination. Would you prefer a focused note-copying change as part of this PR, or to resolve dict/set exception preservation separately in gh-149313?

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

The exception replacement is already tracked in #149313, with an existing implementation in PR #149505 (which chains the original exception). I intentionally kept this PR limited to tuple_hash() to avoid duplicating that work.

Oh ok, if it's already tracked somewhere else, I'm fine with not addressing the issue in this PR.

Comment thread Lib/test/test_tuple.py Resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting merge type-feature A feature request or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants