Есть ли способ зарегистрировать обратный вызов обработчика сообщений для объекта google.colab.kernel.comms? - PullRequest
1 голос
/ 05 августа 2020

Пример в advanced_outputs.ipynb, отправляющий данные из ядра клиенту , - это почти то, что мне нужно. И этот пример ссылается на документацию Jupyter comms . Но прав ли я сейчас, хотя я могу открыть новое соединение с клиентом из ядра с прикрепленными данными, я пока не могу отправлять несколько сообщений по одному и тому же соединению?

Рабочий процесс, который, как мне кажется, мне нужен, например , в javascript:

  (async () => {
    google.colab.kernel.comms.registerTarget('example_comms', (comm, message) => {
      document.body.appendChild(document.createTextNode('comm opened.'))
      comm.send('comm opened');
      console.log(comm)
      comm.on_msg(function(msg) {
        var p = document.createElement("p")
        var t = document.createTextNode(message.data.foo)
        p.appendChild(t)
        document.body.appendChild(p)
      })
    });
  })()

и в python

channel = comm.Comm(target_name='example_comms')
def handle_message(msg):
  print(f"python received message: {msg['content']['data']}")
channel.on_msg(handle_message)
for i in range(10):
  channel.send(data={'foo': i})

Но похоже, что google.colab.kernel.comms.Comm не экспорт on_msg или какой-либо другой способ отправки нескольких сообщений по одному и тому же каналу? У объекта ipykernel.comm.Comm есть метод send, но я не думаю, что его можно использовать в настоящее время?

Вот блокнот с минимальными изменениями из примера advanced_outputs. Его запуск вызывает ошибку в консоли, что on_msg не определено.

Я подозреваю (но, по общему признанию, еще не измерил производительность, чтобы подтвердить), что создание нового соединения для каждого сообщения - это накладные расходы, которые я хочу избегать. Любая помощь приветствуется!

1 Ответ

0 голосов
/ 28 августа 2020

Думаю, это то, что вы ищете:

from IPython.display import Javascript

comm_ = None
def target_func(comm, msg):
  global comm_
  comm_ = comm  # The connection you are looking for !

  ## Register handler for later messages
  @comm.on_msg
  def _recv(msg):
    # Use msg['content']['data'] for the data in the message
    comm.send({'echo': msg['content']['data']})
        
  @comm.on_close
  def _close(msg):
    global comm_
    comm_ = None
get_ipython().kernel.comm_manager.register_target('comm_target', target_func)

Javascript('''
(async () => {
  const channel = await google.colab.kernel.comms.open('comm_target');

  (async function() {
    for await (const message of channel.messages) {
      console.log(message.data)
    }
  })();
})()
''')

Вопреки тому, что вы делаете, он устанавливает канал связи от клиента к ядру, что, на мой взгляд, более естественно. Затем на этом этапе вы получаете постоянное соединение comm_, которое можно использовать для отправки сообщений на интерфейс javascript:

comm_.send(data="test")

Также можно получить список всех открытых подключений, выполняющих:

get_ipython().kernel.comm_manager.comms

Таким образом, можно избавиться от уродливой глобальной переменной и вместо этого отслеживать изменение этого атрибута. Его также можно использовать, например, для ожидания установления соединения.

Обратите внимание, что в случае Jupyter Notebook код Python такой же, но не код Javascript. Вот соответствующая реализация:

const channel = await Jupyter.notebook.kernel.comm_manager.new_comm('comm_target')

channel.on_msg(function(message) {
    console.log(message.content.data);
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...