v0.150.0
 1from __future__ import annotations
 2
 3import functools
 4import inspect
 5from collections.abc import Callable
 6from typing import Any
 7
 8
 9@functools.lru_cache(maxsize=512)
10def _get_func_parameters(
11    func: Callable[..., Any], remove_first: bool
12) -> tuple[inspect.Parameter, ...]:
13    parameters = tuple(inspect.signature(func).parameters.values())
14    if remove_first:
15        parameters = parameters[1:]
16    return parameters
17
18
19def _get_callable_parameters(
20    meth_or_func: Callable[..., Any],
21) -> tuple[inspect.Parameter, ...]:
22    is_method = inspect.ismethod(meth_or_func)
23    func = meth_or_func.__func__ if is_method else meth_or_func
24    return _get_func_parameters(func, remove_first=is_method)
25
26
27def get_func_args(func: Callable[..., Any]) -> list[str]:
28    params = _get_callable_parameters(func)
29    return [
30        param.name
31        for param in params
32        if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
33    ]
34
35
36def func_accepts_kwargs(func: Callable[..., Any]) -> bool:
37    """Return True if function 'func' accepts keyword arguments **kwargs."""
38    return any(p for p in _get_callable_parameters(func) if p.kind == p.VAR_KEYWORD)
39
40
41def method_has_no_args(meth: Callable[..., Any]) -> bool:
42    """Return True if a method only accepts 'self'."""
43    count = len(
44        [p for p in _get_callable_parameters(meth) if p.kind == p.POSITIONAL_OR_KEYWORD]
45    )
46    return count == 0 if inspect.ismethod(meth) else count == 1