-
-
Notifications
You must be signed in to change notification settings - Fork 33.3k
gh-137969: Fix double evaluation of ForwardRefs which rely on globals
#140974
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
8708272
fc00d21
71c9074
1c79ac7
2630802
d02e635
02b8a83
e20da27
65c40d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -150,33 +150,42 @@ def evaluate( | |
| if globals is None: | ||
| globals = {} | ||
|
|
||
| if type_params is None and owner is not None: | ||
| type_params = getattr(owner, "__type_params__", None) | ||
|
|
||
| if locals is None: | ||
| locals = {} | ||
| if isinstance(owner, type): | ||
| locals.update(vars(owner)) | ||
| elif ( | ||
| type_params is not None | ||
| or isinstance(self.__cell__, dict) | ||
| or self.__extra_names__ | ||
| ): | ||
| # Create a new locals dict if necessary, | ||
| # to avoid mutating the argument. | ||
| locals = dict(locals) | ||
|
|
||
| if type_params is None and owner is not None: | ||
| # "Inject" type parameters into the local namespace | ||
| # (unless they are shadowed by assignments *in* the local namespace), | ||
| # as a way of emulating annotation scopes when calling `eval()` | ||
| type_params = getattr(owner, "__type_params__", None) | ||
|
|
||
| # Type parameters exist in their own scope, which is logically | ||
| # between the locals and the globals. We simulate this by adding | ||
| # them to the globals. Similar reasoning applies to nonlocals stored in cells. | ||
| if type_params is not None or isinstance(self.__cell__, dict): | ||
| globals = dict(globals) | ||
| # "Inject" type parameters into the local namespace | ||
| # (unless they are shadowed by assignments *in* the local namespace), | ||
| # as a way of emulating annotation scopes when calling `eval()` | ||
| if type_params is not None: | ||
| for param in type_params: | ||
| globals[param.__name__] = param | ||
| if param.__name__ not in locals: | ||
| locals[param.__name__] = param | ||
dr-carlos marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # Similar logic can be used for nonlocals, which should not | ||
| # override locals. | ||
| if isinstance(self.__cell__, dict): | ||
| for cell_name, cell_value in self.__cell__.items(): | ||
| try: | ||
| globals[cell_name] = cell_value.cell_contents | ||
| if cell_name not in locals: | ||
| locals[cell_name] = cell_value.cell_contents | ||
|
||
| except ValueError: | ||
| pass | ||
|
|
||
| if self.__extra_names__: | ||
| locals = {**locals, **self.__extra_names__} | ||
| locals.update(self.__extra_names__) | ||
|
|
||
| arg = self.__forward_arg__ | ||
| if arg.isidentifier() and not keyword.iskeyword(arg): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| Fix :meth:`annotationlib.ForwardRef.evaluate` returning | ||
| :class:`~annotationlib.ForwardRef` objects which don't update with new | ||
| globals. |
Uh oh!
There was an error while loading. Please reload this page.