@@ -194,10 +194,12 @@ def get_annotations(
194194 obj : object ,
195195 args : dict [str , object ],
196196 key : str = '__annotate__' ,
197+ annos_ok : bool = True ,
197198) -> Any | None :
198199 """Get the annotations on an object, substituting in type vars."""
199200
200201 rr = None
202+ globs = None
201203 if af := typing .cast (types .FunctionType , getattr (obj , key , None )):
202204 # Substitute in names that are provided but keep the existing
203205 # values for everything else.
@@ -208,10 +210,13 @@ def get_annotations(
208210 )
209211 )
210212
211- ff = types .FunctionType (
212- af .__code__ , af .__globals__ , af .__name__ , None , closure
213- )
213+ globs = af .__globals__
214+ ff = types .FunctionType (af .__code__ , globs , af .__name__ , None , closure )
214215 rr = ff (annotationlib .Format .VALUE )
216+ elif annos_ok and (rr := getattr (obj , "__annotations__" , None )):
217+ globs = {}
218+ if mod := sys .modules .get (obj .__module__ ):
219+ globs .update (vars (mod ))
215220
216221 if isinstance (rr , dict ) and any (isinstance (v , str ) for v in rr .values ()):
217222 # Copy in any __type_params__ that aren't provided for, so that if
@@ -223,14 +228,16 @@ def get_annotations(
223228 args [str (param )] = param
224229
225230 for k , v in rr .items ():
231+ # Eval strings
226232 if isinstance (v , str ):
233+ v = eval (v , globs , args )
227234 # Handle cases where annotation is explicitly a string,
228235 # e.g.:
229- #
230236 # class Foo[X]:
231237 # x: "Foo[X | None]"
232-
233- rr [k ] = eval (v , af .__globals__ , args )
238+ if isinstance (v , str ):
239+ v = eval (v , globs , args )
240+ rr [k ] = v
234241
235242 return rr
236243
@@ -241,29 +248,6 @@ def get_local_defns(boxed: Boxed) -> tuple[dict[str, Any], dict[str, Any]]:
241248
242249 if (rr := get_annotations (boxed .cls , boxed .str_args )) is not None :
243250 annos .update (rr )
244- elif anns := getattr (boxed .cls , "__annotations__" , None ):
245- # TODO: substitute vars in this case
246- _globals = {}
247- if mod := sys .modules .get (boxed .cls .__module__ ):
248- _globals .update (vars (mod ))
249- _globals .update (boxed .str_args )
250-
251- _locals = dict (boxed .cls .__dict__ )
252- _locals .update (boxed .str_args )
253-
254- for k , v in anns .items ():
255- if isinstance (v , str ):
256- result = eval (v , _globals , _locals )
257- # Handle cases where annotation is explicitly a string
258- # e.g.
259- # class Foo[T]:
260- # x: "Bar[T]"
261- if isinstance (result , str ):
262- result = eval (result , _globals , _locals )
263- annos [k ] = result
264-
265- else :
266- annos [k ] = v
267251
268252 for name , orig in boxed .cls .__dict__ .items ():
269253 if name in EXCLUDED_ATTRIBUTES :
@@ -274,9 +258,14 @@ def get_local_defns(boxed: Boxed) -> tuple[dict[str, Any], dict[str, Any]]:
274258 if isinstance (stuff , types .FunctionType ):
275259 local_fn : Any = None
276260
277- if (rr := get_annotations (stuff , boxed .str_args )) is not None :
261+ # TODO: This annos_ok thing is a hack because processing
262+ # __annotations__ on methods broke stuff and I didn't want
263+ # to chase it down yet.
264+ if (
265+ rr := get_annotations (stuff , boxed .str_args , annos_ok = False )
266+ ) is not None :
278267 local_fn = make_func (orig , rr )
279- elif anns := getattr (stuff , "__annotations__" , None ):
268+ elif getattr (stuff , "__annotations__" , None ):
280269 # XXX: This is totally wrong; we still need to do
281270 # substitute in class vars
282271 local_fn = stuff
0 commit comments