1"""Infrastructure for registering and firing callbacks on application events.
2
3Unlike :mod:`IPython.core.hooks`, which lets end users set single functions to
4be called at specific times, or a collection of alternative methods to try,
5callbacks are designed to be used by extension authors. A number of callbacks
6can be registered for the same event without needing to be aware of one another.
7
8The functions defined in this module are no-ops indicating the names of available
9events and the arguments which will be passed to them.
10
11.. note::
12
13 This API is experimental in IPython 2.0, and may be revised in future versions.
14"""
15
16
17class EventManager:
18 """Manage a collection of events and a sequence of callbacks for each.
19
20 This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
21 instances as an ``events`` attribute.
22
23 .. note::
24
25 This API is experimental in IPython 2.0, and may be revised in future versions.
26 """
27
28 def __init__(self, shell, available_events, print_on_error=True):
29 """Initialise the :class:`CallbackManager`.
30
31 Parameters
32 ----------
33 shell
34 The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
35 available_events
36 An iterable of names for callback events.
37 print_on_error:
38 A boolean flag to set whether the EventManager will print a warning which a event errors.
39 """
40 self.shell = shell
41 self.callbacks = {n:[] for n in available_events}
42 self.print_on_error = print_on_error
43
44 def register(self, event, function):
45 """Register a new event callback.
46
47 Parameters
48 ----------
49 event : str
50 The event for which to register this callback.
51 function : callable
52 A function to be called on the given event. It should take the same
53 parameters as the appropriate callback prototype.
54
55 Raises
56 ------
57 TypeError
58 If ``function`` is not callable.
59 KeyError
60 If ``event`` is not one of the known events.
61 """
62 if not callable(function):
63 raise TypeError('Need a callable, got %r' % function)
64 if function not in self.callbacks[event]:
65 self.callbacks[event].append(function)
66
67 def unregister(self, event, function):
68 """Remove a callback from the given event."""
69 if function in self.callbacks[event]:
70 return self.callbacks[event].remove(function)
71
72 raise ValueError('Function {!r} is not registered as a {} callback'.format(function, event))
73
74 def trigger(self, event, *args, **kwargs):
75 """Call callbacks for ``event``.
76
77 Any additional arguments are passed to all callbacks registered for this
78 event. Exceptions raised by callbacks are caught, and a message printed.
79 """
80 for func in self.callbacks[event][:]:
81 try:
82 func(*args, **kwargs)
83 except (Exception, KeyboardInterrupt):
84 if self.print_on_error:
85 print(
86 "Error in callback {} (for {}), with arguments args {},kwargs {}:".format(
87 func, event, args, kwargs
88 )
89 )
90 self.shell.showtraceback()
91
92# event_name -> prototype mapping
93available_events = {}
94
95def _define_event(callback_function):
96 available_events[callback_function.__name__] = callback_function
97 return callback_function
98
99# ------------------------------------------------------------------------------
100# Callback prototypes
101#
102# No-op functions which describe the names of available events and the
103# signatures of callbacks for those events.
104# ------------------------------------------------------------------------------
105
106@_define_event
107def pre_execute():
108 """Fires before code is executed in response to user/frontend action.
109
110 This includes comm and widget messages and silent execution, as well as user
111 code cells.
112 """
113 pass
114
115@_define_event
116def pre_run_cell(info):
117 """Fires before user-entered code runs.
118
119 Parameters
120 ----------
121 info : :class:`~IPython.core.interactiveshell.ExecutionInfo`
122 An object containing information used for the code execution.
123 """
124 pass
125
126@_define_event
127def post_execute():
128 """Fires after code is executed in response to user/frontend action.
129
130 This includes comm and widget messages and silent execution, as well as user
131 code cells.
132 """
133 pass
134
135@_define_event
136def post_run_cell(result):
137 """Fires after user-entered code runs.
138
139 Parameters
140 ----------
141 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
142 The object which will be returned as the execution result.
143 """
144 pass
145
146@_define_event
147def shell_initialized(ip):
148 """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.
149
150 This is before extensions and startup scripts are loaded, so it can only be
151 set by subclassing.
152
153 Parameters
154 ----------
155 ip : :class:`~IPython.core.interactiveshell.InteractiveShell`
156 The newly initialised shell.
157 """
158 pass