##// END OF EJS Templates
Ensure `function` is actually a function....
Fabio Niephaus -
Show More
@@ -1,188 +1,189 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 functools import wraps
16 from functools import wraps
17 from inspect import isfunction
17 try:
18 try:
18 from inspect import getfullargspec
19 from inspect import getfullargspec
19 except:
20 except:
20 from inspect import getargspec as getfullargspec # for Python2 compatibility.
21 from inspect import getargspec as getfullargspec # for Python2 compatibility.
21
22
22 # original function -> wrapper function mapping
23 # original function -> wrapper function mapping
23 compatibility_wrapper_functions = {}
24 compatibility_wrapper_functions = {}
24
25
25 def _compatibility_wrapper_for(function):
26 def _compatibility_wrapper_for(function):
26 """Returns a wrapper for a function without args that accepts any args."""
27 """Returns a wrapper for a function without args that accepts any args."""
27 if len(getfullargspec(function).args) > 0:
28 if len(getfullargspec(function).args) > 0:
28 raise TypeError('%s cannot have arguments' % function)
29 raise TypeError('%s cannot have arguments' % function)
29 if function in compatibility_wrapper_functions:
30 if function in compatibility_wrapper_functions:
30 return compatibility_wrapper_functions[function]
31 return compatibility_wrapper_functions[function]
31 @wraps(function)
32 @wraps(function)
32 def wrapper(*args, **kwargs):
33 def wrapper(*args, **kwargs):
33 function()
34 function()
34 compatibility_wrapper_functions[function] = wrapper
35 compatibility_wrapper_functions[function] = wrapper
35 return wrapper
36 return wrapper
36
37
37 class EventManager(object):
38 class EventManager(object):
38 """Manage a collection of events and a sequence of callbacks for each.
39 """Manage a collection of events and a sequence of callbacks for each.
39
40
40 This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
41 This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
41 instances as an ``events`` attribute.
42 instances as an ``events`` attribute.
42
43
43 .. note::
44 .. note::
44
45
45 This API is experimental in IPython 2.0, and may be revised in future versions.
46 This API is experimental in IPython 2.0, and may be revised in future versions.
46 """
47 """
47 def __init__(self, shell, available_events):
48 def __init__(self, shell, available_events):
48 """Initialise the :class:`CallbackManager`.
49 """Initialise the :class:`CallbackManager`.
49
50
50 Parameters
51 Parameters
51 ----------
52 ----------
52 shell
53 shell
53 The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
54 The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
54 available_callbacks
55 available_callbacks
55 An iterable of names for callback events.
56 An iterable of names for callback events.
56 """
57 """
57 self.shell = shell
58 self.shell = shell
58 self.callbacks = {n:[] for n in available_events}
59 self.callbacks = {n:[] for n in available_events}
59
60
60 def register(self, event, function):
61 def register(self, event, function):
61 """Register a new event callback
62 """Register a new event callback
62
63
63 Parameters
64 Parameters
64 ----------
65 ----------
65 event : str
66 event : str
66 The event for which to register this callback.
67 The event for which to register this callback.
67 function : callable
68 function : callable
68 A function to be called on the given event. It should take the same
69 A function to be called on the given event. It should take the same
69 parameters as the appropriate callback prototype.
70 parameters as the appropriate callback prototype.
70
71
71 Raises
72 Raises
72 ------
73 ------
73 TypeError
74 TypeError
74 If ``function`` is not callable.
75 If ``function`` is not callable.
75 KeyError
76 KeyError
76 If ``event`` is not one of the known events.
77 If ``event`` is not one of the known events.
77 """
78 """
78 if not callable(function):
79 if not callable(function):
79 raise TypeError('Need a callable, got %r' % function)
80 raise TypeError('Need a callable, got %r' % function)
80
81
81 callback_proto = available_events.get(event)
82 callback_proto = available_events.get(event)
82 if (callable(callback_proto) and
83 if (isfunction(callback_proto) and isfunction(function) and
83 len(getfullargspec(callback_proto).args) > 0 and
84 len(getfullargspec(callback_proto).args) > 0 and
84 len(getfullargspec(function).args) == 0):
85 len(getfullargspec(function).args) == 0):
85 # `callback_proto` requires args but `function` does not, so a
86 # `callback_proto` has args but `function` does not, so a
86 # compatibility wrapper is needed.
87 # compatibility wrapper is needed.
87 self.callbacks[event].append(_compatibility_wrapper_for(function))
88 self.callbacks[event].append(_compatibility_wrapper_for(function))
88 else:
89 else:
89 self.callbacks[event].append(function)
90 self.callbacks[event].append(function)
90
91
91 def unregister(self, event, function):
92 def unregister(self, event, function):
92 """Remove a callback from the given event."""
93 """Remove a callback from the given event."""
93 wrapper = compatibility_wrapper_functions.get(function)
94 wrapper = compatibility_wrapper_functions.get(function)
94 if wrapper:
95 if wrapper:
95 self.callbacks[event].remove(wrapper)
96 self.callbacks[event].remove(wrapper)
96 else:
97 else:
97 self.callbacks[event].remove(function)
98 self.callbacks[event].remove(function)
98
99
99 def trigger(self, event, *args, **kwargs):
100 def trigger(self, event, *args, **kwargs):
100 """Call callbacks for ``event``.
101 """Call callbacks for ``event``.
101
102
102 Any additional arguments are passed to all callbacks registered for this
103 Any additional arguments are passed to all callbacks registered for this
103 event. Exceptions raised by callbacks are caught, and a message printed.
104 event. Exceptions raised by callbacks are caught, and a message printed.
104 """
105 """
105 for func in self.callbacks[event][:]:
106 for func in self.callbacks[event][:]:
106 try:
107 try:
107 func(*args, **kwargs)
108 func(*args, **kwargs)
108 except Exception:
109 except Exception:
109 print("Error in callback {} (for {}):".format(func, event))
110 print("Error in callback {} (for {}):".format(func, event))
110 self.shell.showtraceback()
111 self.shell.showtraceback()
111
112
112 # event_name -> prototype mapping
113 # event_name -> prototype mapping
113 available_events = {}
114 available_events = {}
114
115
115 def _define_event(callback_proto):
116 def _define_event(callback_proto):
116 available_events[callback_proto.__name__] = callback_proto
117 available_events[callback_proto.__name__] = callback_proto
117 return callback_proto
118 return callback_proto
118
119
119 # ------------------------------------------------------------------------------
120 # ------------------------------------------------------------------------------
120 # Callback prototypes
121 # Callback prototypes
121 #
122 #
122 # No-op functions which describe the names of available events and the
123 # No-op functions which describe the names of available events and the
123 # signatures of callbacks for those events.
124 # signatures of callbacks for those events.
124 # ------------------------------------------------------------------------------
125 # ------------------------------------------------------------------------------
125
126
126 @_define_event
127 @_define_event
127 def pre_execute(result):
128 def pre_execute(result):
128 """Fires before code is executed in response to user/frontend action.
129 """Fires before code is executed in response to user/frontend action.
129
130
130 This includes comm and widget messages and silent execution, as well as user
131 This includes comm and widget messages and silent execution, as well as user
131 code cells.
132 code cells.
132
133
133 Parameters
134 Parameters
134 ----------
135 ----------
135 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
136 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
136 The object which will be returned as the execution result.
137 The object which will be returned as the execution result.
137 """
138 """
138 pass
139 pass
139
140
140 @_define_event
141 @_define_event
141 def pre_run_cell(result):
142 def pre_run_cell(result):
142 """Fires before user-entered code runs.
143 """Fires before user-entered code runs.
143
144
144 Parameters
145 Parameters
145 ----------
146 ----------
146 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
147 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
147 The object which will be returned as the execution result.
148 The object which will be returned as the execution result.
148 """
149 """
149 pass
150 pass
150
151
151 @_define_event
152 @_define_event
152 def post_execute(result):
153 def post_execute(result):
153 """Fires after code is executed in response to user/frontend action.
154 """Fires after code is executed in response to user/frontend action.
154
155
155 This includes comm and widget messages and silent execution, as well as user
156 This includes comm and widget messages and silent execution, as well as user
156 code cells.
157 code cells.
157
158
158 Parameters
159 Parameters
159 ----------
160 ----------
160 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
161 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
161 The object which will be returned as the execution result.
162 The object which will be returned as the execution result.
162 """
163 """
163 pass
164 pass
164
165
165 @_define_event
166 @_define_event
166 def post_run_cell(result):
167 def post_run_cell(result):
167 """Fires after user-entered code runs.
168 """Fires after user-entered code runs.
168
169
169 Parameters
170 Parameters
170 ----------
171 ----------
171 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
172 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
172 The object which will be returned as the execution result.
173 The object which will be returned as the execution result.
173 """
174 """
174 pass
175 pass
175
176
176 @_define_event
177 @_define_event
177 def shell_initialized(ip):
178 def shell_initialized(ip):
178 """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.
179 """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.
179
180
180 This is before extensions and startup scripts are loaded, so it can only be
181 This is before extensions and startup scripts are loaded, so it can only be
181 set by subclassing.
182 set by subclassing.
182
183
183 Parameters
184 Parameters
184 ----------
185 ----------
185 ip : :class:`~IPython.core.interactiveshell.InteractiveShell`
186 ip : :class:`~IPython.core.interactiveshell.InteractiveShell`
186 The newly initialised shell.
187 The newly initialised shell.
187 """
188 """
188 pass
189 pass
@@ -1,110 +1,94 b''
1 .. _events:
1 .. _events:
2 .. _callbacks:
2 .. _callbacks:
3
3
4 ==============
4 ==============
5 IPython Events
5 IPython Events
6 ==============
6 ==============
7
7
8 Extension code can register callbacks functions which will be called on specific
8 Extension code can register callbacks functions which will be called on specific
9 events within the IPython code. You can see the current list of available
9 events within the IPython code. You can see the current list of available
10 callbacks, and the parameters that will be passed with each, in the callback
10 callbacks, and the parameters that will be passed with each, in the callback
11 prototype functions defined in :mod:`IPython.core.callbacks`.
11 prototype functions defined in :mod:`IPython.core.callbacks`.
12
12
13 To register callbacks, use :meth:`IPython.core.events.EventManager.register`.
13 To register callbacks, use :meth:`IPython.core.events.EventManager.register`.
14 For example::
14 For example::
15
15
16 class VarWatcher(object):
16 class VarWatcher(object):
17 def __init__(self, ip):
17 def __init__(self, ip):
18 self.shell = ip
18 self.shell = ip
19 self.last_x = None
19 self.last_x = None
20
20
21 def pre_execute(self):
21 def pre_execute(self):
22 self.last_x = self.shell.user_ns.get('x', None)
22 self.last_x = self.shell.user_ns.get('x', None)
23
23
24 def post_execute(self, result):
24 def post_execute(self, result):
25 if result.error_before_exec:
25 if result.error_before_exec:
26 print('Error before execution: %s' % result.error_before_exec)
26 print('Error before execution: %s' % result.error_before_exec)
27 if self.shell.user_ns.get('x', None) != self.last_x:
27 if self.shell.user_ns.get('x', None) != self.last_x:
28 print("x changed!")
28 print("x changed!")
29
29
30 def load_ipython_extension(ip):
30 def load_ipython_extension(ip):
31 vw = VarWatcher(ip)
31 vw = VarWatcher(ip)
32 ip.events.register('pre_execute', vw.pre_execute)
32 ip.events.register('pre_execute', vw.pre_execute)
33 ip.events.register('post_execute', vw.post_execute)
33 ip.events.register('post_execute', vw.post_execute)
34
34
35
35
36 Events
36 Events
37 ======
37 ======
38
38
39 These are the events IPython will emit. Callbacks will be passed no arguments, unless otherwise specified.
39 These are the events IPython will emit. Callbacks will be passed no arguments, unless otherwise specified.
40
40
41 shell_initialized
41 shell_initialized
42 -----------------
42 -----------------
43
43
44 .. code-block:: python
44 .. code-block:: python
45
45
46 def shell_initialized(ipython):
46 def shell_initialized(ipython):
47 ...
47 ...
48
48
49 This event is triggered only once, at the end of setting up IPython.
49 This event is triggered only once, at the end of setting up IPython.
50 Extensions registered to load by default as part of configuration can use this to execute code to finalize setup.
50 Extensions registered to load by default as part of configuration can use this to execute code to finalize setup.
51 Callbacks will be passed the InteractiveShell instance.
51 Callbacks will be passed the InteractiveShell instance.
52
52
53 pre_run_cell
53 pre_run_cell
54 ------------
54 ------------
55
55
56 ``pre_run_cell`` fires prior to interactive execution (e.g. a cell in a notebook).
56 ``pre_run_cell`` fires prior to interactive execution (e.g. a cell in a notebook).
57 It can be used to note the state prior to execution, and keep track of changes.
57 It can be used to note the state prior to execution, and keep track of changes.
58 The object which will be returned as the execution result is provided as an
58 The object which will be returned as the execution result is provided as an
59 argument, even though the actual result is not yet available.
59 argument, even though the actual result is not yet available.
60
60
61 pre_execute
61 pre_execute
62 -----------
62 -----------
63
63
64 ``pre_execute`` is like ``pre_run_cell``, but is triggered prior to *any* execution.
64 ``pre_execute`` is like ``pre_run_cell``, but is triggered prior to *any* execution.
65 Sometimes code can be executed by libraries, etc. which
65 Sometimes code can be executed by libraries, etc. which
66 skipping the history/display mechanisms, in which cases ``pre_run_cell`` will not fire.
66 skipping the history/display mechanisms, in which cases ``pre_run_cell`` will not fire.
67 The object which will be returned as the execution result is provided as an
67 The object which will be returned as the execution result is provided as an
68 argument, even though the actual result is not yet available.
68 argument, even though the actual result is not yet available.
69
69
70 post_run_cell
70 post_run_cell
71 -------------
71 -------------
72
72
73 ``post_run_cell`` runs after interactive execution.
73 ``post_run_cell`` runs after interactive execution (e.g. a cell in a notebook).
74 It can be used to cleanup or notify or perform operations on any side effects
74 It can be used to cleanup or notify or perform operations on any side effects produced during execution.
75 produced during execution.
75 For instance, the inline matplotlib backend uses this event to display any figures created but not explicitly displayed during the course of the cell.
76 For instance, the inline matplotlib backend uses this event to display any
77 figures created but not explicitly displayed during the course of the cell.
78 The object which will be returned as the execution result is provided as an
76 The object which will be returned as the execution result is provided as an
79 argument.
77 argument.
80
78
81 post_execute
79 post_execute
82 ------------
80 ------------
83
81
84 The same as ``pre_execute``, ``post_execute`` is like ``post_run_cell``,
82 The same as ``pre_execute``, ``post_execute`` is like ``post_run_cell``,
85 but fires for *all* executions, not just interactive ones.
83 but fires for *all* executions, not just interactive ones.
86
84
87 finally_run_cell
88 -------------
89
90 ``finally_run_cell`` is like ``post_run_cell``, but fires after *all* executions
91 (even when, for example, a ``SyntaxError`` was raised).
92 Additionally, the execution result is provided as an argument.
93
94 finally_execute
95 ------------
96
97 ``finally_execute`` is like ``post_execute``, but fires after *all* executions
98 (even when, for example, a ``SyntaxError`` was raised).
99 Additionally, the execution result is provided as an argument.
100
101
85
102 .. seealso::
86 .. seealso::
103
87
104 Module :mod:`IPython.core.hooks`
88 Module :mod:`IPython.core.hooks`
105 The older 'hooks' system allows end users to customise some parts of
89 The older 'hooks' system allows end users to customise some parts of
106 IPython's behaviour.
90 IPython's behaviour.
107
91
108 :doc:`inputtransforms`
92 :doc:`inputtransforms`
109 By registering input transformers that don't change code, you can monitor
93 By registering input transformers that don't change code, you can monitor
110 what is being executed.
94 what is being executed.
General Comments 0
You need to be logged in to leave comments. Login now