Safely dealing with arbitrary file objects

S

Steven D'Aprano

I have found myself writing functions rather like these:

def openfile(filename):
if filename == '-':
# convention for shell scripts in Unix-land is to use
# '-' for stdin/stdout for reading/writing.
outfile = sys.stdout
if filename == '2-':
outfile = sys.stderr
else:
outfile = file(filename, 'w')
return outfile

def closefile(fileobj):
# don't close standard file objects, or their replacements
if not fileobj in (sys.stdout, sys.stderr, sys.stdin,
sys.__stdout__, sys.__stderr__, sys.__stdin__):
fileobj.close()


def processfile(filename):
outfile = openfile(filename)
try:
# lots of processing here, which may raise exceptions
var = "stuff happens"
outfile.write(var)
finally:
closefile(outfile)



A question:

I know I'm being paranoid about not closing files I shouldn't close, but
am I being excessively paranoid, or not paranoid enough?

Suggestions for improvements welcome; while I'm happy to read suggestions
using the new with statement, I can't yet rely on having Python 2.5 or
better so I have to stick to try...finally.
 
S

Scott David Daniels

Steven said:
I have found myself writing functions rather like these:

def openfile(filename):
if filename == '-':
# convention for shell scripts in Unix-land is to use
# '-' for stdin/stdout for reading/writing.
outfile = sys.stdout
if filename == '2-':
outfile = sys.stderr
else:
outfile = file(filename, 'w')
return outfile

def closefile(fileobj):
# don't close standard file objects, or their replacements
if not fileobj in (sys.stdout, sys.stderr, sys.stdin,
sys.__stdout__, sys.__stderr__, sys.__stdin__):
fileobj.close()


def processfile(filename):
outfile = openfile(filename)
try:
# lots of processing here, which may raise exceptions
var = "stuff happens"
outfile.write(var)
finally:
closefile(outfile)



A question:

I know I'm being paranoid about not closing files I shouldn't close, but
am I being excessively paranoid, or not paranoid enough?

Suggestions for improvements welcome; while I'm happy to read suggestions
using the new with statement, I can't yet rely on having Python 2.5 or
better so I have to stick to try...finally.

How about a wrapper that passes along all but 'close', so you don't
have to worry about closing something you means to leave open.
Something like:

class DontCloseOutput(file):
'''subclass file only to allow isinstance checks to work out'''
def __init__(self, referent):
self._actual = referent
self.closed = False

def close(self):
self.closed = True

def write(self, data):
if self.closed:
raise IOError('Tried to write closed pseudo-file')
return self._actual.write(data)


def writeopen(filename):
if filename == '-':
# convention for shell scripts in Unix-land is to use
# '-' for stdin/stdout for reading/writing.
return DontCloseOutput(sys.stdout)
if filename == '2-':
return DontCloseOutput(sys.stderr)
else:
return open(filename, 'w') # BTW, open is the preferred way
# file was for a short time.

You of course can do similar things (probably forwarding more message)
with readopen.

-Scott David Daniels
(e-mail address removed)
 
P

Paul Hankin

I have found myself writing functions rather like these:

def openfile(filename):
if filename == '-':
# convention for shell scripts in Unix-land is to use
# '-' for stdin/stdout for reading/writing.
outfile = sys.stdout
if filename == '2-':
outfile = sys.stderr
else:
outfile = file(filename, 'w')
return outfile

def closefile(fileobj):
# don't close standard file objects, or their replacements
if not fileobj in (sys.stdout, sys.stderr, sys.stdin,
sys.__stdout__, sys.__stderr__, sys.__stdin__):
fileobj.close()

def processfile(filename):
outfile = openfile(filename)
try:
# lots of processing here, which may raise exceptions
var = "stuff happens"
outfile.write(var)
finally:
closefile(outfile)

A question:

I know I'm being paranoid about not closing files I shouldn't close, but
am I being excessively paranoid, or not paranoid enough?

Suggestions for improvements welcome; while I'm happy to read suggestions
using the new with statement, I can't yet rely on having Python 2.5 or
better so I have to stick to try...finally.

With with:

import sys

class leave_open(object):
def __init__(self, obj):
self.obj = obj
def __enter__(self):
return self.obj
def __exit__(self, *exc_info):
pass

def open_file(name, special={'-':sys.stdout, '2-':sys.stderr}):
if name in special:
return leave_open(special[name])
else:
return open(name, 'w')

def process_file(filename):
with open_file(filename) as outfile:
outfile.write("stuff happens")


Without with:

import sys

def open_file(name, special={'-':sys.stdout, '2-':sys.stderr}):
if name in special:
return False, special[name]
return True, open(name, 'w')

def process_file(filename):
should_close, outfile = open_file(filename)
try:
outfile.write('stuff happens')
finally:
if should_close:
outfile.close()
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top