manager.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. """Base class to manage comms"""
  2. # Copyright (c) IPython Development Team.
  3. # Distributed under the terms of the Modified BSD License.
  4. import sys
  5. import logging
  6. from traitlets.config import LoggingConfigurable
  7. from ipython_genutils.importstring import import_item
  8. from ipython_genutils.py3compat import string_types
  9. from traitlets import Instance, Unicode, Dict, Any, default
  10. from .comm import Comm
  11. class CommManager(LoggingConfigurable):
  12. """Manager for Comms in the Kernel"""
  13. kernel = Instance('ipykernel.kernelbase.Kernel')
  14. comms = Dict()
  15. targets = Dict()
  16. # Public APIs
  17. def register_target(self, target_name, f):
  18. """Register a callable f for a given target name
  19. f will be called with two arguments when a comm_open message is received with `target`:
  20. - the Comm instance
  21. - the `comm_open` message itself.
  22. f can be a Python callable or an import string for one.
  23. """
  24. if isinstance(f, string_types):
  25. f = import_item(f)
  26. self.targets[target_name] = f
  27. def unregister_target(self, target_name, f):
  28. """Unregister a callable registered with register_target"""
  29. return self.targets.pop(target_name)
  30. def register_comm(self, comm):
  31. """Register a new comm"""
  32. comm_id = comm.comm_id
  33. comm.kernel = self.kernel
  34. self.comms[comm_id] = comm
  35. return comm_id
  36. def unregister_comm(self, comm):
  37. """Unregister a comm, and close its counterpart"""
  38. # unlike get_comm, this should raise a KeyError
  39. comm = self.comms.pop(comm.comm_id)
  40. def get_comm(self, comm_id):
  41. """Get a comm with a particular id
  42. Returns the comm if found, otherwise None.
  43. This will not raise an error,
  44. it will log messages if the comm cannot be found.
  45. """
  46. try:
  47. return self.comms[comm_id]
  48. except KeyError:
  49. self.log.warning("No such comm: %s", comm_id)
  50. if self.log.isEnabledFor(logging.DEBUG):
  51. # don't create the list of keys if debug messages aren't enabled
  52. self.log.debug("Current comms: %s", list(self.comms.keys()))
  53. # Message handlers
  54. def comm_open(self, stream, ident, msg):
  55. """Handler for comm_open messages"""
  56. content = msg['content']
  57. comm_id = content['comm_id']
  58. target_name = content['target_name']
  59. f = self.targets.get(target_name, None)
  60. comm = Comm(comm_id=comm_id,
  61. primary=False,
  62. target_name=target_name,
  63. )
  64. self.register_comm(comm)
  65. if f is None:
  66. self.log.error("No such comm target registered: %s", target_name)
  67. else:
  68. try:
  69. f(comm, msg)
  70. return
  71. except Exception:
  72. self.log.error("Exception opening comm with target: %s", target_name, exc_info=True)
  73. # Failure.
  74. try:
  75. comm.close()
  76. except:
  77. self.log.error("""Could not close comm during `comm_open` failure
  78. clean-up. The comm may not have been opened yet.""", exc_info=True)
  79. def comm_msg(self, stream, ident, msg):
  80. """Handler for comm_msg messages"""
  81. content = msg['content']
  82. comm_id = content['comm_id']
  83. comm = self.get_comm(comm_id)
  84. if comm is None:
  85. return
  86. try:
  87. comm.handle_msg(msg)
  88. except Exception:
  89. self.log.error('Exception in comm_msg for %s', comm_id, exc_info=True)
  90. def comm_close(self, stream, ident, msg):
  91. """Handler for comm_close messages"""
  92. content = msg['content']
  93. comm_id = content['comm_id']
  94. comm = self.get_comm(comm_id)
  95. if comm is None:
  96. return
  97. del self.comms[comm_id]
  98. try:
  99. comm.handle_close(msg)
  100. except Exception:
  101. self.log.error('Exception in comm_close for %s', comm_id, exc_info=True)
  102. __all__ = ['CommManager']