ValueError: I/O operation on closed file. with python3

Peter Otten __peter__ at web.de
Wed Jun 12 03:26:47 EDT 2013


Adam Mercer wrote:

> Hi
> 
> I'm trying to update one of my scripts so that it runs under python2
> and python3, but I'm running into an issue that the following example
> illustrates:
> 
> $ cat test.py
> try:
>   # python-2.x
>   from urllib2 import urlopen
>   from ConfigParser import ConfigParser
> except ImportError:
>   # python-3.x
>   from urllib.request import urlopen
>   from configparser import ConfigParser
> 
> server='http://www.lsc-group.phys.uwm.edu/~ram/files'
> 
> fp = urlopen('%s/latest.ini' % server).fp
> cp = ConfigParser()
> cp.readfp(fp)
> print(cp.get('version', '10.8'))
> $
> 
> This works as expected when using python2:
> 
> $ python2.7 test.py
> 5.2.10
> $
> 
> but when using python3 I receive the following error:
> 
> $ python3.3 test.py
> Traceback (most recent call last):
>   File "test.py", line 14, in <module>
>     cp.readfp(fp)
>   File
>   
"/opt/local/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/configparser.py",
> line 753, in readfp
>     self.read_file(fp, source=filename)
>   File
>   
"/opt/local/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/configparser.py",
> line 708, in read_file
>     self._read(f, source)
>   File
>   
"/opt/local/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/configparser.py",
> line 1010, in _read
>     for lineno, line in enumerate(fp, start=1):
> ValueError: I/O operation on closed file.
> $
> 
> Is there a way to get this working in both python2 and python3?
> 
> This is a small script and I'm starting to have some users wanting to
> use python3 and others sticking to python2 so I'd like to accommodate
> them both if possible.

Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 30 2012, 14:49:00) 
[GCC 4.6.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from urllib.request import urlopen
>>> url = "http://www.lsc-group.phys.uwm.edu/~ram/files/latest.ini"
>>> fp = urlopen(url).fp
>>> fp.read()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: read of closed file
>>> resp = urlopen(url)
>>> resp.fp.read()
b'[version]\n10.5 = 5.2.4\n10.6 = 5.2.10\n10.7 = 5.2.10\n10.8 = 5.2.10\n'

I don't know whether this behaviour is intentional or accidental (I assume 
the latter).

I then ran into another problem:

>>> from configparser import ConfigParser
>>> p = ConfigParser()
>>> resp = urlopen(url)
>>> p.readfp(resp.fp)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.3/configparser.py", line 754, in readfp
    self.read_file(fp, source=filename)
  File "/usr/local/lib/python3.3/configparser.py", line 709, in read_file
    self._read(f, source)
  File "/usr/local/lib/python3.3/configparser.py", line 1011, in _read
    if line.strip().startswith(prefix):
TypeError: startswith first arg must be bytes or a tuple of bytes, not str

So ConfigParser.readfp() (which has been deprecated in favour of 
read_file(), btw.) expects a text file:

>>> from io import TextIOWrapper
>>> resp = urlopen(url)
>>> p.readfp(TextIOWrapper(resp.fp))
>>> p.get("version", "10.8")
'5.2.10'

Applying these findings to your script:

from contextlib import contextmanager
try:
  # python-2.x
  from urllib2 import urlopen
  from ConfigParser import ConfigParser

  @contextmanager
  def my_urlopen(url):
      yield urlopen(url).fp

except ImportError:
  # python-3.x
  from urllib.request import urlopen
  from configparser import ConfigParser
  import io

  @contextmanager
  def my_urlopen(url):
      resp = urlopen(url)
      yield io.TextIOWrapper(resp.fp)

server='http://www.lsc-group.phys.uwm.edu/~ram/files'

cp = ConfigParser()
with my_urlopen('%s/latest.ini' % server) as fp:
    cp.readfp(fp)

print(cp.get('version', '10.8'))

I've run it with 2.6, 2.7, 3.2, and 3.3. 





More information about the Python-list mailing list