Skip to content

Commit

Permalink
Remove the jupyter-ioloop-handler method
Browse files Browse the repository at this point in the history
Replace it with a handler function passed to `jupyter-ioloop-start`.

* jupyter-ioloop-comm.el: Do it.

* jupyter-channel-ioloop-comm.el: Do it.

* jupyter-channel-ioloop-comm.el: Do it.

* jupyter-ioloop.el: Do it. Update comments and doc.
(jupyter-ioloop--delete-process): Remove. It was used to perform
cleanup when the object used for dispatching to a handler method was
garbage collected. Since we no longer rely on having an object for
dispatching, preferring just a function to call, its not needed.

(jupyter-ioloop--make-filter): Fall back to calling handler function
instead of `jupyter-ioloop-handler`. Only handle ioloop start/stop
events internally. This means we can remove the check for those in
`jupyter-ioloop-comm`.

* test/jupyter-test.el: Update tests to take into account above
  changes.
  • Loading branch information
nnicandro committed Apr 22, 2021
1 parent 786f61f commit 1fa6ed4
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 78 deletions.
30 changes: 13 additions & 17 deletions jupyter-channel-ioloop-comm.el
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,19 @@
(with-slots (ioloop session) comm
(unless (jupyter-ioloop-alive-p ioloop)
(jupyter-channel-ioloop-set-session ioloop (oref comm session))
(cl-call-next-method))
(jupyter-ioloop-start
ioloop (lambda (event)
(pcase (car event)
;; These channel events are from `jupyter-channel-ioloop'
('start-channel
(setf (jupyter-proxy-channel-alive-p
(plist-get (oref comm channels) (cadr event)))
t))
('stop-channel
(setf (jupyter-proxy-channel-alive-p
(plist-get (oref comm channels) (cadr event)))
nil))
(_ (jupyter-event-handler comm event))))))
(cl-loop
for channel in '(:hb :shell :iopub :stdin)
do (jupyter-start-channel comm channel))))
Expand All @@ -101,22 +113,6 @@
do (jupyter-stop-channel comm channel))
(cl-call-next-method))

;;;; `jupyter-channel-ioloop' events

(cl-defmethod jupyter-ioloop-handler ((_ioloop jupyter-channel-ioloop)
(comm jupyter-channel-ioloop-comm)
(event (head stop-channel)))
(setf (jupyter-proxy-channel-alive-p
(plist-get (oref comm channels) (cadr event)))
nil))

(cl-defmethod jupyter-ioloop-handler ((_ioloop jupyter-channel-ioloop)
(comm jupyter-channel-ioloop-comm)
(event (head start-channel)))
(setf (jupyter-proxy-channel-alive-p
(plist-get (oref comm channels) (cadr event)))
t))

;;;; Channel querying methods

(cl-defmethod jupyter-comm-alive-p ((comm jupyter-channel-ioloop-comm))
Expand Down
12 changes: 3 additions & 9 deletions jupyter-ioloop-comm.el
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,15 @@
((ioloop :type jupyter-ioloop))
:abstract t)

;; Fall back method that catches IOLoop events that have not been handled by
;; the communication layer already.
(cl-defmethod jupyter-ioloop-handler ((_ioloop jupyter-ioloop)
(comm jupyter-ioloop-comm)
event)
(unless (memq (car event) '(start quit))
(jupyter-event-handler comm event)))

(cl-defmethod jupyter-send ((comm jupyter-ioloop-comm) &rest event)
(apply #'jupyter-send (oref comm ioloop) event))

(cl-defmethod jupyter-comm-start ((comm jupyter-ioloop-comm))
(with-slots (ioloop) comm
(unless (jupyter-ioloop-alive-p ioloop)
(jupyter-ioloop-start ioloop comm))))
(jupyter-ioloop-start
ioloop (lambda (event)
(jupyter-event-handler comm event))))))

(cl-defmethod jupyter-comm-stop ((comm jupyter-ioloop-comm))
(with-slots (ioloop) comm
Expand Down
60 changes: 20 additions & 40 deletions jupyter-ioloop.el
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,19 @@
;; environment. You add an event that can be handled in the ioloop environment
;; by calling `jupyter-ioloop-add-event' before calling `jupyter-ioloop-start'.
;;
;; In the event handler of the ioloop, you may optionally return another event
;; back to the parent process. In this case, when the parent process receives
;; the event it is dispatched to an appropriate `jupyter-ioloop-handler'.
;; When one of the events added through `jupyter-ioloop-add-event'
;; returns something other than nil, it is sent back to the parent
;; process and the handler function passed to `jupyter-ioloop-start'
;; is called.
;;
;; An example that will echo back what was sent to the ioloop as a message in
;; the parent process:
;;
;; (cl-defmethod jupyter-ioloop-handler ((ioloop jupyter-ioloop) (tag (eql :tag1)) (event (head echo)))
;; (message "%s" (cadr event)))
;;
;; (let ((ioloop (jupyter-ioloop))
;; (jupyter-ioloop-add-event ioloop echo (data)
;; "Return DATA back to the parent process."
;; (list 'echo data))
;; (jupyter-ioloop-start ioloop :tag1)
;; (jupyter-ioloop-start ioloop (lambda (event) (message "%s" (cadr event))))
;; (jupyter-send ioloop 'echo "Message")
;; (jupyter-ioloop-stop ioloop))

Expand Down Expand Up @@ -128,10 +126,10 @@ listen for stdin/socket events using `jupyter-ioloop-poller'.
You add events to be handled by the subprocess using
`jupyter-ioloop-add-event', the return value of any event added
is what is sent to the parent Emacs process and what will
eventually be used as the EVENT argument of
`jupyter-ioloop-handler', which see. To suppress the subprocess
from sending anything back to the parent, ensure nil is returned
by the form created by `jupyter-ioloop-add-event'.
eventually be the sole argument to the handler function passed to
`jupyter-ioloop-start'. To suppress the subprocess from sending
anything back to the parent, ensure nil is returned by the form
created by `jupyter-ioloop-add-event'.
See `jupyter-channel-ioloop' for an example of its usage.")

Expand All @@ -143,15 +141,6 @@ See `jupyter-channel-ioloop' for an example of its usage.")
(when (process-live-p process)
(delete-process process))))))

(cl-defgeneric jupyter-ioloop-handler ((_ioloop jupyter-ioloop) obj event)
"Define a new IOLOOP handler, dispatching on OBJ, for EVENT.
OBJ will be the value of the object passed to
`jupyter-ioloop-start' and EVENT will be an event as received by
a filter function described in `zmq-start-process'."
;; Don't error on built in events
(unless (memq (car-safe event) '(start quit))
(error "Unhandled event (%s %s)" (type-of obj) event)))

(defun jupyter-ioloop-wait-until (ioloop event cb &optional timeout progress-msg)
"Wait until EVENT occurs on IOLOOP.
If EVENT occurs, call CB and return its value if non-nil. CB is
Expand Down Expand Up @@ -342,13 +331,6 @@ nothing."
(zmq-poller-remove jupyter-ioloop-poller socket)
(cl-decf jupyter-ioloop-nsockets)))

(defun jupyter-ioloop--delete-process (process)
(when-let* ((stdin (process-get process :stdin))
(socket-p (zmq-socket-p stdin)))
(zmq-close stdin)
(process-put process :stdin nil))
(delete-process process))

(defun jupyter-ioloop--body (ioloop on-stdin)
`(let (events)
(condition-case nil
Expand Down Expand Up @@ -414,26 +396,25 @@ polling the STDIN file handle."
(with-slots (process) ioloop
(and (process-live-p process) (process-get process :start))))

(defun jupyter-ioloop--make-filter (ioloop ref)
(defun jupyter-ioloop--make-filter (ioloop handler)
(lambda (event)
(with-slots (process) ioloop
(let ((process (oref ioloop process)))
(process-put process :last-event event)
(cond
((eq (car-safe event) 'start)
(process-put process :start t))
((eq (car-safe event) 'quit)
(process-put process :quit t)))
(process-put process :last-event event))
(let ((obj (jupyter-weak-ref-resolve ref)))
(if obj (jupyter-ioloop-handler ioloop obj event)
(jupyter-ioloop--delete-process (oref ioloop process))))))
(process-put process :quit t))
(t
(funcall handler event))))))

(cl-defgeneric jupyter-ioloop-start ((ioloop jupyter-ioloop)
object
handler
&key buffer)
"Start an IOLOOP.
OBJECT is an object which is used to dispatch on when the current
Emacs process receives an event to handle from IOLOOP, see
`jupyter-ioloop-handler'.
HANDLER is a function of one argument and will be passed an event
received by the subprocess that IOLOOP represents, an event is
just a list.
If IOLOOP was previously running, it is stopped first.
Expand Down Expand Up @@ -465,8 +446,7 @@ the IOLOOP subprocess buffer, see `zmq-start-process'."
;; scope, the ioloop process should be killed off. This
;; wouldn't happen if we hold a strong reference to
;; OBJECT.
:filter (jupyter-ioloop--make-filter
ioloop (jupyter-weak-ref object))
:filter (jupyter-ioloop--make-filter ioloop handler)
:buffer buffer)))
(oset ioloop process process)
(when stdin
Expand Down
24 changes: 12 additions & 12 deletions test/jupyter-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -961,7 +961,7 @@
(let ((ioloop (jupyter-ioloop))
(jupyter-default-timeout 2))
(should-not (process-live-p (oref ioloop process)))
(jupyter-ioloop-start ioloop :tag1)
(jupyter-ioloop-start ioloop #'ignore)
(should (equal (jupyter-ioloop-last-event ioloop) '(start)))
(with-slots (process) ioloop
(should (process-live-p process))
Expand All @@ -973,17 +973,17 @@
(defvar jupyter-ioloop-test-handler-called nil
"Flag variable used for testing the `juyter-ioloop'.")

(cl-defmethod jupyter-ioloop-handler ((_ioloop jupyter-ioloop)
(_tag (eql :test))
(event (head test)))
(should (equal (cadr event) "message"))
(setq jupyter-ioloop-test-handler-called t))
(defun jupyter-test-ioloop-start (ioloop)
(jupyter-ioloop-start
ioloop (lambda (event)
(should (equal (cadr event) "message"))
(setq jupyter-ioloop-test-handler-called t))))

(ert-deftest jupyter-ioloop-wait-until ()
:tags '(ioloop)
(let ((ioloop (jupyter-ioloop)))
(should-not (jupyter-ioloop-last-event ioloop))
(jupyter-ioloop-start ioloop :test)
(jupyter-test-ioloop-start ioloop)
(should (equal (jupyter-ioloop-last-event ioloop) '(start)))
(jupyter-ioloop-stop ioloop)))

Expand All @@ -994,13 +994,13 @@
(setq jupyter-ioloop-test-handler-called nil)
(jupyter-ioloop-add-callback ioloop
`(lambda () (zmq-prin1 (list 'test "message"))))
(jupyter-ioloop-start ioloop :test)
(jupyter-test-ioloop-start ioloop)
(jupyter-ioloop-stop ioloop)
(should jupyter-ioloop-test-handler-called)))
(ert-info ("Callback added after starting the ioloop")
(let ((ioloop (jupyter-ioloop)))
(setq jupyter-ioloop-test-handler-called nil)
(jupyter-ioloop-start ioloop :test)
(jupyter-test-ioloop-start ioloop)
(should (process-live-p (oref ioloop process)))
(jupyter-ioloop-add-callback ioloop
`(lambda () (zmq-prin1 (list 'test "message"))))
Expand All @@ -1014,7 +1014,7 @@
(setq jupyter-ioloop-test-handler-called nil)
(jupyter-ioloop-add-setup ioloop
(zmq-prin1 (list 'test "message")))
(jupyter-ioloop-start ioloop :test)
(jupyter-test-ioloop-start ioloop)
(jupyter-ioloop-stop ioloop)
(should jupyter-ioloop-test-handler-called)))

Expand All @@ -1024,7 +1024,7 @@
(setq jupyter-ioloop-test-handler-called nil)
(jupyter-ioloop-add-teardown ioloop
(zmq-prin1 (list 'test "message")))
(jupyter-ioloop-start ioloop :test)
(jupyter-test-ioloop-start ioloop)
(jupyter-ioloop-stop ioloop)
(should jupyter-ioloop-test-handler-called)))

Expand All @@ -1035,7 +1035,7 @@
(jupyter-ioloop-add-event ioloop test (data)
"Echo DATA back to the parent process."
(list 'test data))
(jupyter-ioloop-start ioloop :test)
(jupyter-test-ioloop-start ioloop)
(jupyter-send ioloop 'test "message")
(jupyter-ioloop-stop ioloop)
(should jupyter-ioloop-test-handler-called)))
Expand Down

0 comments on commit 1fa6ed4

Please sign in to comment.