— python, neovim, pyenv, pipenv, jupyter — 2 min read
Let's all agree on this: writing code and executing Jupyter Notebooks is annoying as hell in the browser. All this inefficient code navigation, mouse-clicking, searching, you name it, is not an optimal approach in the long term. To overcome this issue I looked into ways how could I improve Jupyter Notebook (Python) development/workflow and this is the result: my most recent setup on how to bring the interactive development of Jupyter Notebooks or into your favorite editor Vim/Neovim.
Note this post is mostly based on Setting up a Neovim and pipenv based Python development environment and NeoVim Notebooks with a few caveats :)
This setup is powered by the following plugins:
use 'untitled-ai/jupyter_ascending.vim'use 'bfredl/nvim-ipy'use 'hkupty/iron.nvim'use 'GCBallesteros/jupytext.vim'use 'kana/vim-textobj-line'use 'kana/vim-textobj-user'use 'GCBallesteros/vim-textobj-hydrogen'
Is a library and plugin that enables interactive REPLs from Neovim. It enables to send a text to the REPL via motion, text object, or visual selection.
1---- IRON-REPL ----------------------------------------------------------------2
3local iron = require('iron')4
5iron.core.add_repl_definitions {6 python = {7 venv_python = {8 command = "pipenv run ipython"9 }10 }11}12
13iron.core.set_config {14 preferred = {15 python = "venv_python",16 }17}18
19vim.cmd [[nnoremap <silent><c-v> <Plug>(iron-visual-send)]]20vim.cmd [[nnoremap <C-l> <Plug>(iron-send-line)]]
This configuration enables me to connect to an existing ipykernel
when running the command from Neovim: :IronRepl
.
ipykernel
from the terminal or by running :RunPipenvKernel
from Neovim:IronRepl
CTRL+V
.The only limitation of this approach is that you can't display charts or tables.
Is a Jupyter front-end for Neovim. Its utility is the same as iron.nvim
however I use nvim-ipy
to integrate Neovim with QT Console. Follow this post by MAx on how to install QT console.
1---- NVIM-IPY -----------------------------------------------------------------2
3vim.g.nvim_ipy_perform_mappings = 04vim.g.ipy_celldef = '# %%'5
6vim.cmd [[map <silent><c-s> <Plug>(IPy-Run)]]7vim.cmd [[map <leader>rc <Plug>(IPy-RunCell)]]
RunQtPipenv
which starts the QT console within the given pipenv
environment.ConnectToPipenvKernel
, this connects neovim with the QT console via nvim-ipy
.CTRL+s
.The only advantage of the QT console is that the output is more "nicely" formated and allows to display plots. However, it has limitations when it to comes tables or DataFrames.
Jupytex makes it possible to transform *.ipynb
file to *.py
or vice-versa. When you open a Jupyter Notebook *.ipynb
file, it is automatically converted from JSON to *.py
through the jupytext utility, and the result is loaded into vim/neovim buffer. Upon saving, the *.ipynb
file is updated with any modifications. jupytext.vim
is doing all this in the background automatically.
The vim-textobj-hydrogen plugin enables navigation between cells. It provides two text-objects, ah
and ih
, and backward and forwards motion for them, [h
and ]h
respectively - used to jump between hydrogen style python notebook cells. All code cells start with # %%
and are used to delimit the individual code and markdown blocks.
For iron.nvim
I use these motions to jump backwards and forwards and execute each cell:
1vim.cmd [[nmap ]x ctrih/^# %%<CR><CR>]]2vim.cmd [[nmap [x ctrah/^# %%<CR><CR>]]
For nvim-ipy
I use the ]h
and ]h
motions to jump between cells and execute them by:
1vim.cmd [[map <leader>rc <Plug>(IPy-RunCell)]]
Jupyter Ascending syncs the state between *.py
file and a running Jupyter *.ipynb
notebook. Its advantage from the previous cases (running an interactive REPL in Neovim or QT Console) is that you have an up and running Jupyter Server with a notebook next to Neovim.
At the moment, it requires to name the python files that end with .sync.py
and the Jupyter notebook with names that end with .sync.ipynb
. So for each Jupyter notebook in JSON format, you will have to generate the python file in the hydrogen format. Fortunately jupyter_ascending
provides a script that takes care of this:
1python -m jupyter_ascending.scripts.make_pair --base examples/test.py
The instructions for installing and running jupyter_ascending
are well documented on github. To interact with jupyter_ascending
from Vim/Neovim install the jupyter_ascending.vim plugin. Currently, the plugin contains a minor "bug" which causes that the notebooks are not synchronized automatically - my PR should fix this issue.
notebook_name.synced.ipynb
notebook_name.synced.py
using jupyter_ascending
script or jupytext
ipykernel
notebook_name.synced.py
- everytime you save it will synchronize with notebook_name.synced.ipynb
1---- JUPYTER ASCENDING --------------------------------------------------------2
3vim.cmd[[ nnoremap <silent><c-x> <Plug>JupyterExecute ]]4vim.cmd[[ nnoremap <silent><c-X> <Plug>JupyterExecuteAll ]]
I have the following commands defined in my python.lua
config file which I have copy-pasted from Max ;). With these commands, I start the ipykernel
, lunch QT console, connect to the given kernel, and so forth.
1" Starts Qt console and connect to pipenv ipykernel2command! -nargs=0 RunQtPipenv call StartConsolePipenv('jupyter qtconsole')3
4" Starts Qt console and connect to an existing ipykernel5command! -nargs=0 RunQtConsole call jobstart("jupyter qtconsole --existing")6
7" Starts pipenv ipykernel8command! -nargs=0 RunPipenvKernel terminal /bin/bash -i -c 'pipenv run python -m ipykernel'9command! -nargs=0 RunPoetryKernel terminal /bin/bash -i -c 'poetry run python -m ipykernel'10
11" Connects nvim-ipy to the existing ipykernel (non-interactive)12command! -nargs=0 ConnectToPipenvKernel call ConnectToPipenvKernel()13
14" Connects nvim-ipy to the existing ipykernel (interactive)15command! -nargs=0 ConnectConsole terminal /bin/bash -i -c 'jupyter console --existing'16command! -nargs=0 AddFilepathToSyspath call AddFilepathToSyspath()