Skip to content

@requireModules

Gracefully handle optional dependencies with automatic fallback behavior.


xpytools.xdeco.requireModules.requireModules

Python
requireModules(modules: Iterable[str], *, exc_raise: bool = False, return_none: bool = True) -> Callable[[T], T]

Decorator to ensure one or more modules are available before running a function.

Parameters:

Name Type Description Default

modules

Iterable[str]

List of module names to xcheck (e.g., ["pandas", "numpy"]).

required

exc_raise

bool

Raise ImportError if one or more modules are missing.

False

return_none

bool

If True and missing modules exist, return None instead of running the function.

True

Returns:

Type Description
Decorated function that gracefully bypasses or raises on missing dependencies.

Examples:

Python Console Session
>>> @requireModules(["pandas"])
... def df_summary(df):
...     import pandas as pd
...     return df.describe()
...
>>> df_summary(None)  # if pandas missing -> returns None, no crash
Source code in xpytools/xdeco/requireModules.py
Python
def requireModules(
        modules: Iterable[str],
        *,
        exc_raise: bool = False,
        return_none: bool = True,
        ) -> Callable[[T], T]:
    """
    Decorator to ensure one or more modules are available before running a function.

    Parameters
    ----------
    modules : Iterable[str]
        List of module names to xcheck (e.g., ["pandas", "numpy"]).
    exc_raise : bool, default=False
        Raise ImportError if one or more modules are missing.
    return_none : bool, default=True
        If True and missing modules exist, return None instead of running the function.

    Returns
    -------
    Decorated function that gracefully bypasses or raises on missing dependencies.

    Examples
    --------
    >>> @requireModules(["pandas"])
    ... def df_summary(df):
    ...     import pandas as pd
    ...     return df.describe()
    ...
    >>> df_summary(None)  # if pandas missing -> returns None, no crash
    """
    missing: list[str] = [
            mod for mod in modules if find_spec(mod) is None
            ]

    def decorator(func: T) -> T:
        @wraps(func)
        def wrapper(*args, **kwargs):
            if missing:
                msg = (
                        f"Missing required module(s): {', '.join(missing)} "
                        f"for function '{func.__name__}'."
                )
                if exc_raise:
                    raise ImportError(msg)
                if return_none:
                    return None
                # If not returning None, fallback to no-op
                return
            return func(*args, **kwargs)

        return cast(T, wrapper)

    return decorator