How to Check If a Function Accepts Certain Arguments in Python
In dynamic Python applications (such as plugin systems, decorators, or frameworks) you often need to determine programmatically whether a specific function accepts certain arguments before calling it. Calling a function with unexpected arguments results in a TypeError.
This guide explores how to inspect function signatures using the standard inspect module to validate parameters, check for default values, and safely handle argument passing.
Understanding Function Signatures
A function signature defines the inputs (parameters) and output (return annotation) of a function. Python's inspect module allows you to access this metadata at runtime.
A signature object contains a collection of Parameters, which hold vital details:
- Name: The name of the argument (e.g.,
x,user_id). - Default: The default value if one exists (or
inspect._empty). - Kind: How the argument can be passed (Positional, Keyword,
*args,**kwargs). - Annotation: Type hints (e.g.,
int,str).
Method 1: Using inspect.signature (Recommended)
The most robust way to analyze arguments is by retrieving the Signature object. This allows you to view all expected parameters.
import inspect
def calculate_total(price: float, tax: float = 0.05) -> float:
return price * (1 + tax)
# ✅ Get the signature object
sig = inspect.signature(calculate_total)
print(f"Function Signature: {sig}")
# Iterate through parameters to see details
for name, param in sig.parameters.items():
print(f"Name: {name}, Default: {param.default}, Annotation: {param.annotation}")
Output:
Function Signature: (price: float, tax: float = 0.05) -> float
Name: price, Default: <class 'inspect._empty'>, Annotation: <class 'float'>
Name: tax, Default: 0.05, Annotation: <class 'float'>
inspect.signature() works on functions, methods, classes, and callables. It handles *args and **kwargs correctly, identifying them by their param.kind.
Method 2: Checking for Specific Parameter Names
If you simply need to know, "Does this function accept an argument named user_id?", you can check for membership in the parameters dictionary.
Checking for Exact Arguments
import inspect
def send_email(email, subject, body):
pass
def send_sms(phone, message):
pass
def has_argument(func, arg_name):
sig = inspect.signature(func)
return arg_name in sig.parameters
# ✅ Check if functions accept 'email'
print(f"send_email accepts 'email'? {has_argument(send_email, 'email')}")
print(f"send_sms accepts 'email'? {has_argument(send_sms, 'email')}")
Output:
send_email accepts 'email'? True
send_sms accepts 'email'? False
Handling **kwargs
A function might not explicitly name an argument but still accept it via **kwargs (variable keyword arguments). You should check for VAR_KEYWORD if you want to be strictly accurate about what can be passed.
import inspect
def dynamic_func(a, **kwargs):
pass
sig = inspect.signature(dynamic_func)
params = sig.parameters
arg_name = "random_arg"
# Check if explicit OR if **kwargs allows it
accepts_arg = (arg_name in params) or \
any(p.kind == p.VAR_KEYWORD for p in params.values())
print(f"dynamic_func accepts '{arg_name}'? {accepts_arg}")
Output:
dynamic_func accepts 'random_arg'? True
Method 3: Runtime Checking with try-except
Sometimes, introspection is unnecessary overhead. The Pythonic "Easier to Ask for Forgiveness than Permission" (EAFP) approach involves trying to call the function and catching the TypeError if the arguments don't match.
This is useful when you are ready to execute the function immediately.
def add_numbers(x, y):
return x + y
# ⛔️ Error: Passing an unexpected argument 'z'
try:
add_numbers(x=5, y=10, z=15)
except TypeError as e:
print(f"Call failed: {e}")
# ✅ Solution: Catch the error to handle invalid signatures gracefully
try:
# Attempt call with valid args
result = add_numbers(5, 10)
print(f"Result: {result}")
except TypeError as e:
print(f"Error: {e}")
Output:
Call failed: add_numbers() got an unexpected keyword argument 'z'
Result: 15
Be careful with TypeError. A TypeError can be raised for reasons other than argument mismatch (e.g., adding a string to an integer inside the function). Ensure you are catching the specific error you expect or validating inputs beforehand.
Conclusion
To check if a function accepts certain arguments:
- Use
inspect.signature(func)for static analysis or validation logic. It is the most accurate and safe method. - Check
sig.parameterskeys to see if specific argument names exist. - Check for
VAR_KEYWORD(**kwargs) if you need to support dynamic arguments. - Use
try-except TypeErrorif you simply want to call the function and handle mismatches at runtime.