Repository navigation
Python 3.14.1+ annotationlib regression: ForwardRef.__hash__ raises TypeError: unhashable type: 'cell' #143831
Description
Bug report
Bug description:
This issue was originally reported in the beartype library (Ref: beartype/beartype#610), but investigation suggests it may stem from a change in Python’s annotationlib.ForwardRef implementation.
Starting with Python 3.14.1, deferred annotations (PEP 649) involving self-referential return type is raising: TypeError: unhashable type: 'cell' at decoration time. This worked correctly in Python 3.14.0 and the issue appears to persist in 3.14.2, suggesting a regression.
The error originates from annotationlib.ForwardRef.__hash__, which attempts to hash internal state containing non-hashable cell objects. Although observed via beartype, this may affects any downstream library that hashes ForwardRef objects. The following code seems to be raising the issue:
tuple(sorted(self.__cell__.items())) if isinstance(self.__cell__, dict) else self.__cell__,
Lines 290 to 299 in 1857a40
| def __hash__(self): | |
| return hash(( | |
| self.__forward_arg__, | |
| self.__forward_module__, | |
| id(self.__globals__), # dictionaries are not hashable, so hash by identity | |
| self.__forward_is_class__, | |
| tuple(sorted(self.__cell__.items())) if isinstance(self.__cell__, dict) else self.__cell__, | |
| self.__owner__, | |
| tuple(sorted(self.__extra_names__.items())) if self.__extra_names__ else None, | |
| )) |
Code:
#!pip install beartype==0.22.9
from beartype import beartype
class MyClass:
@beartype
def foo(self) -> MyClass:
return MyClass()
print(MyClass().foo())Observed behavior:
The program fails during decoration with the error: TypeError: unhashable type: 'cell'
File ".../beartype/_check/metadata/hint/hintsane.py", line 242, in __init__
self._hash = hash((hint, hint_recursable_to_depth, typearg_to_hint))
File ".../python3.14/annotationlib.py", line 291, in __hash__
return hash((..., tuple(sorted(self.__extra_names__.items()))))
TypeError: unhashable type: 'cell'
Inspection shows:
>>> self.__cell__
{'__classdict__': <cell at 0x...: dict object>}
>>> tuple(sorted(self.__cell__.items()))
(('__classdict__', <cell at 0x...>),)Since cell objects are not hashable, this causes hash to fail.
Affected Versions:
Python 3.14.1 and 3.14.2
OS Tested on: MacOS
CPython versions tested on:
3.14
Operating systems tested on:
macOS
Activity
johnslavik commentedon Jan 14, 2026
Smaller reproducer (without beartype):
johnslavik commentedon Jan 14, 2026
Bisects to b1027d4 (GH-138164).
Details
johnslavik commentedon Jan 14, 2026
@abhinand-c, the easiest workaround in your case with beartype is to stringify the annotation:
EDIT: You found it too, nice :) beartype/beartype#610 (comment)
It makes deferred annotations blind to the class scope.
The unhashable cell in your reproduction is the transient class dict cell used in the annotation scope to resolve symbols in the class scope on the annotation level. The default
__annotate__functions of methods always consume__classdict__, so this regression will now reproduce everytime we try hash an annotationlib forward reference from any annotation in a method, because (1) this cell always appears (2) forward references are produced when eager resolution fails.johnslavik commentedon Jan 14, 2026
Cells are not hashable.
ForwardRef.__hash__already provides an id for globals because dictionaries aren't hashable. Maybe we can do the same here?cpython/Lib/annotationlib.py
Lines 290 to 294 in 2b33b52
This problem didn't occur prior to the change because
ForwardRef.__cell__was alwaysNone. There was a comment sayingForwardRef.__cell__et al. could become non-Noneif the stringifier object was manually assigned__class__:cpython/Lib/annotationlib.py
Lines 85 to 89 in d590685
However, this wasn't true for
__cell__at that time:cpython/Lib/annotationlib.py
Lines 645 to 651 in d590685
Only then b1027d4 delivered on that promise:
cpython/Lib/annotationlib.py
Lines 655 to 663 in b1027d4
Since then cells show up while hashing and we will run into this for every cell, not just in method annotations scenario.
gh-143831: Compare cells by identity in forward references (#143848)
pythongh-143831: Compare cells by identity in forward references (pyt…
[3.14] gh-143831: Compare cells by identity in forward references (GH…
pythongh-143831: Compare cells by identity in forward references (pyt…