39

I have a function that resembles the one below. I'm not sure how to use the os module to get back to my original working directory at the conclusion of the jar's execution.

def run(): 
    owd = os.getcwd()
    #first change dir to build_dir path
    os.chdir(testDir)
    #run jar from test directory
    os.system(cmd)
    #change dir back to original working directory (owd)

note: I think my code formatting is off - not sure why. My apologies in advance

Daniel Spiewak
  • 54,515
  • 14
  • 108
  • 120
Amara
  • 13,839
  • 9
  • 32
  • 28
  • If you put four spaces before each line of your code, SO will format it more nicely. – sep332 Nov 18 '08 at 17:22
  • I just fixed that for @Amara :) .. they had used a
     tag to open, but a  to end. It's all clean and happy now, though :D
    – warren Nov 18 '08 at 17:23
  • Also answered in http://stackoverflow.com/questions/299249/how-can-i-get-my-python-version-25-script-to-run-a-jar-file-inside-a-folder-ins. – S.Lott Nov 18 '08 at 17:52
  • "I'm not sure how to use the os module to get back to my original working directory at the conclusion of the jar's execution. " I don't understand how there is a question. OP clearly knew how to find out the original working directory, and how to set the current working directory to a given path. So - use the "set current working directory" thingy, and tell it to use "the original working directory" (already saved in a variable) as the directory to set it to? *What exactly is the difficulty*? – Karl Knechtel Mar 15 '23 at 08:11

8 Answers8

75

A context manager is a very appropriate tool for this job:

from contextlib import contextmanager
import os

@contextmanager
def cwd(path):
    oldpwd = os.getcwd()
    os.chdir(path)
    try:
        yield
    finally:
        os.chdir(oldpwd)

...used as:

os.chdir('/tmp') # for testing purposes, be in a known directory
print(f'before context manager: {os.getcwd()}')
with cwd('/'):
    # code inside this block, and only inside this block, is in the new directory
    print(f'inside context manager: {os.getcwd()}')

print(f'after context manager: {os.getcwd()}')

...which will yield something like:

before context manager: /tmp
inside context manager: /
after context manager: /tmp

This is actually superior to the cd - shell builtin, inasmuch as it also takes care of changing directories back when a block is exited due to an exception being thrown.


For your specific use case, this would instead be:

with cwd(testDir):
    os.system(cmd)

Another option to consider is using subprocess.call() instead of os.system(), which will let you specify a working directory for the command to run:

# note: better to modify this to not need shell=True if possible
subprocess.call(cmd, cwd=testDir, shell=True)

... which would prevent you from needing to change the interpreter's directory at all.

Note that now it is recommended to use subprocess.run (instead of call) but the same arguments are available, and in particular cwd: https://docs.python.org/3/library/subprocess.html#using-the-subprocess-module.

JayRizzo
  • 3,234
  • 3
  • 33
  • 49
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • 2
    Thanks for the .call(cwd=...) idea! – antimirov Sep 14 '20 at 14:05
  • @Jean-Francois Please be a bit more light-handed in making edits that don't improve correctness (or PEP-8 compliance, which the spacing fix f/e certainly does). Some of the changes from single to double quotes are ones I don't particularly agree with from a personal stylistic choice perspective, and I'm unaware of any correctness or convention reason for the change at hand. – Charles Duffy Jun 20 '22 at 03:50
  • @CharlesDuffy Sorry for that. I'm biased by my extensive usage of `black`, which uses `"` as a default for string. And it's true that PEP-8 (or Google-Style for what matters) doesn't recommend one over the other. I reverted the quotes. – Jean-Francois T. Jun 21 '22 at 13:53
34

You simply need to add the line:

os.chdir(owd)

Just a note this was also answered in your other question.

grieve
  • 13,220
  • 10
  • 49
  • 61
  • Noted. :) I Wanted to ensure my question was more specific and detailed in order to get the best help and also, post a code sample to add some more clarity to my question. – Amara Nov 18 '08 at 17:41
16

The advice to use os.chdir(owd) is good. It would be wise to put the code which needs the changed directory in a try:finally block (or in python 2.6 and later, a with: block.) That reduces the risk that you will accidentally put a return in the code before the change back to the original directory.

def run(): 
    owd = os.getcwd()
    try:
        #first change dir to build_dir path
        os.chdir(testDir)
        #run jar from test directory
        os.system(cmd)
    finally:
        #change dir back to original working directory (owd)
        os.chdir(owd)
Alex Coventry
  • 68,681
  • 4
  • 36
  • 40
4

Python 3.11 update:

You can now simply use contextlib.chdir from stdlib. It changes the directory when entering the block, it then restores the old directory back:

from contextlib import chdir
from os import getcwd

print(f"Before: {getcwd()}")

with chdir("/"):
    print(f"inside: {getcwd()}")

print(f"after: {getcwd()}")
JayRizzo
  • 3,234
  • 3
  • 33
  • 49
S.B
  • 13,077
  • 10
  • 22
  • 49
3

A context-manager is overkill for this situation (executing a system command). The best solution is to use the subprocess module instead (Python 2.4 onwards) and the run or popen methods with the cwd argument.

So, your code can be replaced with:

def run(): 
    #run jar from test directory
    subprocess.run(cmd, cwd=testDir)

See https://bugs.python.org/issue25625 and https://docs.python.org/3/library/subprocess.html#subprocess-replacements.

Nzbuu
  • 5,241
  • 1
  • 29
  • 51
2

os.chdir(owd) should do the trick (like you've done when changing to testDir)

2

Python is case sensitive so when typing the path make sure it's the same as the directory you want to set.

import os

os.getcwd()

os.chdir('C:\\')
bluish
  • 26,356
  • 27
  • 122
  • 180
eagle3ye
  • 21
  • 1
  • I'm not sure it is Python that is case sensitive, I think it's the operating system that is (Linux, etc). – Flimm Jan 14 '22 at 15:31
0

I looked around the answers on StackOverflow and eventually decided to write my own decorator for this purpose:

from collections.abc import Callable
from functools import wraps
from typing import ParamSpec, TypeVar


T = TypeVar('T')
P = ParamSpec('P')
    
    
def enter_subdir(subdir: str) -> Callable[[Callable[P, T]], Callable[P, T]]:
    """During the execution of a function, temporarily enter a subdirectory."""

    def decorator(function: Callable[P, T]) -> Callable[P, T]:
        @wraps(function)
        def wrapper(*args, **kwargs) -> T:
            os.makedirs(subdir, exist_ok=True)
            os.chdir(subdir)
            result = function(*args, **kwargs)
            os.chdir("..")
            return result

        return wrapper

    return decorator
Somebody Out There
  • 347
  • 1
  • 5
  • 15