Monday, January 21, 2013

with fail():

with fail()::
So, yesterday's article on
with
turned out the be a bit wrong. As lesson in reading the
explanatory text and not just the example code. (Or possibly a lesson
in providing better example code!).

So, I gave an example along the lines of:

@contextmanager
def chdir(path):
# This code is wrong. Don't use it!
cwd = os.getcwd()
os.chdir(path)
yield
os.chdir(cwd)


Now, this appears to mostly work. The problem occurs when you have some code like:

with chdir('/'):
raise Exception()


In this case the final cleanup code (os.chdir(cwd))
will not be executed, which is almost certainly not what you want.
The correct was to write this is:

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


Writing it this way the final os.chdir(cwd) will be executed,
and the exception will still be propagated. So I think this kind of sucks, because
I'd really have preferred it to have this behaviour by default. To this end
I've created a simplecontextmanager function decorator. Basically you
can use it like so:

@simplecontextmanager
def chdir(path):
cwd = os.getcwd()
os.chdir(path)
yield
os.chdir(cwd)


Looks pretty much like the first example, however it will have the semantics of
the second example. Now clearly the simplecontextmanager doesn't provide
the whole range of flexibility that contextmanager does, but for simple
code it is more straightforward.

My pyutil git repo has been
updated to have working version of the chdir, umask and
update_env context managers, and also contains the source for the
new simplecontextmanager.

Moral of the story, test your code and read the docs carefully!

DIGITAL JUICE

No comments:

Post a Comment

Thank's!