Skip to content

Commit

Permalink
Implement interrupt_request messages
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nnicandro committed Oct 16, 2023
1 parent 0480c47 commit e89e528
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 31 deletions.
13 changes: 11 additions & 2 deletions jupyter-base.el
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,8 @@ fields:
(cl-defmethod jupyter-session-endpoints ((session jupyter-session))
"Return a property list containing the endpoints from SESSION."
(cl-destructuring-bind
(&key shell_port iopub_port stdin_port hb_port ip transport
(&key shell_port iopub_port stdin_port hb_port control_port
ip transport
&allow-other-keys)
(jupyter-session-conn-info session)
(cl-assert (and transport ip))
Expand All @@ -439,7 +440,8 @@ fields:
for (channel . port) in `((:hb . ,hb_port)
(:stdin . ,stdin_port)
(:shell . ,shell_port)
(:iopub . ,iopub_port))
(:iopub . ,iopub_port)
(:control . ,control_port))
do (cl-assert port) and
collect channel and collect (funcall addr port)))))

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

(defun jupyter-channel-from-request-type (type)
"Return the name of the channel that a request with TYPE is sent on."
(pcase type
((or "input_reply" "input_request") "stdin")
("interrupt_request" "control")
(_ "shell")))

;;; Connecting to a kernel's channels

(eval-when-compile (require 'tramp))
Expand Down
20 changes: 17 additions & 3 deletions jupyter-client.el
Original file line number Diff line number Diff line change
Expand Up @@ -483,9 +483,19 @@ longer connected to a kernel."

(cl-defmethod jupyter-interrupt-kernel ((client jupyter-kernel-client))
"Interrupt the kernel CLIENT is connected to."
(let ((kaction-sub (jupyter-kernel-action-subscriber client)))
(jupyter-run-with-io kaction-sub
(jupyter-publish 'interrupt))))
(let ((interrupt-mode (jupyter-kernel-action client
(lambda (kernel)
(plist-get
(jupyter-kernelspec-plist
(jupyter-kernel-spec kernel))
:interrupt_mode)))))
(if (equal interrupt-mode "message")
(jupyter-run-with-client client
(jupyter-sent (jupyter-interrupt-request)))
(let ((kaction-sub (jupyter-kernel-action-subscriber client)))
(jupyter-run-with-io kaction-sub
(jupyter-publish 'interrupt))))))


;;; Waiting for messages

Expand Down Expand Up @@ -1747,6 +1757,10 @@ CLIENT is a kernel client."

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

;;;; Interrupt

(define-jupyter-client-handler interrupt-reply)

;;; IOPUB handlers

(define-jupyter-client-handler comm-open ((client jupyter-kernel-client) req msg)
Expand Down
16 changes: 9 additions & 7 deletions jupyter-kernel-process.el
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Call the next method if ARGS does not contain a :spec or
(let ((spec (plist-get args :spec))
(conn-info (plist-get args :conn-info)))
(cond
(spec
((and spec (not conn-info))
(when (stringp spec)
(plist-put args :spec
(or (jupyter-guess-kernelspec spec)
Expand All @@ -114,7 +114,7 @@ Call the next method if ARGS does not contain a :spec or
(cl-defmethod jupyter-zmq-io ((kernel jupyter-kernel-process))
(unless (jupyter-kernel-process-connect-p kernel)
(jupyter-launch kernel))
(let ((channels '(:shell :iopub :stdin))
(let ((channels '(:shell :iopub :stdin :control))
session ch-group hb kernel-io ioloop shutdown)
(cl-macrolet ((continue-after
(cond on-timeout)
Expand Down Expand Up @@ -385,7 +385,6 @@ See also https://jupyter-client.readthedocs.io/en/stable/kernels.html#kernel-spe
(cl-defmethod jupyter-restart ((_kernel jupyter-kernel-process))
(cl-call-next-method))

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

(provide 'jupyter-kernel-process)

Expand Down
27 changes: 13 additions & 14 deletions jupyter-monads.el
Original file line number Diff line number Diff line change
Expand Up @@ -300,15 +300,16 @@ Ex. Subscribe to a publisher and unsubscribe after receiving two
(defun jupyter-sent (dreq)
(jupyter-mlet* ((client (jupyter-get-state))
(req dreq))
(jupyter-run-with-io (jupyter-kernel-io client)
(jupyter-do
(jupyter-subscribe (jupyter-request-message-publisher req))
(jupyter-publish
(list 'send
(if (jupyter-request-idle-p req) "stdin" "shell")
(jupyter-request-type req)
(jupyter-request-content req)
(jupyter-request-id req)))))
(let ((type (jupyter-request-type req)))
(jupyter-run-with-io (jupyter-kernel-io client)
(jupyter-do
(jupyter-subscribe (jupyter-request-message-publisher req))
(jupyter-publish
(list 'send
(jupyter-channel-from-request-type type)
type
(jupyter-request-content req)
(jupyter-request-id req))))))
(jupyter-return req)))

(defun jupyter-idle (dreq &optional timeout)
Expand Down Expand Up @@ -448,10 +449,7 @@ the callbacks."
TYPE is the message type of the message that CONTENT, a property
list, represents."
(declare (indent 1))
(let ((ih jupyter-inhibit-handlers)
(ch (if (member type '("input_reply" "input_request"))
"stdin"
"shell")))
(let ((ih jupyter-inhibit-handlers))
(lambda (client)
(let* ((req (jupyter-generate-request
client
Expand All @@ -460,7 +458,8 @@ list, represents."
:client client
;; Anything sent to stdin is a reply not a request
;; so consider the "request" completed.
:idle-p (string= ch "stdin")
:idle-p (string= "stdin"
(jupyter-channel-from-request-type type))
:inhibited-handlers ih))
(pub (jupyter-message-publisher req)))
(setf (jupyter-request-message-publisher req) pub)
Expand Down
9 changes: 8 additions & 1 deletion jupyter-repl.el
Original file line number Diff line number Diff line change
Expand Up @@ -2135,7 +2135,14 @@ like the symbol `jupyter-repl-client', which is the default. "
(jupyter-client
(jupyter-kernel
:conn-info file
:connect-p t)
:connect-p t
;; Interrupting a kernel with a message is the only way to
;; interrupt kernels connected to using a connection file since
;; there is no way of telling what kind of kernel it is that is
;; being connected to using this method. See
;; `jupyter-interrupt-kernel'.
:spec (make-jupyter-kernelspec
:plist '(:interrupt_mode "message")))
client-class)
repl-name associate-buffer display))

Expand Down
8 changes: 4 additions & 4 deletions jupyter-zmq-channel-ioloop.el
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
(require 'jupyter-zmq-channel-ioloop)
(push 'jupyter-zmq-channel-ioloop--recv-messages jupyter-ioloop-post-hook)
(cl-loop
for channel in '(:shell :stdin :iopub)
for channel in '(:shell :stdin :iopub :control)
unless (object-assoc channel :type jupyter-channel-ioloop-channels)
do (push (jupyter-zmq-channel
:session jupyter-channel-ioloop-session
Expand All @@ -64,9 +64,9 @@ message on the channel and print a list with the form
(message CHANNEL-TYPE . MSG...)
to stdout. CHANNEL-TYPE is the channel on which MSG was received,
either :shell, :stdin, or :iopub. MSG is a list as returned by
`jupyter-recv'."
to stdout. CHANNEL-TYPE is the channel on which MSG was
received, either :shell, :stdin, :iopub, or :control. MSG is a
list as returned by `jupyter-recv'."
(let (messages)
(dolist (channel jupyter-channel-ioloop-channels)
(with-slots (type socket) channel
Expand Down

0 comments on commit e89e528

Please sign in to comment.