This change localizes all `zmq` related functionality to `jupyter-ioloop` and
`jupyter-zmq-*` files.
* jupyter-channel-ioloop-comm.el: Add better commentary.
(jupyter-base): Require.
(jupyter-channel-ioloop-comm): Add `ioloop-class` slot
(initialize-instance [jupyter-channel-ioloop-comm]): Use it.
* jupyter-channel-ioloop.el (jupyter-base, jupyter-zmq-channel): Un-require.
(jupyter-ioloop-session, jupyter-ioloop-channels):
Rename to `jupyter-channel-ioloop-session` `jupyter-channel-ioloop-channels` and
update all callers.
(jupyter-channel-ioloop): Make into an abstract class.
(initialize-instance [jupyter-channel-ioloop]): Re-add
`jupyter-channel-ioloop-add-send-event`. Don't add to
`jupyter-ioloop-post-hook`.
(jupyter-channel-ioloop-recv-messages): Remove.
(jupyter-channel-ioloop--set-session, jupyter-ioloop-start)
(jupyter-channel-ioloop-add-send-event): Doc changes.
(jupyter-channel-ioloop-add-start-channel-event)
(jupyter-channel-ioloop-add-stop-channel-event):
Don't add/remove from the `jupyter-ioloop-poller`.
Now expected to be handled in the `jupyter-channel` subclass.
Update documentation. In addition, for the start-channel event,
do not attempt to add a channel if one doesn't already exist.
* jupyter-ioloop.el
(jupyter-ioloop-add-teardown):
Remove mention of `jupyter-channel-ioloop` behavior.
(jupyter-ioloop-add-arg-type): Update example variable.
(jupyter-ioloop-environment-p): New function.
* jupyter-kernel-manager.el (jupyter-channel): Require.
(jupyter-make-client): Require and use `jupyter-zmq-channel-ioloop`.
(jupyter-start-channels): Use `make-instance`.
(jupyter-interrupt-kernel): Remove `condition-case`. Not needed since
preventing socket blocking is now handled by `jupyter-recv`.
* jupyter-repl.el
(jupyter-connect-repl): Require and use `jupyter-zmq-channel-ioloop`.
* jupyter-zmq-channel-ioloop.el: New file.
* jupyter-zmq-channel.el (jupyter-ioloop-poller-remove)
(jupyter-ioloop-poller-add): New declares.
(jupyter-start-channel):
Add to `jupyter-ioloop-poller` when in `jupyter-ioloop-environment-p`.
(jupyter-stop-channel):
Only disconnect the socket from its endpoint instead of closing it, leave that
up to garbage collection.
Remove from `jupyter-ioloop-poller` when in `jupyter-ioloop-environment-p`.
(jupyter-recv): Handle non-blocking.
* test/jupyter-test.el
(jupyter-zmq-channel): Use non-blocking `zmq-send` since socket is no longer
closed when calling `jupyter-stop-channel`.
(jupyter-ioloop-test-eval-ioloop): Rename to `jupyter-test-ioloop-eval-event`,
update all callers, and move to `test/test-helper.el`.
(jupyter-channel-ioloop-send-event, jupyter-channel-ioloop-stop-channel-event)
(jupyter-channel-ioloop-start-channel-event): Fix tests for variable name
changes. Use `jupyter-test-channel-ioloop`. Update `jupyter-ioloop-poller`,
addition/removal from poller is now done in the `jupyter-channel` subclass by
checking `jupyter-ioloop-environment-p`.
* test/test-helper.el (jupyter-zmq-channel-ioloop): Require.
(initialize-instance [jupyter-echo-client]): Use it.
(jupyter-test-channel-ioloop): New macro.
(jupyter-test-ioloop-eval-event): New function.
Having the `jupyter-comm-layer` abstraction means we do not need to do so.
* jupyter-base.el (zmq): Un-require.
(jupyter-socket-types): Move to `jupyter-channels.el`.
(jupyter-session): Don't mention zmq in doc string.
(jupyter-available-local-ports, jupyter-make-ssh-tunnel): New functions.
(jupyter-tunnel-connection): Use them.
* jupyter-channel-ioloop-comm.el: New file.
* jupyter-channels.el (jupyter-messages): Un-require.
(jupyter-comm-layer, zmq): New requires.
(jupyter-socket-types): Moved from `jupyter-base.el`.
(jupyter-send, jupyter-recv):
Implementations for `jupyter-session` moved from `jupyter-messages.el`.
(jupyter-sync-channel-comm): `jupyter-comm-layer` implementation for
`jupyter-sync-channel` objects moved from `jupyter-comm-layer.el`.
* jupyter-comm-layer.el (jupyter-channel-ioloop): Un-require.
(jupyter-sync-channel-comm): Move implementation to `jupyter-channels.el`.
(jupyter-ioloop-comm): Move implementation to new file `jupyter-ioloop-comm.el`.
(jupyter-channel-ioloop-comm):
Move implementation to new file `jupyter-channel-ioloop-comm.el`.
* jupyter-ioloop-comm.el: New file.
* jupyter-ioloop.el (zmq): Require.
* jupyter-kernel-manager.el
(jupyter-make-client): Ensure `jupyter-channel-ioloop-comm` is required.
* jupyter-messages.el (jupyter-send)
(jupyter-recv): Moved to `jupyter-channels.el`
* jupyter-repl.el
(jupyter-connect-repl): Ensure `jupyter-channel-ioloop-comm` is required.
* test/jupyter-test.el (jupyter-available-local-ports): New test.
* test/test-helper.el (jupyter-channel-ioloop-comm): New require.
Also fix misuse of `alist-get`. `alist-get` uses `assq` for comparison by
default, but we are comparing strings. This caused issues when caching REPL
buffers and the subsequent re-use of them during testing.
The previous mechanism to communicate with a kernel was too low level from the
perspective of a client. The client interfaced directly with the subprocess
abstraction, `jupyter-ioloop`, and had to handle all "events" that occurred in
the `jupyter-ioloop`, e.g. when a channel was started or stopped. But in
reality such events should not be the concern of a client.
A client should only care about events that are directly related to kernel
messages and not events related to the implementation details of *how*
communication occurs.
This commit abstracts out the way in which a client communicates with its
kernel by introducing a new `jupyter-comm-layer` class. The
`jupyter-comm-layer` class takes care of managing the communication channel
between a kernel and its clients as well as sending events to all registered
clients. This way, clients operate solely at the level of events on the
communication layer. All a client does is register itself to receive events on
the communication layer and send events on the layer.
* jupyter-base.el (jupyter-session-endpoints): New function.
* jupyter-client.el (jupyter-kernel-client): Remove ioloop and channels slots.
Add kcomm slot.
(initialize-instance): Unconditionally stop channels.
(jupyter-initialize-connection): Change into a method call.
Call `jupyter-initialize-connection` on the `kcomm` slot.
(jupyter-with-client-buffer): Remove stale comment.
(jupyter-send): Call `jupyter-send` on the `kcomm` slot.
(jupyter-ioloop-handler): Remove all method definitions, replace `sent` and
`message` methods with their `jupyter-event-handler` equivalents.
(jupyter-hb-pause, jupyter-hb-unpause, jupyter-hb-beating):
(jupyter-channel-alive-p, jupyter-start-channel, jupyter-stop-channel):
(jupyter-start-channels, jupyter-stop-channels):
Replace with calls to their equivalents using the `kcomm` slot.
* jupyter-comm-layer.el: New file.
* jupyter-kernel-manager (jupyter-make-client): Set a client's `kcomm` slot to
`jupyter-channel-ioloop-comm`.
* jupyter-messages.el (jupyter-decode-message): Use `list` directly. There
seemed to be issues when using the new `jupyter-sync-channel-comm` due to
using quoted lists.
* test/jupyter-test.el: Add `jupyter-comm-layer` test. Update other tests.
* test/test-helper.el: Add `jupyter-comm-layer` mock objects. Update
`jupyter-echo-client`.