1 """Infrastructure for registering and firing callbacks on application events. 2 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, 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. 7 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. 10 11 .. note:: 12 13 This API is experimental in IPython 2.0, and may be revised in future versions. 14 """ 15 16 17 class EventManager(object): 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 93 available_events = {} 94 95 def _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 107 def 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 116 def 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 127 def 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 136 def 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 147 def 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 159