[Image-SIG] PIL 1.1.6 ImageFile.Parser destroys data for PngImagePlugin
Fredrik Lundh
fredrik at pythonware.com
Wed Nov 4 16:43:56 CET 2009
Thanks for the detailed analysis. The fix in 1.1.7 is slightly
different from the one you propose:
http://hg.effbot.org/pil-2009-raclette/changeset/fe4688f15fed/
Not sure why the code considers it important to close the file at that
point; I'll take another look at a look at the code and the history of
that file when I find the time.
</F>
On Wed, Nov 4, 2009 at 2:16 PM, Nils Olaf de Reus
<nils.de.reus at ivm.vu.nl> wrote:
> I am experiencing inconvenience from the behaviour of ImageFile.py in
> PIL 1.1.6 when attempting to read in a PNG image.
>
> The method I see documented to retreive an image through ImageFile is by
> invoking close() on the ImageFile.Parser, like this.
>
>>>> import ImageFile
>>>> import urllib
>>>>
>>>> f = urllib.urlopen('<url-path-to-png-file>')
>>>>
>>>> p = ImageFile.Parser()
>>>>
>>>> while True:
> ... s = f.read(1024)
> ... if not s:
> ... break
> ... p.feed(s)
> ...
>>>> im = p.close()
>
> The way ImageFile.Parser works, it creates an ImageFile._ParserFile to
> act as a file, and calls Image.open() on that. So far so good, and this
> results in a valid png image object.. for a very brief moment, because
> in the 'finally' section immediately after, it calls close() on that
> ImageFile._ParserFile.
>
> The definition of close() in ImageFile._ParserFile is as follows:
>
> def close(self):
> self.data = self.offset = None
>
> But PngImagePlugin does not copy data on opening - it just keeps a
> reference to the source. So this just now destroyed the data on the
> object that PngImagePlugin is still referring to as the location of its
> IDAT chunks.. not a bright move, because we have now created a condition
> where from the point of view of the PngImagePlugin, self.fp is an
> ImageFile._ParserFile instance, and self.fp.data is None.
>
> Which means that as soon as we try to do anything with that image,
> PngImagePlugin is going to be firing read() instructions at its self.fp,
> which in turn blindly trusts that its self.data supports __getitem__()..
> and predictably, there is our Traceback:
>
>>>> im.convert('RGB')
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "/usr/lib/python2.5/site-packages/PIL/Image.py", line 653, in
> convert
> self.load()
> File "/usr/lib/python2.5/site-packages/PIL/ImageFile.py", line 189, in
> load
> s = read(self.decodermaxblock)
> File "/usr/lib/python2.5/site-packages/PIL/PngImagePlugin.py", line
> 365, in load_read
> return self.fp.read(bytes)
> File "/usr/lib/python2.5/site-packages/PIL/ImageFile.py", line 300, in
> read
> data = self.data[pos:pos+bytes]
> TypeError: 'NoneType' object is unsubscriptable
>
>
> I am currently working around the issue by creating my own
> ImageFile._ParserFile object that avoids having its data None'd.
>
>>>> import ImageFile
>>>> import urllib
>>>>
>>>> f = urllib.urlopen('<url-path-to-png-file>')
>>>>
>>>> p = ImageFile.Parser()
>>>>
>>>> while True:
> ... s = f.read(1024)
> ... if not s:
> ... break
> ... p.feed(s)
> ...
>>>> virtualfile = ImageFile._ParserFile(p.data)
>>>> im = Image.open(virtualfile)
>>>> p.close()
>>>> im.convert('RGB')
> <Image.Image instance at 0x2b890f5cff80>
>
>
> This works great -just need to keep the virtualfile around until I'm
> really done with it- but obviously I am not happy having to use what is
> supposed to be an internal from ImageFile.
>
> >From where I stand, the sensible thing would be to eliminate these two
> lines from ImageFile.Parser.close():
>
> finally:
> fp.close() # explicitly close the virtual file
>
> ..except that because of the comment, I must assume that there is a
> reason to be doing this. Could someone please shed light on why it is
> necessary to explicitly close fp there?
>
> Kind regards,
> Nils Olaf de Reus
>
>
> _______________________________________________
> Image-SIG maillist - Image-SIG at python.org
> http://mail.python.org/mailman/listinfo/image-sig
>
More information about the Image-SIG
mailing list