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