From 5126ce3b315d98a9744c3f1a3e3e9a744708c5ea Mon Sep 17 00:00:00 2001 From: Ryan Nelson Date: Sat, 21 Mar 2015 10:29:41 -0400 Subject: [PATCH 1/7] Allow URL strings to be passed to imread These modifications allow valid URL strings to be passed directly into imread. URL recognition is done using the standard library function urlparse. If the string contains a valid url scheme, it will be used to download and process the data using a combination of urlopen and BytesIO (StringIO in python 2). --- lib/matplotlib/image.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 2428147218bb..56dca1578cb4 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -7,6 +7,9 @@ unicode_literals) import six +from six.moves.urllib.parse import urlparse +from six.moves.urllib.request import urlopen +from six import BytesIO import os import warnings @@ -1225,10 +1228,17 @@ def pilread(fname): except ImportError: return None if cbook.is_string_like(fname): - # force close the file after reading the image - with open(fname, "rb") as fh: + parsed = urlparse(fname) + # If the string is a URL, then download the data + if parsed.scheme is not '': + fh = BytesIO(urlopen(fname).read()) image = Image.open(fh) return pil_to_array(image) + else: + # force close the file after reading the image + with open(fname, "rb") as fh: + image = Image.open(fh) + return pil_to_array(image) else: image = Image.open(fname) return pil_to_array(image) @@ -1236,8 +1246,13 @@ def pilread(fname): handlers = {'png': _png.read_png, } if format is None: if cbook.is_string_like(fname): - basename, ext = os.path.splitext(fname) - ext = ext.lower()[1:] + parsed = urlparse(fname) + # If the string is a URL, assume png + if parsed.scheme is not '': + ext = 'png' + else: + basename, ext = os.path.splitext(fname) + ext = ext.lower()[1:] elif hasattr(fname, 'name'): basename, ext = os.path.splitext(fname.name) ext = ext.lower()[1:] @@ -1260,8 +1275,14 @@ def pilread(fname): # reader extension, since Python handles them quite well, but it's # tricky in C. if cbook.is_string_like(fname): - with open(fname, 'rb') as fd: + parsed = urlparse(fname) + # If fname is a URL, download the data + if parsed.scheme is not '': + fd = BytesIO(urlopen(fname).read()) return handler(fd) + else: + with open(fname, 'rb') as fd: + return handler(fd) else: return handler(fname) From c5bc3013d8060f9fed34a2237bf08f9978cfb72b Mon Sep 17 00:00:00 2001 From: Ryan Nelson Date: Tue, 14 Apr 2015 08:07:58 -0400 Subject: [PATCH 2/7] Remove file name logic from pilread function The Pillow docs state that Image.open can handle either a filename string or a file object. That simplifies the pilread function substantially. --- lib/matplotlib/image.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 56dca1578cb4..d952932ee4d7 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -1227,21 +1227,8 @@ def pilread(fname): from PIL import Image except ImportError: return None - if cbook.is_string_like(fname): - parsed = urlparse(fname) - # If the string is a URL, then download the data - if parsed.scheme is not '': - fh = BytesIO(urlopen(fname).read()) - image = Image.open(fh) - return pil_to_array(image) - else: - # force close the file after reading the image - with open(fname, "rb") as fh: - image = Image.open(fh) - return pil_to_array(image) - else: - image = Image.open(fname) - return pil_to_array(image) + image = Image.open(fname) + return pil_to_array(image) handlers = {'png': _png.read_png, } if format is None: From ff90cf0eefd0f9fc4cd1ec0bbb736edc710796a6 Mon Sep 17 00:00:00 2001 From: Ryan Nelson Date: Thu, 16 Apr 2015 11:51:01 -0400 Subject: [PATCH 3/7] Update identity test to equality test It seems that the identity test for the parsed URL scheme was not sufficient. I changed this to an equality test, and things are now working properly. --- lib/matplotlib/image.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index d952932ee4d7..db815456598c 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -1235,7 +1235,7 @@ def pilread(fname): if cbook.is_string_like(fname): parsed = urlparse(fname) # If the string is a URL, assume png - if parsed.scheme is not '': + if parsed.scheme != '': ext = 'png' else: basename, ext = os.path.splitext(fname) @@ -1264,7 +1264,8 @@ def pilread(fname): if cbook.is_string_like(fname): parsed = urlparse(fname) # If fname is a URL, download the data - if parsed.scheme is not '': + if parsed.scheme != '': + print('Scheme: %s' % parsed.scheme) fd = BytesIO(urlopen(fname).read()) return handler(fd) else: From cce999d5620926d1ab227a15412ebd214b65b642 Mon Sep 17 00:00:00 2001 From: Ryan Nelson Date: Thu, 28 May 2015 10:49:30 -0400 Subject: [PATCH 4/7] Remove debug print function --- lib/matplotlib/image.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index db815456598c..9773fd1d9449 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -1265,7 +1265,6 @@ def pilread(fname): parsed = urlparse(fname) # If fname is a URL, download the data if parsed.scheme != '': - print('Scheme: %s' % parsed.scheme) fd = BytesIO(urlopen(fname).read()) return handler(fd) else: From 11f792522a73642d1d22f8278e615e5294f0b8af Mon Sep 17 00:00:00 2001 From: Ryan Nelson Date: Thu, 28 May 2015 11:54:04 -0400 Subject: [PATCH 5/7] Update Whats New documentation. --- doc/users/whats_new.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index f3dfbb720e89..54568d54c6a5 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -75,6 +75,13 @@ directive - ``close-figs`` - that closes any previous figure windows before creating the plots. This can help avoid some surprising duplicates of plots when using ``plot_directive``. +Support for URL string arguments to ``imread`` +---------------------------------------------- + +The ``imread`` function now accepts URL strings that point to remote PNG +files. This circumvents the generation of a HTTPResponse object directly. + + .. _whats-new-1-4: new in matplotlib-1.4 From 683004c500d548aaa6a6ac4bde5ffd9c6d3cb1c0 Mon Sep 17 00:00:00 2001 From: Ryan Nelson Date: Thu, 28 May 2015 11:54:24 -0400 Subject: [PATCH 6/7] Update imread docstring for URL strings. --- lib/matplotlib/image.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 9773fd1d9449..f223f0db9b23 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -1204,8 +1204,9 @@ def imread(fname, format=None): """ Read an image from a file into an array. - *fname* may be a string path or a Python file-like object. If - using a file object, it must be opened in binary mode. + *fname* may be a string path, a valid URL, or a Python + file-like object. If using a file object, it must be opened in binary + mode. If *format* is provided, will try to read file of that type, otherwise the format is deduced from the filename. If nothing can @@ -1218,7 +1219,9 @@ def imread(fname, format=None): matplotlib can only read PNGs natively, but if `PIL `_ is installed, it will use it to load the image and return an array (if possible) which - can be used with :func:`~matplotlib.pyplot.imshow`. + can be used with :func:`~matplotlib.pyplot.imshow`. Note, URL strings + may not be compatible with PIL. Check the PIL documentation for more + information. """ def pilread(fname): From d44d0fe76e8ab67841ae7a724c2ca502e86051ac Mon Sep 17 00:00:00 2001 From: Ryan Nelson Date: Thu, 28 May 2015 12:03:11 -0400 Subject: [PATCH 7/7] Remove trailing whitespace. --- lib/matplotlib/image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index f223f0db9b23..a5b5b4ab88d0 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -1219,7 +1219,7 @@ def imread(fname, format=None): matplotlib can only read PNGs natively, but if `PIL `_ is installed, it will use it to load the image and return an array (if possible) which - can be used with :func:`~matplotlib.pyplot.imshow`. Note, URL strings + can be used with :func:`~matplotlib.pyplot.imshow`. Note, URL strings may not be compatible with PIL. Check the PIL documentation for more information. """