events.py 5.5 KB

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