Remove jupyter-connection.el

This commit is contained in:
Nathaniel Nicandro 2020-09-09 14:00:45 -05:00
parent 17a09d5ac8
commit 9d801b72a3
4 changed files with 0 additions and 303 deletions

View file

@ -1,299 +0,0 @@
;;; jupyter-connection.el --- Linking kernels and clients -*- lexical-binding: t -*-
;; Copyright (C) 2020 Nathaniel Nicandro
;; Author: Nathaniel Nicandro <nathanielnicandro@gmail.com>
;; Created: 21 Apr 2020
;; 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:
;; This file defines the `jupyter-connect' function used to connect a
;; client to a kernel.
;;
;;
;; Before a client and kernel can be connected, a connection to a
;; kernel must be made to get at the
;; This file defines the `jupyter-connection' method which takes a
;; kernel and a client and returns an instance of the
;; `jupyter-connection' struct type, representing the set of IO
;; actions and state transformations required to establish
;; communication between a kernel and a client.
;;; Code:
(require 'jupyter-kernel)
(require 'jupyter-client)
(require 'thunk)
(defgroup jupyter-connection nil
"Linking kernels and clients"
:group 'jupyter)
(defvar jupyter-connections (make-hash-table :weakness 'key)
"A cache for Jupyter kernel connections.
The keys are objects like kernels, clients, and other auxiliary
objects. The values are lists whose elements are related in some
way to connecting clients to kernels in the current Emacs
session.
This table is used by `jupyter-io' to store the connection lists
returned by `jupyter-connection'. It is also used by
`jupyter-connect' to keep track of the kernel a client is
connected to.")
;; FIXME: Remove the channel argument
(cl-defmethod jupyter-alive-p ((io function) &optional _channel)
"Return non-nil if CONN is live."
(jupyter-send io 'alive-p))
(cl-defmethod jupyter-start ((io function) &optional _channel)
(jupyter-send io 'start))
(cl-defmethod jupyter-stop ((io function) &optional _channel)
(jupyter-send io 'stop))
;;;; `jupyter-connection'
(defmacro jupyter-run-handlers (place event)
"Evaluate the list of functions in PLACE using ARGS.
Set PLACE to those functions that returned non-nil."
`(setf ,place
(let ((handlers ,place)
(event ,event)
(new-handlers '()))
(while handlers
(let ((h (pop handlers)))
(when (funcall h event)
(push h new-handlers))))
new-handlers)))
(cl-defgeneric jupyter-connection (thing)
"Establish a connection to THING.
Return a list (IO ...), where IO is a function used to perform
I/O actions on THING, the rest of the list is ignored.
IO takes arguments like (TYPE ARGS...) where TYPE is a symbol
describing the type of I/O action/query that ARGS are parameters
to.
This method should not be called directly, use `jupyter-io'.
At a minimum, IO should accept to the following sets of I/O
actions/queries:
* Connection lifetime
- ('start) :: Enable I/O.
- ('stop) :: Disable I/O.
- ('alive-p &optional CHANNEL) :: Return non-nil if I/O
connection is live. If CHANNEL is specified and the I/O
connection resolves individual channels, return non-nil if
CHANNEL is live.
* Message sending/receiving
- ('send ARGS...) :: Send ARGS as a message to THING.
- ('add-handler FN) :: Add FN, a function, as a handler of
messages received from THING.
- ('remove-handler FN) :: Remove FN as a handler.
'send and 'add-handler are also responsible for enabling the
connection if it has not been already.
* Heartbeat channel [1]
- ('hb) :: Return a `jupyter-hb-channel' or nil if the
connection does not support one.
\[1] https://jupyter-client.readthedocs.io/en/stable/messaging.html#heartbeat-for-kernels")
(declare-function jupyter-kernel-process "ext:jupyter-kernel-process")
(cl-defmethod jupyter-connection ((kernel jupyter-kernel))
"Return a default connection list for KERNEL."
(list
(lambda (&rest args)
(pcase (car args)
((or 'start 'stop 'alive-p))
((or 'message 'add-handler 'remove-handler) (error "Not implemented"))
('hb nil)
(_ (error "Unhandled IO: %s" args))))))
;; Kernel -> IO Kernel-IO
(cl-defmethod jupyter-connect ((kernel jupyter-kernel))
;; Any -> IO Kernel-IO
(jupyter-launch kernel)
(jupyter-return (jupyter-io kernel)))
;; Client -> IO Client
(cl-defmethod jupyter-connect ((client jupyter-kernel-client))
;; Any -> IO Client-IO
(lambda (_)
(let ((io (jupyter-io (jupyter-kernel client))))
(jupyter-return
(lambda (&rest args)
(let ((jupyter-current-client client))
(apply io args)))))))
(defun jupyter-io (thing)
"Return a function that can be used to perform I/O with THING.
THING must implement both `jupyter-alive-p' and
`jupyter-connection'. If THING is not alive an error is
signaled, otherwise the connection of THING is cached and the I/O
function returned.
The function takes arguments like (TYPE ARGS...) where TYPE is a
symbol describing the type of I/O action/query that ARGS are
parameters to."
(cl-assert (jupyter-alive-p thing))
(pcase-let ((`(,io . ,_)
(or (gethash thing jupyter-connections)
(puthash thing (jupyter-connection thing)
jupyter-connections))))
io))
(cl-defmethod jupyter-send ((io function) &rest args)
"Send ARGS on IO."
(apply io args))
(cl-defmethod jupyter-send ((kernel jupyter-kernel) &rest args)
"Send a message to KERNEL.
Any associated messages will be received by KERNEL's handlers.
See `jupyter-connection' for how a handler can be added."
(apply (jupyter-io kernel) 'message args))
;; `jupyter-connection' is an example of a monad that passes monadic
;; values like (IO ....), where ... is the context, mainly finalizers,
;; of the IO connection functions. `jupyter-connection' is actually
;; taking a monadic value and returning another monadic value.
;;
;; TODO I need to have a bind like function that I can use here. That
;; way I can probably get rid of the boiler plate. The bind function
;; takes the IO function and wraps it with another one, and returns a
;; new list like what is being done here. Mapping from one category to
;; another.
;;
;; Can't be :extra since we don't know when those files will be loaded
(defun jupyter-with-client-handlers (io)
"Return an I/O function that can take clients as handlers.
The returned function allows the argument to the 'add-handler and
'remove-handler I/O actions to take `jupyter-kernel-client'
objects.
All other I/O actions are passed through to IO."
(let ((handlers (make-hash-table :weakness 'key)))
(cl-macrolet ((kernel-io (&rest args)
(if (eq (car (last args)) 'args)
`(apply io ,@args)
`(funcall io ,@args))))
(cl-labels
((handler
(client)
;; Make a weak ref. to CLIENT so that remhandler, below,
;; will be called if CLIENT is not disconnected before being
;; garbage collected.
(letrec
((ref (jupyter-weak-ref client))
(h (lambda (event)
;; FIXME: Handle the `sent' event, but not here.
;; How can I get rid of it?
(pcase (car event)
((and 'message (let `(,channel ,_idents . ,msg) (cdr event)))
(if-let ((client (jupyter-weak-ref-resolve ref)))
(jupyter-handle-message client channel msg)
(kernel-io 'remove-handler h)))))))
h)))
(list
(lambda (&rest args)
(pcase args
(`(,(and (or 'add-handler 'remove-handler) action)
,(and (guard (object-of-class-p (cadr args) 'jupyter-kernel-client))
(let client (cadr args))))
(let ((h (gethash client handlers)))
(pcase action
((and 'add-handler (guard (not h)))
(kernel-io action (setf (gethash client handlers) (handler client))))
((and 'remove-handler (guard h))
(kernel-io action (progn (remhash client handlers) h))))))
(_ (kernel-io args)))))))))
(cl-defmethod jupyter-connection :around ((kernel jupyter-kernel))
(jupyter-bind (cl-call-next-method) #'jupyter-with-client-handlers))
(cl-defmethod jupyter-connect ((client jupyter-kernel-client) (kernel jupyter-kernel))
"Connect CLIENT to KERNEL.
When CLIENT has been connected to KERNEL, it handles messages
KERNEL sends to its clients. Return an I/O function that can be
used to send messages to KERNEL.
The connection exists as long as CLIENT exists or until CLIENT is
connected to a different kernel. The connection of a client can
be removed manually with `jupyter-disconnect'.
See `jupyter-connection' for more info. on the I/O function."
(jupyter-disconnect client)
(jupyter-launch kernel)
(let ((io (jupyter-io kernel)))
(jupyter-send io 'add-handler client)
(puthash client (list kernel) jupyter-connections)
io))
(cl-defmethod jupyter-connect ((kernel jupyter-kernel) (client jupyter-kernel-client))
(jupyter-connect client kernel))
(cl-defmethod jupyter-disconnect ((client jupyter-kernel-client))
"Disconnect CLIENT from its kernel, if any."
(when-let* ((kernel (car (gethash client jupyter-connections))))
(jupyter-send (jupyter-io kernel) 'remove-handler client)
(remhash client jupyter-connections)))
(cl-defmethod jupyter-kernel ((client jupyter-kernel-client))
"Return the kernel CLIENT is connected to.
Return nil if CLIENT is not connected to any kernel."
(let ((kernel (car (gethash client jupyter-connections))))
kernel))
(cl-defmethod jupyter-clients ((kernel jupyter-kernel))
"Return a list of clients KERNEL is connected to."
(let ((clients '()))
(maphash (lambda (k v)
(when (and (eieio-object-p k)
(object-of-class-p k 'jupyter-kernel-client)
(memq kernel v))
(push k clients)))
jupyter-connections)
clients))
(cl-defmethod jupyter-disconnect ((kernel jupyter-kernel))
"Disconnect all clients of KERNEL."
(cl-loop for client in (jupyter-clients kernel)
do (jupyter-disconnect client)))
(cl-defmethod jupyter-shutdown :extra "IO" ((kernel jupyter-kernel))
"Stop KERNEL's I/O connections."
(jupyter-disconnect kernel)
(when (jupyter-alive-p kernel)
(jupyter-stop (jupyter-io kernel)))
(cl-call-next-method))
(provide 'jupyter-connection)
;;; jupyter-connection.el ends here

View file

@ -57,7 +57,6 @@
(eval-when-compile (require 'subr-x)) (eval-when-compile (require 'subr-x))
(require 'jupyter-base) (require 'jupyter-base)
(require 'jupyter-mime) (require 'jupyter-mime)
(require 'jupyter-connection)
(require 'jupyter-kernelspec) (require 'jupyter-kernelspec)
(require 'jupyter-widget-client) (require 'jupyter-widget-client)
(require 'ring) (require 'ring)

View file

@ -54,8 +54,6 @@ Used in, e.g. a `jupyter-server-kernel-list-mode' buffer.")
;; communication channel with one. ;; communication channel with one.
(defclass jupyter-server (jupyter-rest-client eieio-instance-tracker) (defclass jupyter-server (jupyter-rest-client eieio-instance-tracker)
((tracking-symbol :initform 'jupyter--servers) ((tracking-symbol :initform 'jupyter--servers)
(conn :type jupyter-connection)
(handlers :type list :initform nil)
(kernelspecs (kernelspecs
:type json-plist :type json-plist
:initform nil :initform nil

View file

@ -28,7 +28,6 @@
(require 'zmq) (require 'zmq)
(require 'jupyter-zmq-channel-ioloop) (require 'jupyter-zmq-channel-ioloop)
(require 'jupyter-connection)
(require 'jupyter-kernel-process) (require 'jupyter-kernel-process)
(require 'jupyter-repl) (require 'jupyter-repl)
(require 'jupyter-org-client) (require 'jupyter-org-client)