hbutils.reflection.func

Function reflection and transformation utilities.

This module provides a collection of decorators and helper functions for working with Python callables. It focuses on manipulating function attributes, dynamically adapting call signatures, and implementing reusable pre/post-processing logic.

The module contains the following main components:

  • fassign() - Assign arbitrary attributes to a function

  • frename() - Rename a function by updating its __name__

  • fcopy() - Create a wrapped copy of a function

  • args_iter() - Iterate over positional and keyword arguments with indices

  • sigsupply() - Attach a supplemental signature to callables

  • dynamic_call() - Enable flexible argument handling for callables

  • static_call() - Restore the original function from a dynamic wrapper

  • pre_process() - Apply argument pre-processing before function execution

  • post_process() - Apply return-value post-processing

  • raising() - Convert returned exceptions into raised exceptions

  • warning_() - Convert returned warnings into emitted warnings

  • freduce() - Turn a binary operation into a variadic reduction

  • get_callable_hint() - Build a typing.Callable hint from annotations

Example:

>>> @fassign(author='hbutils')
... def add(a, b):
...     return a + b
>>> add.author
'hbutils'
>>>
>>> dynamic_call(lambda x, y: x ** y)(2, 3, 4)
8
>>>
>>> @freduce(init=0)
... def plus(a, b):
...     return a + b
>>> plus(1, 2, 3)
6

Note

Some utilities (e.g., dynamic_call()) require inspectable signatures. For builtin callables without signatures, use sigsupply() to provide one.

__all__

hbutils.reflection.func.__all__ = ['fassign', 'frename', 'fcopy', 'args_iter', 'sigsupply', 'dynamic_call', 'static_call', 'pre_process', 'post_process', 'raising', 'warning_', 'freduce', 'get_callable_hint']

Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

NO_INITIAL

hbutils.reflection.func.NO_INITIAL = <SingletonMark 'no_initial'>

Singleton mark for some situation.

Can be used when some default value is needed, especially when None has meaning which is not default. This class creates unique marker objects that can be used as sentinel values.

Example::
>>> NO_VALUE = SingletonMark("no_value")
>>> NO_VALUE is SingletonMark("no_value")  # True

Note

SingletonMark is a value-based singleton class, can be used to create an unique value, especially in the cases which None is not suitable for the default value.

fassign

hbutils.reflection.func.fassign(**assigns: Any) Callable[[Callable[[...], Any]], Callable[[...], Any]][source]

Do assignments to function attributes.

This decorator allows you to assign arbitrary attributes to a function object. It’s useful for adding metadata or custom properties to functions.

Parameters:

assigns (Any) – Keyword arguments representing attribute names and their values to assign.

Returns:

A decorator function that assigns the specified attributes to the target function.

Return type:

Callable

Examples:

>>> @fassign(__name__='fff')
>>> def func(a, b):
>>>     return a + b
>>> func.__name__
'fff'

frename

hbutils.reflection.func.frename(new_name: str) Callable[[Callable[[...], Any]], Callable[[...], Any]][source]

Rename the given function.

This decorator changes the __name__ attribute of a function to the specified new name.

Parameters:

new_name (str) – New name of function.

Returns:

Decorator to rename the function.

Return type:

Callable

Examples:

>>> @frename('fff')
>>> def func(a, b):
>>>     return a + b
>>> func.__name__
'fff'

fcopy

hbutils.reflection.func.fcopy(func: _FuncType) _FuncType[source]

Make a copy of given function.

Creates a new function that wraps the original function, effectively creating a copy with the same behavior but a different identity. The wrapper preserves the original function’s metadata using functools.wraps.

Parameters:

func (Callable) – Function to be copied.

Returns:

Copied function.

Return type:

Callable

Examples:

>>> def func(a, b):
...     return a + b
>>> nfunc = fcopy(func)
>>> nfunc(1, 2)
3
>>> nfunc is func
False

args_iter

hbutils.reflection.func.args_iter(*args: Any, **kwargs: Any) Generator[Tuple[int | str, Any], None, None][source]

Iterate all the arguments with index and value.

This generator function yields (index, value) pairs for all arguments. For positional arguments, indices are integers starting from 0. For keyword arguments, indices are strings representing the argument names. Keyword arguments are yielded in sorted order by key.

Parameters:
  • args (Tuple[Any]) – Positional arguments to iterate over.

  • kwargs (Dict[str, Any]) – Keyword arguments to iterate over.

Yield:

Tuples of (index, value) where index is int for positional args and str for keyword args.

Return type:

Generator[Tuple[Union[int, str], Any], None, None]

Examples:

>>> for index, value in args_iter(1, 2, 3, a=1, b=2, c=3):
...     print(index, value)
0 1
1 2
2 3
a 1
b 2
c 3

sigsupply

hbutils.reflection.func.sigsupply(func: Callable[[...], Any], sfunc: Callable[[...], Any]) Callable[[...], Any][source]

Supply a signature for builtin functions or methods.

This function provides a workaround for builtin functions that don’t have inspectable signatures. It attaches a supplemental function’s signature to the builtin function, allowing it to be processed by dynamic_call() and other signature-dependent operations.

Parameters:
  • func (Callable) – Original function, can be a native function or builtin function.

  • sfunc (Callable) – Supplemental function with a valid signature. Its implementation doesn’t matter, only its signature is used.

Returns:

The original function if it already has a signature, or a wrapped version with the supplemental signature attached.

Return type:

Callable

Examples:

>>> dynamic_call(max)([1, 2, 3])  # no sigsupply
ValueError: no signature found for builtin <built-in function max>
>>> dynamic_call(sigsupply(max, lambda x: None))([1, 2, 3])  # use it as func(x) when builtin
3

dynamic_call

hbutils.reflection.func.dynamic_call(func: Callable[[...], Any]) Callable[[...], Any][source]

Decorate function to support dynamic calling with flexible arguments.

This decorator makes a function accept any number of arguments, automatically filtering them based on the function’s signature. Extra positional arguments are ignored unless the function has *args, and extra keyword arguments are ignored unless the function has **kwargs.

Parameters:

func (Callable) – Original function to be decorated.

Returns:

Decorated function that supports dynamic calling.

Return type:

Callable

Examples:

>>> dynamic_call(lambda x, y: x ** y)(2, 3)  # 8
8
>>> dynamic_call(lambda x, y: x ** y)(2, 3, 4)  # 8, 3rd is ignored
8
>>> dynamic_call(lambda x, y, t, *args: (args, (t, x, y)))(1, 2, 3, 4, 5)  # ((4, 5), (3, 1, 2))
((4, 5), (3, 1, 2))
>>> dynamic_call(lambda x, y: (x, y))(y=2, x=1)  # (1, 2), keyword supported
(1, 2)
>>> dynamic_call(lambda x, y, **kwargs: (kwargs, x, y))(1, k=2, y=3)  # ({'k': 2}, 1, 3)
({'k': 2}, 1, 3)

Note

Simple dynamic_call() cannot support builtin functions because they do not have python signatures. If you need to deal with builtin functions, you can use sigsupply() to add a signature onto the function when necessary.

static_call

hbutils.reflection.func.static_call(func: Callable[[...], Any], static_ok: bool = True) Callable[[...], Any][source]

Convert a dynamic-call function back to its original static form.

This function unwraps a function that has been decorated with dynamic_call(), returning the original function. It’s the inverse operation of dynamic_call().

Parameters:
  • func (Callable) – Given dynamic function to convert.

  • static_ok (bool) – Allow given function to be already static, default is True.

Returns:

Original static function.

Return type:

Callable

Raises:

TypeError – If static_ok is False and the function is already static.

pre_process

hbutils.reflection.func.pre_process(processor: Callable[[...], Any]) Callable[[Callable[[...], Any]], Callable[[...], Any]][source]

Create a decorator that pre-processes function arguments.

This decorator applies a processor function to the arguments before passing them to the original function. The processor can transform both positional and keyword arguments.

Parameters:

processor (Callable) – Pre-processor function that transforms arguments.

Returns:

Function decorator that applies pre-processing.

Return type:

Callable

Examples:

>>> @pre_process(lambda x, y: (-x, (x + 2) * y))
>>> def plus(a, b):
>>>     return a + b
>>>
>>> plus(1, 2)  # 5, 5 = -1 + (1 + 2) * 2
5

Note

The processor can return: - A tuple of (args_list, kwargs_dict) for both positional and keyword arguments - A tuple/list for positional arguments only - A dict for keyword arguments only - A single value which will be passed as the first positional argument

post_process

hbutils.reflection.func.post_process(processor: Callable[[...], Any]) Callable[[Callable[[...], Any]], Callable[[...], Any]][source]

Create a decorator that post-processes function return values.

This decorator applies a processor function to the return value of the original function before returning it to the caller.

Parameters:

processor (Callable) – Post-processor function that transforms the return value.

Returns:

Function decorator that applies post-processing.

Return type:

Callable

Examples:

>>> @post_process(lambda x: -x)
>>> def plus(a, b):
>>>     return a + b
>>>
>>> plus(1, 2)  # -3
-3

raising

hbutils.reflection.func.raising(func: Callable[[...], Any] | BaseException | Type[BaseException]) Callable[[...], Any][source]

Decorate function to raise exceptions instead of returning them.

This decorator transforms functions that return exception objects into functions that raise those exceptions. It can also be used directly with exception classes or instances to create raising callables.

Parameters:

func (Union[Callable, BaseException, Type[BaseException]]) – Function that returns exceptions, or an exception class/instance.

Returns:

Decorated function that raises exceptions.

Return type:

Callable

Examples:

>>> raising(RuntimeError)()  # Raises RuntimeError
RuntimeError
>>> raising(lambda x: ValueError('value error - %s' % (repr(x), )))(1)  # Raises ValueError
ValueError: value error - 1

warning_

hbutils.reflection.func.warning_(func: Callable[[...], Any] | Warning | Type[Warning] | str) Callable[[...], Any][source]

Decorate function to issue warnings instead of returning them.

This decorator transforms functions that return warning objects into functions that issue those warnings using the warnings module. It can also be used directly with warning classes, instances, or strings to create warning callables.

Parameters:

func (Union[Callable, Warning, Type[Warning], str]) – Function that returns warnings, or a warning class/instance/string.

Returns:

Decorated function that issues warnings.

Return type:

Callable

Examples:

>>> warning_(RuntimeWarning)()  # Issues RuntimeWarning
>>> warning_(lambda x: Warning('value warning - %s' % (repr(x), )))(1)  # Issues Warning

freduce

hbutils.reflection.func.freduce(init: ~typing.Any = <SingletonMark 'no_initial'>, pass_kwargs: bool = True) Callable[[Callable[[...], _ElementType]], Callable[[...], _ElementType]][source]

Make a binary function reducible over multiple arguments.

This decorator transforms a binary function into a variadic function that applies the binary operation repeatedly (reduction). Similar to functools.reduce but as a decorator with more flexibility.

Parameters:
  • init (Any) – Initial value or generator function. If NO_INITIAL, the first argument is used as the initial value. Can be a value or a callable that returns a value.

  • pass_kwargs (bool) – Whether to pass keyword arguments to the initial function and wrapped function.

Returns:

Decorator for the original binary function.

Return type:

Callable

Raises:

SyntaxError – If no initial value is provided and no arguments are passed to the function.

Examples:

>>> @freduce(init=0)
>>> def plus(a, b):
>>>     return a + b
>>>
>>> plus()            # 0
0
>>> plus(1)           # 1
1
>>> plus(1, 2)        # 3
3
>>> plus(1, 2, 3, 4)  # 10
10

get_callable_hint

hbutils.reflection.func.get_callable_hint(f: Callable[[...], Any]) Any[source]

Get the type hint of a callable as a Callable type annotation.

This function extracts type hints from a callable and returns a Callable type annotation that represents the function’s signature. If the function has only positional parameters, it returns a specific Callable type; otherwise, it returns Callable[…, Any].

Parameters:

f (Callable) – Callable object to extract type hints from.

Returns:

Type hint representing the callable’s signature.

Return type:

type

Examples:

>>> def f1(x: float, y: str) -> int:
...     pass
>>> get_callable_hint(f1)  # Callable[[float, str], int]
typing.Callable[[float, str], int]
>>>
>>> def f2(x: float, y: str, *, z: int):
...     pass
>>> get_callable_hint(f2)  # Callable[..., Any]
typing.Callable[..., typing.Any]