Igor Lankin
Igor Lankin

Reputation: 1486

sh.cd using context manager

here is what I am basically trying to do:

import sh, os

with sh.cd('/tmp'):
  print os.getcwd()

print os.getcwd()

I get the following error though

line 3, in <module>
    with sh.cd('/tmp'):
AttributeError: __exit__

What am I missing here? Are there alternative solutions to change directory within a context?

Upvotes: 2

Views: 2492

Answers (2)

Erik Cederstrand
Erik Cederstrand

Reputation: 10235

sh now has the pushd() function that can be used as a context manager to change the current directory temporarily:

import sh

with sh.pushd("/tmp"):
    sh.touch("a_file")

See https://amoffat.github.io/sh/sections/command_class.html?highlight=pushd#pushd

Upvotes: 3

dano
dano

Reputation: 94951

You can't use just any class/function as a context manager, it has to actually explicitly be implemented that way, using either the contextlib.contextmanager decorator on a function, or in the case of a class, by defining the __enter__ and __exit__ instance methods.

The sh.cd function you're using is simply a wrapper around os.chdir:

>>> import sh
>>> sh.cd
<bound method Environment.b_cd of {}>

b_cd is defined as:

def b_cd(self, path):
    os.chdir(path)

As you can see, it's just a normal function; it can't be used as a context manager.

The link whereswalden provided shows a good way of implementing the behavior you want as a class. It could similarly be implemented as a function like this:

import contextlib
import os

@contextlib.contextmanager
def cd(path):
   old_path = os.getcwd()
   os.chdir(path)
   try:
       yield
   finally:
       os.chdir(old_path)

Sample usage:

print(os.getcwd())
with cd("/"):
    print os.getcwd()
print(os.getcwd())

Output:

'/home/dan'
'/'
'/home/dan'

Upvotes: 7

Related Questions