bug(N): a @staticmethod read through an INSTANCE binds a receiver, so type(k.stat).__name__ says 'method'
Found while fixing regression-test-nilpy-test-nilpy-type-name-of-a-big-int
(the function-vs-method type name). Filed rather than fixed, because it is a
pre-existing divergence of the ATTRIBUTE READ, not of the name table, and it is
a different mechanism.
Repro
class K:
@staticmethod
def stat(a, b):
return a * 10 + b
k = K()
print(K.stat(1, 2)) # 12 -- agrees
print(k.stat(3, 4)) # 34 -- agrees
f = k.stat
print(f(5, 6)) # 56 -- agrees
print(type(k.stat).__name__) # CPython: function NilPy: method
Every VALUE agrees with CPython. Only the type name diverges.
Cause
pydynattr_get_v's method arm builds pybound_new_star(mi^.Code, obj, ...) for
any method name read as a value — it has no notion of @staticmethod, so it
binds the instance as the receiver. PyVarTypeNameOf then correctly reports
'method', because "what makes something a bound method is being bound to
something" (the rule PyCallableStr and now PyVarTypeNameOf share). The
receiver is the thing that is wrong, not the naming.
The values still come out right because the compiled staticmethod body has no
self parameter and the dispatcher calls it through the plain-function arm --
the bound receiver is simply never passed. So this is visible ONLY through
type(...).__name__ (and, presumably, str() of the value, which
PyCallableStr would render <bound method ...> for the same reason).
Why it is prio 25, not higher
A program that branches on type(x).__name__ for a staticmethod read off an
instance takes the wrong arm — that is a real defect, and it is why this is a
bug- and not a note. But no value is wrong, no program crashes, and the
spelling (staticmethod reached through an instance rather than the class) is
uncommon.
Deliberately NOT asserted
test/test_nilpy_type_name_function_vs_method.npy carries the case as a comment
naming this ticket rather than as an assertion, so the test stays oracle-diffed
and all-green. Turn the comment back into print(type(k.stat).__name__) and
regenerate the .expected from CPython when this is fixed -- that is the gate.