@@ -319,19 +319,40 @@ def disconnect(self, slot: Callable | None = None, missing_ok: bool = True) -> N
319319 def _slot_index (self , slot : Callable ) -> int :
320320 """Get index of `slot` in `self._slots`. Return -1 if not connected.
321321
322- Override to handle _relay_partial objects directly without wrapping
323- them in WeakFunction, which would create different objects.
322+ In interpreted mode, `_relay_partial` callbacks are stored as
323+ weak references to the callable object itself, so comparing the
324+ dereferenced callback to the provided `_relay_partial` works. In compiled
325+ mode (mypyc), `weak_callback` may normalize a `_relay_partial` to a strong
326+ reference to its `__call__` method (a MethodWrapperType), to avoid segfaults on
327+ garbage collection. In that case the default WeakCallback equality logic is the
328+ correct and more robust path.
329+
330+ Therefore, try the base implementation first (which compares normalized
331+ WeakCallback objects). If that fails and we're dealing with a
332+ `_relay_partial`, fall back to comparing the dereferenced callable to the
333+ provided slot for backward compatibility.
324334 """
325- if not isinstance (slot , _relay_partial ):
326- # For non-_relay_partial objects, use the default behavior
327- return super ()._slot_index (slot )
328-
329- with self ._lock :
330- # For _relay_partial objects, compare directly against callbacks
331- for i , s in enumerate (self ._slots ):
332- if s .dereference () == slot :
333- return i
334- return - 1 # pragma: no cover
335+ # First, try the standard equality path used by SignalInstance
336+ idx = super ()._slot_index (slot )
337+ if idx != - 1 :
338+ return idx
339+
340+ # Fallback: handle direct comparison for `_relay_partial` instances
341+ if isinstance (slot , _relay_partial ):
342+ with self ._lock :
343+ for i , s in enumerate (self ._slots ):
344+ deref = s .dereference ()
345+ # Case 1: stored deref is the _relay_partial itself (interpreted)
346+ if deref == slot :
347+ return i
348+ # Case 2: compiled path where we stored __call__ bound method
349+ # (these will never hit on codecov, but they are covered in tests)
350+ owner = getattr (deref , "__self__" , None ) # pragma: no cover
351+ if (
352+ isinstance (owner , _relay_partial ) and owner == slot
353+ ): # pragma: no cover
354+ return i
355+ return - 1 # pragma: no cover
335356
336357
337358# NOTE
@@ -612,6 +633,7 @@ def connect(
612633 max_args : int | None = None ,
613634 on_ref_error : RefErrorChoice = "warn" ,
614635 priority : int = 0 ,
636+ emit_on_evented_child_events : bool = False ,
615637 ) -> Callable [[F ], F ] | F :
616638 if slot is None :
617639 return self ._psygnal_relay .connect (
@@ -622,6 +644,7 @@ def connect(
622644 max_args = max_args ,
623645 on_ref_error = on_ref_error ,
624646 priority = priority ,
647+ emit_on_evented_child_events = emit_on_evented_child_events ,
625648 )
626649 else :
627650 return self ._psygnal_relay .connect (
@@ -633,6 +656,7 @@ def connect(
633656 max_args = max_args ,
634657 on_ref_error = on_ref_error ,
635658 priority = priority ,
659+ emit_on_evented_child_events = emit_on_evented_child_events ,
636660 )
637661
638662 def connect_direct (
0 commit comments