[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