-
Notifications
You must be signed in to change notification settings - Fork 123
/
Copy pathein-kernel.el
612 lines (538 loc) · 27.2 KB
/
ein-kernel.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
;;; ein-kernel.el --- Communicate with IPython notebook server -*- lexical-binding:t -*-
;; Copyright (C) 2012- Takafumi Arakaki
;; Author: Takafumi Arakaki <aka.tkf at gmail.com>
;; This file is NOT part of GNU Emacs.
;; ein-kernel.el is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; ein-kernel.el is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with ein-kernel.el. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; `ein:kernel' is the proxy class of notebook server state.
;; It agglomerates both the "kernel" and "session" objects of server described here
;; https://github.com/jupyter/jupyter/wiki/Jupyter-Notebook-Server-API
;; It may have been better to keep them separate to allow parallel reasoning with
;; the notebook server, but that time is past.
;;; Code:
(require 'ansi-color)
(require 'ein-core)
(require 'ein-classes)
(require 'ein-log)
(require 'ein-websocket)
(require 'ein-events)
(require 'ein-query)
(require 'ein-ipdb)
(declare-function ein:notebook-get-opened-notebook "ein-notebook")
(declare-function ein:notebooklist-get-buffer "ein-notebooklist")
(declare-function ein:notebooklist-reload "ein-notebooklist")
(defun ein:$kernel-session-url (kernel)
(concat "/api/sessions/" (ein:$kernel-session-id kernel)))
;;;###autoload
(defalias 'ein:kernel-url-or-port 'ein:$kernel-url-or-port)
;;;###autoload
(defalias 'ein:kernel-id 'ein:$kernel-kernel-id)
(make-obsolete-variable 'ein:pre-kernel-execute-functions nil "0.17.0")
(make-obsolete-variable 'ein:on-shell-reply-functions nil "0.17.0")
(make-obsolete-variable 'ein:on-kernel-connect-functions nil "0.17.0")
(defun ein:kernel-new (url-or-port path kernelspec base-url events &optional api-version)
(make-ein:$kernel
:url-or-port url-or-port
:path path
:kernelspec kernelspec
:events events
:api-version (or api-version 5)
:session-id (ein:utils-uuid)
:kernel-id nil
:websocket nil
:base-url base-url
:oinfo-cache (make-hash-table :test #'equal)
:username "username"
:msg-callbacks (make-hash-table :test 'equal)))
(defun ein:kernel-del (kernel)
"Destructor for `ein:$kernel'."
(ein:kernel-disconnect kernel))
(defun ein:kernel--get-msg (kernel msg-type content)
(list
:header (list
:msg_id (ein:utils-uuid)
:username (ein:$kernel-username kernel)
:session (ein:$kernel-session-id kernel)
:version "5.0"
:date (format-time-string "%Y-%m-%dT%T" (current-time)) ; ISO 8601 timestamp
:msg_type msg-type)
:metadata (make-hash-table)
:content content
:parent_header (make-hash-table)))
(cl-defun ein:kernel-session-p (kernel callback &optional iteration)
"Don't make any changes on the server side. CALLBACK with arity
2, kernel and a boolean whether session exists on server."
(unless iteration
(setq iteration 0))
(let ((session-id (ein:$kernel-session-id kernel)))
(ein:query-singleton-ajax
(ein:url (ein:$kernel-url-or-port kernel) "api/sessions" session-id)
:type "GET"
:parser #'ein:json-read
:complete (apply-partially #'ein:kernel-session-p--complete session-id)
:success (apply-partially #'ein:kernel-session-p--success kernel session-id callback)
:error (apply-partially #'ein:kernel-session-p--error kernel callback iteration))))
(cl-defun ein:kernel-session-p--complete (_session-id
&key data response
&allow-other-keys
&aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data)))
(ein:log 'debug "ein:kernel-session-p--complete %s" resp-string))
(cl-defun ein:kernel-session-p--error (kernel callback iteration
&key error-thrown _symbol-status data
&allow-other-keys)
(if (ein:aand (plist-get data :message) (cl-search "not found" it))
(when callback (funcall callback kernel nil))
(let* ((max-tries 3)
(tries-left (1- (- max-tries iteration))))
(ein:log 'verbose "ein:kernel-session-p--error [%s], %s tries left"
(car error-thrown) tries-left)
(if (> tries-left 0)
(ein:kernel-session-p kernel callback (1+ iteration))))))
(cl-defun ein:kernel-session-p--success (kernel session-id callback
&key data &allow-other-keys)
(let ((session-p (equal (plist-get data :id) session-id)))
(ein:log 'verbose "ein:kernel-session-p--success: session-id=%s session-p=%s"
session-id session-p)
(when callback (funcall callback kernel session-p))))
(cl-defun ein:kernel-restart-session (kernel)
"Server side delete of KERNEL session and subsequent restart with all new state"
(ein:kernel-delete-session
(lambda (kernel)
(ein:events-trigger (ein:$kernel-events kernel) 'status_restarting.Kernel)
(ein:kernel-retrieve-session kernel 0
(lambda (kernel)
(ein:events-trigger (ein:$kernel-events kernel)
'status_restarted.Kernel))))
:kernel kernel))
(cl-defun ein:kernel-retrieve-session (kernel &optional iteration callback)
"Formerly ein:kernel-start, but that was a misnomer.
The server 1. really starts a session (and an accompanying
kernel), and 2. may not even start a session if one exists for
the same path.
If picking up from where we last left off, that is, we restart
emacs and reconnect to same server, jupyter will hand us back the
original, still running session.
CALLBACK of arity 1, the kernel."
;; The server logic is here (could not find other documentation)
;; https://github.com/jupyter/notebook/blob/04a686dbaf9dfe553324a03cb9e6f778cf1e3da1/notebook/services/sessions/handlers.py#L56-L81
(unless iteration
(setq iteration 0))
(if (<= (ein:$kernel-api-version kernel) 2)
(error "Api %s unsupported" (ein:$kernel-api-version kernel))
(let ((kernel-id (ein:$kernel-kernel-id kernel))
(kernelspec (ein:$kernel-kernelspec kernel))
(path (ein:$kernel-path kernel)))
(ein:query-singleton-ajax
(ein:url (ein:$kernel-url-or-port kernel) "api/sessions")
:type "POST"
:data (ein:json-encode
`((path . ,path)
(type . "notebook")
,@(if kernelspec
`((kernel .
((name . ,(ein:$kernelspec-name kernelspec))
,@(if kernel-id
`((id . ,kernel-id)))))))))
:parser #'ein:json-read
:complete (apply-partially #'ein:kernel-retrieve-session--complete kernel callback)
:success (apply-partially #'ein:kernel-retrieve-session--success kernel callback)
:error (apply-partially #'ein:kernel-retrieve-session--error kernel iteration callback)))))
(cl-defun ein:kernel-retrieve-session--complete
(_kernel _callback
&key data response
&allow-other-keys
&aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data)))
(ein:log 'debug "ein:kernel-retrieve-session--complete %s" resp-string))
(cl-defun ein:kernel-retrieve-session--error
(kernel iteration callback
&key error-thrown _symbol-status &allow-other-keys)
(let* ((max-tries 3)
(tries-left (1- (- max-tries iteration))))
(ein:log 'verbose "ein:kernel-retrieve-session--error [%s], %s tries left"
(car error-thrown) tries-left)
(sleep-for 0 (* (1+ iteration) 500))
(if (> tries-left 0)
(ein:kernel-retrieve-session kernel (1+ iteration) callback))))
(cl-defun ein:kernel-retrieve-session--success (kernel callback &key data &allow-other-keys)
(let ((session-id (plist-get data :id)))
(if (plist-get data :kernel)
(setq data (plist-get data :kernel)))
(cl-destructuring-bind (&key id &allow-other-keys) data
(ein:log 'verbose "ein:kernel-retrieve-session--success: kernel-id=%s session-id=%s"
id session-id)
(setf (ein:$kernel-kernel-id kernel) id)
(setf (ein:$kernel-session-id kernel) session-id)
(setf (ein:$kernel-ws-url kernel) (ein:kernel--ws-url (ein:$kernel-url-or-port kernel)))
(setf (ein:$kernel-kernel-url kernel)
(concat (file-name-as-directory (ein:$kernel-base-url kernel)) id)))
(ein:kernel-start-websocket kernel callback)))
(defun ein:kernel-reconnect-session (kernel &optional callback)
"If session does not already exist, prompt user to create a new session.
Otherwise, return extant session.
`ein:kernel-retrieve-session; both retrieves and creates.
CALLBACK takes one argument kernel (e.g., execute cell now that
we're reconnected)."
(ein:kernel-disconnect kernel)
(ein:kernel-session-p
kernel
(apply-partially
(lambda (callback* kernel session-p)
(when (or session-p
(and (not noninteractive) (y-or-n-p "Session not found. Restart?")))
(ein:events-trigger (ein:$kernel-events kernel) 'status_reconnecting.Kernel)
(ein:kernel-retrieve-session
kernel 0
(apply-partially
(lambda (callback** kernel)
(ein:events-trigger (ein:$kernel-events kernel)
'status_reconnected.Kernel)
(when callback** (funcall callback** kernel)))
callback*))))
callback)))
(defun ein:kernel--ws-url (url-or-port)
"Assuming URL-OR-PORT already normalized by `ein:url'.
See https://github.com/ipython/ipython/pull/3307"
(let* ((parsed-url (url-generic-parse-url url-or-port))
(protocol (if (string= (url-type parsed-url) "https") "wss" "ws")))
(format "%s://%s:%s%s"
protocol
(url-host parsed-url)
(url-port parsed-url)
(url-filename parsed-url))))
(defun ein:kernel--handle-websocket-reply (kernel _ws frame)
(-when-let* ((packet (websocket-frame-payload frame))
(channel (plist-get (ein:json-read-from-string packet) :channel)))
(cond ((string-equal channel "iopub")
(ein:kernel--handle-iopub-reply kernel packet))
((string-equal channel "shell")
(ein:kernel--handle-shell-reply kernel packet))
((string-equal channel "stdin")
(ein:kernel--handle-stdin-reply kernel packet))
(t (ein:log 'warn "Received reply from unforeseen channel %s" channel)))))
(defun ein:start-single-websocket (kernel open-callback)
"OPEN-CALLBACK (kernel) (e.g., execute cell)"
(let ((ws-url (concat (ein:$kernel-ws-url kernel)
(ein:$kernel-kernel-url kernel)
"/channels?session_id="
(ein:$kernel-session-id kernel))))
(ein:log 'verbose "WS start: %s" ws-url)
(setf (ein:$kernel-websocket kernel)
(ein:websocket ws-url kernel
(apply-partially #'ein:kernel--handle-websocket-reply kernel)
(lambda (ws)
(-if-let* ((websocket (websocket-client-data ws))
(kernel (ein:$websocket-kernel websocket)))
(unless (ein:$websocket-closed-by-client websocket)
(ein:log 'verbose "WS closed unexpectedly: %s" (websocket-url ws))
(ein:kernel-disconnect kernel))
(ein:log 'error "ein:start-single-websocket: on-close no client data for %s." (websocket-url ws))))
(apply-partially
(lambda (cb ws)
(-if-let* ((websocket (websocket-client-data ws))
(kernel (ein:$websocket-kernel websocket)))
(progn
(awhen (and (ein:kernel-live-p kernel) cb)
(funcall it kernel))
(ein:log 'verbose "WS opened: %s" (websocket-url ws)))
(ein:log 'error "ein:start-single-websocket: on-open no client data for %s." (websocket-url ws))))
open-callback)))))
(defun ein:kernel-start-websocket (kernel callback)
(cond ((<= (ein:$kernel-api-version kernel) 2)
(error "Api version %s unsupported" (ein:$kernel-api-version kernel)))
(t (ein:start-single-websocket kernel callback))))
(defun ein:kernel-on-connect (_kernel _content _metadata)
(ein:log 'info "Kernel connect_request_reply received."))
(defun ein:kernel-disconnect (kernel)
"Close websocket connection to running kernel, but do not
delete the kernel on the server side"
(ein:events-trigger (ein:$kernel-events kernel) 'status_disconnected.Kernel)
(aif (ein:$kernel-websocket kernel)
(progn (ein:websocket-close it)
(setf (ein:$kernel-websocket kernel) nil))))
(defun ein:kernel-live-p (kernel)
(and (ein:$kernel-p kernel)
(ein:aand (ein:$kernel-websocket kernel) (ein:websocket-open-p it))))
(defun ein:kernel-when-ready (kernel callback)
"Execute CALLBACK of arity 1 (the kernel) when KERNEL is ready.
Warn user otherwise."
(if (ein:kernel-live-p kernel)
(funcall callback kernel)
(ein:log 'verbose "Kernel %s unavailable" (ein:$kernel-kernel-id kernel))
(ein:kernel-reconnect-session kernel callback)))
(defun ein:kernel-object-info-request (kernel objname callbacks &optional cursor-pos detail-level)
"Send object info request of OBJNAME to KERNEL.
When calling this method pass a CALLBACKS structure of the form:
(:object_info_reply (FUNCTION . ARGUMENT))
Call signature::
(`funcall' FUNCTION ARGUMENT CONTENT METADATA)
CONTENT and METADATA are given by `object_info_reply' message.
`object_info_reply' message is documented here:
http://ipython.org/ipython-doc/dev/development/messaging.html#object-information
"
(cl-assert (ein:kernel-live-p kernel) nil "object_info_reply: Kernel is not active.")
(when objname
(if (<= (ein:$kernel-api-version kernel) 2)
(error "Api version %s unsupported" (ein:$kernel-api-version kernel)))
(let* ((content (if (< (ein:$kernel-api-version kernel) 5)
(list
;; :text ""
:oname (format "%s" objname)
:cursor_pos (or cursor-pos 0)
:detail_level (or detail-level 0))
(list
:code (format "%s" objname)
:cursor_pos (or cursor-pos 0)
:detail_level (or detail-level 0))))
(msg (ein:kernel--get-msg kernel "inspect_request"
(append content (list :detail_level 1))))
(msg-id (plist-get (plist-get msg :header) :msg_id)))
(ein:websocket-send-shell-channel kernel msg)
(ein:kernel-set-callbacks-for-msg kernel msg-id callbacks))))
(cl-defun ein:kernel-execute (kernel code &optional callbacks
&key
(silent t)
(store-history t)
(user-expressions (make-hash-table))
(allow-stdin t)
(stop-on-error nil))
"Execute CODE on KERNEL.
The CALLBACKS plist looks like:
(:execute_reply EXECUTE-REPLY-CALLBACK
:output OUTPUT-CALLBACK
:clear_output CLEAR-OUTPUT-CALLBACK
:set_next_input SET-NEXT-INPUT)
Right hand sides ending -CALLBACK above are of the form (FUNCTION
ARG1 ... ARGN).
(Hindsight: this was all much better implemented using `apply-partially')
Return randomly generated MSG-ID tag uniquely identifying
expectation of a kernel response."
(cl-assert (ein:kernel-live-p kernel) nil "execute_reply: Kernel is not active.")
(let* ((content (list
:code code
:silent (or silent json-false)
:store_history (or store-history json-false)
:user_expressions user-expressions
:allow_stdin allow-stdin
:stop_on_error (or stop-on-error json-false)))
(msg (ein:kernel--get-msg kernel "execute_request" content))
(msg-id (plist-get (plist-get msg :header) :msg_id)))
(ein:log 'debug "ein:kernel-execute: code=%s msg_id=%s" code msg-id)
(ein:websocket-send-shell-channel kernel msg)
(ein:kernel-set-callbacks-for-msg kernel msg-id callbacks)
(unless silent
(mapc #'ein:funcall-packed
(ein:$kernel-after-execute-hook kernel)))
msg-id))
(defun ein:kernel-connect-request (kernel callbacks)
"Request basic information for a KERNEL.
When calling this method pass a CALLBACKS structure of the form::
(:connect_reply (FUNCTION . ARGUMENT))
Call signature::
(`funcall' FUNCTION ARGUMENT CONTENT METADATA)
CONTENT and METADATA are given by `kernel_info_reply' message.
`connect_request' message is documented here:
http://ipython.org/ipython-doc/dev/development/messaging.html#connect
Example::
(ein:kernel-connect-request
(ein:get-kernel)
\\='(:kernel_connect_reply (message . \"CONTENT: %S\\nMETADATA: %S\")))
"
;(cl-assert (ein:kernel-live-p kernel) nil "connect_reply: Kernel is not active.")
(let* ((msg (ein:kernel--get-msg kernel "connect_request" (make-hash-table)))
(msg-id (plist-get (plist-get msg :header) :msg_id)))
(ein:websocket-send-shell-channel kernel msg)
(ein:kernel-set-callbacks-for-msg kernel msg-id callbacks)
msg-id))
(defun ein:kernel-interrupt (kernel)
(when (ein:kernel-live-p kernel)
(ein:log 'info "Interrupting kernel")
(ein:query-singleton-ajax
(ein:url (ein:$kernel-url-or-port kernel)
(ein:$kernel-kernel-url kernel)
"interrupt")
:type "POST"
:success (lambda (&rest _ignore)
(ein:log 'info "Sent interruption command.")))))
(defvar ein:force-sync)
(declare-function ein:content-query-sessions "ein-contents-api")
(cl-defun ein:kernel-delete-session (&optional callback
&key url-or-port path kernel
&aux (session-id))
"Regardless of success or error, we clear all state variables of
kernel and funcall CALLBACK (kernel)"
(cond (kernel
(setq url-or-port (ein:$kernel-url-or-port kernel))
(setq path (ein:$kernel-path kernel))
(setq session-id (ein:$kernel-session-id kernel)))
((and url-or-port path)
(aif (ein:notebook-get-opened-notebook url-or-port path)
(progn
(setq kernel (ein:$notebook-kernel it))
(setq session-id (ein:$kernel-session-id kernel)))
(let ((ein:force-sync t))
(ein:content-query-sessions
url-or-port
(lambda (session-hash)
(setq session-id (car (gethash path session-hash))))
nil))))
(t (error "ein:kernel-delete-session: need kernel, or url-or-port and path")))
(if session-id
(ein:query-singleton-ajax
(ein:url url-or-port "api/sessions" session-id)
:type "DELETE"
:complete (apply-partially #'ein:kernel-delete-session--complete kernel session-id callback)
:error (apply-partially #'ein:kernel-delete-session--error session-id nil)
:success (apply-partially #'ein:kernel-delete-session--success session-id
(aif (ein:notebooklist-get-buffer url-or-port)
(buffer-local-value 'ein:%notebooklist% it))
nil))
(ein:log 'verbose "ein:kernel-delete-session: no sessions found for %s" path)
(when callback
(funcall callback kernel))))
(cl-defun ein:kernel-delete-session--error (session-id _callback
&key _response error-thrown
&allow-other-keys)
(ein:log 'error "ein:kernel-delete-session--error %s: ERROR %s DATA %s"
session-id (car error-thrown) (cdr error-thrown)))
(cl-defun ein:kernel-delete-session--success (session-id nblist _callback
&key _data _symbol-status _response
&allow-other-keys)
(ein:log 'verbose "ein:kernel-delete-session--success: %s deleted" session-id)
(when nblist
(ein:notebooklist-reload nblist)))
(cl-defun ein:kernel-delete-session--complete (kernel _session-id callback
&key data response
&allow-other-keys
&aux (resp-string (format "STATUS: %s DATA: %s" (request-response-status-code response) data)))
(ein:log 'verbose "ein:kernel-delete-session--complete %s" resp-string)
(when kernel
(ein:kernel-disconnect kernel))
(when callback (funcall callback kernel)))
;; Reply handlers.
(defun ein:kernel-get-callbacks-for-msg (kernel msg-id)
(gethash msg-id (ein:$kernel-msg-callbacks kernel)))
(defun ein:kernel-set-callbacks-for-msg (kernel msg-id callbacks)
"Set up promise for MSG-ID."
(puthash msg-id callbacks (ein:$kernel-msg-callbacks kernel)))
(defun ein:kernel--handle-stdin-reply (kernel packet)
(cl-destructuring-bind
(&key header _parent_header _metadata content &allow-other-keys)
(ein:json-read-from-string packet)
(let ((msg-type (plist-get header :msg_type))
(msg-id (plist-get header :msg_id))
(password (plist-get content :password)))
(ein:log 'debug "ein:kernel--handle-stdin-reply: msg_type=%s msg_id=%s"
msg-type msg-id)
(cond ((string-equal msg-type "input_request")
(if (not (eql password :json-false))
(let* ((passwd (read-passwd (plist-get content :prompt)))
(content (list :value passwd))
(msg (ein:kernel--get-msg kernel "input_reply" content)))
(ein:websocket-send-stdin-channel kernel msg))
(cond ((string-match "^\\(ipdb> \\|(Pdb) \\)"
(plist-get content :prompt))
(aif (ein:ipdb-get-session kernel)
(pop-to-buffer (ein:$ipdb-session-buffer it))
(let* ((url-or-port (ein:$kernel-url-or-port kernel))
(path (ein:$kernel-path kernel))
(notebook (ein:notebook-get-opened-notebook
url-or-port path)))
(ein:ipdb-start-session
kernel
(match-string 1 (plist-get content :prompt))
notebook))))
(t (let* ((in (read-string (plist-get content :prompt)))
(content (list :value in))
(msg (ein:kernel--get-msg kernel "input_reply" content)))
(ein:websocket-send-stdin-channel kernel msg))))))))))
(defun ein:kernel--handle-payload (kernel callbacks payload)
(cl-loop with events = (ein:$kernel-events kernel)
for p in (append payload nil)
for text = (or (plist-get p :text) (plist-get (plist-get p :data) :text/plain))
for source = (plist-get p :source)
if (member source '("IPython.kernel.zmq.page.page"
"IPython.zmq.page.page"
"page"))
do (unless (equal (ein:trim text) "")
(ein:events-trigger
events 'open_with_text.Pager (list :text text)))
else if
(member
source
'("IPython.kernel.zmq.zmqshell.ZMQInteractiveShell.set_next_input"
"IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input"
"set_next_input"))
do (let ((cb (plist-get callbacks :set_next_input)))
(when cb (ein:funcall-packed cb text)))))
(defun ein:kernel--handle-shell-reply (kernel packet)
(cl-destructuring-bind
(&key header content metadata parent_header &allow-other-keys)
(ein:json-read-from-string packet)
(let* ((msg-type (plist-get header :msg_type))
(msg-id (plist-get parent_header :msg_id))
(callbacks (ein:kernel-get-callbacks-for-msg kernel msg-id)))
(ein:log 'debug "ein:kernel--handle-shell-reply: msg_type=%s msg_id=%s"
msg-type msg-id)
(aif (plist-get callbacks (intern-soft (format ":%s" msg-type)))
(ein:funcall-packed it content metadata)
(ein:log 'info "ein:kernel--handle-shell-reply: No :%s callback for msg_id=%s"
msg-type msg-id))
(aif (plist-get content :payload)
(ein:kernel--handle-payload kernel callbacks it))
(let ((events (ein:$kernel-events kernel)))
(ein:case-equal msg-type
(("execute_reply")
(aif (plist-get content :execution_count)
(ein:events-trigger events 'execution_count.Kernel it))))))))
(defun ein:kernel--handle-iopub-reply (kernel packet)
(cl-destructuring-bind
(&key content metadata parent_header header &allow-other-keys)
(ein:json-read-from-string packet)
(let* ((msg-type (plist-get header :msg_type))
(msg-id (plist-get header :msg_id))
(parent-id (plist-get parent_header :msg_id))
(callbacks (ein:kernel-get-callbacks-for-msg kernel parent-id))
(events (ein:$kernel-events kernel)))
(ein:log 'debug
"ein:kernel--handle-iopub-reply: msg_type=%s msg_id=%s parent_id=%s"
msg-type msg-id parent-id)
(ein:case-equal msg-type
(("stream" "display_data" "pyout" "pyerr" "error" "execute_result")
(aif (plist-get callbacks :output) ;; ein:cell--handle-output
(ein:funcall-packed it msg-type content metadata)
(ein:log 'warn (concat "ein:kernel--handle-iopub-reply: "
"No :output callback for parent_id=%s")
parent-id))
(when (ein:ipdb-get-session kernel)
(ein:ipdb--handle-iopub-reply kernel packet)))
(("status")
(ein:case-equal (plist-get content :execution_state)
(("busy")
(ein:events-trigger events 'status_busy.Kernel))
(("idle")
(ein:events-trigger events 'status_idle.Kernel)
(awhen (ein:ipdb-get-session kernel)
(ein:ipdb-stop-session it)))
(("dead")
(ein:kernel-disconnect kernel)
(awhen (ein:ipdb-get-session kernel)
(ein:ipdb-stop-session it)))))
(("data_pub")
(ein:log 'verbose "ein:kernel--handle-iopub-reply: data_pub %S" packet))
(("clear_output")
(aif (plist-get callbacks :clear_output)
(ein:funcall-packed it content metadata)
(ein:log 'info (concat "ein:kernel--handle-iopub-reply: "
"No :clear_output callback for parent_id=%s")
parent-id)))))))
(provide 'ein-kernel)
;;; ein-kernel.el ends here