Using uv with Emacs

When working in Python, I've traditionally used virtualenvwrapper, but I recently tinkered with some Jupyter widget work, which necessitated installing miniconda as well.

So yeah, my environments are a superfund site and reminiscent of this XKCD comic.

XKCD Python Environments

Just to make things more entertaining, I've started using uv on my Python projects, thanks to the prosthelytizing of both Jeff Triplett and Michael Kennedy and Brian Okkan on the Python Bytes Podcast

This has been great for some of the data and application projects I've recently spun up, and I think I'll be moving to using exclusively uv from now on (and might even try out its packaging capabilities for CyTriangle).

The only source of frustration has been the lack of support/tooling for uv on Emacs (though I would be happy to be proven wrong!). It's a bit difficult to search for, since uv is also a library in Python, and all my search turned up so far is a nascent (as of 3 weeks ago) uv-menu.

Really what I wanted was a drop-in function for pyvenv-workon after looking at pyvenv.el, so I wrote one myself!

(defun uv-activate ()
  "Activate Python environment managed by uv based on current project directory.
Looks for .venv directory in project root and activates the Python interpreter."
  (interactive)
  (let* ((project-root (project-root (project-current t)))
         (venv-path (expand-file-name ".venv" project-root))
         (python-path (expand-file-name
                       (if (eq system-type 'windows-nt)
                           "Scripts/python.exe"
                         "bin/python")
                       venv-path)))
    (if (file-exists-p python-path)
        (progn
          ;; Set Python interpreter path
          (setq python-shell-interpreter python-path)

          ;; Update exec-path to include the venv's bin directory
          (let ((venv-bin-dir (file-name-directory python-path)))
            (setq exec-path (cons venv-bin-dir
                                  (remove venv-bin-dir exec-path))))

          ;; Update PATH environment variable
          (setenv "PATH" (concat (file-name-directory python-path)
                                 path-separator
                                 (getenv "PATH")))

          ;; Update VIRTUAL_ENV environment variable
          (setenv "VIRTUAL_ENV" venv-path)

          ;; Remove PYTHONHOME if it exists
          (setenv "PYTHONHOME" nil)

          (message "Activated UV Python environment at %s" venv-path))
      (error "No UV Python environment found in %s" project-root))))

Running this function via M-x uv-activate should mean that then M-x run-python will spin up an interactive python process with your uv environment for tinkering.