Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3e7bedb

Browse files
committed
add callback disconnect by passing function
1 parent 4edc215 commit 3e7bedb

3 files changed

Lines changed: 80 additions & 0 deletions

File tree

lib/matplotlib/cbook.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ class CallbackRegistry:
229229
>>> callbacks.process('drink', 123)
230230
drink 123
231231
232+
>>> callbacks.disconnect_func('drink', ondrink) # disconnect by func
233+
>>> callbacks.process('drink', 123) # nothing will be called
234+
232235
In practice, one should always disconnect all callbacks when they are
233236
no longer needed to avoid dangling references (and thus memory leaks).
234237
However, real code in Matplotlib rarely does so, and due to its design,
@@ -349,6 +352,15 @@ def disconnect(self, cid):
349352
if len(self.callbacks[signal]) == 0: # Clean up empty dicts
350353
del self.callbacks[signal]
351354

355+
def disconnect_func(self, signal, func):
356+
"""
357+
Disconnect the callback for *func* registered to *signal*.
358+
359+
No error is raised if such a callback does not exist.
360+
"""
361+
proxy = _weak_or_strong_ref(func, None)
362+
self._remove_proxy(signal, proxy)
363+
352364
def process(self, s, *args, **kwargs):
353365
"""
354366
Process signal *s*.

lib/matplotlib/cbook.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class CallbackRegistry:
3434
) -> None: ...
3535
def connect(self, signal: Any, func: Callable) -> int: ...
3636
def disconnect(self, cid: int) -> None: ...
37+
def disconnect_func(self, signal: Any, func: Callable) -> None: ...
3738
def process(self, s: Any, *args, **kwargs) -> None: ...
3839
def blocked(
3940
self, *, signal: Any | None = ...

lib/matplotlib/tests/test_cbook.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,73 @@ def test_callback_wrong_disconnect(self, pickle, cls):
292292
# check we still have callbacks registered
293293
self.is_not_empty()
294294

295+
@pytest.mark.parametrize('pickle', [True, False])
296+
@pytest.mark.parametrize('cls', [Hashable, Unhashable])
297+
def test_callback_disconnect_func(self, pickle, cls):
298+
# ensure we start with an empty registry
299+
self.is_empty()
300+
301+
# create a class for testing
302+
mini_me = cls()
303+
304+
# test that we can add a callback
305+
self.connect(self.signal, mini_me.dummy, pickle)
306+
self.is_not_empty()
307+
308+
# disconnect by function reference
309+
self.callbacks.disconnect_func(self.signal, mini_me.dummy)
310+
311+
# check we now have no callbacks registered
312+
self.is_empty()
313+
314+
@pytest.mark.parametrize('pickle', [True, False])
315+
@pytest.mark.parametrize('cls', [Hashable, Unhashable])
316+
def test_callback_disconnect_func_wrong(self, pickle, cls):
317+
# ensure we start with an empty registry
318+
self.is_empty()
319+
320+
# create a class for testing
321+
mini_me = cls()
322+
323+
# test that we can add a callback
324+
self.connect(self.signal, mini_me.dummy, pickle)
325+
self.is_not_empty()
326+
327+
# try to disconnect with wrong signal - should do nothing
328+
self.callbacks.disconnect_func('wrong_signal', mini_me.dummy)
329+
330+
# check we still have callbacks registered
331+
self.is_not_empty()
332+
333+
# try to disconnect with wrong function - should do nothing
334+
mini_me2 = cls()
335+
self.callbacks.disconnect_func(self.signal, mini_me2.dummy)
336+
337+
# check we still have callbacks registered
338+
self.is_not_empty()
339+
340+
def test_callback_disconnect_func_redefined(self):
341+
# Test that redefining a function name doesn't affect disconnect_func.
342+
# When you redefine a function, it creates a new function object,
343+
# so disconnect_func should not disconnect the original.
344+
self.is_empty()
345+
346+
def func():
347+
pass
348+
349+
self.callbacks.connect(self.signal, func)
350+
self.is_not_empty()
351+
352+
# Redefine func - this creates a new function object
353+
def func():
354+
pass
355+
356+
# Try to disconnect with the redefined function
357+
self.callbacks.disconnect_func(self.signal, func)
358+
359+
# Original callback should still be registered
360+
self.is_not_empty()
361+
295362
@pytest.mark.parametrize('pickle', [True, False])
296363
@pytest.mark.parametrize('cls', [Hashable, Unhashable])
297364
def test_registration_on_non_empty_registry(self, pickle, cls):

0 commit comments

Comments
 (0)