Source code for scitex_introspect._signature

#!/usr/bin/env python3
# Timestamp: 2025-01-20
# File: /home/ywatanabe/proj/scitex-code/src/scitex/introspect/_signature.py

"""Signature extraction utilities."""

from __future__ import annotations

import inspect
from typing import Any

from ._resolve import get_type_info, resolve_object


def _format_annotation(annotation: Any) -> str:
    """Format a type annotation as a string."""
    if annotation is None:
        return "None"
    if hasattr(annotation, "__name__"):
        return annotation.__name__
    return str(annotation).replace("typing.", "")


def _build_signature_string(
    obj: Any,
    parameters: list[dict],
    return_annotation: str | None,
) -> str:
    """Build a human-readable signature string."""
    name = getattr(obj, "__name__", "?")

    param_strs = []
    for p in parameters:
        s = p["name"]
        if "annotation" in p:
            s += f": {p['annotation']}"
        if "default" in p:
            s += f" = {p['default']}"
        param_strs.append(s)

    sig = f"{name}({', '.join(param_strs)})"
    if return_annotation:
        sig += f" -> {return_annotation}"

    return sig


[docs] def q( dotted_path: str, include_defaults: bool = True, include_annotations: bool = True, ) -> dict: """ Get the signature of a function, method, or class. Like IPython's `func?` (quick info). Parameters ---------- dotted_path : str Dotted path to the callable (e.g., 'scitex.plt.plot') include_defaults : bool Include default values in signature include_annotations : bool Include type annotations Returns ------- dict name: str signature: str - Human-readable signature parameters: list[dict] - Detailed parameter info return_annotation: str | None type_info: dict Examples -------- >>> q("scitex.plt.plot") {'name': 'plot', 'signature': 'plot(spec: dict, ...) -> dict', ...} """ obj, error = resolve_object(dotted_path) if error: return {"success": False, "error": error} type_info = get_type_info(obj) callable_obj = obj if inspect.isclass(obj): callable_obj = obj.__init__ try: sig = inspect.signature(callable_obj) except (ValueError, TypeError) as e: return { "success": False, "error": f"Cannot get signature: {e}", "type_info": type_info, } parameters = [] for name, param in sig.parameters.items(): if name == "self": continue param_info = { "name": name, "kind": str(param.kind).split(".")[-1], } if include_annotations and param.annotation != inspect.Parameter.empty: param_info["annotation"] = _format_annotation(param.annotation) if include_defaults and param.default != inspect.Parameter.empty: param_info["default"] = repr(param.default) parameters.append(param_info) return_annotation = None if include_annotations and sig.return_annotation != inspect.Signature.empty: return_annotation = _format_annotation(sig.return_annotation) sig_str = _build_signature_string(obj, parameters, return_annotation) return { "success": True, "name": getattr(obj, "__name__", dotted_path.split(".")[-1]), "signature": sig_str, "parameters": parameters, "return_annotation": return_annotation, "type_info": type_info, }