Using Python in Emacs
Introduction
I will keep this note up to date with details on how I use emacs for Python development.
Setting up a Python project
There are a few things I want when setting up a Python project:
- a virtual environment
- a language server (for use via eglot)
- an interpreter that works well with emacs
To that end, when setting up a project, I follow these steps:
- Create a new directory for my project, or navigate to an existing project directory.
- Create a new virtual environment in that directory, with e.g.
uv venv -p 3.13 myenv
(I tend to use uv for python package/environment management these days). - Activate the virtual environment. I use pyvenv and activate environments via
pyvenv-activate
. Sometimes, I still need to explicitly callsource ./myenv/bin/activate
in e.g. an open vterm buffer. In general, it's a good idea to make sure the environment is active and running as expected before doing anything. - Set up eglot—in your virtual environment, install
python-lsp-ruff
orruff-lsp
or whatever Python language server provides the functionality you want. It's also worth installingisort
if you want to sort your imports with e.g.python-sort-imports
. - Install and configure
gnureadline
for native completions (if you get theWarning (python): Your ‘python-shell-interpreter’ doesn’t seem to support readline, yet ‘python-shell-completion-native’ was t and "python3" is not part of the ‘python-shell-completion-native-disabled-interpreters’ list. Native completions have been disabled locally.
warning). See here for instructions. - Create/open a python buffer and start eglot with
M-x eglot
.
Using multiple language servers with eglot
For some reason, I recall this working without additional configuration before. But it's now, so I might as well document how to get it working. My best guess is that I overrode the defaults as the eglot docs note that "Eglot comes with a fairly complete set of associations of major-modes to popular language servers predefined."
Suppose you have installed two different options for a Python LSP. Follow the instructions here.
(with-eval-after-load 'eglot (add-to-list 'eglot-server-programs `(python-mode . ,(eglot-alternatives '(("ruff" "server") ("basedpyright-langserver" "--stdio"))))))