Python: execfile from other file's working directory?

Kyle Kaitan picture Kyle Kaitan · Nov 28, 2009 · Viewed 7.8k times · Source

I have some code that loads a default configuration file and then allows users to supply their own Python files as additional supplemental configuration or overrides of the defaults:

# foo.py

def load(cfg_path=None):
    # load default configuration
    exec(default_config)

    # load user-specific configuration
    if cfg_path:
        execfile(cfg_path)

There is a problem, though: execfile() executes directives in the file specified by cfg_path as if it were in the working directory of foo.py, not its own working directory. Thus, import directives might fail if the cfg_path file does, say, from m import x where m is a module in the same directory as cfg_path.

How do I execfile() from the working directory of its argument, or otherwise achieve an equivalent result? Also, I've been told that execfile is deprecated in Python 3 and that I should be using exec, so if there's a better way that I should be doing this, I'm all ears.

Note: I don't think solutions which merely change the working directory are correct. That won't put those modules on the interpreter's module-lookup path, as far as I can tell.

Answer

Alex Martelli picture Alex Martelli · Nov 28, 2009

os.chdir lets you change the working directory as you wish (you can extract the working directory of cfg_path with os.path.dirname); be sure to first get the current directory with os.getcwd if you want to restore it when you're done exec'ing cfg_path.

Python 3 does indeed remove execfile (in favor of a sequence where you read the file, compile the contents, then exec them), but you need not worry about that, if you're currently coding in Python 2.6, since the 2to3 source to source translation deals with all this smoothly and seamlessly.

Edit: the OP says, in a comment, that execfile launches a separate process and does not respect the current working directory. This is false, and here's an example showing that it is:

import os

def makeascript(where):
  f = open(where, 'w')
  f.write('import os\nprint "Dir in file:", os.getcwd()\n')
  f.close()

def main():
  where = '/tmp/bah.py'
  makeascript(where)
  execfile(where)
  os.chdir('/tmp')
  execfile(where)

if __name__ == '__main__':
  main()

Running this on my machine produces output such as:

Dir in file: /Users/aleax/stko
Dir in file: /private/tmp

clearly showing that execfile does keep using the working directory that's set at the time execfile executes. (If the file executed changes the working directory, that will be reflected after execfile returns -- exactly because everything is taking place in the same process!).

So, whatever problems the OP is still observing are not tied to the current working directory (it's hard to diagnose what they may actually be, without seeing the code and the exact details of the observed problems;-).