Skip to content

Commit

Permalink
add ipython-shell-send/run-jupyter-existing
Browse files Browse the repository at this point in the history
  • Loading branch information
jackkamm committed Sep 10, 2018
1 parent 36523a3 commit ff944b4
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 31 deletions.
34 changes: 15 additions & 19 deletions Readme.org
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
* 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
ipython-shell-send-region
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.
74 changes: 62 additions & 12 deletions ipython-shell-send.el
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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

Expand Down

0 comments on commit ff944b4

Please sign in to comment.