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)] )