Emacs python setup
Posted on Tue 01 January 2019 in posts
I got a new laptop recently and so set up Emacs on it for the first
time. Since my emacs init.el
file has gotten progressively messier
over the past several years, I took the opportunity to think about the
variety of python packages I use and set things up cleanly. The
following is what I settled on; the steps involved weren't as clear as I
had hoped, so I'm writing this all down in case I need to do it again.
Some notes on how I use Emacs and python before I start:
-
I use
conda
to manage my virtual environments. Most projects, but not all, have a correspondingenvironment.yml
file in their repository. I installedminiconda
at~/miniconda3
(conda version 4.5.12). -
I do the early parts of my development, the playing around with ideas before I start writing scripts, in a Jupyter Notebook. I also do the end of my analysis in them, creating plots and putting the parts together in them. I keep most of my functions in scripts, runnable from the command line so that I can submit them to NYU's computing cluster. Therefore, I don't need the full IDE experience; I don't need to run a shell session from within Emacs, and I prefer using Jupyter to ein, org-babel, or anything similar.
-
I primarily use el-get to manage my Emacs packages, falling back on use-package when an appropriate
el-get
recipe isn't available. My configurations for both of these are very basic (and I installeduse-package
usingel-get
).
Because I don't need the full IDE experience and want to use conda
to
manage my virtual environments, I cannot use
Elpy, which combines a variety
of python-related packages together with a minimum of setup difficulty
(here's a guide
to getting started with it). So I decided on using
jedi.el (with the company
backend) for auto-completion, the
included python-mode (in Emacs 26.1) for basic syntax highlighting,
conda.el for managing virtual
environments, and flycheck (with
pylint and
flake8) for syntax- and
style-checking. Wanting to use conda
and the company backend meant
things were slightly more complicated to set up.
First, conda.el
. This was very straightforward to setup just following
the instructions on the Github
page. The following is
the relevant block in my init.el
:
(use-package conda
:ensure t)
(require 'conda)
;; if you want auto-activation (see below for details), include:
(conda-env-autoactivate-mode t)
(custom-set-variables
'(conda-anaconda-home "~/miniconda3"))
Next, company-mode
and jedi.el
, both of which I can install using
el-get
. (Note that, if you want to use jedi with the company backend,
do not install the regular jedi.el
: you should only install
company-jedi
, see this
issue).
(setq my:el-get-packages
'(company-mode))
(el-get-bundle elpa:jedi-core)
(el-get-bundle company-jedi :depends (company-mode))
(eval-after-load "company-jedi"
'(setq jedi:server-command (list "~/miniconda3/envs/emacs-jedi/bin/python" jedi:server-script)))
(require 'company-jedi)
(el-get 'sync my:el-get-packages)
However, I can't use the automatic jedi:install-server
command, and so
need to do some manual set up. I mostly followed the instructions from
Update 2 of this stackoverflow
answer, with some changes:
-
Create a conda environment (for current example the environment is named emacs-jedi) by doing:
conda create -n emacs-jedi python
-
Install the following python packages:
jedi
,sexpdata
,epc
. Onlyjedi
is on conda, so I did the following:pip install sexpdata epc; conda install jedi
. -
Install the jediepcserver. Navigate to the
jedi-core
install directory (probably~/.emacs.d/el-get/jedi-core
; it should containjediepcserver.py
), then run:python setup.py install
(note the directory has to exist, so the(el-get-bundle elpa:jedi-core)
needs to be run before this).
After doing the above, company-jedi
should now be setup. The following
are the relevant config blocks in my init.el
(add-hook 'conda-postactivate-hook 'jedi:stop-server)
(add-hook 'conda-postdeactivate-hook 'jedi:stop-server)
(defun my/python-mode-hook ()
(add-to-list 'company-backends 'company-jedi))
(add-hook 'python-mode-hook 'my/python-mode-hook)
(add-hook 'python-mode-hook 'jedi:setup)
(setq jedi:complete-on-dot t)
Finally, install and set-up flycheck. This is relatively
straightforward: flycheck can be installed using el-get
:
(setq my:el-get-packages
'(flycheck))
(el-get 'sync my:el-get-packages)
Now, install the pylint and flake8 executables, in the base conda environment:
conda activate base
pip install flake8 pylint
And tell flycheck where to find the executables (and add a couple extra lines of configuration):
(add-hook 'after-init-hook #'global-flycheck-mode)
(setq-default flycheck-emacs-lisp-load-path 'inherit)
(setq flycheck-flake8-maximum-line-length 99)
(setq flycheck-python-pylint-executable "~/miniconda3/bin/pylint")
(setq flycheck-python-flake8-executable "~/miniconda3/bin/flake8")
Once that's done, all you need to do is check things are set up. Open up
any python file in Emacs and type C-u C-c ! v
to see the status of
flycheck and, if pylint and flake8 are deactivated, type C-u C-c !
x
to activate them.
If everything works, flycheck will start underlining things in yellow and red to tell you that things are against the style guide or will raise errors. If you start to type something, jedi will suggest possible completions, and all of these will be based on the packages installed in the appropriate virtual environment.
All told, the following shows the relevant parts of my init file:
(setq my:el-get-packages
'(company-mode
flycheck))
(el-get-bundle elpa:jedi-core)
(el-get-bundle company-jedi :depends (company-mode))
(eval-after-load "company-jedi"
'(setq jedi:server-command (list "~/miniconda3/envs/emacs-jedi/bin/python" jedi:server-script)))
(require 'company-jedi)
(el-get 'sync my:el-get-packages)
(use-package conda
:ensure t)
(require 'conda)
;; if you want auto-activation (see below for details), include:
(conda-env-autoactivate-mode t)
(custom-set-variables
'(conda-anaconda-home "~/miniconda3"))
(add-hook 'conda-postactivate-hook 'jedi:stop-server)
(add-hook 'conda-postdeactivate-hook 'jedi:stop-server)
(defun my/python-mode-hook ()
(add-to-list 'company-backends 'company-jedi))
(add-hook 'python-mode-hook 'my/python-mode-hook)
(add-hook 'python-mode-hook 'jedi:setup)
(setq jedi:complete-on-dot t)
(add-hook 'after-init-hook #'global-flycheck-mode)
(setq-default flycheck-emacs-lisp-load-path 'inherit)
(setq flycheck-flake8-maximum-line-length 99)
(setq flycheck-python-pylint-executable "~/miniconda3/bin/pylint")
(setq flycheck-python-flake8-executable "~/miniconda3/bin/flake8")