A keyword argument through a callable field is refused
Repro
class Holder:
def go(self, x, mode=1):
return x
def call(o):
return o.zap(1, mode=2)
class Box:
def __init__(self):
self.v = 0
b = Box()
b.zap = lambda n, mode=0: n + mode
print(call(b)) # CPython: 3. pxx: TypeError at run time.
Holder.go is there only to keep zap off every other path: no class declares
zap, so the call is dispatched on the receiver at run time.
test/nilpy_open_world_kwarg_fail.npy is this program and asserts the refusal,
so closing this ticket means changing that fixture from a refusal row to a
value row.
Why it refuses
PyDynMethN looks the name up on the receiver's RTTI. A METHOD hit goes to
PyHostCall, which binds a kwNames list against the parameter names the RTTI
records — that path is correct and is what
test_nilpy_open_world_keyword_dispatch.npy asserts. A miss falls through to
pydynattr_get and then pyvar_callv0..4, which take positional slots only.
Binding mode=2 to a slot there is the mis-binding
bug-nilpy-pyeval-fallback-still-binds-host-kwargs-by-position was filed
about, so it raises instead.
The measurement to do first
Whether the callable's own value already carries its parameter names. A def and
a lambda are boxed by pybound_new_sig / PyMakeFuncValueFor and
feature-n-a-callable-value-carries-its-signature-type says a signature travels
with the value — if the NAMES are in it, this is a binder call and not a design
question. If only arity and defaults are there, it is a bigger job and should
be ranked on how much real code writes a keyword through a callable attribute,
which is not obviously much.