From dc23e64d6e9c2a57958eee9b11f9cbb219826a21 Mon Sep 17 00:00:00 2001 From: Martin Fitzpatrick Date: Mon, 30 Mar 2015 21:53:09 +0200 Subject: [PATCH] No-seek implementation of HttpResponse Image.open This is an alternative implementation for `Image.open()` to support opening of images from HttpResponse objects. It uses `.seekable()` to determine whether the file supports seeking (with failover for this attribute not being supported e.g. in `urllib2` response objects). The prefix is chopped off as normal and pre-pended back onto the bytes fed into the `io.IOBytes` object. This avoids loading the image object totally (duplicating it in memory) until we are sure we have a factory for the file. Add AttributeError exception catch; add io.IOBytes wrapper only once Add callable .seek test for Python2.x --- PIL/Image.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 58944f891..1f09bcfa1 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -2250,9 +2250,11 @@ def open(fp, mode="r"): filename = "" try: - fp.seek(0) - except (AttributeError, io.UnsupportedOperation): - fp = io.BytesIO(fp.read()) + requires_iobytes_wrapper = not fp.seekable() + except AttributeError: + # Not a subclass of io.IOBase; probably Python 2.7 file (or urllib object) + # Check we have a seek fn on the object + requires_iobytes_wrapper = not callable( getattr(fp, "seek", None) ) prefix = fp.read(16) @@ -2262,6 +2264,9 @@ def open(fp, mode="r"): try: factory, accept = OPEN[i] if not accept or accept(prefix): + if requires_iobytes_wrapper: + fp = io.BytesIO(prefix + fp.read()) + requires_iobytes_wrapper = False # Wrapping once is enough fp.seek(0) im = factory(fp, filename) _decompression_bomb_check(im.size) @@ -2277,6 +2282,9 @@ def open(fp, mode="r"): try: factory, accept = OPEN[i] if not accept or accept(prefix): + if requires_iobytes_wrapper: + fp = io.BytesIO(prefix + fp.read()) + requires_iobytes_wrapper = False # Wrapping once is enough fp.seek(0) im = factory(fp, filename) _decompression_bomb_check(im.size)