How do I write a decorator that restores the cwd?

How do I write a decorator that restores the current working directory to what it was before the decorated function was called? In other words, if I use the decorator on a function that does an os.chdir(), the cwd will not be changed after the function is called.

Answers


The path.py module (which you really should use if dealing with paths in python scripts) has a context manager:

subdir = d / 'subdir' #subdir is a path object, in the path.py module
with subdir:
  # here current dir is subdir

#not anymore

(credits goes to this blog post from Roberto Alsina)


The answer for a decorator has been given; it works at the function definition stage as requested.

With Python 2.5+, you also have an option to do that at the function call stage using a context manager:

from __future__ import with_statement # needed for 2.5 ≤ Python < 2.6
import contextlib, os

@contextlib.contextmanager
def remember_cwd():
    curdir= os.getcwd()
    try: yield
    finally: os.chdir(curdir)

which can be used if needed at the function call time as:

print "getcwd before:", os.getcwd()
with remember_cwd():
    walk_around_the_filesystem()
print "getcwd after:", os.getcwd()

It's a nice option to have.

EDIT: I added error handling as suggested by codeape. Since my answer has been voted up, it's fair to offer a complete answer, all other issues aside.


The given answers fail to take into account that the wrapped function may raise an exception. In that case, the directory will never be restored. The code below adds exception handling to the previous answers.

as a decorator:

def preserve_cwd(function):
    @functools.wraps(function)
    def decorator(*args, **kwargs):
        cwd = os.getcwd()
        try:
            return function(*args, **kwargs)
        finally:
            os.chdir(cwd)
    return decorator

and as a context manager:

@contextlib.contextmanager
def remember_cwd():
    curdir = os.getcwd()
    try:
        yield
    finally:
        os.chdir(curdir)

def preserve_cwd(function):
   def decorator(*args, **kwargs):
      cwd = os.getcwd()
      result = function(*args, **kwargs)
      os.chdir(cwd)
      return result
   return decorator

Here's how it's used:

@preserve_cwd
def test():
  print 'was:',os.getcwd()
  os.chdir('/')
  print 'now:',os.getcwd()

>>> print os.getcwd()
/Users/dspitzer
>>> test()
was: /Users/dspitzer
now: /
>>> print os.getcwd()
/Users/dspitzer

Need Your Help

How to change the date format from MM/DD/YYYY to YYYY-MM-DD in PL/SQL?

sql oracle datetime

I have a date column in a table stored as MM/DD/YYYY format. I have to select and store the same date in another table in YYYY-MM-DD format i.e. XSD Date Format. But I am not able to do it. I am us...

Convert comma separated entry to columns

r csv

I have a dataset with several columns, one of which is a column for reaction times. These reaction times are comma separated to denote the reaction times (of the same participant) for the different