Relative Import Paths Using Mod_Python
A long while back (a year already? Jeeze!) David Brenner and I wrote a web application without using any frameworks, only mod_python and python server pages (PSP). We ran into a few problems along the way, one of which being that the import path mod_python used had to be composed of absolute paths. It isn’t a huge problem to just hard code a path to your project’s lib directory, but it is hardly portable and adds to the fragility of the system.
I’m now doing another project (more on that later), again using mod_python and psp. This time around however, I have a hack that gets around the absolute path problem. The solution looks like this:
# This file has only one function: to add directories to the path
# so we can import other python files/modules.
import sys
# Default directories to add to the path
_dirs = ["/src", "/lib"]
_applicationRoot = "sampleWebApp"
# Will go through the list and add the directories given relative to the
# lostafollower base path. Allows us to do paths relative to our installation
# instead of hard-coding absolute paths.
def addLibs( dirs ):
# If [] was provided, just use the default.
if not dirs:
dirs = _dirs
base = ""
for entry in sys.path:
if entry.find(_applicationRoot) != -1:
base = entry
break
for dir in dirs:
try:
# Test if the path contains the path we want
sys.path.index(base+dir)
except:
# The path doesn't already contain our path
sys.path = sys.path+[base+dir]
The reason we normally can’t use relative paths when doing imports is because the mod_python handler considers the root (”/”) of the filesystem to be the current working directory of the application, regardless of where the application is actually running from. However, Apache will put whatever alias it has for your application’s directory [1] in the PYTHONPATH for mod_python, which is what allows us to do this hack. By finding the path that Apache has in the PYTHONPATH, we can add whatever paths we want relative to that. No .htaccess PythonPath directives, no hard coding of paths to your project’s module locations. Just:
- Put the above code in a file (I call it pathHack.py) in your application’s root directory (root as far as Apache is concerned) [1]
- import pathHack (or whatever you called it) in whichever file needs your other modules
- Call pathHack.addLibs([]) to add the defaults (/path/to/sampleWebApp/lib, etc), or pathHack.addLibs(”/lib/3rdParty”) to add /path/to/sampleWebApp/lib/3rdParty to your path
- import any python module in those newly added directories!
That is really it. Hard coded paths begone!
[1]
When I say “the alias it [Apache] has for your application’s directory,” what I mean is that even though, for example, my application runs out of /home/robertp/src/sampleApp/src, mod_python sees the root as being /home/robertp/src/sampleApp because of some unknown magic. It doesn’t really matter what the path is, in all honesty – when specifying a value for _applicationRoot in the solution code above, you are just looking for a string unique to the path to your application (just be sure to make all supplied paths relative from that base). Try just your project’s deployment folder for starters. If you are still having trouble finding it, create a quick psp file in the same folder as your other psp files, and do:
import sys from mod_python import apache req.write(str(sys.path))
That will just print out all the path elements to your browser window when visited. Pick out your application’s name or path from there, and set _applicationRoot to that. It isn’t completely ideal, but it is better than hard-coding ;)