I use Emacs for Python development along with several linters. When I activate a Python virtual environment (venv) from within Emacs, I would like to set the linter binaries according to the following rules:
- If the venv has a particular linter installed, use it
- If the venv does not have a particular linter, use the one in a pre-specified, default venv
- If the linter does not exist in either the active venv or the default venv, set the linter binary to
For example, let's say I activate a venv called
my_venv that has
pylint installed, but no
flake8 is however installed in my default
linters venv. After activating
my_venv, the linters that will be used are
- pylint -- my_venv
- flake8 -- linters
The reason for implementing this is that I develop Python on multiple machines that share a single, version-controlled
init.el file. I do not want to guarantee that I have the same Python binaries and venvs across these machines; this implementation helps decouple my Emacs setup from the Python venvs that are present on a machine.
- The code will go inside my
- I use flycheck as the interface between Emacs and the linters
- I use pyvenv for Python virtual environments in Emacs
(defvar linter-execs '((flycheck-python-flake8-executable "bin/flake8") (flycheck-python-pylint-executable "bin/pylint") (flycheck-python-pycompile-executable "bin/python"))) (defvar default-linter-venv-path (concat (getenv "WORKON_HOME") "/linters/")) (defun switch-linters () "Switch linter executables to those in the current venv. If the venv does not have any linter packages, then they will be set to those in the `default-linter-venv-path` venv. If these do not exist, then no linter will be set." (dolist (exec linter-execs) (let ((venv-linter-bin (concat pyvenv-virtual-env (nth 1 exec))) (default-linter-bin (concat default-linter-venv-path (nth 1 exec))) (flycheck-var (nth 0 exec))) (cond ((file-exists-p venv-linter-bin) (set flycheck-var venv-linter-bin)) ((file-exists-p default-linter-bin) (set flycheck-var default-linter-bin)) (t (set flycheck-var nil)))))) (add-hook 'pyvenv-post-activate-hooks 'switch-linters)
linter-execsis a list of two-element lists. The first element of an entry is the
flycheckvariable that contains the path of a linter binary. The second element is the relative path of the binary from within the venv.
- The default linter venv is
switch-lintersis the call-back function attached to
- The conditional checks for the presence of the linter binary files, first in the current venv and next in the default venv. Failing these, it sets the binary to
nilin the line
(t (set flycheck-var nil))
- Is this idiomatic elisp, or is it too "Pythonic"?
linter-execsthe proper way to implement a list of tuples in elisp?