I am relatively new to decorators, context-managers etc in python. Basically I want to do something like the following:
@contextmanager
def cd(to_dir):
from_dir = os.getcwd()
try:
os.chdir(os.path.expanduser(to_dir))
yield
except Exception:
log.error(traceback.format_exc())
log.error(f"Failed to cd into {to_dir} from {from_dir}")
finally:
os.chdir(from_dir)
I would like to use it in something like this:
with cd('here'):
...
...
...
I specifically want to ensure that the body of the with statement is not executed if the cd
into here
fails which is why I am attempting to catch the Exception inside the context manager and prevent it from yield
ing.
However it seems a python generator must yield or I get an error such as:
raise RuntimeError("generator didn't yield") from None |
RuntimeError: generator didn't yield
How do I suppress execution of the body of with
if the chdir fails?
One solution I can think of is this:
@contextmanager
def cd(to_dir):
from_dir = os.getcwd()
try:
os.chdir(os.path.expanduser(to_dir))
yield True
except Exception:
log.error(traceback.format_exc())
log.error(f"Failed to cd into {to_dir} from {from_dir}")
yield False
finally:
os.chdir(from_dir)
Then use as follows:
with cd('here') as success:
if success:
....
....
This however does require the additional conditionals whereever the with statement is used, so not so convenient to change everywhere in an existing program. Another issue with this is that if an exception occurs within the body of the with
statement it also is captured by except
clause inside the cd contextmanager function leading to a bogus error regarding failing to cd.
A context manager has no way of skipping the context body, other than raising an error. The context manager protocol only allows to return a value to be bound in the body (the target of as
) but not to indicate skipping the body. Consequently, if entering a context might fail either a) do not suppress the failure exception or b) replace the failure exception with a new exception.
@contextmanager
def cd(to_dir):
from_dir = os.getcwd()
# if this fails, the exception bubbles out of the context manager
os.chdir(os.path.expanduser(to_dir))
try:
yield
finally:
os.chdir(from_dir)
If the initial os.chdir
fails, the exception will "exit" the context at the with
statement, without entering the body.