Language servers enable IDE-like features in text editors that support the Language Server Protocol. pylsp is a language server for Python. I use vim-lsp to integrate language servers into Vim. Configuring a language server in vim-lsp is straightforward for most languages, but not for Python.
Any non-trivial Python codebase uses a virtual environment (venv) to store its dependencies. pylsp needs to know the location of the venv, for the currently open file, to function correctly. When using Vim in the terminal, pylsp can be made aware of the location by activating the venv, before starting Vim. It is not possible to do this when starting a Vim GUI, like GVim, outside the terminal. If you use a modern Python dependency manager like uv or Poetry, this post offers a solution that works both inside and outside the terminal.
This post assumes that you have installed and configured vim-lsp. It focuses only on pylsp-specific configuration. Follow vim-lsp’s documentation if you haven’t installed it already. It also assumes that you have pylsp installed. If not, install it with one of the following commands.
uv tool install python-lsp-server
# or
pipx install python-lsp-server
pylsp uses Jedi for most of its features. It can be pointed to the
right venv by setting pylsp.plugins.jedi.environment
to the path of the
python/python3 executable in the venv.
The location of the python executable within the venv can be found in uv, using the following command.
uv python find
If you use Poetry, try
poetry env info --executable
In vim-lsp, additional configuration is passed to the language server by
setting the workspace_config
key in the call to lsp#register_server
. Its
value is usually a dict, but we cannot use that as lsp#register_server
is
called when Vim starts. When started outside the terminal, Vim starts with
$HOME
as the working directory and we do not know the project whose venv must
be used. We must instead detect the venv after we switch to a project’s
directory.
vim-lsp allows us to use a lambda that returns a dict as the value for
workspace_config
, instead of a dict itself. The lambda will be called when
the language server is started, rather than when Vim starts up. Since the
language server is started when we open a file whose type is allow-listed for
the server, we are likely to be in the project’s directory at this point.
Let’s first define a function to detect the venv location using uv/Poetry and return it in a dict.
def PyLspConfig(): dict<any>
# Use poetry env info --executable, if you use Poetry.
var python = trim(system('uv python find'))
return {'pylsp': {'plugins': {'jedi': {'environment': python}}}}
enddef
We can then set workspace_config
to a lambda that calls PyLspConfig
.
if executable('pylsp')
au User lsp_setup {
call lsp#register_server({
'name': 'pylsp',
'cmd': ['pylsp'],
'allowlist': ['python'],
'workspace_config': (server_info) => PyLspConfig(), })
}
endif
Note: The code snippets above use Vim9 script syntax and won’t work as-is in legacy Vim script.
vim-lsp configures the language server only once, on startup. If you switch to
a different project’s directory, without quitting Vim, pylsp will continue to
use the venv of the directory you were in earlier. To workaround this, you can
either restart Vim or stop pylsp using :LspStopServer
.
All opinions are my own. Copyright 2005 Chandra Sekar S.