@@ -189,84 +189,67 @@ def make_func(
189189 return new_func
190190
191191
192- def _get_closure_types (af : types .FunctionType ) -> dict [str , type ]:
193- # Generate a fallback mapping of closure classes.
194- # This is needed for locally defined generic types which reference
195- # themselves in their type annotations.
196- if not af .__closure__ :
197- return {}
198- return {
199- name : variable .cell_contents
200- for name , variable in zip (
201- af .__code__ .co_freevars , af .__closure__ , strict = True
202- )
203- }
204-
205-
206192EXCLUDED_ATTRIBUTES = typing .EXCLUDED_ATTRIBUTES - {'__init__' } # type: ignore[attr-defined]
207193
208194
209- def get_local_defns ( boxed : Boxed ) -> tuple [ dict [ str , Any ], dict [ str , Any ]]:
210- annos : dict [ str , Any ] = {}
211- dct : dict [str , Any ] = {}
212-
213- if af := typing . cast (
214- types . FunctionType , getattr ( boxed . cls , "__annotate__" , None )
215- ):
216- # Class has annotations, let's resolve generic arguments
217-
218- closure_types = _get_closure_types ( af )
219- args = tuple (
220- types . CellType (
221- boxed . cls . __dict__
222- if name == "__classdict__"
223- else boxed . str_args [ name ]
224- if name in boxed . str_args
225- else closure_types [ name ]
195+ def get_annotations (
196+ obj : object ,
197+ args : dict [str , object ],
198+ key : str = '__annotate__' ,
199+ annos_ok : bool = True ,
200+ ) -> Any | None :
201+ """Get the annotations on an object, substituting in type vars."""
202+
203+ rr = None
204+ globs = None
205+ if af := typing . cast ( types . FunctionType , getattr ( obj , key , None )):
206+ # Substitute in names that are provided but keep the existing
207+ # values for everything else.
208+ closure = tuple (
209+ types . CellType ( args [ name ]) if name in args else orig_value
210+ for name , orig_value in zip (
211+ af . __code__ . co_freevars , af . __closure__ or (), strict = True
226212 )
227- for name in af .__code__ .co_freevars
228213 )
229214
230- ff = types .FunctionType (
231- af .__code__ , af .__globals__ , af .__name__ , None , args
232- )
215+ globs = af .__globals__
216+ ff = types .FunctionType (af .__code__ , globs , af .__name__ , None , closure )
233217 rr = ff (annotationlib .Format .VALUE )
234-
235- if rr :
236- for k , v in rr .items ():
218+ elif annos_ok and (rr := getattr (obj , "__annotations__" , None )):
219+ globs = {}
220+ if mod := sys .modules .get (obj .__module__ ):
221+ globs .update (vars (mod ))
222+
223+ if isinstance (rr , dict ) and any (isinstance (v , str ) for v in rr .values ()):
224+ # Copy in any __type_params__ that aren't provided for, so that if
225+ # we have to eval, we have them.
226+ if params := getattr (obj , "__type_params__" , None ):
227+ args = args .copy ()
228+ for param in params :
229+ if str (param ) not in args :
230+ args [str (param )] = param
231+
232+ for k , v in rr .items ():
233+ # Eval strings
234+ if isinstance (v , str ):
235+ v = eval (v , globs , args )
236+ # Handle cases where annotation is explicitly a string,
237+ # e.g.:
238+ # class Foo[X]:
239+ # x: "Foo[X | None]"
237240 if isinstance (v , str ):
238- # Handle cases where annotation is explicitly a string,
239- # e.g.:
240- #
241- # class Foo[X]:
242- # x: "Foo[X | None]"
241+ v = eval (v , globs , args )
242+ rr [k ] = v
243243
244- annos [k ] = eval (v , af .__globals__ , boxed .str_args )
245- else :
246- annos [k ] = v
247- elif af := getattr (boxed .cls , "__annotations__" , None ):
248- # TODO: substitute vars in this case
249- _globals = {}
250- if mod := sys .modules .get (boxed .cls .__module__ ):
251- _globals .update (vars (mod ))
252- _globals .update (boxed .str_args )
253-
254- _locals = dict (boxed .cls .__dict__ )
255- _locals .update (boxed .str_args )
256-
257- for k , v in af .items ():
258- if isinstance (v , str ):
259- result = eval (v , _globals , _locals )
260- # Handle cases where annotation is explicitly a string
261- # e.g.
262- # class Foo[T]:
263- # x: "Bar[T]"
264- if isinstance (result , str ):
265- result = eval (result , _globals , _locals )
266- annos [k ] = result
244+ return rr
267245
268- else :
269- annos [k ] = v
246+
247+ def get_local_defns (boxed : Boxed ) -> tuple [dict [str , Any ], dict [str , Any ]]:
248+ annos : dict [str , Any ] = {}
249+ dct : dict [str , Any ] = {}
250+
251+ if (rr := get_annotations (boxed .cls , boxed .str_args )) is not None :
252+ annos .update (rr )
270253
271254 for name , orig in boxed .cls .__dict__ .items ():
272255 if name in EXCLUDED_ATTRIBUTES :
@@ -275,42 +258,18 @@ def get_local_defns(boxed: Boxed) -> tuple[dict[str, Any], dict[str, Any]]:
275258 stuff = inspect .unwrap (orig )
276259
277260 if isinstance (stuff , types .FunctionType ):
278- local_fn : types .FunctionType | classmethod | staticmethod | None = (
279- None
280- )
281-
282- if af := typing .cast (
283- types .FunctionType , getattr (stuff , "__annotate__" , None )
284- ):
285- params = dict (
286- zip (
287- map (str , stuff .__type_params__ ),
288- stuff .__type_params__ ,
289- strict = True ,
290- )
291- )
292-
293- closure_types = _get_closure_types (af )
294- args = tuple (
295- types .CellType (
296- boxed .cls .__dict__
297- if name == "__classdict__"
298- else params [name ]
299- if name in params
300- else boxed .str_args [name ]
301- if name in boxed .str_args
302- else closure_types [name ]
303- )
304- for name in af .__code__ .co_freevars
305- )
306-
307- ff = types .FunctionType (
308- af .__code__ , af .__globals__ , af .__name__ , None , args
309- )
310- rr = ff (annotationlib .Format .VALUE )
311-
261+ local_fn : Any = None
262+
263+ # TODO: This annos_ok thing is a hack because processing
264+ # __annotations__ on methods broke stuff and I didn't want
265+ # to chase it down yet.
266+ if (
267+ rr := get_annotations (stuff , boxed .str_args , annos_ok = False )
268+ ) is not None :
312269 local_fn = make_func (orig , rr )
313- elif af := getattr (stuff , "__annotations__" , None ):
270+ elif getattr (stuff , "__annotations__" , None ):
271+ # XXX: This is totally wrong; we still need to do
272+ # substitute in class vars
314273 local_fn = stuff
315274
316275 if local_fn is not None :
0 commit comments