hamacs/ha-programming-python.org
2021-11-18 12:12:19 -08:00

4.5 KiB

Configuring Python in Emacs

A literate programming file for configuring Python.

Introduction

The critical part of Python integration with Emacs is running LSP in Python using direnv. And the only question to ask is if the Python we run it in Docker or in a virtual environment.

Virtual Environment

For a local virtual machine, simply put the following in your .envrc file:

layout_python3

That is pretty slick and simple.

The old way, that we still use if you need a particular version of Python, is to install pyenv globally:

pip install pyenv

And have this in your .envrc file:

use python 3.7.1

Also, you need the following in your ~/.config/direnv/direnvrc file (which I have):

use_python() {
  local python_root=$(pyenv root)/versions/$1
  load_prefix "$python_root"
  if [[ -x "$python_root/bin/python" ]]; then
    layout python "$python_root/bin/python"
  else
    echo "Error: $python_root/bin/python can't be executed."
    exit
  fi
}

Docker Environment

Docker really allows you to isolate your project's environment. The downside is that you are using Docker and probably a bloated container. On my work laptop, a Mac, this creates a behemoth virtual machine that immediately spins the fans like a wind tunnel.

But, but… think of the dependencies!

Enough of the rant (I go back and forth), after getting Docker installed and running (ooo Podman … shiny), and you've created a Dockerfile for your project, let's install container-env.

Your project's .envrc file would contain something like:

CONTAINER_NAME=my-docker-container
CONTAINER_WRAPPERS=(python3 pip3 yamllint)
CONTAINER_EXTRA_ARGS="--env SOME_ENV_VAR=${SOME_ENV_VAR}"

container_layout

Python Dependencies

Whew. Each Python project's requirements-dev.txt file would reference the python-lsp-server (not the unmaintained python-language-server):

python-lsp-server[all]

Note: This does mean, you would have a tox.ini with this line:

  [tox]
  minversion = 1.6
  skipsdist = True
  envlist = linters
  ignore_basepython_conflict = True

  [testenv]
  basepython = python3
  install_command = pip install {opts} {packages}
  deps = -r{toxinidir}/test-requirements.txt
  commands = stestr run {posargs}
             stestr slowest
  # ...

Using Jedi Instead

Do we want to use the Jedi version of LSP? Not sure what it buys us.

(use-package lsp-jedi
  :config
  (with-eval-after-load "lsp-mode"
    (add-to-list 'lsp-disabled-clients 'pyls)
    (add-to-list 'lsp-enabled-clients 'jedi)))

LSP Integration of Python

Now that the LSP Integration is complete, we can stitch the two projects together:

  (use-package lsp-mode
    :hook ((python-mode . lsp)))

And we're done.