Skip to content

inspect.Signature.bind_partial() incorrectly succeeds for missing required positional arguments when called with no arguments vs. unpacked empty iterables on Python 3.13.2t #134299

Open
@brianfreud

Description

@brianfreud

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:

  1. sig.bind_partial() (called with no arguments): Incorrectly succeeds and returns an empty BoundArguments.arguments dictionary. In some observed cases, it raises TypeError: too many positional arguments, which is also incorrect. The expected behavior is a TypeError for the missing pos_arg_required.
  2. sig.bind_partial(*tuple(), **dict()) (called with unpacked empty tuple and dict): Incorrectly succeeds and returns an empty BoundArguments.arguments dictionary. Expected behavior is a TypeError for the missing pos_arg_required.
  3. sig.bind() (called with no arguments): Correctly fails with TypeError: missing a required argument: 'pos_arg_required'.
  4. sig.bind(*tuple(), **dict()) (called with unpacked empty tuple and dict): Correctly fails with TypeError: 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

Metadata

Metadata

Assignees

Labels

stdlibPython modules in the Lib dirtype-bugAn unexpected behavior, bug, or error

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions