33import ast
44import builtins
55import enum
6- import functools
76import keyword
87import sys
98import types
@@ -33,18 +32,16 @@ class Format(enum.IntEnum):
3332# preserved for compatibility with the old typing.ForwardRef class. The remaining
3433# names are private.
3534_SLOTS = (
36- "__forward_evaluated__" ,
37- "__forward_value__" ,
3835 "__forward_is_argument__" ,
3936 "__forward_is_class__" ,
4037 "__forward_module__" ,
4138 "__weakref__" ,
4239 "__arg__" ,
43- "__ast_node__" ,
44- "__code__" ,
4540 "__globals__" ,
46- "__owner__" ,
41+ "__code__" ,
42+ "__ast_node__" ,
4743 "__cell__" ,
44+ "__owner__" ,
4845 "__stringifier_dict__" ,
4946)
5047
@@ -77,14 +74,12 @@ def __init__(
7774 raise TypeError (f"Forward reference must be a string -- got { arg !r} " )
7875
7976 self .__arg__ = arg
80- self .__forward_evaluated__ = False
81- self .__forward_value__ = None
8277 self .__forward_is_argument__ = is_argument
8378 self .__forward_is_class__ = is_class
8479 self .__forward_module__ = module
80+ self .__globals__ = None
8581 self .__code__ = None
8682 self .__ast_node__ = None
87- self .__globals__ = None
8883 self .__cell__ = None
8984 self .__owner__ = owner
9085
@@ -96,17 +91,11 @@ def evaluate(self, *, globals=None, locals=None, type_params=None, owner=None):
9691
9792 If the forward reference cannot be evaluated, raise an exception.
9893 """
99- if self .__forward_evaluated__ :
100- return self .__forward_value__
10194 if self .__cell__ is not None :
10295 try :
103- value = self .__cell__ .cell_contents
96+ return self .__cell__ .cell_contents
10497 except ValueError :
10598 pass
106- else :
107- self .__forward_evaluated__ = True
108- self .__forward_value__ = value
109- return value
11099 if owner is None :
111100 owner = self .__owner__
112101
@@ -172,8 +161,6 @@ def evaluate(self, *, globals=None, locals=None, type_params=None, owner=None):
172161 else :
173162 code = self .__forward_code__
174163 value = eval (code , globals = globals , locals = locals )
175- self .__forward_evaluated__ = True
176- self .__forward_value__ = value
177164 return value
178165
179166 def _evaluate (self , globalns , localns , type_params = _sentinel , * , recursive_guard ):
@@ -231,18 +218,30 @@ def __forward_code__(self):
231218 def __eq__ (self , other ):
232219 if not isinstance (other , ForwardRef ):
233220 return NotImplemented
234- if self .__forward_evaluated__ and other .__forward_evaluated__ :
235- return (
236- self .__forward_arg__ == other .__forward_arg__
237- and self .__forward_value__ == other .__forward_value__
238- )
239221 return (
240222 self .__forward_arg__ == other .__forward_arg__
241223 and self .__forward_module__ == other .__forward_module__
224+ # Use "is" here because we use id() for this in __hash__
225+ # because dictionaries are not hashable.
226+ and self .__globals__ is other .__globals__
227+ and self .__forward_is_class__ == other .__forward_is_class__
228+ and self .__code__ == other .__code__
229+ and self .__ast_node__ == other .__ast_node__
230+ and self .__cell__ == other .__cell__
231+ and self .__owner__ == other .__owner__
242232 )
243233
244234 def __hash__ (self ):
245- return hash ((self .__forward_arg__ , self .__forward_module__ ))
235+ return hash ((
236+ self .__forward_arg__ ,
237+ self .__forward_module__ ,
238+ id (self .__globals__ ), # dictionaries are not hashable, so hash by identity
239+ self .__forward_is_class__ ,
240+ self .__code__ ,
241+ self .__ast_node__ ,
242+ self .__cell__ ,
243+ self .__owner__ ,
244+ ))
246245
247246 def __or__ (self , other ):
248247 return types .UnionType [self , other ]
@@ -251,11 +250,14 @@ def __ror__(self, other):
251250 return types .UnionType [other , self ]
252251
253252 def __repr__ (self ):
254- if self .__forward_module__ is None :
255- module_repr = ""
256- else :
257- module_repr = f", module={ self .__forward_module__ !r} "
258- return f"ForwardRef({ self .__forward_arg__ !r} { module_repr } )"
253+ extra = []
254+ if self .__forward_module__ is not None :
255+ extra .append (f", module={ self .__forward_module__ !r} " )
256+ if self .__forward_is_class__ :
257+ extra .append (", is_class=True" )
258+ if self .__owner__ is not None :
259+ extra .append (f", owner={ self .__owner__ !r} " )
260+ return f"ForwardRef({ self .__forward_arg__ !r} { '' .join (extra )} )"
259261
260262
261263class _Stringifier :
@@ -277,8 +279,6 @@ def __init__(
277279 # represent a single name).
278280 assert isinstance (node , (ast .AST , str ))
279281 self .__arg__ = None
280- self .__forward_evaluated__ = False
281- self .__forward_value__ = None
282282 self .__forward_is_argument__ = False
283283 self .__forward_is_class__ = is_class
284284 self .__forward_module__ = None
@@ -765,9 +765,10 @@ def get_annotations(
765765 if hasattr (unwrap , "__wrapped__" ):
766766 unwrap = unwrap .__wrapped__
767767 continue
768- if isinstance (unwrap , functools .partial ):
769- unwrap = unwrap .func
770- continue
768+ if functools := sys .modules .get ("functools" ):
769+ if isinstance (unwrap , functools .partial ):
770+ unwrap = unwrap .func
771+ continue
771772 break
772773 if hasattr (unwrap , "__globals__" ):
773774 obj_globals = unwrap .__globals__
0 commit comments