from __future__ import annotations import inspect from typing import Any, Callable def function_has_argument(func: Callable[..., Any], arg_name: str) -> bool: """Returns whether or not the given function has a specific parameter""" sig = inspect.signature(func) return arg_name in sig.parameters def assert_signatures_in_sync( source_func: Callable[..., Any], check_func: Callable[..., Any], *, exclude_params: set[str] = set(), ) -> None: """Ensure that the signature of the second function matches the first.""" check_sig = inspect.signature(check_func) source_sig = inspect.signature(source_func) errors: list[str] = [] for name, source_param in source_sig.parameters.items(): if name in exclude_params: continue custom_param = check_sig.parameters.get(name) if not custom_param: errors.append(f"the `{name}` param is missing") continue if custom_param.annotation != source_param.annotation: errors.append( f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(custom_param.annotation)}" ) continue if errors: raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors))