In my last post I brought up how to get around mod_python’s default inability to handle relative paths for imported modules. The next night after writing that, I faced a very similar issue with configuration files.
It is a sin to duplicate data in a project (except when caching), so most sane desktop applications will have a central store that contains the configuration parameters for the application (usually packaged as a resource with the application, or in the registry for Windows users). Mod_python can’t do this because, as was discussed in my last post, it views the current working directory of whatever script it is executing as the root directory of the filesystem (”/”). So where do you put the configuration file for your mod_python based project if you can’t refer to it relatively, don’t want to hardcode an absolute path, and want to distribute the configuration with the project?
The solution hit me like a bucket of cold water. Give up on the property or ini format file, and make your configuration a python dict object that you can directly import:
# config.py
#
# This is the project's configuration. Import it when a class needs to access
# project configuration variables.
#
_config = {
# The name of my app so pathHack knows what string to look for
'rootname': 'sampleWebApp',
# Database options
'db_host': 'localhost',
'db_dbname': 'sampleWebApp',
'db_user': 'sampleWebAppUser',
'db_password': 's0m3p455w0rd',
# Debug logfile path
'debug_logfile': '/tmp/lostafollower.log'
};
This file needs to live in your project’s root directory as far as mod_python is concerned. For more information, read my last post. If you’ve already read it, then you’ll understand when I say that config.py lives in the same folder as pathHack.py.
Once you can import it from your main source files, use it like this:
<%
# sample usage of config
from config import _config as _c
req.write(_c['rootname'])
%>
Tada! Extraordinarily simple configuration file that is easily accessible and can be distributed with your mod_python application. I’m pretty happy about it :)
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 ;)