Fix unspy replacing staticmethods and classmethods with plain functions.
Review Request #15147 — Created July 2, 2026 and submitted — Latest diff uploaded
When spying on a
staticmethodorclassmethoddefined directly on a
class (passing the method plusowner=),unspy()replaced the
descriptor on the class with a plain function, instead of restoring the
original staticmethod or classmethod.The cause was in how the spy records the value to restore. When the
owner does not need patching (a class that owns the attribute directly,
not slippery, not inherited), the spy storedself.orig_funcas the
restore value. For a staticmethod or classmethod,orig_funcis the
function obtained through attribute access, which has already been
unwrapped by the descriptor. Onunspy(), that unwrapped function was
assigned back onto the class, discarding the descriptor.While spied, everything still worked, because the spy swaps the code
object in place and the descriptor keeps pointing at the same function.
The corruption only appeared afterunspy(): the attribute was now a
plain function, so a later call through an instance bound the instance
as the first positional argument. For a staticmethod this raised a
TypeError. For a classmethod the instance was silently passed where
the class was expected.We now store the attribute value exactly as defined in the owner
class dict for this case, matching what the owner-patching path
already does, so the descriptor is preserved acrossunspy().This also removes a workaround in the test base class that manually
restoredMathClass.class_do_mathafter each test. That workaround
existed only to paper over this bug, and it corrupted the classmethod
descriptor for later tests. One test
(test_construction_with_call_fake_and_classmethod) depended on that
corruption, asserting object identity across two classmethod accesses;
it now compares by equality, which is correct for a classmethod that
returns a fresh bound method each time.
- Commented out the fix and saw the new unit tests fail.
- Ran the full test suite on Python 3.14.
- Verified all staticmethod and classmethod tests pass in isolation,
confirming they no longer depend on inter-test state.