Remove jupyter-send-* methods

These methods were not used much internally.
This commit is contained in:
Nathaniel Nicandro 2020-04-29 22:20:28 -05:00
parent 0c6d90cc7e
commit 348df7feb5
9 changed files with 240 additions and 292 deletions

View file

@ -443,12 +443,14 @@ each other. A client has a request table to keep track of all
requests that are not considered idle. The most recent idle requests that are not considered idle. The most recent idle
request is also kept track of. request is also kept track of.
Each request contains: a message ID, a time sent, a last message Each request contains: a message ID, a request type, a request
received by the client that sent it, a list of message types that message, a time sent, a last message received by the client that
tell the client to not call the handler methods of those types, sent it, a list of message types that tell the client to not call
and an alist mapping message types to callback functions a client the handler methods of those types, and an alist mapping message
should call." types to callback functions a client should call."
(id "") (id (jupyter-new-uuid) :read-only t)
(type nil :read-only t)
(content nil :read-only t)
(time (current-time) :read-only t) (time (current-time) :read-only t)
(idle-p nil) (idle-p nil)
(last-message nil) (last-message nil)

View file

@ -169,7 +169,7 @@ For example to prevent a client from calling its :execute-reply
handler: handler:
(let ((jupyter-inhibit-handlers '(:execute-reply))) (let ((jupyter-inhibit-handlers '(:execute-reply)))
(jupyter-send-execute-request client ...)) (jupyter-send client :execute-request ...)))
In addition, if the first element of the list is the symbol In addition, if the first element of the list is the symbol
`not', then inhibit handlers not in the list. `not', then inhibit handlers not in the list.
@ -300,45 +300,6 @@ method is called."
,(or doc (format "A :%s handler." type)) ,(or doc (format "A :%s handler." type))
,@body)) ,@body))
;; FIXME: Remove the need to call `jupyter-message-*' functions by
;; introducing some kind of property list defaults mechanism (e.g. by
;; appending the defaults to the property list passed to
;; `jupyter-send-*').
;;
;; - Document the :inhibit-handlers key
;; - Document that client is bound to the kernel client in BODY
(defmacro define--jupyter-client-sender (type &optional doc &rest body)
(declare (indent 2) (doc-string 2))
(when doc
(unless (stringp doc)
(setq body (cons doc body)
doc nil)))
(let (defaults)
(while (keywordp (car body))
(push (pop body) defaults)
(push (pop body) defaults))
(cl-callf nreverse defaults)
(cl-labels ((keyword-name (k) (intern (substring (symbol-name k) 1)))
(as-keyword
(k) (if (keywordp k) k
(intern (format ":%s" k)))))
`(cl-defgeneric ,(intern (format "jupyter-send-%s" type))
((client jupyter-kernel-client)
&key (inhibit-handlers nil)
,@(cl-loop for (k v) on defaults by #'cddr
collect (list (keyword-name k) v)))
,(or doc (format "Send an :%s message." type))
(declare (indent 1))
(let ((jupyter-inhibit-handlers
(or inhibit-handlers jupyter-inhibit-handlers))
(msg
(,(intern (format "jupyter-message-%s" type))
,@(cl-loop
for (k _) on defaults by #'cddr
nconc (list k (keyword-name k))))))
(prog1 (jupyter-send client :shell ,(as-keyword type) msg)
,@body))))))
;;; Initializing a `jupyter-kernel-client' ;;; Initializing a `jupyter-kernel-client'
(cl-defmethod initialize-instance ((client jupyter-kernel-client) &optional _slots) (cl-defmethod initialize-instance ((client jupyter-kernel-client) &optional _slots)
@ -433,11 +394,12 @@ If it does not contain a valid value, raise an error."
unless (plist-member jupyter-message-types msg-type) unless (plist-member jupyter-message-types msg-type)
do (error "Not a valid message type (`%s')" msg-type)))) do (error "Not a valid message type (`%s')" msg-type))))
(cl-defmethod jupyter-send ((client jupyter-kernel-client) ;; FIXME: Remove the need for this
channel (defun jupyter--merge-message-defaults (type content)
type (let ((req (intern (concat "jupyter-" (substring (symbol-name type) 1)))))
message (apply req content)))
&optional msg-id)
(cl-defmethod jupyter-send ((client jupyter-kernel-client) (type symbol) &rest content)
"Send a message on CLIENT's CHANNEL. "Send a message on CLIENT's CHANNEL.
Return a `jupyter-request' representing the sent message. CHANNEL Return a `jupyter-request' representing the sent message. CHANNEL
is one of the channel keywords, either (:stdin or :shell). is one of the channel keywords, either (:stdin or :shell).
@ -449,25 +411,36 @@ response to the sent message, see `jupyter-add-callback' and
`jupyter-request-inhibited-handlers'." `jupyter-request-inhibited-handlers'."
(declare (indent 1)) (declare (indent 1))
(jupyter-verify-inhibited-handlers) (jupyter-verify-inhibited-handlers)
(let ((msg-id (or msg-id (jupyter-new-uuid)))) (pcase-let* ((req
(jupyter-request
:id (if (stringp (car content)) (pop content)
(jupyter-new-uuid))
:type type
:content (jupyter--merge-message-defaults type content)))
((cl-struct jupyter-request id) req))
(when jupyter--debug (when jupyter--debug
;; The logging of messages is deferred until the next command loop for ;; The logging of messages is deferred until the next command loop for
;; security reasons. When sending :input-reply messages that read ;; security reasons. When sending :input-reply messages that read
;; passwords, clearing the password string using `clear-string' happens ;; passwords, clearing the password string using `clear-string' happens
;; *after* the call to `jupyter-send'. ;; *after* the call to `jupyter-send'.
(run-at-time 0 nil (lambda () (message "SENDING: %s %s %s" type msg-id message)))) (run-at-time 0 nil (lambda () (message "SENDING: %s %s %s" type id content))))
(jupyter--send (let ((channel (if (memq type '(:input-reply :input-request)) :stdin :shell))
(jupyter-kernel-connection (oref client kernel)) (requests (oref client requests)))
'send channel type message msg-id) (cl-call-next-method
;; Anything sent to stdin is a reply not a request so don't add it as a (jupyter-kernel-connection (oref client kernel))
;; pending request 'send channel content id)
(unless (eq channel :stdin) ;; Anything sent to stdin is a reply not a request so consider
(let ((req (jupyter-generate-request client message)) ;; the "request" completed.
(requests (oref client requests))) (setf (jupyter-request-idle-p req) (eq channel :stdin))
(setf (jupyter-request-id req) msg-id) (setf (jupyter-request-inhibited-handlers req) jupyter-inhibit-handlers)
(setf (jupyter-request-inhibited-handlers req) jupyter-inhibit-handlers) (puthash id req requests)
(puthash msg-id req requests) (puthash "last-sent" req requests))))
(puthash "last-sent" req requests)))))
(cl-defmethod jupyter-send :after ((client jupyter-kernel-client) (_type (eql :execute-request)) &rest _content)
(jupyter-server-mode-set-client client))
(cl-defmethod jupyter-send ((type symbol) &rest content)
(apply #'cl-call-next-method jupyter-current-client type content))
;;; Pending requests ;;; Pending requests
@ -625,7 +598,7 @@ After CLIENT shuts down the kernel it is connected to, it is no
longer connected to a kernel." longer connected to a kernel."
(when-let* ((kernel (and (slot-boundp client 'kernel) (when-let* ((kernel (and (slot-boundp client 'kernel)
(oref client kernel)))) (oref client kernel))))
(jupyter-wait-until-idle (jupyter-send-shutdown-request client)) (jupyter-wait-until-idle (jupyter-send client :shutdown-request))
(jupyter-shutdown kernel))) (jupyter-shutdown kernel)))
(cl-defmethod jupyter-interrupt-kernel ((client jupyter-kernel-client)) (cl-defmethod jupyter-interrupt-kernel ((client jupyter-kernel-client))
@ -680,7 +653,7 @@ interpreted as additional CALLBACKS to add to REQ. So to add
multiple callbacks you would do multiple callbacks you would do
(jupyter-add-callback (jupyter-add-callback
(jupyter-send-execute-request client :code \"1 + 2\") (jupyter-send client (jupyter-execute-request :code \"1 + 2\"))
:status (lambda (msg) ...) :status (lambda (msg) ...)
:execute-reply (lambda (msg) ...) :execute-reply (lambda (msg) ...)
:execute-result (lambda (msg) ...))" :execute-result (lambda (msg) ...))"
@ -968,10 +941,7 @@ user. Otherwise `read-from-minibuffer' is used."
(with-timeout-unsuspend timeout-spec))) (with-timeout-unsuspend timeout-spec)))
(quit "")))) (quit ""))))
(unwind-protect (unwind-protect
(jupyter-send (jupyter-send client :input-reply :value value)
client :stdin
:input-reply (jupyter-message-input-reply
:value value))
(when (eq password t) (when (eq password t)
(clear-string value))) (clear-string value)))
value))) value)))
@ -1048,8 +1018,8 @@ text/plain representation."
(interactive (list (jupyter-read-expression) nil)) (interactive (list (jupyter-read-expression) nil))
(let ((msg (jupyter-wait-until-received :execute-result (let ((msg (jupyter-wait-until-received :execute-result
(let* ((jupyter-inhibit-handlers t) (let* ((jupyter-inhibit-handlers t)
(req (jupyter-send-execute-request jupyter-current-client (req (jupyter-send jupyter-current-client
:code code :store-history nil))) :execute-request :code code :store-history nil)))
(prog1 req (prog1 req
(jupyter-add-callback req (jupyter-add-callback req
:execute-reply :execute-reply
@ -1202,8 +1172,8 @@ current buffer that STR was extracted from.")
(cl-check-type jupyter-current-client jupyter-kernel-client (cl-check-type jupyter-current-client jupyter-kernel-client
"Not a valid client") "Not a valid client")
(let ((jupyter-inhibit-handlers '(not :input-request)) (let ((jupyter-inhibit-handlers '(not :input-request))
(req (jupyter-send-execute-request jupyter-current-client (req (jupyter-send jupyter-current-client
:code str :store-history nil))) :execute-request :code str :store-history nil)))
(prog1 req (prog1 req
(jupyter-eval-add-callbacks req beg end)))) (jupyter-eval-add-callbacks req beg end))))
@ -1418,15 +1388,6 @@ STR is displayed after the region."
;;;;; Handlers ;;;;; Handlers
(define--jupyter-client-sender execute-request
:code nil
:silent nil
:store-history t
:user-expressions nil
:allow-stdin (jupyter-alive-p client :stdin)
:stop-on-error nil
(jupyter-server-mode-set-client client))
(define-jupyter-client-handler execute-reply) (define-jupyter-client-handler execute-reply)
(cl-defgeneric jupyter-handle-payload ((source symbol) _payload) (cl-defgeneric jupyter-handle-payload ((source symbol) _payload)
@ -1493,8 +1454,8 @@ DETAIL is the detail level to use for the request and defaults to
(error "Need a valid `jupyter-current-client'")) (error "Need a valid `jupyter-current-client'"))
(let ((client jupyter-current-client) (let ((client jupyter-current-client)
(msg (jupyter-wait-until-received :inspect-reply (msg (jupyter-wait-until-received :inspect-reply
(jupyter-send-inspect-request jupyter-current-client (jupyter-send jupyter-current-client
:code code :pos pos :detail detail) :inspect-request :code code :pos pos :detail detail)
;; Longer timeout for the Julia kernel ;; Longer timeout for the Julia kernel
jupyter-long-timeout))) jupyter-long-timeout)))
(if msg (if msg
@ -1536,19 +1497,10 @@ DETAIL is the detail level to use for the request and defaults to
(symbol-at-point))))) (symbol-at-point)))))
(message "Inspect timed out")))) (message "Inspect timed out"))))
(define--jupyter-client-sender inspect-request
:code nil
:pos 0
:detail 0)
(define-jupyter-client-handler inspect-reply) (define-jupyter-client-handler inspect-reply)
;;;; Completion ;;;; Completion
(define--jupyter-client-sender complete-request
:code nil
:pos 0)
(define-jupyter-client-handler complete-reply) (define-jupyter-client-handler complete-reply)
;;;;; Code context ;;;;; Code context
@ -1847,9 +1799,8 @@ Run FUN when the completions are available."
(cl-destructuring-bind (code pos) (cl-destructuring-bind (code pos)
(jupyter-code-context 'completion) (jupyter-code-context 'completion)
(let ((req (let ((jupyter-inhibit-handlers t)) (let ((req (let ((jupyter-inhibit-handlers t))
(jupyter-send-complete-request (jupyter-send jupyter-current-client
jupyter-current-client :complete-request :code code :pos pos))))
:code code :pos pos))))
(prog1 req (prog1 req
(jupyter-add-callback req :complete-reply fun))))) (jupyter-add-callback req :complete-reply fun)))))
@ -1966,46 +1917,16 @@ snippet text property, if any, and if `yasnippet' is available."
;;;; History ;;;; History
(define--jupyter-client-sender history-request
:output nil
:raw nil
:hist-access-type "tail"
:session nil
:start nil
:stop nil
:n 10
:pattern nil
:unique nil)
(define-jupyter-client-handler history-reply) (define-jupyter-client-handler history-reply)
;;;; Is Complete ;;;; Is Complete
(define--jupyter-client-sender is-complete-request
:code nil)
(define-jupyter-client-handler is-complete-reply) (define-jupyter-client-handler is-complete-reply)
;;;; Comms ;;;; Comms
(define--jupyter-client-sender comm-info-request
:target-name nil)
(define-jupyter-client-handler comm-info-reply) (define-jupyter-client-handler comm-info-reply)
(define--jupyter-client-sender comm-open
:id nil
:target-name nil
:data nil)
(define--jupyter-client-sender comm-msg
:id nil
:data nil)
(define--jupyter-client-sender comm-close
:id nil
:data nil)
;;;; Kernel info ;;;; Kernel info
(defvar jupyter-kernel-language-mode-properties nil (defvar jupyter-kernel-language-mode-properties nil
@ -2030,12 +1951,12 @@ name are changed to \"-\" and all uppercase characters lowered."
(cl-check-type client jupyter-kernel-client) (cl-check-type client jupyter-kernel-client)
(or (oref client kernel-info) (or (oref client kernel-info)
(let* ((jupyter-inhibit-handlers t) (let* ((jupyter-inhibit-handlers t)
(req (jupyter-send-kernel-info-request client))
(msg (jupyter-wait-until-received :kernel-info-reply (msg (jupyter-wait-until-received :kernel-info-reply
(jupyter-send client :kernel-info-request)
;; Go to great lengths to ensure we have waited long ;; Go to great lengths to ensure we have waited long
;; enough. When communicating with slow to start kernels ;; enough. When communicating with slow to start kernels
;; behind a kernel server this is necessary. ;; behind a kernel server this is necessary.
req (* 3 jupyter-long-timeout) "Requesting kernel info..."))) (* 3 jupyter-long-timeout) "Requesting kernel info...")))
(unless msg (unless msg
(error "Kernel did not respond to kernel-info request")) (error "Kernel did not respond to kernel-info request"))
(prog1 (oset client kernel-info (jupyter-message-content msg)) (prog1 (oset client kernel-info (jupyter-message-content msg))
@ -2087,15 +2008,10 @@ CLIENT is a kernel client."
(support (intern (format "jupyter-%s" lang)))) (support (intern (format "jupyter-%s" lang))))
(require support nil t))) (require support nil t)))
(define--jupyter-client-sender kernel-info-request)
(define-jupyter-client-handler kernel-info-reply) (define-jupyter-client-handler kernel-info-reply)
;;;; Shutdown ;;;; Shutdown
(define--jupyter-client-sender shutdown-request
:restart nil)
(define-jupyter-client-handler shutdown-reply) (define-jupyter-client-handler shutdown-reply)
;;; IOPUB handlers ;;; IOPUB handlers

View file

@ -122,7 +122,8 @@ Make the character after `point' invisible."
If the Pkg prompt can't be retrieved from the kernel, return If the Pkg prompt can't be retrieved from the kernel, return
nil." nil."
(when-let* ((msg (jupyter-wait-until-received :execute-reply (when-let* ((msg (jupyter-wait-until-received :execute-reply
(jupyter-send-execute-request jupyter-current-client (jupyter-send jupyter-current-client
:execute-request
:code "" :code ""
:silent t :silent t
:user-expressions :user-expressions
@ -189,14 +190,15 @@ nil."
(defun jupyter-julia--setup-hooks (client) (defun jupyter-julia--setup-hooks (client)
(let ((jupyter-inhibit-handlers t)) (let ((jupyter-inhibit-handlers t))
(jupyter-send-execute-request client (jupyter-send client
:store-history nil :execute-request
:silent t :store-history nil
;; This is mainly for supporting the :dir header argument in `org-mode' :silent t
;; source blocks. We send this after initializing the REPL and after a ;; This is mainly for supporting the :dir header argument in `org-mode'
;; kernel restart so that we can get proper line numbers when an error ;; source blocks. We send this after initializing the REPL and after a
;; occurs. ;; kernel restart so that we can get proper line numbers when an error
:code "\ ;; occurs.
:code "\
if !isdefined(Main, :__JUPY_saved_dir) if !isdefined(Main, :__JUPY_saved_dir)
Core.eval(Main, :(__JUPY_saved_dir = Ref(\"\"))) Core.eval(Main, :(__JUPY_saved_dir = Ref(\"\")))
let popdir = () -> begin let popdir = () -> begin

View file

@ -150,8 +150,7 @@ request on KERNEL's control channel if its kernelspec has an
(format "%s://%s:%d" transport ip control_port))))) (format "%s://%s:%d" transport ip control_port)))))
;; TODO: `with-live-jupyter-channel' ;; TODO: `with-live-jupyter-channel'
(jupyter-start channel) (jupyter-start channel)
(jupyter-send channel :interrupt-request (jupyter-send channel :interrupt-request '())
(jupyter-message-interrupt-request))
(jupyter-with-timeout (jupyter-with-timeout
((format "Interrupting %s kernel" ((format "Interrupting %s kernel"
(jupyter-kernel-name kernel)) (jupyter-kernel-name kernel))

View file

@ -369,36 +369,44 @@ and `:msg_type'."
;;; Control messages ;;; Control messages
(cl-defun jupyter-message-interrupt-request () (cl-defun jupyter-interrupt-request ()
(list)) (jupyter-request
:type :interrupt-request
:content (list)))
;;; stdin messages ;;; stdin messages
(cl-defun jupyter-message-input-reply (&key value) (cl-defun jupyter-input-reply (&key value)
(cl-check-type value string) (cl-check-type value string)
(list :value value)) (jupyter-request
:type :input-reply
:content (list :value value)))
;;; shell messages ;;; shell messages
(cl-defun jupyter-message-kernel-info-request () (cl-defun jupyter-kernel-info-request ()
(list)) (jupyter-request
:type :kernel-info-request
:content (list)))
(cl-defun jupyter-message-execute-request (&key (cl-defun jupyter-execute-request (&key
code code
(silent nil) (silent nil)
(store-history t) (store-history t)
(user-expressions nil) (user-expressions nil)
(allow-stdin t) (allow-stdin t)
(stop-on-error nil)) (stop-on-error nil))
(cl-check-type code string) (cl-check-type code string)
(cl-check-type user-expressions json-plist) (cl-check-type user-expressions json-plist)
(list :code code :silent (if silent t jupyter--false) (jupyter-request
:store_history (if store-history t jupyter--false) :type :execute-request
:user_expressions (or user-expressions jupyter--empty-dict) :content (list :code code :silent (if silent t jupyter--false)
:allow_stdin (if allow-stdin t jupyter--false) :store_history (if store-history t jupyter--false)
:stop_on_error (if stop-on-error t jupyter--false))) :user_expressions (or user-expressions jupyter--empty-dict)
:allow_stdin (if allow-stdin t jupyter--false)
:stop_on_error (if stop-on-error t jupyter--false))))
(cl-defun jupyter-message-inspect-request (&key code pos detail) (cl-defun jupyter-inspect-request (&key code (pos 0) (detail 0))
(setq detail (or detail 0)) (setq detail (or detail 0))
(unless (member detail '(0 1)) (unless (member detail '(0 1))
(error "Detail can only be 0 or 1 (%s)" detail)) (error "Detail can only be 0 or 1 (%s)" detail))
@ -406,71 +414,89 @@ and `:msg_type'."
(setq pos (marker-position pos))) (setq pos (marker-position pos)))
(cl-check-type code string) (cl-check-type code string)
(cl-check-type pos integer) (cl-check-type pos integer)
(list :code code :cursor_pos pos :detail_level detail)) (jupyter-request
:type :inspect-request
:content (list :code code :cursor_pos pos :detail_level detail)))
(cl-defun jupyter-message-complete-request (&key code pos) (cl-defun jupyter-complete-request (&key code (pos 0))
(when (markerp pos) (when (markerp pos)
(setq pos (marker-position pos))) (setq pos (marker-position pos)))
(cl-check-type code string) (cl-check-type code string)
(cl-check-type pos integer) (cl-check-type pos integer)
(list :code code :cursor_pos pos)) (jupyter-request
:type :complete-requesr
:content (list :code code :cursor_pos pos)))
(cl-defun jupyter-message-history-request (&key (cl-defun jupyter-history-request (&key
output output
raw raw
hist-access-type (hist-access-type "tail")
session session
start start
stop stop
n (n 10)
pattern pattern
unique) unique)
(unless (member hist-access-type '("range" "tail" "search")) (unless (member hist-access-type '("range" "tail" "search"))
(error "History access type can only be one of (range, tail, search)")) (error "History access type can only be one of (range, tail, search)"))
(append (jupyter-request
(list :output (if output t jupyter--false) :raw (if raw t jupyter--false) :type :history-request
:hist_access_type hist-access-type) :content
(cond (append
((equal hist-access-type "range") (list :output (if output t jupyter--false) :raw (if raw t jupyter--false)
(cl-check-type session integer) :hist_access_type hist-access-type)
(cl-check-type start integer) (cond
(cl-check-type stop integer) ((equal hist-access-type "range")
(list :session session :start start :stop stop)) (cl-check-type session integer)
((equal hist-access-type "tail") (cl-check-type start integer)
(cl-check-type n integer) (cl-check-type stop integer)
(list :n n)) (list :session session :start start :stop stop))
((equal hist-access-type "search") ((equal hist-access-type "tail")
(cl-check-type pattern string) (cl-check-type n integer)
(cl-check-type n integer) (list :n n))
(list :pattern pattern :unique (if unique t jupyter--false) :n n))))) ((equal hist-access-type "search")
(cl-check-type pattern string)
(cl-check-type n integer)
(list :pattern pattern :unique (if unique t jupyter--false) :n n))))))
(cl-defun jupyter-message-is-complete-request (&key code) (cl-defun jupyter-is-complete-request (&key code)
(cl-check-type code string) (cl-check-type code string)
(list :code code)) (jupyter-request
:type :is-complete-request
:content (list :code code)))
(cl-defun jupyter-message-comm-info-request (&key target-name) (cl-defun jupyter-message-comm-info-request (&key target-name)
(when target-name (cl-check-type target-name string)
(cl-check-type target-name string) (jupyter-request
(list :target_name target-name))) :type :comm-info-request
:content (list :target_name target-name)))
(cl-defun jupyter-message-comm-open (&key id target-name data) (cl-defun jupyter-comm-open (&key id target-name data)
(cl-check-type id string) (cl-check-type id string)
(cl-check-type target-name string) (cl-check-type target-name string)
(cl-check-type data json-plist) (cl-check-type data json-plist)
(list :comm_id id :target_name target-name :data data)) (jupyter-request
:type :comm-open
:content (list :comm_id id :target_name target-name :data data)))
(cl-defun jupyter-message-comm-msg (&key id data) (cl-defun jupyter-comm-msg (&key id data)
(cl-check-type id string) (cl-check-type id string)
(cl-check-type data json-plist) (cl-check-type data json-plist)
(list :comm_id id :data data)) (jupyter-request
:type :comm-msg
:content (list :comm_id id :data data)))
(cl-defun jupyter-message-comm-close (&key id data) (cl-defun jupyter-comm-close (&key id data)
(cl-check-type id string) (cl-check-type id string)
(cl-check-type data json-plist) (cl-check-type data json-plist)
(list :comm_id id :data data)) (jupyter-request
:type :comm-close
:content (list :comm_id id :data data)))
(cl-defun jupyter-message-shutdown-request (&key restart) (cl-defun jupyter-shutdown-request (&key restart)
(list :restart (if restart t jupyter--false))) (jupyter-request
:type :shutdown-request
:content (list :restart (if restart t jupyter--false))))
;;; Convenience functions and macros ;;; Convenience functions and macros

View file

@ -602,10 +602,9 @@ first move is to the beginning of the current cell."
(defun jupyter-repl-goto-cell (req) (defun jupyter-repl-goto-cell (req)
"Go to the cell beginning position of REQ. "Go to the cell beginning position of REQ.
REQ should be a `jupyter-request' that corresponds to one of the REQ should be a `jupyter-request' associated with a cell in the
`jupyter-send-execute-request's created by a cell in the `current-buffer'. Note that the `current-buffer' is assumed to
`current-buffer'. Note that the `current-buffer' is assumed to be be a Jupyter REPL buffer."
a Jupyter REPL buffer."
(goto-char (point-max)) (goto-char (point-max))
(unless (catch 'done (unless (catch 'done
(while (= (jupyter-repl-previous-cell) 0) (while (= (jupyter-repl-previous-cell) 0)
@ -811,33 +810,28 @@ lines, truncate it to something less than
(ring-remove jupyter-repl-history -2)) (ring-remove jupyter-repl-history -2))
(ring-remove+insert+extend jupyter-repl-history code)) (ring-remove+insert+extend jupyter-repl-history code))
(cl-defmethod jupyter-send-execute-request ((client jupyter-repl-client) (defun jupyter-repl-execute-cell (client)
&key code "Execute the last REPL cell of CLIENT.
(silent nil) Return the `jupyter-request' representing the executed code."
(store-history t) (cl-check-type client jupyter-repl-client)
(user-expressions nil) (jupyter-with-repl-buffer client
(allow-stdin t) (jupyter-repl-truncate-buffer)
(stop-on-error nil)) (save-excursion
(if code (cl-call-next-method) (goto-char (jupyter-repl-cell-code-beginning-position))
(jupyter-with-repl-buffer client (run-hooks 'jupyter-repl-cell-pre-send-hook))
(jupyter-repl-truncate-buffer) (let ((code (string-trim (jupyter-repl-cell-code))))
(save-excursion (let ((req (jupyter-send client
(goto-char (jupyter-repl-cell-code-beginning-position)) :execute-request
(run-hooks 'jupyter-repl-cell-pre-send-hook)) :code code
(setq code (string-trim (jupyter-repl-cell-code))) ;; Handle empty code cells as just an update of the
;; Handle empty code cells as just an update of the prompt number ;; prompt number
(if (= (length code) 0) :silent (and (= (length code) 0) t))))
(setq silent t)
;; Needed by the prompt insertion below
(oset client execution-count (1+ (oref client execution-count)))
(jupyter-repl-history-add code))
(let ((req (cl-call-next-method
client :code code :silent silent :store-history store-history
:user-expressions user-expressions :allow-stdin allow-stdin
:stop-on-error stop-on-error)))
(jupyter-repl-without-continuation-prompts (jupyter-repl-without-continuation-prompts
(jupyter-repl-cell-mark-busy) (jupyter-repl-cell-mark-busy)
(jupyter-repl-finalize-cell req) (jupyter-repl-finalize-cell req)
(jupyter-repl-history-add code)
;; Needed by the prompt insertion below
(oset client execution-count (1+ (oref client execution-count)))
(jupyter-repl-insert-prompt 'in)) (jupyter-repl-insert-prompt 'in))
(save-excursion (save-excursion
(jupyter-repl-backward-cell) (jupyter-repl-backward-cell)
@ -1111,17 +1105,17 @@ elements."
(jupyter-with-message-content msg (status indent) (jupyter-with-message-content msg (status indent)
(pcase status (pcase status
("complete" ("complete"
(jupyter-send-execute-request client)) (jupyter-repl-execute-cell client))
("incomplete" ("incomplete"
(insert "\n") (insert "\n")
(if (= (length indent) 0) (jupyter-repl-indent-line) (if (= (length indent) 0) (jupyter-repl-indent-line)
(insert indent))) (insert indent)))
("invalid" ("invalid"
;; Force an execute to produce a traceback ;; Force an execute to produce a traceback
(jupyter-send-execute-request client)) (jupyter-repl-execute-cell client))
("unknown" ("unknown"
;; Let the kernel decide if the code is complete ;; Let the kernel decide if the code is complete
(jupyter-send-execute-request client)))))) (jupyter-repl-execute-cell client))))))
(defun jupyter-repl--insert-banner-and-prompt (client) (defun jupyter-repl--insert-banner-and-prompt (client)
(jupyter-with-repl-buffer client (jupyter-with-repl-buffer client
@ -1185,7 +1179,7 @@ execute the current cell."
(not jupyter-repl-allow-RET-when-busy)) (not jupyter-repl-allow-RET-when-busy))
(error "Kernel busy")) (error "Kernel busy"))
(cond (cond
(force (jupyter-send-execute-request jupyter-current-client)) (force (jupyter-repl-execute-cell jupyter-current-client))
((or jupyter-repl-use-builtin-is-complete ((or jupyter-repl-use-builtin-is-complete
(and jupyter-repl-allow-RET-when-busy (and jupyter-repl-allow-RET-when-busy
(jupyter-kernel-busy-p jupyter-current-client))) (jupyter-kernel-busy-p jupyter-current-client)))
@ -1199,8 +1193,8 @@ execute the current cell."
(t (t
(let ((res (jupyter-wait-until-received :is-complete-reply (let ((res (jupyter-wait-until-received :is-complete-reply
(let ((jupyter-inhibit-handlers '(not :is-complete-reply))) (let ((jupyter-inhibit-handlers '(not :is-complete-reply)))
(jupyter-send-is-complete-request (jupyter-send jupyter-current-client
jupyter-current-client :is-complete-request
:code (jupyter-repl-cell-code))) :code (jupyter-repl-cell-code)))
jupyter-repl-maximum-is-complete-timeout))) jupyter-repl-maximum-is-complete-timeout)))
(unless res (unless res
@ -1451,9 +1445,10 @@ value."
;; update the REPL state ;; update the REPL state
(unless jupyter-repl-echo-eval-p (unless jupyter-repl-echo-eval-p
'(not :input-request)))) '(not :input-request))))
(setq req (jupyter-send-execute-request jupyter-current-client (setq req (jupyter-send jupyter-current-client
:code str :execute-request
:store-history jupyter-repl-echo-eval-p)) :code str
:store-history jupyter-repl-echo-eval-p))
(if jupyter-repl-echo-eval-p (if jupyter-repl-echo-eval-p
(jupyter-repl-replace-cell-code cell-previous-code)))) (jupyter-repl-replace-cell-code cell-previous-code))))
;; Add callbacks to display evaluation output in pop-up buffers either when ;; Add callbacks to display evaluation output in pop-up buffers either when
@ -1696,8 +1691,11 @@ Return the buffer switched to."
;; ring. ;; ring.
(ring-insert jupyter-repl-history 'jupyter-repl-history) (ring-insert jupyter-repl-history 'jupyter-repl-history)
(let ((jupyter-inhibit-handlers '(:status))) (let ((jupyter-inhibit-handlers '(:status)))
(jupyter-send-history-request jupyter-current-client (jupyter-send jupyter-current-client
:n jupyter-repl-history-maximum-length :raw nil :unique t)) :history-request
:n jupyter-repl-history-maximum-length
:raw nil
:unique t))
(erase-buffer) (erase-buffer)
;; Add local hooks ;; Add local hooks
(add-hook 'kill-buffer-query-functions #'jupyter-repl-kill-buffer-query-function nil t) (add-hook 'kill-buffer-query-functions #'jupyter-repl-kill-buffer-query-function nil t)
@ -1815,7 +1813,10 @@ Also update the cell count of the current REPL input prompt using
the updated state." the updated state."
(let* ((client jupyter-current-client) (let* ((client jupyter-current-client)
(req (let ((jupyter-inhibit-handlers t)) (req (let ((jupyter-inhibit-handlers t))
(jupyter-send-execute-request client :code "" :silent t)))) (jupyter-send client
:execute-request
:code ""
:silent t))))
(jupyter-add-callback req (jupyter-add-callback req
:execute-reply :execute-reply
(jupyter-message-lambda (execution_count) (jupyter-message-lambda (execution_count)

View file

@ -208,7 +208,7 @@ return nil."
(with-current-buffer (org-babel-jupyter-initiate-session session params) (with-current-buffer (org-babel-jupyter-initiate-session session params)
(goto-char (point-max)) (goto-char (point-max))
(and (org-babel-jupyter--insert-variable-assignments params) (and (org-babel-jupyter--insert-variable-assignments params)
(jupyter-send-execute-request jupyter-current-client)) (jupyter-repl-execute-cell jupyter-current-client))
(current-buffer))) (current-buffer)))
(defun org-babel-load-session:jupyter (session body params) (defun org-babel-load-session:jupyter (session body params)
@ -428,7 +428,9 @@ These parameters are handled internally."
(delq fparam params))) (delq fparam params)))
(defun org-babel-jupyter--execute (code async-p) (defun org-babel-jupyter--execute (code async-p)
(let ((req (jupyter-send-execute-request jupyter-current-client :code code))) (let ((req (jupyter-send jupyter-current-client
(jupyter-execute-request
:code code))))
`(,req `(,req
,(cond ,(cond
(async-p (async-p

View file

@ -47,8 +47,7 @@
:tags '(mock) :tags '(mock)
(jupyter-with-echo-client client (jupyter-with-echo-client client
(ert-info ("Mock echo client echo's messages back to channel.") (ert-info ("Mock echo client echo's messages back to channel.")
(let* ((msg (jupyter-message-execute-request :code "foo")) (let ((req (jupyter-send client :execute-request :code "foo")))
(req (jupyter-send client :shell :execute-request msg)))
(sleep-for 0.3) (sleep-for 0.3)
(setq msgs (nreverse (ring-elements (oref client messages)))) (setq msgs (nreverse (ring-elements (oref client messages))))
(should (= (length msgs) 3)) (should (= (length msgs) 3))
@ -71,7 +70,7 @@
(ert-deftest jupyter-wait-until-idle () (ert-deftest jupyter-wait-until-idle ()
:tags '(callbacks) :tags '(callbacks)
(jupyter-with-echo-client client (jupyter-with-echo-client client
(let ((req (jupyter-send-execute-request client :code "foo"))) (let ((req (jupyter-send client :execute-request :code "foo")))
(ert-info ("Blocking callbacks") (ert-info ("Blocking callbacks")
(jupyter-wait-until-idle req) (jupyter-wait-until-idle req)
(should (jupyter-request-idle-p req))) (should (jupyter-request-idle-p req)))
@ -86,7 +85,7 @@
(cb (lambda (msg) (cb (lambda (msg)
(should (eq (jupyter-message-type msg) :status)) (should (eq (jupyter-message-type msg) :status))
(setq callback-count (1+ callback-count)))) (setq callback-count (1+ callback-count))))
(req (jupyter-send-execute-request client :code "foo"))) (req (jupyter-send client :execute-request :code "foo")))
(jupyter-add-callback req :status cb) (jupyter-add-callback req :status cb)
(jupyter-wait-until-idle req) (jupyter-wait-until-idle req)
(should (= callback-count 2)))) (should (= callback-count 2))))
@ -96,7 +95,7 @@
(setq callback-count (1+ callback-count)) (setq callback-count (1+ callback-count))
(should (memq (jupyter-message-type msg) (should (memq (jupyter-message-type msg)
'(:status :execute-reply))))) '(:status :execute-reply)))))
(req (jupyter-send-execute-request client :code "foo"))) (req (jupyter-send client :execute-request :code "foo")))
(jupyter-add-callback req '(:status :execute-reply) cb) (jupyter-add-callback req '(:status :execute-reply) cb)
(jupyter-wait-until-idle req) (jupyter-wait-until-idle req)
(should (= callback-count 3)))))) (should (= callback-count 3))))))
@ -409,19 +408,19 @@
(jupyter-test-with-python-client client (jupyter-test-with-python-client client
(ert-info ("Kernel info") (ert-info ("Kernel info")
(let ((res (jupyter-wait-until-received :kernel-info-reply (let ((res (jupyter-wait-until-received :kernel-info-reply
(jupyter-send-kernel-info-request client)))) (jupyter-send client :kernel-info-request))))
(should res) (should res)
(should (json-plist-p res)) (should (json-plist-p res))
(should (eq (jupyter-message-type res) :kernel-info-reply)))) (should (eq (jupyter-message-type res) :kernel-info-reply))))
(ert-info ("Comm info") (ert-info ("Comm info")
(let ((res (jupyter-wait-until-received :comm-info-reply (let ((res (jupyter-wait-until-received :comm-info-reply
(jupyter-send-comm-info-request client)))) (jupyter-send client :comm-info-request))))
(should-not (null res)) (should-not (null res))
(should (json-plist-p res)) (should (json-plist-p res))
(should (eq (jupyter-message-type res) :comm-info-reply)))) (should (eq (jupyter-message-type res) :comm-info-reply))))
(ert-info ("Execute") (ert-info ("Execute")
(let ((res (jupyter-wait-until-received :execute-reply (let ((res (jupyter-wait-until-received :execute-reply
(jupyter-send-execute-request client :code "y = 1 + 2")))) (jupyter-send client :execute-request :code "y = 1 + 2"))))
(should-not (null res)) (should-not (null res))
(should (json-plist-p res)) (should (json-plist-p res))
(should (eq (jupyter-message-type res) :execute-reply)))) (should (eq (jupyter-message-type res) :execute-reply))))
@ -429,45 +428,49 @@
(cl-letf (((symbol-function 'read-from-minibuffer) (cl-letf (((symbol-function 'read-from-minibuffer)
(lambda (_prompt &rest _args) "foo"))) (lambda (_prompt &rest _args) "foo")))
(let ((res (jupyter-wait-until-received :execute-result (let ((res (jupyter-wait-until-received :execute-result
(jupyter-send-execute-request client :code "input('')")))) (jupyter-send client :execute-request :code "input('')"))))
(should-not (null res)) (should-not (null res))
(should (json-plist-p res)) (should (json-plist-p res))
(should (eq (jupyter-message-type res) :execute-result)) (should (eq (jupyter-message-type res) :execute-result))
(should (equal (jupyter-message-data res :text/plain) "'foo'"))))) (should (equal (jupyter-message-data res :text/plain) "'foo'")))))
(ert-info ("Inspect") (ert-info ("Inspect")
(let ((res (jupyter-wait-until-received :inspect-reply (let ((res (jupyter-wait-until-received :inspect-reply
(jupyter-send-inspect-request client (jupyter-send client
:code "list((1, 2, 3))" :inspect-request
:pos 2 :code "list((1, 2, 3))"
:detail 0)))) :pos 2
:detail 0))))
(should-not (null res)) (should-not (null res))
(should (json-plist-p res)) (should (json-plist-p res))
(should (eq (jupyter-message-type res) :inspect-reply)))) (should (eq (jupyter-message-type res) :inspect-reply))))
(ert-info ("Complete") (ert-info ("Complete")
(let ((res (jupyter-wait-until-received :complete-reply (let ((res (jupyter-wait-until-received :complete-reply
(jupyter-send-complete-request client (jupyter-send client
:code "foo = lis" :complete-request
:pos 8)))) :code "foo = lis"
:pos 8))))
(should-not (null res)) (should-not (null res))
(should (json-plist-p res)) (should (json-plist-p res))
(should (eq (jupyter-message-type res) :complete-reply)))) (should (eq (jupyter-message-type res) :complete-reply))))
(ert-info ("History") (ert-info ("History")
(let ((res (jupyter-wait-until-received :history-reply (let ((res (jupyter-wait-until-received :history-reply
(jupyter-send-history-request client (jupyter-send client
:hist-access-type "tail" :n 2)))) :history-request
:hist-access-type "tail" :n 2))))
(should-not (null res)) (should-not (null res))
(should (json-plist-p res)) (should (json-plist-p res))
(should (eq (jupyter-message-type res) :history-reply)))) (should (eq (jupyter-message-type res) :history-reply))))
(ert-info ("Is Complete") (ert-info ("Is Complete")
(let ((res (jupyter-wait-until-received :is-complete-reply (let ((res (jupyter-wait-until-received :is-complete-reply
(jupyter-send-is-complete-request client (jupyter-send client
:code "for i in range(5):")))) :is-complete-request
:code "for i in range(5):"))))
(should-not (null res)) (should-not (null res))
(should (json-plist-p res)) (should (json-plist-p res))
(should (eq (jupyter-message-type res) :is-complete-reply)))) (should (eq (jupyter-message-type res) :is-complete-reply))))
(ert-info ("Shutdown") (ert-info ("Shutdown")
(let ((res (jupyter-wait-until-received :shutdown-reply (let ((res (jupyter-wait-until-received :shutdown-reply
(jupyter-send-shutdown-request client)))) (jupyter-send client :shutdown-request))))
(should-not (null res)) (should-not (null res))
(should (json-plist-p res)) (should (json-plist-p res))
(should (eq (jupyter-message-type res) :shutdown-reply)) (should (eq (jupyter-message-type res) :shutdown-reply))
@ -814,14 +817,14 @@
:tags '(client handlers) :tags '(client handlers)
(jupyter-test-with-python-client client (jupyter-test-with-python-client client
(let* ((jupyter-inhibit-handlers '(:stream)) (let* ((jupyter-inhibit-handlers '(:stream))
(req (jupyter-send-kernel-info-request client))) (req (jupyter-send client :kernel-info-request)))
(should (equal (jupyter-request-inhibited-handlers req) (should (equal (jupyter-request-inhibited-handlers req)
'(:stream))) '(:stream)))
(should-not (jupyter--request-allows-handler-p (should-not (jupyter--request-allows-handler-p
req (jupyter-test-message req (jupyter-test-message
req :stream (list :name "stdout" :text "foo")))) req :stream (list :name "stdout" :text "foo"))))
(setq jupyter-inhibit-handlers '(:foo)) (setq jupyter-inhibit-handlers '(:foo))
(should-error (jupyter-send-kernel-info-request client))))) (should-error (jupyter-send client :kernel-info-request)))))
(ert-deftest jupyter-requests-pending-p () (ert-deftest jupyter-requests-pending-p ()
:tags '(client) :tags '(client)
@ -835,7 +838,7 @@
;; print the class object on failure and will fail at doing so. ;; print the class object on failure and will fail at doing so.
(setq pending (jupyter-requests-pending-p client)) (setq pending (jupyter-requests-pending-p client))
(should-not pending) (should-not pending)
(let ((req (jupyter-send-kernel-info-request client))) (let ((req (jupyter-send client :kernel-info-request)))
(ert-info ("Pending after send") (ert-info ("Pending after send")
(setq pending (jupyter-requests-pending-p client)) (setq pending (jupyter-requests-pending-p client))
(should pending) (should pending)
@ -843,8 +846,9 @@
(setq pending (jupyter-requests-pending-p client)) (setq pending (jupyter-requests-pending-p client))
(should-not pending)) (should-not pending))
(ert-info ("Pending until idle received") (ert-info ("Pending until idle received")
(setq req (jupyter-send-execute-request client (setq req (jupyter-send client
:code "import time; time.sleep(0.2)")) :execute-request
:code "import time; time.sleep(0.2)"))
;; Empty out the pending-requests slot of CLIENT ;; Empty out the pending-requests slot of CLIENT
(jupyter-wait-until-received :status req) (jupyter-wait-until-received :status req)
(setq pending (jupyter-requests-pending-p client)) (setq pending (jupyter-requests-pending-p client))
@ -930,11 +934,11 @@
(ert-deftest jupyter-idle-sync () (ert-deftest jupyter-idle-sync ()
:tags '(client hook) :tags '(client hook)
(jupyter-test-with-python-client client (jupyter-test-with-python-client client
(let ((req (jupyter-send-execute-request client :code "1 + 1"))) (let ((req (jupyter-send client :execute-request :code "1 + 1")))
(should-not (jupyter-request-idle-p req)) (should-not (jupyter-request-idle-p req))
(jupyter-idle-sync req) (jupyter-idle-sync req)
(should (jupyter-request-idle-p req))) (should (jupyter-request-idle-p req)))
(let ((req (jupyter-send-execute-request client :code "1 + 1"))) (let ((req (jupyter-send client :execute-request :code "1 + 1")))
(should (null jupyter-test-idle-sync-hook)) (should (null jupyter-test-idle-sync-hook))
(jupyter-add-idle-sync-hook 'jupyter-test-idle-sync-hook req) (jupyter-add-idle-sync-hook 'jupyter-test-idle-sync-hook req)
(should-not (null jupyter-test-idle-sync-hook)) (should-not (null jupyter-test-idle-sync-hook))
@ -1106,13 +1110,12 @@
(ert-deftest jupyter-zmq-channel-ioloop-send-fast () (ert-deftest jupyter-zmq-channel-ioloop-send-fast ()
:tags '(ioloop queue) :tags '(ioloop queue)
;; :expected-result :failed
(jupyter-test-with-python-client client (jupyter-test-with-python-client client
(let ((jupyter-current-client client)) (let ((jupyter-current-client client))
(jupyter-send-execute-request client :code "1 + 1") (jupyter-send client :execute-request :code "1 + 1")
(jupyter-send-execute-request client :code "1 + 1") (jupyter-send client :execute-request :code "1 + 1")
(jupyter-send-execute-request client :code "1 + 1") (jupyter-send client :execute-request :code "1 + 1")
(let ((req (jupyter-send-execute-request client :code "1 + 1"))) (let ((req (jupyter-send client :execute-request :code "1 + 1")))
(should (should
(equal (equal
(jupyter-message-data (jupyter-message-data
@ -1576,7 +1579,7 @@ last element being the newest element added to the history."
(jupyter-ert-info ("Cell boundary errors") (jupyter-ert-info ("Cell boundary errors")
(goto-char (point-max)) (goto-char (point-max))
(jupyter-repl-replace-cell-code "1 + 1") (jupyter-repl-replace-cell-code "1 + 1")
(jupyter-wait-until-idle (jupyter-send-execute-request client)) (jupyter-wait-until-idle (jupyter-repl-execute-cell client))
(forward-line -2) (forward-line -2)
(should (eq (car (get-text-property (1- (point)) 'jupyter-cell)) (should (eq (car (get-text-property (1- (point)) 'jupyter-cell))
'out)) 'out))
@ -1814,8 +1817,9 @@ next(x"))))))
(unwind-protect (unwind-protect
(let ((msg (jupyter-wait-until-received :execute-result (let ((msg (jupyter-wait-until-received :execute-result
(let ((jupyter-inhibit-handlers t)) (let ((jupyter-inhibit-handlers t))
(jupyter-send-execute-request cclient (jupyter-send cclient
:code "1 + 1"))))) :execute-request
:code "1 + 1")))))
(should msg) (should msg)
(should (equal (jupyter-message-data msg :text/plain) "2"))) (should (equal (jupyter-message-data msg :text/plain) "2")))
(with-current-buffer (oref cclient buffer) (with-current-buffer (oref cclient buffer)

View file

@ -97,12 +97,8 @@ handling a message is always
:send #'ignore :send #'ignore
:alive-p #'ignore)))) :alive-p #'ignore))))
(cl-defmethod jupyter-send ((client jupyter-echo-client) (cl-defmethod jupyter-send ((client jupyter-echo-client) (type symbol) &rest content)
channel (let ((req (jupyter-request :type type :content content)))
type
message
&optional _flags)
(let ((req (jupyter-request :id (jupyter-new-uuid))))
(if (string-match "request" (symbol-name type)) (if (string-match "request" (symbol-name type))
(setq type (intern (replace-match "reply" nil nil (symbol-name type)))) (setq type (intern (replace-match "reply" nil nil (symbol-name type))))
(error "Not a request message type (%s)" type)) (error "Not a request message type (%s)" type))
@ -198,7 +194,7 @@ If the `current-buffer' is not a REPL, this is identical to
(when ,saved (when ,saved
(if (oref ,saved manager) (if (oref ,saved manager)
(jupyter-shutdown-kernel (oref ,saved manager)) (jupyter-shutdown-kernel (oref ,saved manager))
(jupyter-send-shutdown-request ,saved)) (jupyter-send ,saved :shutdown-request))
;; FIXME: This will be removed after the transition ;; FIXME: This will be removed after the transition
(ignore-errors (jupyter-stop-channels ,saved))) (ignore-errors (jupyter-stop-channels ,saved)))
(let ((client (,client-fun (jupyter-kernelspec-name ,spec)))) (let ((client (,client-fun (jupyter-kernelspec-name ,spec))))