Skip to content
Search

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,
        )

See also

author Zer0 sitemap