From ff944b436db381e6772a26c09b0b20a097eb323e Mon Sep 17 00:00:00 2001 From: Jack Kamm Date: Sun, 9 Sep 2018 21:46:22 -0700 Subject: [PATCH] add ipython-shell-send/run-jupyter-existing --- Readme.org | 34 +++++++++----------- ipython-shell-send.el | 74 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 77 insertions(+), 31 deletions(-) diff --git a/Readme.org b/Readme.org index 4260d91..e427dfe 100644 --- a/Readme.org +++ b/Readme.org @@ -1,13 +1,13 @@ * ipython-shell-send - -This is a small emacs package to send code regions to the IPython interpreter. -It provides functionality similar to the built-in ~python-shell-send-*~ -functions from ~python.el~. The main difference is that ~ipython-shell-send~ can send regions -containing IPython magic (such as ~!ls~ or ~%timeit~), whereas ~python.el~ -in general cannot (aside from the special case noted in Details/Note). -=ipython-shell-send= provides the following functions which can send the buffer, -highlighted region, or current defun to the IPython interpreter: +This package adds extra IPython functionality for Emacs' python.el. +It adds the following two features: +1. Connect to and run existing jupyter consoles, e.g. on a remote server. +2. Allow IPython magic in code blocks sent to the inferior Python buffer. + +The first feature is provided by the function ~ipython-shell-send/run-jupyter-existing~. Calling this function will prompt for an existing jupyter kernel, and then run it in the inferior Python buffer from ~python.el~. To run a remote jupyter kernel, call ~ipython-shell-send/run-jupyter-existing~ from within a Tramp buffer visiting the remote machine. + +The second feature is provided by the following functions: #+BEGIN_SRC emacs-lisp ipython-shell-send-buffer @@ -15,18 +15,14 @@ highlighted region, or current defun to the IPython interpreter: ipython-shell-send-defun #+END_SRC -Note to use these functions, you must make sure -to start an IPython shell when calling ~run-python~. +which are analogous to their counterparts ~python-shell-*~ from ~python.el~, except that they can handle IPython magic commands such as ~!ls~ or ~%timeit~. -** Details +** Example: running a remote Jupyter kernel in Emacs -The code for ~ipython-shell-send-*~ is almost exactly the same as ~python-shell-send-*~, -which works by sending code to a tmpfile executed by the Python function ~exec()~. -The only difference is that ~ipython-shell-send-*~ uses the IPython magic ~%run -i~ to execute the -tmpfile instead. +First, start a Jupyter console named "my-remote-console" on the remote machine: -*** Note +#+BEGIN_SRC sh + jupyter --console -f my-remote-console +#+END_SRC -Note that the built-in ~python.el~ is able to send IPython magic in one special case, when the region consists -of only 1 line. This precludes using IPython magics in for loops or functions, and can also be inconvenient when -needing to send multiple lines of IPython magic to the console. Thus my motivation for writing this package. +Then in emacs, visit any file on the remote machine, e.g. =C-x C-f /scp:username@remotehost:~=. Then call =M-x ipython-shell-send/run-jupyter-existing=, and enter "my-remote-console" at the prompt. This will create the usual inferior =*Python*= buffer, but connected to a remote Jupyter console instead of starting a new Python subprocess. diff --git a/ipython-shell-send.el b/ipython-shell-send.el index 11dadac..1f9bafb 100644 --- a/ipython-shell-send.el +++ b/ipython-shell-send.el @@ -23,20 +23,20 @@ ;;; Commentary: -;; This is a package for sending code to the IPython interpreter. -;; It provides functionality similar to the `python-shell-send-*' -;; functions in python.el, but is able to send code regions -;; containing IPython magic (such as `!ls' or `%timeit'), -;; whereas python.el only has limited support for this. +;; This package adds extra IPython functionality for Emacs' python.el. +;; It adds the following two features: +;; 1. Connect to and run existing jupyter consoles, e.g. on a remote server. +;; 2. Allow IPython magic in code blocks sent to the inferior Python buffer. ;; -;; The functions provided by ipython-shell-send are -;; `ipython-shell-send-region', `ipython-shell-send-buffer', -;; and `ipython-shell-send-defun'. They are essentially equivalent -;; to their `python-shell-send-*' equivalents in `python.el', -;; except better able to handle IPython magic. +;; The first feature is provided by the function +;; `ipython-shell-send/run-jupyter-existing', which is analogous +;; to python.el's `run-python', except it connects to an existing Jupyter +;; console instead of starting a new Python subprocess. ;; -;; Note to use the ipython-shell-send, you must make sure -;; to start an IPython shell when calling `run-python'. +;; The second feature is provided by the functions +;; `ipython-shell-send-buffer', `ipython-shell-send-region', and +;; `ipython-shell-send-defun', which are analogous to `python-shell-send-*' +;; in python.el, except that they can handle IPython magic commands. ;;; Code: @@ -168,6 +168,56 @@ t when called interactively." (or temp-file-name file-name)) process))) +(defun ipython-shell-send/run-jupyter-existing--command (kernel) + "Return string for the command to connect to an existing jupyter KERNEL." + (concat "jupyter console --simple-prompt --existing " kernel)) + +;;;###autoload +(defun ipython-shell-send/run-jupyter-existing (dedicated show) + "Run existing Jupyter kernel within inferior Python buffer. + +Prompts for an existing kernel, then opens it in the standard +inferior Python buffer from python.el. For example, the kernel +may correspond to a running Jupyter notebook, or may have been +started manually with the 'jupyter console' command. Leaving +the prompt blank will select the most recent kernel. + +To connect to a remote kernel, call this function from within +a Tramp buffer on the remote machine. + +When called interactively with `prefix-arg', it allows the +user to edit such choose whether the interpreter +should be DEDICATED for the current buffer. When numeric +prefix arg is other than 0 or 4 do not SHOW." + (interactive + (if current-prefix-arg + (list + (y-or-n-p "Make dedicated process? ") + (= (prefix-numeric-value current-prefix-arg) 4)) + (list nil t))) + (run-python + (read-shell-command + "Run Python: " + (ipython-shell-send/run-jupyter-existing--command + (condition-case err + (completing-read + "Kernel file (blank for most recent): " + (cdr + (cdr + (directory-files + (concat + (file-remote-p default-directory) + (string-trim + (let ((shell-file-name "/bin/sh")) + (shell-command-to-string + (concat "python3 -c 'import jupyter_core.paths as jsp; " + "print(jsp.jupyter_runtime_dir())'")))))))) + nil nil "") + (file-error + (warn (error-message-string err)) + nil)))) + dedicated show)) + (provide 'ipython-shell-send) ;;; ipython-shell-send.el ends here