Skip to content

Commit 9b0ddec

Browse files
committed
Restore self-reference binding in get_annotations()
1 parent 3975a83 commit 9b0ddec

1 file changed

Lines changed: 13 additions & 11 deletions

File tree

typemap/type_eval/_apply_generic.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ def get_annotations(
196196
obj: object,
197197
args: dict[str, object],
198198
key: str = '__annotate__',
199+
cls: type | None = None,
199200
annos_ok: bool = True,
200201
) -> Any | None:
201202
"""Get the annotations on an object, substituting in type vars."""
@@ -221,14 +222,23 @@ def get_annotations(
221222
globs.update(vars(mod))
222223

223224
if isinstance(rr, dict) and any(isinstance(v, str) for v in rr.values()):
225+
args = args.copy()
224226
# Copy in any __type_params__ that aren't provided for, so that if
225227
# we have to eval, we have them.
226228
if params := getattr(obj, "__type_params__", None):
227-
args = args.copy()
228229
for param in params:
229230
if str(param) not in args:
230231
args[str(param)] = param
231232

233+
# Include the class itself in args so that self-referential string
234+
# annotations (e.g. from `from __future__ import annotations`) in
235+
# nested scopes can be resolved during eval. (This only half
236+
# solves that general problem, but it is the best we can do.)
237+
rcls = cls or obj
238+
if isinstance(rcls, (type, typing.TypeAliasType)):
239+
if rcls.__name__ not in args:
240+
args[rcls.__name__] = rcls
241+
232242
for k, v in rr.items():
233243
# Eval strings
234244
if isinstance(v, str):
@@ -248,15 +258,7 @@ def get_local_defns(boxed: Boxed) -> tuple[dict[str, Any], dict[str, Any]]:
248258
annos: dict[str, Any] = {}
249259
dct: dict[str, Any] = {}
250260

251-
# Include the class itself in args so that self-referential string
252-
# annotations (e.g. from `from __future__ import annotations`) in
253-
# nested scopes can be resolved during eval. (This only half
254-
# solves that general problem, but it is the best we can do.)
255-
str_args = boxed.str_args
256-
if boxed.cls.__name__ not in str_args:
257-
str_args = {**str_args, boxed.cls.__name__: boxed.cls}
258-
259-
if (rr := get_annotations(boxed.cls, str_args)) is not None:
261+
if (rr := get_annotations(boxed.cls, boxed.str_args)) is not None:
260262
annos.update(rr)
261263

262264
for name, orig in boxed.cls.__dict__.items():
@@ -272,7 +274,7 @@ def get_local_defns(boxed: Boxed) -> tuple[dict[str, Any], dict[str, Any]]:
272274
# __annotations__ on methods broke stuff and I didn't want
273275
# to chase it down yet.
274276
if (
275-
rr := get_annotations(stuff, str_args, annos_ok=False)
277+
rr := get_annotations(stuff, boxed.str_args, cls=boxed.cls, annos_ok=False)
276278
) is not None:
277279
local_fn = make_func(orig, rr)
278280
elif getattr(stuff, "__annotations__", None):

0 commit comments

Comments
 (0)