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 diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 2428147218bb..a5b5b4ab88d0 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 @@ -1201,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 @@ -1215,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): @@ -1224,20 +1230,19 @@ def pilread(fname): from PIL import Image except ImportError: return None - if cbook.is_string_like(fname): - # 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: 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 != '': + 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 +1265,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 != '': + fd = BytesIO(urlopen(fname).read()) return handler(fd) + else: + with open(fname, 'rb') as fd: + return handler(fd) else: return handler(fname) diff --git a/lib/matplotlib/tests/baseline_images/test_image/imread_url.png b/lib/matplotlib/tests/baseline_images/test_image/imread_url.png new file mode 100644 index 000000000000..dc2d80cefe31 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_image/imread_url.png differ diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 89338d33fdad..34f47f25ffc3 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -89,6 +89,15 @@ def test_image_python_io(): buffer.seek(0) plt.imread(buffer) +@image_comparison(baseline_images=['imread_url'], extensions=['png']) +def test_imread_url_string(): + try: + img = plt.imread('http://matplotlib.org/_static/logo2.png') + except URLError: + print('No network access. Test imread_url_string skipped.') + img = plt.imread(os.path.join(os.path.dirname(__file__), + 'baseline_images', 'test_image', 'imread_url.png')) + @knownfailureif(not HAS_PIL) def test_imread_pil_uint16(): img = plt.imread(os.path.join(os.path.dirname(__file__),