Source code for py_research.reflect.runtime
"""Utils for reflecting the Python runtime."""
import inspect
from collections.abc import Callable, Sequence
from types import ModuleType
from typing import Any, TypeVar
T = TypeVar("T")
def _get_calling_frame(offset: int = 0):
stack = inspect.stack()
if len(stack) < offset + 3:
raise RuntimeError("No caller!")
return stack[offset + 2]
[docs]
def get_calling_module(offset: int = 0) -> ModuleType | None:
"""Return the name of the module calling the current function."""
return inspect.getmodule(_get_calling_frame(offset + 1).frame)
[docs]
def get_full_args_dict(
func: Callable, args: Sequence, kwargs: dict[str, Any] | None = None
) -> dict[str, Any]:
"""Return dict of all args + kwargs.
Args:
func: Function to inspect.
args: Positional arguments given to the function.
kwargs: Keyword arguments given to the function.
Returns:
Dictionary of all args + kwargs.
"""
argspec = inspect.getfullargspec(func)
arg_defaults = argspec.defaults or []
kwdefaults = dict(zip(argspec.args[-len(arg_defaults) :], arg_defaults))
posargs = dict(zip(argspec.args[: len(args)], args))
return {**kwdefaults, **posargs, **(kwargs or {})}
[docs]
def get_return_type(func: Callable) -> type | None:
"""Get the return type annotation of given function, if any."""
sig = inspect.signature(func)
return (
sig.return_annotation
if sig.return_annotation != inspect.Signature.empty
else None
)
[docs]
def get_all_subclasses(cls: type[T]) -> set[type[T]]:
"""Return all subclasses of given class."""
return set(cls.__subclasses__()).union(
[s for c in cls.__subclasses__() for s in get_all_subclasses(c)]
)