Skip to content

Commit e89e528

Browse files
committed
Implement interrupt_request messages
close #495 * jupyter-base.el (jupyter-session-endpoints): Consider control_port in the connection info of a session. (jupyter-channel-from-request-type): New function. * jupyter-client.el (jupyter-interrupt-kernel): Send an interrupt_request when the kernel supports it. (jupyter-handle-interrupt-reply): New client handler. * jupyter-kernel-process.el (jupyter-kernel): Don't try to create a launch-able kernel object when the connection info is provided. (jupyter-zmq-io): Consider the control channel. (jupyter-interrupt): Raise an error if trying to interrupt a kernel when the mode of interruption is message based. * jupyter-monads.el (jupyter-sent): Determine the channel to send on based off of the request type. (jupyter-request): Use `jupyter-channel-from-request-type` to check if the channel of a request is stdin. * jupyter-repl.el (jupyter-connect-repl): Spoof a valid kernelspec so that `jupyter-interrupt-kernel` can be used. * jupyter-zmq-channel-ioloop.el (initialize-instance): Consider the control channel. (jupyter-zmq-channel-ioloop--recv-messages): Update documentation.
1 parent 0480c47 commit e89e528

File tree

6 files changed

+62
-31
lines changed

6 files changed

+62
-31
lines changed

jupyter-base.el

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,8 @@ fields:
430430
(cl-defmethod jupyter-session-endpoints ((session jupyter-session))
431431
"Return a property list containing the endpoints from SESSION."
432432
(cl-destructuring-bind
433-
(&key shell_port iopub_port stdin_port hb_port ip transport
433+
(&key shell_port iopub_port stdin_port hb_port control_port
434+
ip transport
434435
&allow-other-keys)
435436
(jupyter-session-conn-info session)
436437
(cl-assert (and transport ip))
@@ -439,7 +440,8 @@ fields:
439440
for (channel . port) in `((:hb . ,hb_port)
440441
(:stdin . ,stdin_port)
441442
(:shell . ,shell_port)
442-
(:iopub . ,iopub_port))
443+
(:iopub . ,iopub_port)
444+
(:control . ,control_port))
443445
do (cl-assert port) and
444446
collect channel and collect (funcall addr port)))))
445447

@@ -469,6 +471,13 @@ call the handler methods of those types."
469471
(message-publisher nil)
470472
(inhibited-handlers nil))
471473

474+
(defun jupyter-channel-from-request-type (type)
475+
"Return the name of the channel that a request with TYPE is sent on."
476+
(pcase type
477+
((or "input_reply" "input_request") "stdin")
478+
("interrupt_request" "control")
479+
(_ "shell")))
480+
472481
;;; Connecting to a kernel's channels
473482

474483
(eval-when-compile (require 'tramp))

jupyter-client.el

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,19 @@ longer connected to a kernel."
483483

484484
(cl-defmethod jupyter-interrupt-kernel ((client jupyter-kernel-client))
485485
"Interrupt the kernel CLIENT is connected to."
486-
(let ((kaction-sub (jupyter-kernel-action-subscriber client)))
487-
(jupyter-run-with-io kaction-sub
488-
(jupyter-publish 'interrupt))))
486+
(let ((interrupt-mode (jupyter-kernel-action client
487+
(lambda (kernel)
488+
(plist-get
489+
(jupyter-kernelspec-plist
490+
(jupyter-kernel-spec kernel))
491+
:interrupt_mode)))))
492+
(if (equal interrupt-mode "message")
493+
(jupyter-run-with-client client
494+
(jupyter-sent (jupyter-interrupt-request)))
495+
(let ((kaction-sub (jupyter-kernel-action-subscriber client)))
496+
(jupyter-run-with-io kaction-sub
497+
(jupyter-publish 'interrupt))))))
498+
489499

490500
;;; Waiting for messages
491501

@@ -1747,6 +1757,10 @@ CLIENT is a kernel client."
17471757

17481758
(define-jupyter-client-handler shutdown-reply)
17491759

1760+
;;;; Interrupt
1761+
1762+
(define-jupyter-client-handler interrupt-reply)
1763+
17501764
;;; IOPUB handlers
17511765

17521766
(define-jupyter-client-handler comm-open ((client jupyter-kernel-client) req msg)

jupyter-kernel-process.el

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Call the next method if ARGS does not contain a :spec or
9191
(let ((spec (plist-get args :spec))
9292
(conn-info (plist-get args :conn-info)))
9393
(cond
94-
(spec
94+
((and spec (not conn-info))
9595
(when (stringp spec)
9696
(plist-put args :spec
9797
(or (jupyter-guess-kernelspec spec)
@@ -114,7 +114,7 @@ Call the next method if ARGS does not contain a :spec or
114114
(cl-defmethod jupyter-zmq-io ((kernel jupyter-kernel-process))
115115
(unless (jupyter-kernel-process-connect-p kernel)
116116
(jupyter-launch kernel))
117-
(let ((channels '(:shell :iopub :stdin))
117+
(let ((channels '(:shell :iopub :stdin :control))
118118
session ch-group hb kernel-io ioloop shutdown)
119119
(cl-macrolet ((continue-after
120120
(cond on-timeout)
@@ -385,7 +385,6 @@ See also https://jupyter-client.readthedocs.io/en/stable/kernels.html#kernel-spe
385385
(cl-defmethod jupyter-restart ((_kernel jupyter-kernel-process))
386386
(cl-call-next-method))
387387

388-
;; TODO Fallback to interrupt_request on kernel's control channel
389388
(cl-defmethod jupyter-interrupt ((kernel jupyter-kernel-process))
390389
"Interrupt KERNEL's process.
391390
The process can be interrupted when the interrupt mode of
@@ -396,10 +395,13 @@ See also https://jupyter-client.readthedocs.io/en/stable/kernels.html#kernel-spe
396395
((cl-struct jupyter-kernel-process spec) kernel)
397396
((cl-struct jupyter-kernelspec plist) spec)
398397
(imode (plist-get plist :interrupt_mode)))
399-
(if (or (null imode) (string= imode "signal"))
400-
(when (process-live-p process)
401-
(interrupt-process process t))
402-
(cl-call-next-method))))
398+
(cond
399+
((or (null imode) (string= imode "signal"))
400+
(when (process-live-p process)
401+
(interrupt-process process t)))
402+
((string= imode "message")
403+
(error "Send an interrupt_request using a client"))
404+
(t (cl-call-next-method)))))
403405

404406
(provide 'jupyter-kernel-process)
405407

jupyter-monads.el

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -300,15 +300,16 @@ Ex. Subscribe to a publisher and unsubscribe after receiving two
300300
(defun jupyter-sent (dreq)
301301
(jupyter-mlet* ((client (jupyter-get-state))
302302
(req dreq))
303-
(jupyter-run-with-io (jupyter-kernel-io client)
304-
(jupyter-do
305-
(jupyter-subscribe (jupyter-request-message-publisher req))
306-
(jupyter-publish
307-
(list 'send
308-
(if (jupyter-request-idle-p req) "stdin" "shell")
309-
(jupyter-request-type req)
310-
(jupyter-request-content req)
311-
(jupyter-request-id req)))))
303+
(let ((type (jupyter-request-type req)))
304+
(jupyter-run-with-io (jupyter-kernel-io client)
305+
(jupyter-do
306+
(jupyter-subscribe (jupyter-request-message-publisher req))
307+
(jupyter-publish
308+
(list 'send
309+
(jupyter-channel-from-request-type type)
310+
type
311+
(jupyter-request-content req)
312+
(jupyter-request-id req))))))
312313
(jupyter-return req)))
313314

314315
(defun jupyter-idle (dreq &optional timeout)
@@ -448,10 +449,7 @@ the callbacks."
448449
TYPE is the message type of the message that CONTENT, a property
449450
list, represents."
450451
(declare (indent 1))
451-
(let ((ih jupyter-inhibit-handlers)
452-
(ch (if (member type '("input_reply" "input_request"))
453-
"stdin"
454-
"shell")))
452+
(let ((ih jupyter-inhibit-handlers))
455453
(lambda (client)
456454
(let* ((req (jupyter-generate-request
457455
client
@@ -460,7 +458,8 @@ list, represents."
460458
:client client
461459
;; Anything sent to stdin is a reply not a request
462460
;; so consider the "request" completed.
463-
:idle-p (string= ch "stdin")
461+
:idle-p (string= "stdin"
462+
(jupyter-channel-from-request-type type))
464463
:inhibited-handlers ih))
465464
(pub (jupyter-message-publisher req)))
466465
(setf (jupyter-request-message-publisher req) pub)

jupyter-repl.el

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2135,7 +2135,14 @@ like the symbol `jupyter-repl-client', which is the default. "
21352135
(jupyter-client
21362136
(jupyter-kernel
21372137
:conn-info file
2138-
:connect-p t)
2138+
:connect-p t
2139+
;; Interrupting a kernel with a message is the only way to
2140+
;; interrupt kernels connected to using a connection file since
2141+
;; there is no way of telling what kind of kernel it is that is
2142+
;; being connected to using this method. See
2143+
;; `jupyter-interrupt-kernel'.
2144+
:spec (make-jupyter-kernelspec
2145+
:plist '(:interrupt_mode "message")))
21392146
client-class)
21402147
repl-name associate-buffer display))
21412148

jupyter-zmq-channel-ioloop.el

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
(require 'jupyter-zmq-channel-ioloop)
4949
(push 'jupyter-zmq-channel-ioloop--recv-messages jupyter-ioloop-post-hook)
5050
(cl-loop
51-
for channel in '(:shell :stdin :iopub)
51+
for channel in '(:shell :stdin :iopub :control)
5252
unless (object-assoc channel :type jupyter-channel-ioloop-channels)
5353
do (push (jupyter-zmq-channel
5454
:session jupyter-channel-ioloop-session
@@ -64,9 +64,9 @@ message on the channel and print a list with the form
6464
6565
(message CHANNEL-TYPE . MSG...)
6666
67-
to stdout. CHANNEL-TYPE is the channel on which MSG was received,
68-
either :shell, :stdin, or :iopub. MSG is a list as returned by
69-
`jupyter-recv'."
67+
to stdout. CHANNEL-TYPE is the channel on which MSG was
68+
received, either :shell, :stdin, :iopub, or :control. MSG is a
69+
list as returned by `jupyter-recv'."
7070
(let (messages)
7171
(dolist (channel jupyter-channel-ioloop-channels)
7272
(with-slots (type socket) channel

0 commit comments

Comments
 (0)