comm.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. define([
  4. 'jquery',
  5. 'base/js/utils',
  6. ], function($, utils) {
  7. "use strict";
  8. //-----------------------------------------------------------------------
  9. // CommManager class
  10. //-----------------------------------------------------------------------
  11. var CommManager = function (kernel) {
  12. this.comms = {};
  13. this.targets = {};
  14. if (kernel !== undefined) {
  15. this.init_kernel(kernel);
  16. }
  17. };
  18. CommManager.prototype.init_kernel = function (kernel) {
  19. /**
  20. * connect the kernel, and register message handlers
  21. */
  22. this.kernel = kernel;
  23. var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
  24. for (var i = 0; i < msg_types.length; i++) {
  25. var msg_type = msg_types[i];
  26. kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
  27. }
  28. };
  29. CommManager.prototype.new_comm = function (target_name, data, callbacks, metadata, comm_id, buffers) {
  30. /**
  31. * Create a new Comm, register it, and open its Kernel-side counterpart
  32. * Mimics the auto-registration in `Comm.__init__` in the Jupyter Comm.
  33. *
  34. * argument comm_id is optional
  35. */
  36. var comm = new Comm(target_name, comm_id);
  37. this.register_comm(comm);
  38. comm.open(data, callbacks, metadata, buffers);
  39. return comm;
  40. };
  41. CommManager.prototype.register_target = function (target_name, f) {
  42. /**
  43. * Register a target function for a given target name
  44. */
  45. this.targets[target_name] = f;
  46. };
  47. CommManager.prototype.unregister_target = function (target_name, f) {
  48. /**
  49. * Unregister a target function for a given target name
  50. */
  51. delete this.targets[target_name];
  52. };
  53. CommManager.prototype.register_comm = function (comm) {
  54. /**
  55. * Register a comm in the mapping
  56. */
  57. this.comms[comm.comm_id] = Promise.resolve(comm);
  58. comm.kernel = this.kernel;
  59. return comm.comm_id;
  60. };
  61. CommManager.prototype.unregister_comm = function (comm) {
  62. /**
  63. * Remove a comm from the mapping
  64. */
  65. delete this.comms[comm.comm_id];
  66. };
  67. // comm message handlers
  68. CommManager.prototype.comm_open = function (msg) {
  69. var content = msg.content;
  70. var that = this;
  71. var comm_id = content.comm_id;
  72. this.comms[comm_id] = utils.load_class(content.target_name, content.target_module,
  73. this.targets).then(function(target) {
  74. var comm = new Comm(content.target_name, comm_id);
  75. comm.kernel = that.kernel;
  76. try {
  77. var response = target(comm, msg);
  78. } catch (e) {
  79. comm.close();
  80. that.unregister_comm(comm);
  81. var wrapped_error = new utils.WrappedError("Exception opening new comm", e);
  82. console.error(wrapped_error);
  83. return Promise.reject(wrapped_error);
  84. }
  85. // Regardless of the target return value, we need to
  86. // then return the comm
  87. return Promise.resolve(response).then(function() {return comm;});
  88. }, utils.reject('Could not open comm', true));
  89. return this.comms[comm_id];
  90. };
  91. CommManager.prototype.comm_close = function(msg) {
  92. var content = msg.content;
  93. if (this.comms[content.comm_id] === undefined) {
  94. console.error('Comm promise not found for comm id ' + content.comm_id);
  95. return;
  96. }
  97. var that = this;
  98. this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
  99. that.unregister_comm(comm);
  100. try {
  101. comm.handle_close(msg);
  102. } catch (e) {
  103. console.log("Exception closing comm: ", e, e.stack, msg);
  104. }
  105. // don't return a comm, so that further .then() functions
  106. // get an undefined comm input
  107. });
  108. return this.comms[content.comm_id];
  109. };
  110. CommManager.prototype.comm_msg = function(msg) {
  111. var content = msg.content;
  112. if (this.comms[content.comm_id] === undefined) {
  113. console.error('Comm promise not found for comm id ' + content.comm_id);
  114. return;
  115. }
  116. this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
  117. return (Promise.resolve(comm.handle_msg(msg))
  118. .catch(utils.reject('Exception handling comm message'))
  119. .then(function() {return comm;}));
  120. });
  121. return this.comms[content.comm_id];
  122. };
  123. //-----------------------------------------------------------------------
  124. // Comm base class
  125. //-----------------------------------------------------------------------
  126. var Comm = function (target_name, comm_id) {
  127. this.target_name = target_name;
  128. this.comm_id = comm_id || utils.uuid();
  129. this._msg_callback = this._close_callback = null;
  130. };
  131. // methods for sending messages
  132. Comm.prototype.open = function (data, callbacks, metadata, buffers) {
  133. var content = {
  134. comm_id : this.comm_id,
  135. target_name : this.target_name,
  136. data : data || {},
  137. };
  138. return this.kernel.send_shell_message("comm_open", content, callbacks, metadata, buffers);
  139. };
  140. Comm.prototype.send = function (data, callbacks, metadata, buffers) {
  141. var content = {
  142. comm_id : this.comm_id,
  143. data : data || {},
  144. };
  145. return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata, buffers);
  146. };
  147. Comm.prototype.close = function (data, callbacks, metadata, buffers) {
  148. var content = {
  149. comm_id : this.comm_id,
  150. data : data || {},
  151. };
  152. return this.kernel.send_shell_message("comm_close", content, callbacks, metadata, buffers);
  153. };
  154. // methods for registering callbacks for incoming messages
  155. Comm.prototype._register_callback = function (key, callback) {
  156. this['_' + key + '_callback'] = callback;
  157. };
  158. Comm.prototype.on_msg = function (callback) {
  159. this._register_callback('msg', callback);
  160. };
  161. Comm.prototype.on_close = function (callback) {
  162. this._register_callback('close', callback);
  163. };
  164. // methods for handling incoming messages
  165. Comm.prototype._callback = function (key, msg) {
  166. var callback = this['_' + key + '_callback'];
  167. if (callback) {
  168. try {
  169. return callback(msg);
  170. } catch (e) {
  171. console.log("Exception in Comm callback", e, e.stack, msg);
  172. }
  173. }
  174. };
  175. Comm.prototype.handle_msg = function (msg) {
  176. return this._callback('msg', msg);
  177. };
  178. Comm.prototype.handle_close = function (msg) {
  179. this._callback('close', msg);
  180. };
  181. return {
  182. 'CommManager': CommManager,
  183. 'Comm': Comm
  184. };
  185. });