Skip to content

Python:Eval

Python eval 함수에 대한 설명

Annotation 누실 문제

예컨데 eval 실행 직전과 직후 site-packages 목록이 바뀐다면 eval 타이밍에 Type Annotation 정보가 포함된 __annotations__ 내용이 유실되어, typestr 으로 바뀌어 들어올 수 있다. 이 문제를 해결하기 위해 다음과 같은 함수를 만들어 eval 직후 local_variablesglobal_variables를 갱신한다.

# -*- 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