mirror of
https://github.com/vale981/emacs-jupyter
synced 2025-03-04 15:41:37 -05:00

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.
160 lines
6.7 KiB
EmacsLisp
160 lines
6.7 KiB
EmacsLisp
;;; jupyter-channel-ioloop-comm.el --- Communication layer using jupyter-channel-ioloop -*- lexical-binding: t -*-
|
|
|
|
;; Copyright (C) 2019-2020 Nathaniel Nicandro
|
|
|
|
;; Author: Nathaniel Nicandro <nathanielnicandro@gmail.com>
|
|
;; Created: 27 Jun 2019
|
|
|
|
;; This program 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, or (at
|
|
;; your option) any later version.
|
|
|
|
;; This program 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 GNU Emacs; see the file COPYING. If not, write to the
|
|
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
;; Boston, MA 02111-1307, USA.
|
|
|
|
;;; Commentary:
|
|
|
|
;; Implements the `jupyter-comm-layer' interface on-top of a `jupyter-ioloop'
|
|
;; subclass that implements the `jupyter-channel' interface through events sent
|
|
;; to the ioloop. The `jupyter-ioloop' must implement a start-channel,
|
|
;; stop-channel, and a send event. For the start-channel and stop-channel
|
|
;; events the `jupyter-ioloop' must send back a list like
|
|
;;
|
|
;; (start-channel :hb) or (stop-channel :shell)
|
|
;;
|
|
;; for confirmation that the corresponding channel was indeed started or
|
|
;; stopped. The start-channel event should accept two arguments, (CHANNEL
|
|
;; ENDPOINT), used to start CHANNEL. The stop-channel event should accept a
|
|
;; single argument, CHANNEL, and stop the channel in the `jupyter-ioloop'
|
|
;; environment.
|
|
;;
|
|
;; Initializing the connection
|
|
;;
|
|
;; The `jupyter-comm-initialize' method should be called before calling
|
|
;; `jupyter-comm-start' and should be passed a `jupyter-session' object used to
|
|
;; initialize the `jupyter-channel-ioloop' object.
|
|
|
|
;;; Code:
|
|
|
|
(require 'jupyter-base)
|
|
(require 'jupyter-ioloop-comm)
|
|
(require 'jupyter-channel-ioloop)
|
|
|
|
(cl-defstruct jupyter-proxy-channel endpoint alive-p)
|
|
|
|
(defclass jupyter-channel-ioloop-comm (jupyter-ioloop-comm
|
|
jupyter-hb-comm
|
|
jupyter-comm-autostop)
|
|
((ioloop-class :type class :initarg :ioloop-class)
|
|
(session :type jupyter-session)
|
|
(channels :type (list-of (or keyword jupyter-proxy-channel)) :initform nil)))
|
|
|
|
(cl-defmethod initialize-instance ((comm jupyter-channel-ioloop-comm) &optional _slots)
|
|
(cl-call-next-method)
|
|
(unless (slot-boundp comm 'ioloop-class)
|
|
(oset comm ioloop-class 'jupyter-channel-ioloop))
|
|
(with-slots (ioloop-class) comm
|
|
(jupyter-error-if-not-client-class-p ioloop-class 'jupyter-channel-ioloop)
|
|
(oset comm ioloop (make-instance ioloop-class))))
|
|
|
|
(cl-defmethod jupyter-comm-id ((comm jupyter-channel-ioloop-comm))
|
|
(format "session=%s" (truncate-string-to-width
|
|
(jupyter-session-id (oref comm session))
|
|
9 nil nil "…")))
|
|
|
|
(cl-defmethod jupyter-comm-initialize ((comm jupyter-channel-ioloop-comm)
|
|
(session jupyter-session))
|
|
(cl-call-next-method)
|
|
(let ((endpoints (jupyter-session-endpoints session)))
|
|
(oset comm session (copy-sequence session))
|
|
(oset comm hb (make-instance
|
|
'jupyter-hb-channel
|
|
:session (oref comm session)
|
|
:endpoint (plist-get endpoints :hb)))
|
|
(oset comm channels (cl-loop
|
|
for channel in '(:stdin :shell :iopub)
|
|
collect channel and
|
|
collect (make-jupyter-proxy-channel
|
|
:endpoint (plist-get endpoints channel)
|
|
:alive-p nil)))))
|
|
|
|
(cl-defmethod jupyter-comm-start ((comm jupyter-channel-ioloop-comm))
|
|
(with-slots (ioloop session) comm
|
|
(unless (jupyter-ioloop-alive-p ioloop)
|
|
(jupyter-channel-ioloop-set-session ioloop (oref comm session))
|
|
(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))))
|
|
|
|
(cl-defmethod jupyter-comm-stop ((comm jupyter-channel-ioloop-comm))
|
|
(cl-loop
|
|
for channel in '(:hb :shell :iopub :stdin)
|
|
do (jupyter-stop-channel comm channel))
|
|
(cl-call-next-method))
|
|
|
|
;;;; Channel querying methods
|
|
|
|
(cl-defmethod jupyter-comm-alive-p ((comm jupyter-channel-ioloop-comm))
|
|
(cl-loop
|
|
for channel in '(:shell :iopub :stdin :hb)
|
|
thereis (jupyter-channel-alive-p comm channel)))
|
|
|
|
(cl-defmethod jupyter-channel-alive-p ((comm jupyter-channel-ioloop-comm) channel)
|
|
(if (eq channel :hb)
|
|
(and (slot-boundp comm 'hb)
|
|
(jupyter-channel-alive-p (oref comm hb)))
|
|
(with-slots (ioloop) comm
|
|
(and ioloop (jupyter-ioloop-alive-p ioloop)
|
|
(jupyter-proxy-channel-alive-p
|
|
(plist-get (oref comm channels) channel))))))
|
|
|
|
;;;; Channel start/stop methods
|
|
|
|
(cl-defmethod jupyter-stop-channel ((comm jupyter-channel-ioloop-comm) channel)
|
|
(when (jupyter-channel-alive-p comm channel)
|
|
(if (eq channel :hb) (jupyter-stop-channel (oref comm hb))
|
|
(with-slots (ioloop) comm
|
|
(jupyter-send ioloop 'stop-channel channel)
|
|
;; Verify that the channel stops
|
|
(jupyter-with-timeout
|
|
(nil jupyter-default-timeout
|
|
(error "Channel not stopped in ioloop subprocess (%s)" channel))
|
|
(not (jupyter-channel-alive-p comm channel)))))))
|
|
|
|
(cl-defmethod jupyter-start-channel ((comm jupyter-channel-ioloop-comm) channel)
|
|
(unless (jupyter-channel-alive-p comm channel)
|
|
(if (eq channel :hb) (jupyter-start-channel (oref comm hb))
|
|
(let ((endpoint (jupyter-proxy-channel-endpoint
|
|
(plist-get (oref comm channels) channel))))
|
|
(with-slots (ioloop) comm
|
|
(jupyter-send ioloop 'start-channel channel endpoint)
|
|
;; Verify that the channel starts
|
|
(jupyter-with-timeout
|
|
(nil jupyter-default-timeout
|
|
(error "Channel not started in ioloop subprocess (%s)" channel))
|
|
(jupyter-channel-alive-p comm channel)))))))
|
|
|
|
(provide 'jupyter-channel-ioloop-comm)
|
|
|
|
;;; jupyter-channel-ioloop-comm.el ends here
|