Skip to content

lythonic.compose

Build typed callable compositions from annotated functions.

Compose: Build typed callable compositions from annotated functions.

This module provides introspection and composition primitives for building higher-level structures (CLIs, pipelines, workflows) from type-annotated callables.

Core Concepts

  • Method: Wrapper around a callable with signature introspection
  • MethodDict: Dictionary of methods indexed by name
  • ParamInfo: Pydantic model describing a single function parameter.
  • MethodInterface: Pydantic model describing a callable's full signature (parameters, return type, docstring). Serializable and comparable.

CLI Support

For building command-line interfaces, see lythonic.compose.cli:

from lythonic.compose.cli import ActionTree, Main, RunContext

Method

Wrapper around a callable that provides introspection of its arguments.

Eagerly builds a MethodInterface from the callable's signature. gref is optional: closures and lambdas that cannot be resolved to a global reference will have gref=None.

Source code in src/lythonic/compose/__init__.py
class Method:
    """
    Wrapper around a callable that provides introspection of its arguments.

    Eagerly builds a `MethodInterface` from the callable's signature.
    `gref` is optional: closures and lambdas that cannot be resolved to a
    global reference will have `gref=None`.
    """

    interface: MethodInterface
    name: str
    gref: GlobalRef | None
    _callable: Callable[..., Any] | None

    def __init__(self, o: Callable[..., Any] | GlobalRef, *, name: str | None = None):
        if isinstance(o, GlobalRef):
            self.gref = o
            self._callable = None
            self.name = name or o.name
        else:
            # Closures / nested functions have '<locals>' in __qualname__ and
            # cannot be resolved by GlobalRef, so skip even trying.
            qualname = getattr(o, "__qualname__", "")
            if "<locals>" in qualname:
                self.gref = None
            else:
                try:
                    self.gref = GlobalRef(o)
                except (ValueError, TypeError):
                    self.gref = None
            self._callable = o
            self.name = name or getattr(o, "__name__", str(o))
        self.interface = MethodInterface.from_callable(self.o)

    @property
    def o(self) -> Callable[..., Any]:
        if self._callable is None:
            assert self.gref is not None
            self._callable = self.gref.get_instance()
        assert self._callable is not None
        return self._callable

    @property
    def args(self) -> list[ParamInfo]:
        return self.interface.params

    @property
    def args_by_name(self) -> dict[str, ParamInfo]:
        return {p.name: p for p in self.interface.params}

    @property
    def return_annotation(self) -> Any | None:
        return self.interface.return_annotation

    @property
    def doc(self) -> str | None:
        return self.interface.doc

    def __call__(self, *args: Any, **kwargs: Any) -> Any:
        return self.o(*args, **kwargs)

    def validate_simple_type_args(self) -> None:
        """
        Validate that all parameters have type annotations whose `KnownType`
        has `simple_type=True`. Raises `ValueError` if any parameter fails.
        """
        self.interface.validate_simple_type_args(self.name)

validate_simple_type_args()

Validate that all parameters have type annotations whose KnownType has simple_type=True. Raises ValueError if any parameter fails.

Source code in src/lythonic/compose/__init__.py
def validate_simple_type_args(self) -> None:
    """
    Validate that all parameters have type annotations whose `KnownType`
    has `simple_type=True`. Raises `ValueError` if any parameter fails.
    """
    self.interface.validate_simple_type_args(self.name)

MethodDict

Bases: Generic[T], dict[str, T]

Dictionary mapping lowercased method names to Method instances.

Use add() to register a callable, or wrap() as a decorator.

Source code in src/lythonic/compose/__init__.py
class MethodDict(Generic[T], dict[str, T]):
    """
    Dictionary mapping lowercased method names to Method instances.

    Use `add()` to register a callable, or `wrap()` as a decorator.
    """

    method_type: type[T]

    def __init__(self, method_type: type[T]):
        super().__init__()
        self.method_type = method_type

    def add(self, o: Callable[..., Any]) -> T:
        m = self.method_type(o)
        self[m.name.lower()] = m
        return m

    def wrap(self, o: Callable[..., Any]) -> T:
        return self.add(o)