Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Application-specific PYTHONPATH

I have an application with a heirarchy of packages. There are a fair number of modules that reference other modules higher up in the package heirarchy. As exemplified below, I can use relative imports to solve this problem. However, running the module directly for testing fails with an Attempted relative import in non-package exception.

Is there a better way to organize my application or my import statements so that modules can both be executed individually for testing and imported from other modules?

Example

\spam
    \morespam
        child.py
    base.py
\eggs
    user.py

base.py

class Base(object):
    def hello(self):
        print 'Um, wot?'

child.py

from ..base import Base       # references the parent package correctly, 
                              # but fails when this module is executed individually

class Child(Base):
    def hello(self):
        print 'Hello, world!'

if __name__ == '__main__':
    import unittest
    # ... unit test code ...

user.py

from spam.morespam.child import Child
print Child().hello()

Existing solution

I've found that I can add the following header added to the top of a module that needs to reference modules higher in the hierarchy:

if __name__ == '__main__':
    import sys, os
    sys.path.append(os.path.abspath(os.path.join(sys.path[0], '..')))

The downside is that I need to add this code all over the place, and it isn't constant: the '..' relative path varies based on the depth of the package in the hierarchy.

Other possible solutions...

  1. I could create a .pth file with my application root. Unfortunately, this must be added to the site-packages folder of my Python installation, so it's not very application-specific.
  2. I could temporarily add my application root to the PYTHONPATH, and make all my modules reference packages from the root. This eliminates the simplicity of running a script by simply calling python.exe whatever.py, though.
  3. I could write some sort of module that, when imported, would look for a marker file in the root folder of my application. This generic module could then be installed to my local Python installation and imported by every module in my app, thus guaranteeing that the root is in PYTHONPATH, regardless of which module I'm executing.
like image 859
MikeWyatt Avatar asked Nov 04 '22 17:11

MikeWyatt


2 Answers

You need to add __init__.py as empty files in folders that you'd want to do imports from. Doing this will cause python to treat the directories as packages, allowing you to use them in import statements:

\spam
    \morespam
        __init__.py
        child.py
    __init__.py
    base.py
\eggs
    __init__.py
    user.py

Once you do this and establish your PYTHONPATH to the base of this directory, you can then do imports:

base.py:

from morespam.child import Child
from ..eggs import user

While a __init__.py file only needs to be present, you can also define the variable __ALL__ in this file as a list of modules in that directory that you'd want to import if a script tries to use import *.

like image 63
Manny D Avatar answered Nov 11 '22 13:11

Manny D


I solved this with PyCharm, which automagically adds my source root to the PYTHONPATH every time it runs my application. As far as I can tell, PyCharm basically does option #2 for me.

like image 35
MikeWyatt Avatar answered Nov 11 '22 15:11

MikeWyatt