1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
from __future__ import annotations
from inspect import isclass
from typing import Any, ForwardRef, Literal, NewType, Optional, TypeVar, get_args
from typing_extensions import (
Annotated,
NotRequired,
ParamSpec,
Required,
TypeAliasType,
TypeGuard,
get_origin,
)
try:
from typing import TypeAliasType as typing_TypeAliasType
AllTypeAliasTypes: tuple[type, ...] = (TypeAliasType, typing_TypeAliasType)
except ImportError:
AllTypeAliasTypes = (TypeAliasType,)
from polyfactory.constants import TYPE_MAPPING
from polyfactory.utils.types import UNION_TYPES, NoneType
P = ParamSpec("P")
T = TypeVar("T")
def is_safe_subclass(annotation: Any, class_or_tuple: type[T] | tuple[type[T], ...]) -> "TypeGuard[type[T]]":
"""Determine whether a given annotation is a subclass of a give type
:param annotation: A type annotation.
:param class_or_tuple: A potential super class or classes.
:returns: A typeguard
"""
origin = get_type_origin(annotation)
if not origin and not isclass(annotation):
return False
try:
return issubclass(origin or annotation, class_or_tuple)
except TypeError: # pragma: no cover
return False
def is_any(annotation: Any) -> "TypeGuard[Any]":
"""Determine whether a given annotation is 'typing.Any'.
:param annotation: A type annotation.
:returns: A typeguard.
"""
return (
annotation is Any
or getattr(annotation, "_name", "") == "typing.Any"
or (get_origin(annotation) in UNION_TYPES and Any in get_args(annotation))
)
def is_dict_key_or_value_type(annotation: Any) -> "TypeGuard[Any]":
"""Determine whether a given annotation is a valid dict key or value type:
``typing.KT`` or ``typing.VT``.
:returns: A typeguard.
"""
return str(annotation) in {"~KT", "~VT"}
def is_union(annotation: Any) -> "TypeGuard[Any]":
"""Determine whether a given annotation is 'typing.Union'.
:param annotation: A type annotation.
:returns: A typeguard.
"""
return get_type_origin(annotation) in UNION_TYPES
def is_optional(annotation: Any) -> "TypeGuard[Any | None]":
"""Determine whether a given annotation is 'typing.Optional'.
:param annotation: A type annotation.
:returns: A typeguard.
"""
origin = get_type_origin(annotation)
return origin is Optional or (get_origin(annotation) in UNION_TYPES and NoneType in get_args(annotation))
def is_literal(annotation: Any) -> bool:
"""Determine whether a given annotation is 'typing.Literal'.
:param annotation: A type annotation.
:returns: A boolean.
"""
return (
get_type_origin(annotation) is Literal
or repr(annotation).startswith("typing.Literal")
or repr(annotation).startswith("typing_extensions.Literal")
)
def is_new_type(annotation: Any) -> "TypeGuard[type[NewType]]":
"""Determine whether a given annotation is 'typing.NewType'.
:param annotation: A type annotation.
:returns: A typeguard.
"""
return hasattr(annotation, "__supertype__")
def is_annotated(annotation: Any) -> bool:
"""Determine whether a given annotation is 'typing.Annotated'.
:param annotation: A type annotation.
:returns: A boolean.
"""
return get_origin(annotation) is Annotated
def is_any_annotated(annotation: Any) -> bool:
"""Determine whether any of the types in the given annotation is
`typing.Annotated`.
:param annotation: A type annotation.
:returns: A boolean
"""
return any(
is_annotated(arg) or (hasattr(arg, "__args__") and is_any_annotated(arg)) for arg in get_args(annotation)
)
def is_type_alias(annotation: Any) -> TypeGuard[TypeAliasType]:
"""Determine if the given type annotation is a PEP 695 type alias.
:param annotation: A type annotation.
:returns: A boolean
"""
return isinstance(annotation, AllTypeAliasTypes)
def is_generic_alias(annotation: Any) -> bool:
"""Determine if the given type annotation is a generic alias.
:param annotation: A type annotation.
:returns: A boolean
"""
return hasattr(annotation, "__origin__") and hasattr(annotation, "__args__")
def is_type_var(annotation: Any) -> TypeGuard[TypeVar]:
"""Determine if the given type annotation is a TypeVar.
Args:
annotation: A type annotation.
Returns:
A boolean.
"""
return isinstance(annotation, TypeVar)
def get_type_origin(annotation: Any) -> Any:
"""Get the type origin of an annotation - safely.
:param annotation: A type annotation.
:returns: A type annotation.
"""
origin = get_origin(annotation)
if origin in (Annotated, Required, NotRequired):
origin = get_args(annotation)[0]
return mapped_type if (mapped_type := TYPE_MAPPING.get(origin)) else origin
def is_forward_ref(annotation: Any) -> TypeGuard[ForwardRef]:
"""Determine if the given type annotation is a ForwardRef.
:param annotation: A type annotation.
:returns: A boolean.
"""
return isinstance(annotation, ForwardRef)
|