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

Skip to content

Commit a9680e7

Browse files
committed
ENH: Add the ability to block callback signals
CallbackRegistry objects now have a context manager .block() method that can be used to temporarily block callback signals from being processed.
1 parent d77783b commit a9680e7

File tree

3 files changed

+72
-1
lines changed

3 files changed

+72
-1
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
``CallbackRegistry`` objects gain a method to temporarily block signals
2+
-----------------------------------------------------------------------
3+
4+
The method `~matplotlib.cbook.CallbackRegistry.block` can be used to block
5+
callback signals from being processed by the ``CallbackRegistry``.
6+
The optional keyword, *signal*, can be used to block a specific signal
7+
from being processed and let all other signals pass.

lib/matplotlib/cbook/__init__.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ def _weak_or_strong_ref(func, callback):
122122

123123
class CallbackRegistry:
124124
"""
125-
Handle registering and disconnecting for a set of signals and callbacks:
125+
Handle registering, processing, blocking, and disconnecting
126+
for a set of signals and callbacks:
126127
127128
>>> def oneat(x):
128129
... print('eat', x)
@@ -143,6 +144,11 @@ class CallbackRegistry:
143144
>>> callbacks.disconnect(id_eat)
144145
>>> callbacks.process('eat', 456) # nothing will be called
145146
147+
>>> with callbacks.block(signal='drink'):
148+
... callbacks.process('drink', 123) # nothing will be called
149+
>>> callbacks.process('drink', 123)
150+
drink 123
151+
146152
In practice, one should always disconnect all callbacks when they are
147153
no longer needed to avoid dangling references (and thus memory leaks).
148154
However, real code in Matplotlib rarely does so, and due to its design,
@@ -280,6 +286,31 @@ def process(self, s, *args, **kwargs):
280286
else:
281287
raise
282288

289+
@contextlib.contextmanager
290+
def block(self, *, signal=None):
291+
"""
292+
Block callback signals from being processed.
293+
294+
A context manager to temporarily block/disable callback signals
295+
from being processed by the registered listeners.
296+
297+
Parameters
298+
----------
299+
signal : str, default: None
300+
The callback signal to block, default blocks all signals
301+
"""
302+
orig = self.callbacks
303+
try:
304+
if signal is None:
305+
# Empty out the callbacks
306+
self.callbacks = {}
307+
else:
308+
# Only remove the specific signal
309+
self.callbacks = {k: orig[k] for k in orig if k != signal}
310+
yield
311+
finally:
312+
self.callbacks = orig
313+
283314

284315
class silent_list(list):
285316
"""

lib/matplotlib/tests/test_cbook.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,39 @@ def test_callbackregistry_custom_exception_handler(monkeypatch, cb, excp):
361361
cb.process('foo')
362362

363363

364+
def test_callbackregistry_blocking():
365+
# Needs an exception handler for interactive testing environments
366+
# that would only print this out instead of raising the exception
367+
def raise_handler(excp):
368+
raise excp
369+
cb = cbook.CallbackRegistry(exception_handler=raise_handler)
370+
def test_func1():
371+
raise ValueError("1 should be blocked")
372+
def test_func2():
373+
raise ValueError("2 should be blocked")
374+
cb.connect("test1", test_func1)
375+
cb.connect("test2", test_func2)
376+
377+
# block all of the callbacks to make sure they aren't processed
378+
with cb.block():
379+
cb.process("test1")
380+
cb.process("test2")
381+
382+
# block individual callbacks to make sure the other is still processed
383+
with cb.block(signal="test1"):
384+
# Blocked
385+
cb.process("test1")
386+
# Should raise
387+
with pytest.raises(ValueError, match="2 should be blocked"):
388+
cb.process("test2")
389+
390+
# Make sure the original callback functions are there after blocking
391+
with pytest.raises(ValueError, match="1 should be blocked"):
392+
cb.process("test1")
393+
with pytest.raises(ValueError, match="2 should be blocked"):
394+
cb.process("test2")
395+
396+
364397
def test_sanitize_sequence():
365398
d = {'a': 1, 'b': 2, 'c': 3}
366399
k = ['a', 'b', 'c']

0 commit comments

Comments
 (0)