main
 1from __future__ import annotations
 2
 3import inspect
 4from typing import Any, Callable
 5
 6
 7def function_has_argument(func: Callable[..., Any], arg_name: str) -> bool:
 8    """Returns whether or not the given function has a specific parameter"""
 9    sig = inspect.signature(func)
10    return arg_name in sig.parameters
11
12
13def assert_signatures_in_sync(
14    source_func: Callable[..., Any],
15    check_func: Callable[..., Any],
16    *,
17    exclude_params: set[str] = set(),
18    description: str = "",
19) -> None:
20    """Ensure that the signature of the second function matches the first."""
21
22    check_sig = inspect.signature(check_func)
23    source_sig = inspect.signature(source_func)
24
25    errors: list[str] = []
26
27    for name, source_param in source_sig.parameters.items():
28        if name in exclude_params:
29            continue
30
31        custom_param = check_sig.parameters.get(name)
32        if not custom_param:
33            errors.append(f"the `{name}` param is missing")
34            continue
35
36        if custom_param.annotation != source_param.annotation:
37            errors.append(
38                f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(custom_param.annotation)}"
39            )
40            continue
41
42    if errors:
43        raise AssertionError(
44            f"{len(errors)} errors encountered when comparing signatures{description}:\n\n" + "\n\n".join(errors)
45        )