A New Way to Edit Jupyter Python Notebooks in Emacs
Monday, May 5, 2025
Table of Contents
I created the Jupyter Ascending package to facilitate editing and executing code in a Jupyter Python notebook from Emacs. It provides commands which utilize the Jupytext and Jupyter Ascending command line tools to
- render .ipynb notebooks as Python source files,
- synchronize the state of the 2 files, and
- execute code in the Jupyter notebook.
Demo
Figure 1: Python usage
Figure 2: Markdown usage
Figure 3: Starting up a session
Features
- Synchronization: Editing and saving the Python buffer automatically updates the Jupyter notebook
- Cell execution commands: Run individual cells or the entire notebook
- Navigation tools: Jump between cells with simple commands
- Cell management: Create new cells and toggle between code and markdown types
- Enhanced markdown editing:
- Edit markdown cells in dedicated markdown buffers (similar to Org mode's special edit mode)
- Automatic comment insertion when pressing return in markdown cells
- Setup utilities: Commands for starting Jupyter notebooks and creating synchronized file pairs
Installation
Jupyter Ascending is available on MELPA.
Prerequisites
The following dependencies must be installed first.
If you haven't already installed Jupyter:
pip install notebook
Then install the Jupyter Ascending command line tool.
pip install jupyter_ascending && python3 -m jupyter nbextension install jupyter_ascending --sys-prefix --py && \ python3 -m jupyter nbextension enable jupyter_ascending --sys-prefix --py && \ python3 -m jupyter serverextension enable jupyter_ascending --sys-prefix --py
Installing Jupyter Ascending (the command line tool) should install jupytext, but if not, run pip install jupytext
.
Configuration
Here's a sample installation which
- provides keybindings for common commands in
jupyter-ascending-mode
and - uses the minor mode when opening python files with the
.sync
suffix just before the file extension.
(use-package jupyter-ascending :ensure t :hook (python-mode . (lambda () (when (and buffer-file-name (string-match-p "\\.sync\\.py\\'" buffer-file-name)) (jupyter-ascending-mode 1)))) :bind (:map jupyter-ascending-mode-map ("C-c C-k" . jupyter-ascending-execute-line) ("C-c C-a" . jupyter-ascending-execute-all) ("C-c C-n" . jupyter-ascending-next-cell) ("C-c C-p" . jupyter-ascending-previous-cell) ("C-c t" . jupyter-ascending-cycle-cell-type) ("C-c '" . jupyter-ascending-edit-markdown-cell)))
Usage
Create a notebook pair with
M-x jupyter-ascending-create-notebook-pair RET example RET
Or, equivalently
python3 -m jupyter_ascending.scripts.make_pair --base example
This creates synced files: example.sync.py
and example.sync.ipynb
If you have an existing Jupyter notebook, create a python file from it:
M-x jupyter-ascending-convert-notebook RET example.ipynb RET
Or, equivalently,
jupytext --to py:percent <file_name>
and then add the .sync
suffix to both files
Start Jupyter and open the notebook:
With example.sync.py
open,
M-x jupyter-ascending-start-notebook
Or, equivalently,
python3 -m jupyter notebook example.sync.ipynb
Within your Python file, a line starting with # %%
deliminates a new cell.
# %% [markdown] # This is a markdown cell. # Below is a code cell. # %% def g(x): return 3*x
When you edit and save the Python source file, you should see the Jupyter notebook running in your browser update straight away.
I recommend you create keybindings for all the following commands:
Command | Description |
---|---|
jupyter-ascending-execute-line |
Execute cell at cursor |
jupyter-ascending-execute-all |
Execute all cells |
jupyter-ascending-next-cell |
Move to next cell or create new cell |
jupyter-ascending-previous-cell |
Move to previous cell |
jupyter-ascending-edit-markdown-cell |
Edit Markdown cell in dedicated buffer |
jupyter-ascending-cycle-cell-type |
Toggle between Markdown and code cells |
Limitations
This package only works with Python notebooks because the Jupyter Ascending command line tool only supports Python notebooks. See here: https://github.com/imbue-ai/jupyter_ascending/issues/25
Alternatives
I think this blog post from 2023 gives a good overview of the other options for editing Jupyter Notebooks from Emacs. It covers emacs-jupyter and ein.
Motivation
I created this package because many of my Computer Science courses force me to use Jupyter notebooks. Of course, I would rather use Org mode, but that's life. And I've since come to appreciate the ability to have auto-completions while editing notebooks, or just treat the notebook as a normal Python file and execute it without Jupyter. I was aware of the other Emacs packages for working with Jupyter, which have the benefit of not being limited to just Python. But this package suits my personal taste very well, so I'm a happy user.