description: I found it surprisingly tricky to get good information
on how to set up lsp-mode
to work with a Python virtual environment.
Here's my solution.
I found it surprisingly tricky to get good information on how to set
up lsp-mode
to work with a Python virtual environment. Here's my
solution.
Global setup
Use pyvenv
. This
provides Emacs lisp code that can set up Emacs to use a particular
virtualenv using the pyvenv-activate
function. I do this in my
init.el
with use-package
:
(use-package pyvenv
:ensure t)
Of course, I also have lsp
installed:
(use-package lsp
:ensure t)
Per-project setup
Add a file named ".dir-locals.el
" to the root of the project. For
example, here's mine for the precovery
project (note: see addendum
below for an updated version):
((python-mode . ((pyvenv-activate . "~/code/b612/precovery/.precovery-venv/")
(pyvenv-post-activate-hooks . (lsp)))))
The pyvenv-activate
cell's value should be a path to a virtualenv directory. For example, that one was made with
cd ~/code/b612/precovery
python -m venv .precovery-venv
Within each per-project virtualenv, it's important to install the python-lsp-server
package:
source .precovery-venv/bin/activate
pip install python-lsp-server
That's all there is to it.
The tricky bit here is really pyvenv-post-activate-hooks
. The lsp
function needs to be called after the Python virtual environment has
been activated.
Addendum (2024-12-17):
A reader named Alexei emailed me with an improvement upon the above
.dir-locals.el
content.
He writes:
This works great for the first file opened in the workspace. The problem shows up with the subsequent files in the same workspace (or at least it did for me).
Since the given venv is already activated, new activations are not happening and pyvenv-post-activate-hooks is not called again. Thus 2nd and subsequent files don't have lsp enabled automatically. I had to manually
M-x lsp
them.[...]
The small modification that worked for me was to change .dir-locals.el to:
((python-mode . ((eval . (pyvenv-activate "~/.virtualenvs/venv"))
(eval . (lsp)))))
Turns out there is a pyvenv-activate function in addition to the var by the same name. Eval executes things on the spot, so no more dealing with eventuality. And the lsp loading will be done for every file, even if venv has already been activated.
I hope you find this useful.
Thanks, Alexei.