Python:Eval
lastmod 2022-07-18created 2022-07-181 edits
Description of the Python eval function
Annotation loss problem
For example, if the site-packages list changes between just before and just after running eval, the contents of __annotations__, which hold the Type Annotation information, can be lost at the time of eval, and the type can come in changed to str. To solve this, create a function like the following and update local_variables and global_variables right after eval.
# -*- coding: utf-8 -*-
from inspect import ismethod
from typing import Any, Dict, Optional
ATTRIBUTE_FUNC = "__func__"
ATTRIBUTE_ANNOTATIONS = "__annotations__"
ATTRIBUTE_WRAPPED = "__wrapped__"
def eval_annotations(
obj: Any,
global_variables: Optional[Dict[str, Any]] = None,
local_variables: Optional[Dict[str, Any]] = None,
) -> None:
"""
If the annotation of the variable executed by the `eval` function
is passed in the form of `str`, it is converted to the type.
"""
if ismethod(obj):
assert hasattr(obj, ATTRIBUTE_FUNC)
eval_annotations(
getattr(obj, ATTRIBUTE_FUNC),
global_variables,
local_variables,
)
return
if not hasattr(obj, ATTRIBUTE_ANNOTATIONS):
return
if global_variables is None:
global_variables = globals()
if local_variables is None:
local_variables = locals()
annotations = getattr(obj, ATTRIBUTE_ANNOTATIONS)
assert isinstance(annotations, dict)
update_annotations: Dict[str, Any] = dict()
for key, annotation in annotations.items():
if isinstance(annotation, str):
type_origin = eval(annotation, global_variables, local_variables)
if type_origin is not None:
eval_annotations(type_origin, global_variables, local_variables)
update_annotations[key] = type_origin
else:
update_annotations[key] = annotation
else:
update_annotations[key] = annotation
setattr(obj, ATTRIBUTE_ANNOTATIONS, update_annotations)
# It can be a wrapped object.
# Where to use: `inspect.signature` -> ... -> `inspect.unwrap`
if hasattr(obj, ATTRIBUTE_WRAPPED):
eval_annotations(
getattr(obj, ATTRIBUTE_WRAPPED),
global_variables,
local_variables,
)