Description
Bug report
Bug description:
Type: Behavior
Component: inspect
Versions: Python 3.13.2t (Python 3.13.2 experimental free-threading build (main, Mar 4 2025, 00:41:57) [GCC 13.3.0])
Description:
The inspect.Signature.bind_partial()
method exhibits inconsistent behavior when binding arguments to a signature that includes required positional arguments.
Specifically, for a function like def func(pos_arg_required, *, kw_only_arg="default"):
, the following occurs:
sig.bind_partial()
(called with no arguments): Incorrectly succeeds and returns an emptyBoundArguments.arguments
dictionary. In some observed cases, it raisesTypeError: too many positional arguments
, which is also incorrect. The expected behavior is aTypeError
for the missingpos_arg_required
.sig.bind_partial(*tuple(), **dict())
(called with unpacked empty tuple and dict): Incorrectly succeeds and returns an emptyBoundArguments.arguments
dictionary. Expected behavior is aTypeError
for the missingpos_arg_required
.sig.bind()
(called with no arguments): Correctly fails withTypeError: missing a required argument: 'pos_arg_required'
.sig.bind(*tuple(), **dict())
(called with unpacked empty tuple and dict): Correctly fails withTypeError: missing a required argument: 'pos_arg_required'
.
This inconsistent behavior of bind_partial()
depending on the call style (no arguments vs. unpacked empty iterables) for the same signature and effective lack of arguments is unexpected and problematic for code relying on bind_partial
to validate partial argument sets.
Steps to Reproduce:
import inspect
def func_to_test(pos_arg_required, *, kw_only_arg="default_kw_val"):
pass
sig = inspect.signature(func_to_test)
print(f"Signature: {sig}")
print("\nTesting sig.bind_partial():")
try:
ba1 = sig.bind_partial()
print(f" SUCCEEDED: {ba1.arguments}") # Problem: This succeeds in the reported environment
except Exception as e:
print(f" FAILED: {type(e).__name__}: {e}")
print("\nTesting sig.bind_partial(*tuple(), **dict()):")
empty_tuple = tuple()
empty_dict = dict()
try:
ba2 = sig.bind_partial(*empty_tuple, **empty_dict)
print(f" SUCCEEDED: {ba2.arguments}") # Problem: This succeeds in the reported environment
except Exception as e:
print(f" FAILED: {type(e).__name__}: {e}")
Expected Result (based on standard Python behavior):
Signature: (pos_arg_required, *, kw_only_arg='default_kw_val')
Testing sig.bind_partial():
FAILED: TypeError: missing a required argument: 'pos_arg_required'
Testing sig.bind_partial(*tuple(), **dict()):
FAILED: TypeError: missing a required argument: 'pos_arg_required'
Actual Result (in the user's environment Python 3.13.2t with PYTHON_GIL=0):
Signature: (pos_arg_required, *, kw_only_arg='default_kw_val')
Testing sig.bind_partial():
SUCCEEDED: {} // Or sometimes TypeError: too many positional arguments
Testing sig.bind_partial(*tuple(), **dict()):
SUCCEEDED: {}
Impact:
This makes it difficult to reliably use bind_partial
to check if a subset of arguments can be bound without providing all required arguments, as its failure mode for missing required positional arguments is inconsistent when called with no arguments vs. unpacked empty iterables. Code that relies on the expected TypeError
from bind_partial
may behave incorrectly.
CPython versions tested on:
3.13
Operating systems tested on:
Linux