[Baypiggies] Conditional Context Managers
Reitmeyer_Richard at emc.com
Reitmeyer_Richard at emc.com
Tue Oct 27 17:59:35 CET 2009
Hello, all.
Alex's "welcome" motivated me to toss in a python question
that's been nagging me for a while. Python has made me
very suspicious of anything that takes more than a little
typing, AKA "lazy," and I've a problem whose solutions all
offend my sensibilities.
Summary:
How do people do conditional "with" statements?
Exposition:
Long ago I wrote some code that took a file object, reads
the lines, and does a little processing.
def foo(file_obj):
lines = [ l.strip() for l in file_obj.readlines() ]
# etc.
return lines[0] # for example purposes
Sometimes I'd call as foo(sys.stdin), sometimes I'd
call after opening and before closing a file.
This evolved to be slightly friendlier for interactive
testing by letting it accept a fileobj or a filename.
def foo2(file_obj_or_name):
file_obj = file_obj_or_name
if isinstance(file_obj, str):
file_obj = open(file_obj_or_name)
lines = [ l.strip() for l in file_obj.readlines() ]
# etc.
if isinstance(file_obj, str): # danger, not exception-safe
file_obj.close()
return lines[0]
Question and possible answers:
Of course with the advent of context managers, I'd really like
to use "with" to avoid the ugliness of the second "if" and the
lack of exception safety. My question for the list is what people
think is the best way to make that work.
1) Repeat the guts, w/ possible refactor of guts.
def foo_guts(file_obj):
lines = [ l.strip() for l in file_obj.readlines() ]
# etc.
return lines[0]
def foo3(file_obj_or_name):
file_obj = file_obj_or_name
if isinstance(file_obj, str):
with open(file_obj_or_name) as file_obj:
retval = foo_guts(file_obj)
else:
retval = foo_guts(file_obj)
return retval
2) Make "trinary with" using the and/or hack.
def foo4(file_obj_or_name):
file_obj = file_obj_or_name
with isinstance(file_obj, str) and \
open(file_obj_or_name) or file_obj as file_obj:
retval = foo_guts(file_obj)
return retval
3) Write a entire context manager.
class FileFromObjOrNameCM(object):
def __init__(self, file_obj_or_name):
self.file_obj_or_name = file_obj_or_name
def __enter__(self):
if isinstance(self.file_obj_or_name, str):
self.file_obj = open(self.file_obj_or_name)
else:
self.file_obj = self.file_obj_or_name
return self.file_obj
def __exit__(self, exc_type, exc_value, traceback):
if isinstance(self.file_obj_or_name, str):
self.file_obj.close()
def foo5(file_obj_or_name):
file_obj = file_obj_or_name
with FileFromObjOrNameCM(file_obj_or_name) as file_obj:
retval = foo_guts(file_obj)
return retval
All of these work, but they are also either 1) ugly; 2) more
typing than it feels like it should take.
Thoughts?
Richard
More information about the Baypiggies
mailing list