Skip to content

Commit 2efd559

Browse files
committed
Merge all __annotations__ handling into get_annotations
1 parent bebaf6a commit 2efd559

1 file changed

Lines changed: 20 additions & 31 deletions

File tree

typemap/type_eval/_apply_generic.py

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)