Source code for typespecs.typing
__all__ = [
"HasAnnotations",
"get_annotation",
"get_annotations",
"get_metadata",
"get_subannotations",
"has_metadata",
"is_literal",
]
# standard library
from typing import Annotated, Any, Literal, Protocol
from typing import _strip_annotations # type: ignore
# dependencies
from typing_extensions import get_args, get_origin
from typing_extensions import get_annotations as _get_annotations
# type hints
[docs]
class HasAnnotations(Protocol):
"""Type hint for any object with annotations."""
__annotations__: dict[str, Any]
[docs]
def get_annotation(obj: Any, /, *, recursive: bool = False) -> Any:
"""Return metadata-stripped annotation of given object.
Args:
obj: Object to inspect.
recursive: Whether to recursively strip all metadata.
Returns:
Metadata-stripped annotation of the object.
"""
if recursive:
return _strip_annotations(obj) # type: ignore
else:
return get_args(obj)[0] if has_metadata(obj) else obj
[docs]
def get_annotations(obj: Any, /) -> dict[str, Any]:
"""Return all annotations of given object.
Prior to Python 3.14, this is identical to
``typing_extensions.get_annotations``.
For Python 3.14 and later, it falls back to
the object's class if ``__annotations__`` is missing.
Args:
obj: Object to inspect.
Returns:
Dictionary of all annotations of the object.
"""
if hasattr(obj, "__annotations__"):
return _get_annotations(obj)
else:
return _get_annotations(type(obj))
[docs]
def get_subannotations(obj: Any, /) -> list[Any]:
"""Return all sub-annotations of given object.
Args:
obj: Object to inspect.
Returns:
List of all sub-annotations of the object.
"""
if is_literal(annotation := get_annotation(obj)):
return []
else:
return list(get_args(annotation))
[docs]
def is_literal(obj: Any, /) -> bool:
"""Check if given object is a literal type.
Args:
obj: Object to inspect.
Returns:
True if the object is a literal type. False otherwise.
"""
return get_origin(obj) is Literal