def evaluate_annotation(tp: Any, globals: Dict[(str, Any)], locals: Dict[(str, Any)], cache: Dict[(str, Any)], *, implicit_str: bool=True):
    if isinstance(tp, ForwardRef):
        tp = tp.__forward_arg__
        implicit_str = True
    if (implicit_str and isinstance(tp, str)):
        if (tp in cache):
            return cache[tp]
        evaluated = eval(tp, globals, locals)
        cache[tp] = evaluated
        return evaluate_annotation(evaluated, globals, locals, cache)
    if hasattr(tp, '__args__'):
        implicit_str = True
        is_literal = False
        args = tp.__args__
        if (not hasattr(tp, '__origin__')):
            if (PY_310 and (tp.__class__ is types.UnionType)):
                converted = Union[args]
                return evaluate_annotation(converted, globals, locals, cache)
            return tp
        if (tp.__origin__ is Union):
            try:
                if (args.index(type(None)) != (len(args) - 1)):
                    args = normalise_optional_params(tp.__args__)
            except ValueError:
                pass
        if (tp.__origin__ is Literal):
            if (not PY_310):
                args = flatten_literal_params(tp.__args__)
            implicit_str = False
            is_literal = True
        evaluated_args = tuple((evaluate_annotation(arg, globals, locals, cache, implicit_str=implicit_str) for arg in args))
        if (is_literal and (not all((isinstance(x, (str, int, bool, type(None))) for x in evaluated_args)))):
            raise TypeError('Literal arguments must be of type str, int, bool, or NoneType.')
        if (evaluated_args == args):
            return tp
        try:
            return tp.copy_with(evaluated_args)
        except AttributeError:
            return tp.__origin__[evaluated_args]
    return tp
def evaluate_annotation(tp: Any, globals: Dict[(str, Any)], locals: Dict[(str, Any)], cache: Dict[(str, Any)], *, implicit_str: bool=True):
    if isinstance(tp, ForwardRef):
        tp = tp.__forward_arg__
        implicit_str = True
    if (implicit_str and isinstance(tp, str)):
        if (tp in cache):
            return cache[tp]
        evaluated = eval(tp, globals, locals)
        cache[tp] = evaluated
        return evaluate_annotation(evaluated, globals, locals, cache)
    if hasattr(tp, '__args__'):
        implicit_str = True
        is_literal = False
        args = tp.__args__
        if (not hasattr(tp, '__origin__')):
            if (PY_310 and (tp.__class__ is types.UnionType)):
                converted = Union[args]
                return evaluate_annotation(converted, globals, locals, cache)
            return tp
        if (tp.__origin__ is Union):
            try:
                if (args.index(type(None)) != (len(args) - 1)):
                    args = normalise_optional_params(tp.__args__)
            except ValueError:
                pass
        if (tp.__origin__ is Literal):
            if (not PY_310):
                args = flatten_literal_params(tp.__args__)
            implicit_str = False
            is_literal = True
        evaluated_args = tuple((evaluate_annotation(arg, globals, locals, cache, implicit_str=implicit_str) for arg in args))
        if (is_literal and (not all((isinstance(x, (str, int, bool, type(None))) for x in evaluated_args)))):
            raise TypeError('Literal arguments must be of type str, int, bool, or NoneType.')
        if (evaluated_args == args):
            return tp
        try:
            return tp.copy_with(evaluated_args)
        except AttributeError:
            return tp.__origin__[evaluated_args]
    return tp