Obtendo a saída do kernel jupyter no script (i) python

Eu gostaria de abrir vários kernels de dentro de uma única sessão ipython, executar código nesses kernels e coletar os resultados. Mas não consigo descobrir como coletar os resultados, ou até mesmo ver stdout / stderr. Como posso fazer essas coisas?

O que eu tenho até agora

Eu gerenciei as duas primeiras etapas (abra os kernels e execute o código neles) com um código como o seguinte:

from jupyter_client import MultiKernelManager
kernelmanager = MultiKernelManager()
remote_id = kernelmanager.start_kernel('python3')
remote_kernel = kernelmanager.get_kernel(remote_id)
remote = remote_kernel.client()
sent_msg_id = remote.execute('2+2')

[Congratulo-me com todas as sugestões de como melhorar isso ou de como fechar esses kernels e clientes.]

Aqui,python3 pode ser o nome de qualquer um dos kernels que configurei (que pode ser listado na linha de comando comjupyter-kernelspec list) E parece que sou capaz de executar qualquer código razoável no lugar de'2+2'. Por exemplo, eu posso gravar em um arquivo, e esse arquivo é realmente criado.

Agora, o problema é como obter o resultado. Eu posso receber alguma mensagem aparentemente relacionada como

reply = remote.get_shell_msg(sent_msg_id)

Essa resposta é um dicionário como este:

{'buffers': [],
 'content': {'execution_count': 2,
  'payload': [],
  'status': 'ok',
  'user_expressions': {}},
 'header': {'date': datetime.datetime(2015, 10, 19, 14, 34, 34, 378577),
  'msg_id': '98e216b4-3251-4085-8eb1-bfceedbae3b0',
  'msg_type': 'execute_reply',
  'session': 'ca4d615d-82b7-487f-88ff-7076c2bdd109',
  'username': 'me',
  'version': '5.0'},
 'metadata': {'dependencies_met': True,
  'engine': '868de9dd-054b-4630-99b7-0face61915a6',
  'started': '2015-10-19T14:34:34.265718',
  'status': 'ok'},
 'msg_id': '98e216b4-3251-4085-8eb1-bfceedbae3b0',
 'msg_type': 'execute_reply',
 'parent_header': {'date': datetime.datetime(2015, 10, 19, 14, 34, 34, 264508),
  'msg_id': '2674c61a-c79a-48a6-b88a-1f2e8da68a80',
  'msg_type': 'execute_request',
  'session': '767ae562-38d6-41a3-a9dc-6faf37d83222',
  'username': 'me',
  'version': '5.0'}}

Isso está documentado emMensagens em Jupyter. O que não está documentado é como realmente usar isso - ou seja, quais funções eu uso, quando e onde encontro mensagens, etc.essa questão e sua resposta, que possui informações úteis úteis, mas não me leva à resposta. Eesta resposta também não obtém nenhuma saída útil.

Então, por exemplo, tentei também obter a mensagem com omsg_id dado no resultado acima, mas apenas trava. Eu tentei de tudo em que consigo pensar, mas não consigo descobrir como recuperar algo do kernel. Como eu faço isso? Posso transferir dados de volta do kernel em algum tipo de string? Posso ver seu stdout e stderr?

fundo

Estou escrevendo uma mágica do ipython para executar um trecho de código em kernels remotos. [Editar: isso agora existe e está disponívelaqui.] A idéia é que eu tenha um notebook no meu laptop e colete dados de vários servidores remotos apenas com uma pequena célula mágica como esta:

%%remote_exec -kernels server1,server2
2+2
! hostname

eu usoremote_ikernel para conectar-se a esses kernels remotos de maneira fácil e automática. Isso parece funcionar muito bem; Eu tenho meu comando mágico com todos os seus sinos e assobios funcionando muito bem, abrindo esses kernels remotos e executando o código. Agora, desejo que alguns dados do controle remoto sejam enviados de volta para o meu laptop - presumivelmente serializando-os de alguma maneira. No momento eu achopickle.dumps epickle.loads seria perfeito para esta parte; Eu só tenho que obter esses bytes criados e usados por essas funções de um kernel para outro. Prefiro não usar arquivos reais para a decapagem, embora isso seja potencialmente aceitável.

Editar:

Parece que é possível com alguma monstruosidade como esta:

remote.get_shell_msg(remote.execute('import pickle'))
sent_msg_id = remote.execute('a=2+2', user_expressions={'output':'pickle.dumps({"a":a})'})
reply = remote.get_shell_msg(sent_msg_id)
output_bytes = reply['content']['user_expressions']['output']['data']['text/plain']
variable_dict = pickle.loads(eval(output_bytes))

E agora,variable_dict['a'] é apenas4. Note, no entanto, queoutput_bytes é uma string que representa esses bytes, então deve serevaled. Isso parece ridículo (e ainda não mostra como eu ficaria stdout). Existe uma maneira melhor? E como eu fico stdout?

Edição 2:

Embora eu esteja insatisfeito com meu hack acima, eu o usei com sucesso para escrever um pequeno módulo chamadoremote_exec hospedado no github, conforme descrito acima. O módulo fornece uma pequena mágica do ipython que eu posso usar para executar o código remotamente em um ou mais outros kernels. Este é um processo mais ou menos automático com o qual estou definitivamente satisfeito - exceto pelo conhecimento incômodo do que está acontecendo por baixo.

questionAnswers(2)

yourAnswerToTheQuestion