Set Python project root in Emacs?

2.8k views Asked by At

emacs-for-python (based on python.el) provides nice capability to start Python process and send buffer there right from Emacs (normally you do it with C-c C-c). However, this way new process is created for every buffer, and Python's working directory is set to the directory of corresponding file.

Instead, I'd like to have single Python process with working directory set to the root of the project. Or maybe even several processes, but started at the root directory, not the directory of the module.


Why do I need it? It's all about imports. Imagine following project structure:

myproject
   package1
      __init__.py
      mod1.py
   package2
      __init__.py
      mod2.py

If I start Python process on file mod1.py, Emacs will automatically set working directory to myproject/package1 so that I will be restricted to importing modules from the same package only. No imports from other packages. No absolute imports. Pain.

Currently I use a trick with sys.path, something like:

import sys, os
sys.path.insert(0, os.path.join(os.path.abspath(__file__), '..', '..'))

at the beginning of each module. But this is really, really, really ugly and inconvenient.

So does anybody have any tips or tricks to set up Python's project root for inferior process in Emacs?

3

There are 3 answers

2
AudioBubble On BEST ANSWER

OK this should get you started

(defvar my-python-shell-dir-setup-code 
  "import os
home = os.path.expanduser('~')
while os.path.isfile('__init__.py') and (os.getcwd() != home):
    os.chdir('..')
del os")

(defun my-python-shell-dir-setup ()
  (let ((process (get-buffer-process (current-buffer))))
    (python-shell-send-string my-python-shell-dir-setup-code process)
    (message "Setup project path")))

(add-hook 'inferior-python-mode-hook 'my-python-shell-dir-setup)

Here is what we are doing my-python-shell-dir-setup-code is simple python code to find project-dir and set it (it quick and dirty you may want to modify it according to your needs). Then we add a inferior-python-mode-hook (i.e. my-shell-dir-setup) to execute the python code in out inferior shell whenever the shell is created.

1
Wilfred Hughes On

I'm not familiar with emacs-for-python, but finding the path to the root of your project is straightforward.

Projectile provides a function projectile-project-root. You could call this in your mod1.py buffer, and it will return "/path/to/myproject". This assumes that projectile can recognise your project root, which will be fine if you've using some sort of VCS.

1
erubio On

The above accepted answer is correct, except there is a bug in the accepted answer above. The correct solution to the question posed is:

(defvar my-python-shell-dir-setup-code 
"import os
home = os.path.expanduser('~')
while (not os.path.isfile('__init__.py')) and (os.getcwd() != home):
    os.chdir('..')
del os")

(defun my-python-shell-dir-setup ()
  (let ((process (get-buffer-process (current-buffer))))
    (python-shell-send-string my-python-shell-dir-setup-code process)
    (message "Setup project path")))

(add-hook 'inferior-python-mode-hook 'my-python-shell-dir-setup)

The difference is that the line while os.path.isfile('__init__.py') and (os.getcwd() != home): should be while (not os.path.isfile('__init__.py')) and (os.getcwd() != home):

We want to step back a directory if there is NOT an __init__.py file.