Exception notes discarded from TypeError within __hash__()
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Python
- Star
- 77.2k
- Fork
- 35.9k
- Chỉ số merge pull request
- Chỉ số pull request đang chờ
Mô tả
Bug report
Bug description:
Context
I'm maintaining this library that requires users to construct deeply nested structures that are used as dict keys. To alleviate the annoyance of figuring out where a non-hashable value has been introduced into the key structure, I started using dedicated data structures with a __hash__(), which adds location information to the exception's notes:
from dataclasses import dataclass
from dataclasses import field
@dataclass
class KeyStruct:
name: str
first: object = field(repr=False)
second: object = field(repr=False)
def __hash__(self) -> int:
try:
h1 = hash(self.first)
except TypeError as e:
e.add_note(f"within {self}.first")
raise
try:
h2 = hash(self.second)
except TypeError as e:
e.add_note(f"within {self}.second")
raise
return hash((self.name, h1, h2))
So when such a structure is used, information about the location of the offending value can be provided:
s = KeyStruct("foo", 1, KeyStruct("bar", 2, KeyStruct("spam", 3, [])))
s_dict = {s: "hello"}
Traceback (most recent call last):
File "/Users/michi/Documents/Projects/Declarative_Python/declarative-python/src/hello.py", line 43, in <module>
s_dict = {s: "hello"}
^^^^^^^^^^^^
File "/Users/michi/Documents/Projects/Declarative_Python/declarative-python/src/hello.py", line 34, in __hash__
h2 = hash(self.second)
File "/Users/michi/Documents/Projects/Declarative_Python/declarative-python/src/hello.py", line 34, in __hash__
h2 = hash(self.second)
File "/Users/michi/Documents/Projects/Declarative_Python/declarative-python/src/hello.py", line 28, in __hash__
h1 = hash(self.first)
TypeError: unhashable type: 'list'
within KeyStruct(name='spam').first
within KeyStruct(name='bar').second
within KeyStruct(name='foo').second
It is not very beautiful, but very helpful. :)
The Bug
The above output is from Python 3.13. The output changes with Python 3.14 (3.14.4 on macOS 14.8.5):
Traceback (most recent call last):
File "/Users/michi/Documents/Projects/Declarative_Python/declarative-python/src/hello.py", line 43, in <module>
s_dict = {s: "hello"}
^^^^^^^^^^^^
TypeError: cannot use 'KeyStruct' as a dict key (unhashable type: 'list')
I suspect that the source of this is the improved error messages introduced with https://github.com/python/cpython/issues/132825. If I interpret this code correctly, the dict and set methods that need to hash a key will catch and discard the raised TypeError and only re-use the exception message.
IMHO his is unfortunate and I consider it a regression.
I'm not sure what the best way to solve this would be:
-
As an experienced Python programmer, I would have expected the implementation of
dictandsetto attach the existing exception as__cause__on the new exception. That would solve my use case. -
An alternative would be to copy the notes from the old to the new exception. If you consider the notes a part of something I'll call the "extended exception message", the code would conceptually replace the exception, modifying, but retaining its "extended exception message" in full.
But this sill has the drawback of discarding the original stack trace, which I also consider valuable in many cases.
-
Update the passage describing the improvement in What’s new in Python 3.14 to document the change in behavior and provide a workaround. E.g.:
-
Improved error message when trying to add an instance of an unhashable type to a
dictorset. (Contributed by CF Bolz-Tereick and Victor Stinner in gh-132828.)(code block)
In those instances, the original
TypeErrorand its notes and stack trace are replaced. You can raise a subclass ofTypeErrorfrom a__hash__()implementation to opt-out of this feature.
AFAICT, the improvement is not documented elsewhere in Python's documentation.
-
CPython versions tested on:
3.14
Operating systems tested on:
macOS
Linked PRs
- gh-149505
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu bằng cách xem xét các phương thức băm của dict và set được nêu trong báo cáo, cùng với điểm vào hash và PR được liên kết gh-149505. So sánh hành vi của Python 3.13 và 3.14, bao gồm các ghi chú về ngoại lệ và việc xử lý traceback. Công việc được xem là hoàn tất khi triển khai giải pháp đã được chấp nhận cho hồi quy hoặc cập nhật What's New nếu hành vi được giữ lại, cùng với phạm vi kiểm thử hồi quy hoặc tài liệu liên quan.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- python
- Lĩnh vực
- backend
- Loại issue
- Lỗi
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức độ hoạt động
- Đình trệ
- Độ rõ ràng
- Khá rõ ràng
- Mức phù hợp với người mới
- 35/100