0

I want to use my .dir-locals.el to set virtualenv name on a project. The variable is set, but not regarded when used as an argument in other concatenated variables.

I've tried added a .dir-locals.el file to a python project. In it I set the virtualenv root name for this particular project. Afterwards I start Emacs, open a .py file to check if all needed variables are altered with the virtualenv name.

The part of my emacs config concerning python-mode

;;; Python mode

(require 'python)
(ensure-package 'python-environment)
(require 'python-environment)
(ensure-package 'python-mode)
(require 'python-mode)
;;; dash used for pip-requirements
(ensure-package 'dash)
(require 'dash)
(require 'pip-requirements)
(require 'jedi-core)
(require 'company-jedi)


(defun init-python-mode()
  (setq-local tab-width 4)
  (setq-local indent-tabs-mode nil)
  (set (make-local-variable 'company-backends)
       '((company-jedi company-dabbrev-code)
         company-capf company-files))
  (setq python-environment-directory (expand-file-name "~/.virtualenvs")
      python-indent-guess-indent-offset nil
      python-indent-offset 4
      jedi:complete-on-dot t
      jedi:use-shortcuts t
      jedi:tooltip-method nil
      jedi:get-in-function-call-delay 0
      python-shell-interpreter (concat python-environment-directory
                                       "/" python-environment-default-root-name
                                       "/bin" "/python")
      jedi:server-command (list (concat python-environment-directory
                                        "/" python-environment-default-root-name
                                        "/bin" "/jediepcserver"))
      python-shell-interpreter (concat python-environment-directory
                                       "/" python-environment-default-root-name
                                       "/bin" "/python")
      flycheck-python-pylint-executable (concat python-environment-directory
                                                "/" python-environment-default-root-name
                                                "/bin" "/pylint")
      flycheck-python-flake8-executable (concat python-environment-directory
                                                "/" python-environment-default-root-name
                                                "/bin" "/flake8")
      flycheck-python-pycompile-executable (concat python-environment-directory
                                                   "/" python-environment-default-root-name
                                                   "/bin" "/python")
      )
  (flycheck-mode t)
)

(defun after-init-python-mode()
  (eldoc-mode -1)
  )

(define-key python-mode-map (kbd "C-c C-k") #'comment-dwim)

(add-to-list 'auto-mode-alist '("\\.py\\'" . python-mode))

(add-hook 'python-mode-hook #'init-python-mode)
(add-hook 'python-mode-hook #'jedi:setup)
(add-hook 'python-mode-hook #'after-init-python-mode)

The .dir-locals.el file for the project

;;; Directory Local Variables
;;; For more information see (info "(emacs) Directory Variables")

((python-mode . ((python-environment-default-root-name . "abc")
         )))

Emacs seems to react to the .dir-locals.el variables which I confirm is safe on startup. Afterwards, by executing: 'describe-variable python-environment-default-root-name' I get this:

python-environment-default-root-name is a variable defined in ‘python-environment.el’.
Its value is "abc"
Original value was "default"
Local in buffer setup.py; global value is "default"

  This variable’s value is directory-local, set by the file
  ‘/home/mkj/dev/adm/.dir-locals.el’.

... which is expected.

When executing: 'describe-variable jedi:server-command', 'describe-variable python-shell-interpreter', as well as other variables using python-environment-default-root-name, I get this:

jedi:server-command is a variable defined in ‘jedi-core.el’.
Its value is ("/home/mkj/.virtualenvs/default/bin/jediepcserver")

  This variable may be risky if used as a file-local variable.
python-shell-interpreter is a variable defined in ‘python.el’.
Its value is "/home/mkj/.virtualenvs/default/bin/python"
Original value was "python"

It seems to me the setq for variable names using python-environment-default-root-name is only set once when the default value is set and the .dir-locals.el value is disregarded or set too late.

Is there a race condition here, or this simply a wrong approach to set virtualenv based variables for python-mode?

Rorschach
  • 31,301
  • 5
  • 78
  • 129
mkjmkjmkj
  • 21
  • 4

2 Answers2

0

You can add a call to hack-local-variables in your hook to make the dir-locals available. Normally, only the mode local variable would set prior to your hook, by calling (hack-local-variables t). The calls relevant to python-mode, your hook and hack-local-variables normally would look like,

1 -> (normal-mode t)
| 2 -> (hack-local-variables t)
| 2 <- hack-local-variables: nil
| 2 -> (python-mode)
| | 3 -> (my-python-hook)
| | 3 <- my-python-hook: ("~/.virtualenvs/default/bin/jediepcserver")
| | 3 -> (hack-local-variables no-mode) ;; you want this called prior to your hook
| | 3 <- hack-local-variables: nil
| 2 <- python-mode: nil
1 <- normal-mode: t

So, you could modify your hook,

(defun my-python-hook ()
  (hack-local-variables)
  ;; ...
  )

This doesn't seem like a good solution, though. I don't use virtualenv, but it would seem python-environment-virtualenv would be relevant here.

Rorschach
  • 31,301
  • 5
  • 78
  • 129
  • I would prefer not to use hack-local-variables :-) – mkjmkjmkj Aug 19 '19 at 06:42
  • I'm not sure why using "python-environment-virtualenv" would solve much ... It doesn't seem to have an impact on how flycheck targets its checker programs, for instance. – mkjmkjmkj Aug 19 '19 at 06:43
  • why would you prefer not to use that? that is how dir-locals are read... – Rorschach Aug 19 '19 at 07:03
  • like I said, I don't use virtualenv and didn't look into the details, but most version managers have a mechanism to associate a version that wouldn't rely on emacs' hacks – Rorschach Aug 19 '19 at 07:05
0

I've found that elpy works better for this purpose. It seems to seamlessly change variables for company backends, python intepreters, flycheck etc., without any hacks just by changing pyvenv-workon variable.

mkjmkjmkj
  • 21
  • 4