python-lsp-server
and black
within the virtual environment..venv/Scripts/Activate
).At work I've been developing several small-ish software projects in Python using poetry to manage the different dependencies, virtual environments, etc. for each of those projects. So far I've mostly been using Visual Studio Code to edit these projects, instead of using my old and trusted Emacs config.
The main reason for using VS Code was due to difficulties with Emacs to get the Python language server protocol correctly working for those projects. It was quite easy to install and set-up the python-lsp-server with emacs for projects using the main installed Python installation on my PC, but each of these projects had their own python virtual environment with various packages either not installed or with a different version of my "main" Python installation. This caused problems with the LSP server, where it would state multiple errors in my code due to it not being able to find the various packages and libraries used in those projects. With VS Code this wasn't a problem, since it was relatively easy to select either the main Python installation on my PC or the Python installation within the poetry virtual environment.
In principle this setup worked for development, but I wasn't entirely happy using it. The editor I mostly used outside of this project was Emacs, which I already set up with a modal editing mode slightly based on Kakoune. While there are several extentions for VS code to add a modal editing mode, they where never really a joy to use. Mostly due to sluggishness and it still feeling like a "hack" on top of a non-modal editor, instead of it beging an integraded experience. Furthermore several functionalities I see as being essential to have in an editor, such as macros, are not available in VS Code.
Lately I've been trying out the Helix editor, and so far it has been a joy to use. It is a lightweight, modal, terminal text editor, similar to VIM. But where with VIM you practically need to install mutliple additional packages to make it sort-of usable for day-to-day use (LSP, FZF, surround, etc.), Helix already has most of these functions build-in. And since the editor is build with those integrations from the ground up it is quite a lot easier and straightforward to use. And the default keybindings are, in my opinion, a lot easier to remember and use as those from VIM. Most of the things I also did with Emacs seem to be relatively easy to do with Helix (with some small tweaks, such as using markdown for notes instead of org mode).
So, in order to try and transfer my workflow to Helix, I've been trying to set it up with both python-lsp-server and poetry, and it seems I've found a workable solution for my use-case.
Each poetry project always gets a seperate directory for storing it's Python
virtual environment. Thanks to this each project can have different versions of
Python and libraries installed without clashing with each other. By default a
directory is made in the {cache-dir}/virtualenvs
of your PC, but poetry can
(and for this trick, should) also be set up to store the virtual-environment
data from a project within a .venv folder in the project root.
This is done by setting the virtualenvs.in-project
within the
poetry configuration
to true
.
The .venv folder created within the project root contains scripts in order to
activate the virtual environment. See
this section in
the Python documentation. Within Windows this can be activated by first opening
a powershell terminal within the project root and then running
.venv\Scripts\Activate.ps1
. Now, for as long as this terminal is open, the
paths to Python and other related binaries and scripts are changed to those kept
in the .venv directory. Note that this only counts when running commands within
that powershell session. Opening a new powershell terminal parallel to this one,
then running Python from that one will use the standard system Python binary.
The trick to using LSP with Helix with a poetry project is to run Helix within
the powershell terminal after enabling the project's virtual environment. This
way, when the Helix process calls tools like python-lsp-server
it will use the
Python installation as used in the poetry project. Therefore the LSP server also
correctly scans the project files with the python packages actually used in the
project.
But for this to work we now have to install python-lsp-server
within the
project's virtual environment. The same counts for tools like black
. Whilst in
the terminal with the virtual environment this can quickly be done using
pip install python-lsp-server black
, but a better way would be to set up these
packages as requirements within the project's pyproject.toml
file.
Configure the languages.toml
file withing the Helix configuration directory
(./config/helix/
or %AppData%\\Roaming\\helix
) to use pylsp
as the
language server (and if wanted black
to be the code formatter), and make sure
to not use hardcoded file paths for these. Otherwise when hardcoded paths
are used it will not use the .venv versions when needed.
An example of this is shown below12:
[[language]]
name = "python"
language-server = { command = "pylsp" }
formatter = { command = "black", args = ["--quiet", "-"] }
auto-format = true
Helix has a startup option to check if the tools for a certain language are
correctly set up: hx --health <language>
. When we open a new terminal window
and run this command with python as our language we should get something like
this:
$ hx --health python
Configured language server: pylsp
Binary for language server: /opt/homebrew/bin/pylsp
Configured debug adapter: None
Highlight queries: ✓
Textobject queries: ✓
Indent queries: ✘
In this case the found language server binary is the one from our system Python
installation. But when this same command is executed from a terminal with the
virtual environment enabled we should see this path change to something like
.venv/Scripts/pylsp
.
Now, when we want to edit files from the given poetry project we need to start a
terminal in the root folder of the poetry project, and then enable the virtual
environment. If we then run hx
within that terminal3 it will use the
project's pylsp server instead of the system's pylsp server.
To make things a bit easier we can make a script or terminal configuration to open a terminal in the project's directory and automatically enable the virtual environment. Within, for example, Windows Terminal, this can be done by creating a new powershell configuration with the project's root as startup path and the followin line as startup command:
powershell -noexit .venv/Scripts/Activate.ps1
Now you can just start this configuration, run hx
, and start editing. And a
2nd terminal with this configuration can be opened to run and debug the python
code.
see https://github.com/JorisL/helix for my latest configuration, including settings to set a max. line length for both formatting and linting. ↩
This is where we also must configure some LSP and formatter related
settings, for example max. line lengths, since these are not read from the
poetry's pyproject.toml
configuration file. According to the Helix
documentation it should be possible to include a languages.toml
settings file
like above within a project, such that you can have different settings LSP or
formatter settings for different projects, but I've not tested that yet. ↩
An added benefit of running hx within this terminal is that the working
directory of Helix is now the project's working directory. Now commands like
<space> f
will only search for files within the project. ↩