Python package path when executed elsewhere

14 December 2004   3 comments   Python

Powered by Fusion×

I stumbled across a problem today with running a python package from a different directory than where the python files are. The package looked like this (in principle):


The script contains this code:

def foo():
   tmpl = open('template.html').read()
   return tmpl.replace('X','Y')

That's fine and works when you run the file directly. The problem comes when you run the script from somewhere completely different (e.g. /home/mrbloggs/otherpackage/). The solution to this comes from Zope's package_home.

The problem is that if your script does the following:

import sys
sys.path.insert(0, '/home/someone/mypackage')
import foobar

Then the file template.html can not be found. That's because upon executing the importing script it holds a different execution path.

So what I did was that I added the following code which I pinched from Zope's package_home:

def package_home(gdict):
   filename = gdict["__file__"]
   return os.path.dirname(filename)

def foo():
   tmpl = open(os.path.join(package_home(globals()), 'template.html')).read()
   return tmpl.replace('X','Y')

Good to know for the future!


adam smith
Thanks for this post--I have been perplexed by this same issue. I did the following to make it a little more convenient to call:

I put this code into a module:

import os

def get_package_home(context):
return os.path.dirname(context["__file__"])

def get_execution_path(context, filename):
return os.path.join(get_package_home(context), filename)

Then in my code, I just need to import it and call it like this:

from package_home import get_execution_path
get_execution_path(globals(), 'template.html')

(It doesn't look like indentation is preserved in these posts.)

Python is not my primary language, so I am not sure if this is possible, but I wonder if we can't get rid of the repetitive globals() in the call by having the get_execution_path method somehow (through introspection) get this information for the calling object.

Also, is there a way to include these methods in an upper level package's file so they can be available to all the code in an application without having to explicitly import it into every module we want to use it in?
adam smith
OK, I finally returned to this, and here is the latest version using introspection to make finding the package home as convenient as possible:

# IN
import os, sys, inspect

def _package_home(filename):
return os.path.dirname(filename)

def package_home(context):
""" this preserves the functionality of the original package_home function
within this new approach"""
return _package_home(context["__file__"])

def execution_path(filename):
return os.path.join(_package_home(inspect.getfile(sys._getframe(1))), filename)

def execution_path2(filename):
"""alternately, you can just use this method to hide a lot of complexity
without needing any of the other functions above"""
return os.path.join(os.path.dirname(inspect.getfile(sys._getframe(1))), filename)

from package_home import execution_path

Again, sorry that I don't know how to preserve indentation in these posts.

The sys._getframe(1) call examines the call stack and retrieves information about the calling function, so no matter how it is executed, it should work.
Very useful, thanks.

Your email will never ever be published

Related posts

Happy Birthday to me! 13 December 2004
Ugliest site of the month, 17 December 2004
Related by keywords:
Zope in DevelopmentMode 26 June 2005