mgilson
mgilson

Reputation: 309949

conditionally close file on exit from function

I have a (recursive) function which I would like to accept either a string or an opened file object. If the argument is a string, then the function opens a file and uses that file object. It seems best to close this opened file object explicitly when I return from the function, but only if a string was passed in. (Imagine the surprise from the user when they pass in an opened file object and find that their file object was closed somewhere). Here's what I'm currently using:

def read_file(f, param):
    do_close = isinstance(f,basestring)
    f = open(f, 'rb') if do_close else f
    try:
        info = f.read(4)
        #check info here
        if info == Info_I_Want(param):
           return f.read(get_data(info))
        else:
           f.seek(goto_new_position(info))
           return read_file(f,param)
    except IKnowThisError:
           return None
    finally:
        if do_close:
           f.close()

You can assume that IKnowThisError will be raised at some point if I don't find the info I want.

This feels very kludgy. Is there a better way?

Upvotes: 1

Views: 251

Answers (3)

Sven Marnach
Sven Marnach

Reputation: 601859

The upcoming Python 3.3 offers a more general solution for this kind of problem, namely contextlib.ExitStack. This allow to conditionally add context managers to the current with-block:

def read_file(f, param):
    with ExitStack() as stack:
        if isinstance(f, basestring):
            f = stack.enter_context(open(f, 'rb'))
        # Your code here

Upvotes: 1

Scharron
Scharron

Reputation: 17767

Why not wrapping your recursive function with a wrapper to avoid overhead ?

def read_file(f, param):
    if isinstance(f, basestring):
        with open(f, 'rb') as real_f:
            return read_file2(real_f, param)
    else:
        return read_file2(real_f, param)

def read_file2(f, param):
    # Now f should be a file object
    ...

Upvotes: 3

ecatmur
ecatmur

Reputation: 157374

How about calling your function recursively?

def read_file(f, param):
    if isinstance(f, basestring):
        with open(f, 'rb') as real_f:
            return read_file(real_f, param)
    else:
        # normal path

Upvotes: 2

Related Questions