I/O Error

Michael Hoffman cam.ac.uk at mh391.invalid
Tue Apr 24 05:22:32 EDT 2007


saif.shakeel at gmail.com wrote:

> file_input = raw_input("Enter The ODX File Path:")
> odx_file_output = raw_input("Enter the output file path : ")
> log_file_output = raw_input("Enter the path for LogFile  : ")
> 
> saveout = sys.stdout
> try:
>     f_open=open(odx_file_output, 'w')
> except:
>     print "cant open file"
>     sys.exit()
> sys.stdout = f_open
> 
> try:
>     input_xml = open(file_input,'r')
> except:
>     print "The File Cannot Be Opened"
>     sys.exit()
> 
> if input_xml.read(5)!='<?xml':
>     print "Invalid File"
>     input_xml.close()
>     sys.exit()
> else:
>     xmldoc = minidom.parse(input_xml)
>     input_xml.close()
> 
> if xmldoc.childNodes[1].getAttribute("DtdVers") == u'1.1.4' or
> xmldoc.childNodes[1].getAttribute("DtdVers")== u'1.1.5':
>     pass
> else:
>     print "Invalid Version"
>     sys.exit()
> 
>                     After this some more code follows,but i have
> pasted only the i/o part .

In the future, it is best to be able to produce a short and complete 
test case. Doing so may help you find your error, without assistance.

> Traceback (most recent call last):
>   File "C:\Projects\ODX Import\code_ini\odxparse.py", line 250, in
> <module>
>     file_input = raw_input("Enter The ODX File Path:")
> ValueError: I/O operation on closed file

Well, you set sys.stdout to f_open, and you probably closed it without 
setting it back. Can't tell because you didn't include the whole script 
(and you shouldn't do that either--make a test case).

Here are some general comments:

1) Redirecting sys.stdout does not seem advisable in this case, and 
seems to be causing some confusion. It's far better to make a new file 
handle for your output. You can print to it using:

print >>filehandle, "message"

2) Interactively asking for filenames like this will cause irritation 
for yourself, and possibly your users if they ever want to automate 
things. My personal preference would be to accept the arguments on the 
command line. If your users don't know how to use a command line, then 
you should really be getting the filenames through some sort of GUI 
instead of raw_input().

3) I try to name my file-related variables consistently so I know where 
they are. Naming the file name variable and the file handle variable 
something completely different is confusing.

4) If you have some abnormal exit condition, you should exit with 
sys.exit(1) or really any number besides 0. Exiting with sys.exit() or 
sys.exit(0) means everything is fine.

5) If an exception occurs that will result in the end of the program, 
there's no point in catching it just to print a less descriptive error 
message and quit. It makes your code harder to understand with all the 
exception catching, and it makes it harder to debug because you lose 
crucial details of where the exception occurred and what its calling 
stack was.

6) Further, I'm not sure how much sense it makes to double-check that 
the file begins with "<?xml". minidom.parse will check that just fine. 
And it will catch all sorts of other errors as well, and you can't do 
them all yourself at this point.

Here's how I would rewrite it using Python 2.5:

from __future__ import with_statement

import sys
from xml.dom import minidom

ACCEPTABLE_DTD_VERSIONS = [u'1.1.4', u'1.1.5']

class DTDVersionError(StandardError):
     pass

def io(infilename, outfilename, logfilename):
     with open(outfilename, "w") as outfile:
         with open(infilename) as infile:
             xmldoc = minidom.parse(input_xml)

     dtd_version = xmldoc.childNodes[1].getAttribute("DtdVers")
     if dtd_version not in ACCEPTABLE_DTD_VERSIONS:
         raise DTDVersionError(infilename)

def main(args):
     return io(*args)

if __name__ == "__main__":
     sys.exit(main(sys.argv[1:]))

As far as the interface goes, you can run this from the commandline as 
example.py INFILE OUTFILE LOGFILE. Or from IDLE as io("INFILE", 
"OUTFILE", "LOGFILE"). That way you can re-run it many times without 
having to retype three file names each time, yuck. If you want to add a 
GUI for other users to select files, you can call it from main() if 
there are no command-line arguments, keeping the bulk of your logic in 
io() separate from the interface.

Let us know if you have any questions about what I have done here.
-- 
Michael Hoffman



More information about the Python-list mailing list