Skip to content

Raise ReferenceError when bound method instance is garbage collected#312

Merged
GrahamDumpleton merged 2 commits intoGrahamDumpleton:developfrom
bysiber:fix/weakfunctionproxy-expired-instance
Mar 3, 2026
Merged

Raise ReferenceError when bound method instance is garbage collected#312
GrahamDumpleton merged 2 commits intoGrahamDumpleton:developfrom
bysiber:fix/weakfunctionproxy-expired-instance

Conversation

@bysiber
Copy link
Contributor

@bysiber bysiber commented Feb 20, 2026

WeakFunctionProxy.__call__ checks self._self_instance (a weakref.ref) for truthiness to detect expired references, but weakref.ref objects are always truthy — even after the referent has been garbage collected. Only calling the ref and checking the return value can detect expiration.

When the instance is GC'd but the function still lives in the class dict (the typical case), the code falls through to the unbound-function path and calls the function without self:

class MyClass:
    def method(self):
        return "hello"

obj = MyClass()
proxy = WeakFunctionProxy(obj.method)
del obj  # instance GC'd, but MyClass.method still lives

proxy()
# Expected: ReferenceError
# Actual: TypeError: method() missing 1 required positional argument: 'self'

The fix adds an explicit check: if _self_instance was set (it was a bound method) but the dereferenced value is None, raise ReferenceError directly.

bysiber and others added 2 commits February 20, 2026 15:08
WeakFunctionProxy.__call__ checks self._self_instance (a weakref.ref)
for truthiness to detect expired references, but weakref.ref objects
are always truthy even after the referent is garbage collected.

When the instance dies but the function still lives (the usual case
since functions live in the class dict), _self_instance() returns
None, making the code fall into the unbound-function path. This calls
the original function without 'self', producing a confusing TypeError
instead of the expected ReferenceError.

Add an explicit check: if _self_instance was set (bound method) but
the dereferenced value is None, raise ReferenceError directly.
@GrahamDumpleton GrahamDumpleton merged commit 2cda4e6 into GrahamDumpleton:develop Mar 3, 2026
@GrahamDumpleton
Copy link
Owner

Thanks for the PR.

@GrahamDumpleton
Copy link
Owner

I have also added some tests specifically for this now as well.

@GrahamDumpleton
Copy link
Owner

Have released wrapt 2.1.2rc1 on PyPi if want to try with that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants