Automatic, temporary directory changing
Posted June 16, 2013 at 09:09 AM | categories: programming | tags: | View Comments
If you are doing some analysis that requires you to change directories, e.g. to read a file, and then change back to another directory to read another file, you have probably run into problems if there is an error somewhere. You would like to make sure that the code changes back to the original directory after each error. We will look at a few ways to accomplish that here.
The try/except/finally method is the traditional way to handle exceptions, and make sure that some code "finally" runs. Let us look at two examples here. In the first example, we try to change into a directory that does not exist.
import os, sys CWD = os.getcwd() # store initial position print 'initially inside {0}'.format(os.getcwd()) TEMPDIR = 'data/run1' # this does not exist try: os.chdir(TEMPDIR) print 'inside {0}'.format(os.getcwd()) except: print 'Exception caught: ',sys.exc_info()[0] finally: print 'Running final code' os.chdir(CWD) print 'finally inside {0}'.format(os.getcwd())
initially inside c:\users\jkitchin\Dropbox\pycse Exception caught: <type 'exceptions.WindowsError'> Running final code finally inside c:\users\jkitchin\Dropbox\pycse
Now, let us look at an example where the directory does exist. We will change into the directory, run some code, and then raise an Exception.
import os, sys CWD = os.getcwd() # store initial position print 'initially inside {0}'.format(os.getcwd()) TEMPDIR = 'data' try: os.chdir(TEMPDIR) print 'inside {0}'.format(os.getcwd()) print os.listdir('.') raise Exception('boom') except: print 'Exception caught: ',sys.exc_info()[0] finally: print 'Running final code' os.chdir(CWD) print 'finally inside {0}'.format(os.getcwd())
initially inside c:\users\jkitchin\Dropbox\pycse inside c:\users\jkitchin\Dropbox\pycse\data ['antoine_data.dat', 'antoine_database.mat', 'commonshellsettings.xml', 'cstr-zeroth-order.xlsx', 'debug-2.txt', 'debug-3.txt', 'debug-4.txt', 'debug.txt', 'example.xlsx', 'example2.xls', 'example3.xls', 'example4.xls', 'example4.xlsx', 'Flash_Example.apw', 'Flash_Example.bkp', 'Flash_Example.def', 'gc-data-21.txt', 'PT.txt', 'raman.txt', 'testdata.txt'] Exception caught: <type 'exceptions.Exception'> Running final code finally inside c:\users\jkitchin\Dropbox\pycse
You can see that we changed into the directory, ran some code, and then caught an exception. Afterwards, we changed back to our original directory. This code works fine, but it is somewhat verbose, and tedious to write over and over. We can get a cleaner syntax with a context manager. The context manager uses the
with keyword in python. In a context manager some code is executed on entering the "context", and code is run on exiting the context. We can use that to automatically change directory, and when done, change back to the original directory. We use the
contextlib.contextmanager decorator on a function. With a function, the code up to a
yield statement is run on entering the context, and the code after the yield statement is run on exiting. We wrap the yield statement in try/except/finally block to make sure our final code gets run.
import contextlib import os, sys @contextlib.contextmanager def cd(path): print 'initially inside {0}'.format(os.getcwd()) CWD = os.getcwd() os.chdir(path) print 'inside {0}'.format(os.getcwd()) try: yield except: print 'Exception caught: ',sys.exc_info()[0] finally: print 'finally inside {0}'.format(os.getcwd()) os.chdir(CWD) # Now we use the context manager with cd('data'): print os.listdir('.') raise Exception('boom') print with cd('data/run2'): print os.listdir('.')
One case that is not handled well with this code is if the directory you want to change into does not exist. In that case an exception is raised on entering the context when you try change into a directory that does not exist. An alternative class based context manager can be found here.
Copyright (C) 2013 by John Kitchin. See the License for information about copying.