def type_str(typ: Union[type, ForwardRef, TypeVar]) -> str:
    """Render the type/type constructor with all referenced types using fully
    qualified names.

    Can also handle ForwardRefs as long as they have been evaluated already,
    ellipses, and TypeVars.
    """

    if isinstance(typ, ForwardRef):
        if not typ.__forward_evaluated__:
            raise ValueError(
                f"Not evaluated ForwardRefs are not supported: {typ}."
            )

        return type_str(typ.__forward_value__)

    if isinstance(typ, TypeVar):
        return typ.__name__

    if typ is ...:
        return "..."

    parts = []

    if hasattr(typ, "__module__"):
        parts.append(typ.__module__)

    if hasattr(typ, "__qualname__"):
        parts.append(typ.__qualname__)
    else:
        if hasattr(typ, "__name__"):
            parts.append(typ.__name__)
        else:
            parts.append(str(typ))

    ret = ".".join(parts)

    if hasattr(typ, "__args__"):
        ret += "[" + ", ".join(type_str(arg) for arg in get_args(typ)) + "]"

    return ret
Render the type/type constructor with all referenced types using fully
    qualified names.

    Can also handle ForwardRefs as long as they have been evaluated already,
    ellipses, and TypeVars.
    
tests/utils.py