##// END OF EJS Templates
Rename callbacks -> events (mostly), fire -> trigger
Thomas Kluyver -
Show More
@@ -1,4 +1,4 b''
1 """Infrastructure for registering and firing callbacks.
1 """Infrastructure for registering and firing callbacks on application events.
2
2
3 Unlike :mod:`IPython.core.hooks`, which lets end users set single functions to
3 Unlike :mod:`IPython.core.hooks`, which lets end users set single functions to
4 be called at specific times, or a collection of alternative methods to try,
4 be called at specific times, or a collection of alternative methods to try,
@@ -10,13 +10,13 b' events and the arguments which will be passed to them.'
10 """
10 """
11 from __future__ import print_function
11 from __future__ import print_function
12
12
13 class CallbackManager(object):
13 class EventManager(object):
14 """Manage a collection of events and a sequence of callbacks for each.
14 """Manage a collection of events and a sequence of callbacks for each.
15
15
16 This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
16 This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
17 instances as a ``callbacks`` attribute.
17 instances as a ``callbacks`` attribute.
18 """
18 """
19 def __init__(self, shell, available_callbacks):
19 def __init__(self, shell, available_events):
20 """Initialise the :class:`CallbackManager`.
20 """Initialise the :class:`CallbackManager`.
21
21
22 Parameters
22 Parameters
@@ -27,14 +27,14 b' class CallbackManager(object):'
27 An iterable of names for callback events.
27 An iterable of names for callback events.
28 """
28 """
29 self.shell = shell
29 self.shell = shell
30 self.callbacks = {n:[] for n in available_callbacks}
30 self.callbacks = {n:[] for n in available_events}
31
31
32 def register(self, name, function):
32 def register(self, event, function):
33 """Register a new callback
33 """Register a new callback
34
34
35 Parameters
35 Parameters
36 ----------
36 ----------
37 name : str
37 event : str
38 The event for which to register this callback.
38 The event for which to register this callback.
39 function : callable
39 function : callable
40 A function to be called on the given event. It should take the same
40 A function to be called on the given event. It should take the same
@@ -45,42 +45,42 b' class CallbackManager(object):'
45 TypeError
45 TypeError
46 If ``function`` is not callable.
46 If ``function`` is not callable.
47 KeyError
47 KeyError
48 If ``name`` is not one of the known callback events.
48 If ``event`` is not one of the known events.
49 """
49 """
50 if not callable(function):
50 if not callable(function):
51 raise TypeError('Need a callable, got %r' % function)
51 raise TypeError('Need a callable, got %r' % function)
52 self.callbacks[name].append(function)
52 self.callbacks[event].append(function)
53
53
54 def unregister(self, name, function):
54 def unregister(self, event, function):
55 """Remove a callback from the given event."""
55 """Remove a callback from the given event."""
56 self.callbacks[name].remove(function)
56 self.callbacks[event].remove(function)
57
57
58 def reset(self, name):
58 def reset(self, event):
59 """Clear all callbacks for the given event."""
59 """Clear all callbacks for the given event."""
60 self.callbacks[name] = []
60 self.callbacks[event] = []
61
61
62 def reset_all(self):
62 def reset_all(self):
63 """Clear all callbacks for all events."""
63 """Clear all callbacks for all events."""
64 self.callbacks = {n:[] for n in self.callbacks}
64 self.callbacks = {n:[] for n in self.callbacks}
65
65
66 def fire(self, name, *args, **kwargs):
66 def trigger(self, event, *args, **kwargs):
67 """Call callbacks for the event ``name``.
67 """Call callbacks for ``event``.
68
68
69 Any additional arguments are passed to all callbacks registered for this
69 Any additional arguments are passed to all callbacks registered for this
70 event. Exceptions raised by callbacks are caught, and a message printed.
70 event. Exceptions raised by callbacks are caught, and a message printed.
71 """
71 """
72 for func in self.callbacks[name]:
72 for func in self.callbacks[event]:
73 try:
73 try:
74 func(*args, **kwargs)
74 func(*args, **kwargs)
75 except Exception:
75 except Exception:
76 print("Error in callback {} (for {}):".format(func, name))
76 print("Error in callback {} (for {}):".format(func, event))
77 self.shell.showtraceback()
77 self.shell.showtraceback()
78
78
79 # event_name -> prototype mapping
79 # event_name -> prototype mapping
80 available_callbacks = {}
80 available_events = {}
81
81
82 def _collect(callback_proto):
82 def _collect(callback_proto):
83 available_callbacks[callback_proto.__name__] = callback_proto
83 available_events[callback_proto.__name__] = callback_proto
84 return callback_proto
84 return callback_proto
85
85
86 # ------------------------------------------------------------------------------
86 # ------------------------------------------------------------------------------
@@ -41,7 +41,7 b' from IPython.core import ultratb'
41 from IPython.core.alias import AliasManager, AliasError
41 from IPython.core.alias import AliasManager, AliasError
42 from IPython.core.autocall import ExitAutocall
42 from IPython.core.autocall import ExitAutocall
43 from IPython.core.builtin_trap import BuiltinTrap
43 from IPython.core.builtin_trap import BuiltinTrap
44 from IPython.core.callbacks import CallbackManager, available_callbacks
44 from IPython.core.events import EventManager, available_events
45 from IPython.core.compilerop import CachingCompiler, check_linecache_ipython
45 from IPython.core.compilerop import CachingCompiler, check_linecache_ipython
46 from IPython.core.display_trap import DisplayTrap
46 from IPython.core.display_trap import DisplayTrap
47 from IPython.core.displayhook import DisplayHook
47 from IPython.core.displayhook import DisplayHook
@@ -468,7 +468,7 b' class InteractiveShell(SingletonConfigurable):'
468
468
469 self.init_syntax_highlighting()
469 self.init_syntax_highlighting()
470 self.init_hooks()
470 self.init_hooks()
471 self.init_callbacks()
471 self.init_events()
472 self.init_pushd_popd_magic()
472 self.init_pushd_popd_magic()
473 # self.init_traceback_handlers use to be here, but we moved it below
473 # self.init_traceback_handlers use to be here, but we moved it below
474 # because it and init_io have to come after init_readline.
474 # because it and init_io have to come after init_readline.
@@ -512,7 +512,7 b' class InteractiveShell(SingletonConfigurable):'
512 self.init_payload()
512 self.init_payload()
513 self.init_comms()
513 self.init_comms()
514 self.hooks.late_startup_hook()
514 self.hooks.late_startup_hook()
515 self.callbacks.fire('shell_initialised', self)
515 self.events.trigger('shell_initialised', self)
516 atexit.register(self.atexit_operations)
516 atexit.register(self.atexit_operations)
517
517
518 def get_ipython(self):
518 def get_ipython(self):
@@ -840,8 +840,8 b' class InteractiveShell(SingletonConfigurable):'
840 # Things related to callbacks
840 # Things related to callbacks
841 #-------------------------------------------------------------------------
841 #-------------------------------------------------------------------------
842
842
843 def init_callbacks(self):
843 def init_events(self):
844 self.callbacks = CallbackManager(self, available_callbacks)
844 self.events = EventManager(self, available_events)
845
845
846 def register_post_execute(self, func):
846 def register_post_execute(self, func):
847 """DEPRECATED: Use ip.callbacks.register('post_execute_explicit', func)
847 """DEPRECATED: Use ip.callbacks.register('post_execute_explicit', func)
@@ -850,7 +850,7 b' class InteractiveShell(SingletonConfigurable):'
850 """
850 """
851 warn("ip.register_post_execute is deprecated, use "
851 warn("ip.register_post_execute is deprecated, use "
852 "ip.callbacks.register('post_execute_explicit', func) instead.")
852 "ip.callbacks.register('post_execute_explicit', func) instead.")
853 self.callbacks.register('post_execute_explicit', func)
853 self.events.register('post_execute_explicit', func)
854
854
855 #-------------------------------------------------------------------------
855 #-------------------------------------------------------------------------
856 # Things related to the "main" module
856 # Things related to the "main" module
@@ -2667,9 +2667,9 b' class InteractiveShell(SingletonConfigurable):'
2667 if silent:
2667 if silent:
2668 store_history = False
2668 store_history = False
2669
2669
2670 self.callbacks.fire('pre_execute')
2670 self.events.trigger('pre_execute')
2671 if not silent:
2671 if not silent:
2672 self.callbacks.fire('pre_execute_explicit')
2672 self.events.trigger('pre_execute_explicit')
2673
2673
2674 # If any of our input transformation (input_transformer_manager or
2674 # If any of our input transformation (input_transformer_manager or
2675 # prefilter_manager) raises an exception, we store it in this variable
2675 # prefilter_manager) raises an exception, we store it in this variable
@@ -2740,9 +2740,9 b' class InteractiveShell(SingletonConfigurable):'
2740 self.run_ast_nodes(code_ast.body, cell_name,
2740 self.run_ast_nodes(code_ast.body, cell_name,
2741 interactivity=interactivity, compiler=compiler)
2741 interactivity=interactivity, compiler=compiler)
2742
2742
2743 self.callbacks.fire('post_execute')
2743 self.events.trigger('post_execute')
2744 if not silent:
2744 if not silent:
2745 self.callbacks.fire('post_execute_explicit')
2745 self.events.trigger('post_execute_explicit')
2746
2746
2747 if store_history:
2747 if store_history:
2748 # Write output to the database. Does nothing unless
2748 # Write output to the database. Does nothing unless
@@ -4,7 +4,7 b' try: # Python 3.3 +'
4 except ImportError:
4 except ImportError:
5 from mock import Mock
5 from mock import Mock
6
6
7 from IPython.core import callbacks
7 from IPython.core import events
8 import IPython.testing.tools as tt
8 import IPython.testing.tools as tt
9
9
10 def ping_received():
10 def ping_received():
@@ -12,35 +12,35 b' def ping_received():'
12
12
13 class CallbackTests(unittest.TestCase):
13 class CallbackTests(unittest.TestCase):
14 def setUp(self):
14 def setUp(self):
15 self.cbm = callbacks.CallbackManager(get_ipython(), {'ping_received': ping_received})
15 self.em = events.EventManager(get_ipython(), {'ping_received': ping_received})
16
16
17 def test_register_unregister(self):
17 def test_register_unregister(self):
18 cb = Mock()
18 cb = Mock()
19
19
20 self.cbm.register('ping_received', cb)
20 self.em.register('ping_received', cb)
21 self.cbm.fire('ping_received')
21 self.em.trigger('ping_received')
22 self.assertEqual(cb.call_count, 1)
22 self.assertEqual(cb.call_count, 1)
23
23
24 self.cbm.unregister('ping_received', cb)
24 self.em.unregister('ping_received', cb)
25 self.cbm.fire('ping_received')
25 self.em.trigger('ping_received')
26 self.assertEqual(cb.call_count, 1)
26 self.assertEqual(cb.call_count, 1)
27
27
28 def test_reset(self):
28 def test_reset(self):
29 cb = Mock()
29 cb = Mock()
30 self.cbm.register('ping_received', cb)
30 self.em.register('ping_received', cb)
31 self.cbm.reset('ping_received')
31 self.em.reset('ping_received')
32 self.cbm.fire('ping_received')
32 self.em.trigger('ping_received')
33 assert not cb.called
33 assert not cb.called
34
34
35 def test_reset_all(self):
35 def test_reset_all(self):
36 cb = Mock()
36 cb = Mock()
37 self.cbm.register('ping_received', cb)
37 self.em.register('ping_received', cb)
38 self.cbm.reset_all()
38 self.em.reset_all()
39 self.cbm.fire('ping_received')
39 self.em.trigger('ping_received')
40 assert not cb.called
40 assert not cb.called
41
41
42 def test_cb_error(self):
42 def test_cb_error(self):
43 cb = Mock(side_effect=ValueError)
43 cb = Mock(side_effect=ValueError)
44 self.cbm.register('ping_received', cb)
44 self.em.register('ping_received', cb)
45 with tt.AssertPrints("Error in callback"):
45 with tt.AssertPrints("Error in callback"):
46 self.cbm.fire('ping_received') No newline at end of file
46 self.em.trigger('ping_received') No newline at end of file
@@ -288,10 +288,10 b' class InteractiveShellTestCase(unittest.TestCase):'
288 post_explicit = mock.Mock()
288 post_explicit = mock.Mock()
289 post_always = mock.Mock()
289 post_always = mock.Mock()
290
290
291 ip.callbacks.register('pre_execute_explicit', pre_explicit)
291 ip.events.register('pre_execute_explicit', pre_explicit)
292 ip.callbacks.register('pre_execute', pre_always)
292 ip.events.register('pre_execute', pre_always)
293 ip.callbacks.register('post_execute_explicit', post_explicit)
293 ip.events.register('post_execute_explicit', post_explicit)
294 ip.callbacks.register('post_execute', post_always)
294 ip.events.register('post_execute', post_always)
295
295
296 try:
296 try:
297 ip.run_cell("1", silent=True)
297 ip.run_cell("1", silent=True)
@@ -306,7 +306,7 b' class InteractiveShellTestCase(unittest.TestCase):'
306 assert post_explicit.called
306 assert post_explicit.called
307 finally:
307 finally:
308 # remove post-exec
308 # remove post-exec
309 ip.callbacks.reset_all()
309 ip.events.reset_all()
310
310
311 def test_silent_noadvance(self):
311 def test_silent_noadvance(self):
312 """run_cell(silent=True) doesn't advance execution_count"""
312 """run_cell(silent=True) doesn't advance execution_count"""
@@ -502,4 +502,4 b' def load_ipython_extension(ip):'
502 """Load the extension in IPython."""
502 """Load the extension in IPython."""
503 auto_reload = AutoreloadMagics(ip)
503 auto_reload = AutoreloadMagics(ip)
504 ip.register_magics(auto_reload)
504 ip.register_magics(auto_reload)
505 ip.callbacks.register('pre_execute_explicit', auto_reload.pre_execute_explicit)
505 ip.events.register('pre_execute_explicit', auto_reload.pre_execute_explicit)
@@ -23,7 +23,7 b' import nose.tools as nt'
23 import IPython.testing.tools as tt
23 import IPython.testing.tools as tt
24
24
25 from IPython.extensions.autoreload import AutoreloadMagics
25 from IPython.extensions.autoreload import AutoreloadMagics
26 from IPython.core.callbacks import CallbackManager, pre_execute_explicit
26 from IPython.core.events import EventManager, pre_execute_explicit
27 from IPython.utils.py3compat import PY3
27 from IPython.utils.py3compat import PY3
28
28
29 if PY3:
29 if PY3:
@@ -41,14 +41,14 b' class FakeShell(object):'
41
41
42 def __init__(self):
42 def __init__(self):
43 self.ns = {}
43 self.ns = {}
44 self.callbacks = CallbackManager(self, {'pre_execute_explicit', pre_execute_explicit})
44 self.events = EventManager(self, {'pre_execute_explicit', pre_execute_explicit})
45 self.auto_magics = AutoreloadMagics(shell=self)
45 self.auto_magics = AutoreloadMagics(shell=self)
46 self.callbacks.register('pre_execute_explicit', self.auto_magics.pre_execute_explicit)
46 self.events.register('pre_execute_explicit', self.auto_magics.pre_execute_explicit)
47
47
48 register_magics = set_hook = noop
48 register_magics = set_hook = noop
49
49
50 def run_code(self, code):
50 def run_code(self, code):
51 self.callbacks.fire('pre_execute_explicit')
51 self.events.trigger('pre_execute_explicit')
52 exec(code, self.ns)
52 exec(code, self.ns)
53
53
54 def push(self, items):
54 def push(self, items):
@@ -134,9 +134,9 b' class Comm(LoggingConfigurable):'
134 """Handle a comm_msg message"""
134 """Handle a comm_msg message"""
135 self.log.debug("handle_msg[%s](%s)", self.comm_id, msg)
135 self.log.debug("handle_msg[%s](%s)", self.comm_id, msg)
136 if self._msg_callback:
136 if self._msg_callback:
137 self.shell.callbacks.fire('pre_execute')
137 self.shell.events.trigger('pre_execute')
138 self._msg_callback(msg)
138 self._msg_callback(msg)
139 self.shell.callbacks.fire('post_execute')
139 self.shell.events.trigger('post_execute')
140
140
141
141
142 __all__ = ['Comm']
142 __all__ = ['Comm']
General Comments 0
You need to be logged in to leave comments. Login now