1

I need to perform an action without changing the global working directory. My case is I have a few folders, and in each, there are a few files. I need to do some computations using those files. Initially, I tried the following:

with os.chdir('/directory'):
    ...some code needing execution inside 

but got AttributeError: __enter__. After reading up online using with seems not to be an option. Therefore I'm looking to find another elegant way of doing so.

I also tried just using os statements like so:

cwd = os.getcwd()

os.chdir('/directory')
..run code inside directory 

os.chdir(cwd)

but this is a pain during debugging and seems like a bad practice.

sage
  • 57
  • 6
  • Why do you need to switch the working directory? Are you calling an external program which needs to be started in the specific folder? – phibel Jan 08 '23 at 15:32
  • `os.chdir` returns `None`, which is not a context manager. That doesn't mean you can't find or write an appropriate context manager. – chepner Jan 08 '23 at 15:36
  • In a nutshell yes, I need to do a specific action within some directory and then continue the program in a working directory, I found an approach using straight os statements to be very convoluted @phibel – sage Jan 08 '23 at 15:38
  • There is a [`contextlib.chdir`](https://docs.python.org/3/library/contextlib.html#contextlib.chdir) in Python 3.11. If you are using an earlier version, you can also refer to its [source code](https://github.com/python/cpython/blob/main/Lib/contextlib.py#L767). – Mechanic Pig Jan 08 '23 at 15:41
  • @sage When you are calling an external program, you could use `subprocess` which has a `cwd` argument: https://stackoverflow.com/questions/21406887/subprocess-changing-directory – phibel Jan 08 '23 at 15:44

3 Answers3

5

You can write your own context manager to temporarily change the working directory.

import contextlib


@contextlib.contextmanager
def new_cd(x):
    d = os.getcwd()

    # This could raise an exception, but it's probably
    # best to let it propagate and let the caller
    # deal with it, since they requested x
    os.chdir(x)

    try:
        yield

    finally:
        # This could also raise an exception, but you *really*
        # aren't equipped to figure out what went wrong if the
        # old working directory can't be restored.
        os.chdir(d)


with new_cd('/directory'):
    ...
chepner
  • 497,756
  • 71
  • 530
  • 681
  • 1
    Is it beneficial to include `try:` and `finally:` here? – sage Jan 08 '23 at 15:39
  • Probably. Even if there's an uncaught exception in the body of the `with` statement, it would be a good idea to try to restore the original working directory before re-raising it. – chepner Jan 08 '23 at 19:38
3

Consider spawning a subprocess:

import subprocess

subprocess.run(cmd, cwd="/directory")

See https://docs.python.org/3/library/subprocess.html.

Ci Leong
  • 92
  • 11
2

You can make your own context manager. This answer is similar @chepner 's, but will still change back the current working directory in case of error.

import contextlib
import os

@contextlib.contextmanager
#temporarily change to a different working directory
def temporaryWorkingDirectory(path):
    _oldCWD = os.getcwd()
    os.chdir(os.path.abspath(path))

    try:
        yield
    finally:
        os.chdir(_oldCWD)

I tested it using this

#test
print(os.getcwd())
with temporaryWorkingDirectory("../"):
    print(os.getcwd())
print(os.getcwd())