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