Source code for turbograph.core.funccall
"""Define and handle function call modes.
A module that defines call modes and utility functions for invoking
functions with different input-passing strategies.
Call modes determine how input values are passed to a function:
- ``"args"``: Positional arguments (e.g., ``func(a, b, c)``).
- ``"kwargs"``: Keyword arguments (e.g., ``func(a=a, b=b, c=c)``).
- ``"arg"``: A single dictionary argument (e.g., ``func({"a": a, "b": b, "c": c})``).
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Literal, TypeVar
if TYPE_CHECKING:
from collections.abc import Callable
CallMode = Literal["args", "kwargs", "arg"]
"""Enumeration of valid call modes for function execution."""
CALL_MODES: set[CallMode] = {"args", "kwargs", "arg"}
"""Tuple of valid function call modes."""
DEFAULT_CALL_MODE: Literal["args"] = "args"
"""The default function call mode."""
Out = TypeVar("Out")
"""Generic output type for function return values."""
[docs]
class CallModeError(Exception):
"""An error raised when an invalid call mode is provided."""
def __init__(self, call_mode: object) -> None:
"""Initialize the error with the invalid call mode.
Args:
call_mode: The invalid call mode provided.
"""
super().__init__(
f"Invalid call mode: {call_mode!r}. Valid call modes are: "
+ ", ".join(map(repr, CALL_MODES))
)
[docs]
def call_func(
func: Callable[..., Out],
inputs: dict[Any, Any],
call_mode: CallMode = DEFAULT_CALL_MODE,
) -> Out:
r"""Invoke a function with dynamically passed inputs based on a call mode.
Args:
func: The function to invoke.
inputs: A dictionary mapping input names to their values.
call_mode: The input passing strategy.
- `"args"` (default): Pass input values as positional arguments.
- `"kwargs"`: Pass input values as keyword arguments.
- `"arg"`: Pass all inputs as a single dictionary argument.
Returns:
The output of the function.
Raises:
ValueError: If an invalid call mode is provided.
Example:
Using different call modes with the same function:
>>> def example_func(a, b, c):
... return a + b + c
>>> inputs = {"a": 1, "b": 2, "c": 3}
Call with positional arguments:
>>> call_func(example_func, inputs, call_mode="args")
6
Call with keyword arguments:
>>> call_func(example_func, inputs, call_mode="kwargs")
6
Call with a single dictionary argument:
>>> def dict_func(data):
... return sum(data.values())
>>> call_func(dict_func, inputs, call_mode="arg")
6
"""
if call_mode == "args":
return func(*inputs.values())
elif call_mode == "kwargs":
return func(**inputs)
elif call_mode == "arg":
return func(inputs)
else:
raise CallModeError(call_mode)