emacsdot-emacselpypyvenv

automatically call pyvenv-activate on elpy-mode-hook


I am new to emacs and try to learn some elisp to configure it.

I wanted to automatically activate a python virtual environment when entering elpy-mode.

I have tried all kinds of variations of the following code

(use-package elpy
  :init
  (elpy-enable)
  :config
  (add-hook 'elpy-mode-hook
            (lambda ()
              (let* ((my-venv-root (locate-dominating-file buffer-file-name "venv"))
                     (my-venv (file-name-as-directory (file-name-concat my-venv-root "venv"))))
                 (message (concat "Venv Root: " my-venv-root))
                 (message (concat "Venv: " my-venv))
                 (when my-venv
                   (message "Try to activate Virtual Environment")
                   (pyvenv-activate my-venv))))))

The manual way M-x pyvenv-activate RET /path/to/project/venv RET followed by C-c C-c works just fine.

But if I put the above code in my init.el file I keep getting the Error Wrong type argument: stringp, nil when trying to run a python file/buffer using C-c C-c

The messages yield correct looking values for my-venv-root and my-venv. For Example:

Venv Root: ~/Documents/Code/Test/
Venv: ~/Documents/Code/Test/venv/
Try to activate Virtual Environment

I am using Debian 12 with emacs 28.2 installed from the debian repositories.


Solution

  • I found a/the solution.

    TL;DR:

    Wrapped the code inside (when buffer-file-name ....

    A little more detailed:

    For some reason the elpy-mode-hook gets called when C-c C-c opens the Inferior Python Shell (But C-h m didn't show elpy, ...). It triggered the hook again with buffer-file-name returning nil, because the Inferior Python Shell has no file name associated with it.

    My solution was to wrap everything in (when buffer-file-name ....

    So my - now working - config looks like that:

    (use-package elpy
      :init
      (elpy-enable)
      :config
      (pyvenv-mode t)
      (add-hook 'elpy-mode-hook
            (lambda ()
              (when buffer-file-name
                (let* ((my-venv-root (locate-dominating-file buffer-file-name "venv"))
                       (my-venv (file-name-as-directory (file-name-concat my-venv-root "venv"))))
                (message (concat "Venv Root: " my-venv-root))
                (message (concat "Venv: " my-venv))
                (when my-venv
                  (message "Try to activate Virtual Environment")
                  (pyvenv-activate my-venv)))))))
    

    I do not fully understand how this could happen, 'cause this is what C-h m gives me in the Inferior Python Shell:

    Enabled minor modes: Auto-Composition Auto-Compression Auto-Encryption
    Blink-Cursor Company Compilation-Shell Display-Line-Numbers
    Electric-Indent File-Name-Shadow Font-Lock Global-Company
    Global-Display-Line-Numbers Global-Eldoc Global-Font-Lock
    Global-Hl-Line Line-Number Menu-Bar Mouse-Wheel Override-Global Pyvenv
    Recentf Save-Place Savehist Shell-Dirtrack Show-Paren Tool-Bar Tooltip
    Transient-Mark Which-Key Windmove
    
    (Information about these minor modes follows the major mode info.)
    
    Inferior Python mode defined in ‘python.el’:
    Major mode for Python inferior process.
    Runs a Python interpreter as a subprocess of Emacs, with Python
    I/O through an Emacs buffer.  Variables ‘python-shell-interpreter’
    and ‘python-shell-interpreter-args’ control which Python
    interpreter is run.  Variables
    ‘python-shell-prompt-regexp’,
    ‘python-shell-prompt-output-regexp’,
    ‘python-shell-prompt-block-regexp’,
    ‘python-shell-font-lock-enable’,
    ‘python-shell-completion-setup-code’,
    ‘python-shell-completion-string-code’,
    ‘python-eldoc-setup-code’, ‘python-eldoc-string-code’,
    ‘python-ffap-setup-code’ and ‘python-ffap-string-code’ can
    customize this mode for different Python interpreters.
    
    ...
    

    So no Elpy here, like in C-h m in a something.py-Buffer:

    Enabled minor modes: Auto-Composition Auto-Compression Auto-Encryption
    Blink-Cursor Company Display-Line-Numbers Eldoc Electric-Indent Elpy
    File-Name-Shadow Flymake Font-Lock Global-Company
    Global-Display-Line-Numbers Global-Eldoc Global-Font-Lock
    Global-Hl-Line Highlight-Indentation Line-Number Menu-Bar Mouse-Wheel
    Override-Global Pyvenv Recentf Save-Place Savehist Shell-Dirtrack
    Show-Paren Tool-Bar Tooltip Transient-Mark Which-Key Windmove Yas
    
    (Information about these minor modes follows the major mode info.)
    
    Python mode defined in ‘python.el’:
    Major mode for editing Python files.
    
    ...
    

    But it works for me.