##// END OF EJS Templates
Add formal support for alt text to IPython.display.Image....
Pete Blois -
Show More
@@ -6,6 +6,7 b''
6
6
7
7
8 from binascii import b2a_base64, hexlify
8 from binascii import b2a_base64, hexlify
9 import html
9 import json
10 import json
10 import mimetypes
11 import mimetypes
11 import os
12 import os
@@ -371,7 +372,7 b' class DisplayObject(object):'
371 with gzip.open(BytesIO(data), 'rt', encoding=encoding) as fp:
372 with gzip.open(BytesIO(data), 'rt', encoding=encoding) as fp:
372 encoding = None
373 encoding = None
373 data = fp.read()
374 data = fp.read()
374
375
375 # decode data, if an encoding was specified
376 # decode data, if an encoding was specified
376 # We only touch self.data once since
377 # We only touch self.data once since
377 # subclasses such as SVG have @data.setter methods
378 # subclasses such as SVG have @data.setter methods
@@ -802,7 +803,7 b' class Image(DisplayObject):'
802
803
803 def __init__(self, data=None, url=None, filename=None, format=None,
804 def __init__(self, data=None, url=None, filename=None, format=None,
804 embed=None, width=None, height=None, retina=False,
805 embed=None, width=None, height=None, retina=False,
805 unconfined=False, metadata=None):
806 unconfined=False, metadata=None, alt=None):
806 """Create a PNG/JPEG/GIF image object given raw data.
807 """Create a PNG/JPEG/GIF image object given raw data.
807
808
808 When this object is returned by an input cell or passed to the
809 When this object is returned by an input cell or passed to the
@@ -847,6 +848,8 b' class Image(DisplayObject):'
847 Set unconfined=True to disable max-width confinement of the image.
848 Set unconfined=True to disable max-width confinement of the image.
848 metadata : dict
849 metadata : dict
849 Specify extra metadata to attach to the image.
850 Specify extra metadata to attach to the image.
851 alt : unicode
852 Alternative text for the image, for use by screen readers.
850
853
851 Examples
854 Examples
852 --------
855 --------
@@ -924,6 +927,7 b' class Image(DisplayObject):'
924 self.height = height
927 self.height = height
925 self.retina = retina
928 self.retina = retina
926 self.unconfined = unconfined
929 self.unconfined = unconfined
930 self.alt = alt
927 super(Image, self).__init__(data=data, url=url, filename=filename,
931 super(Image, self).__init__(data=data, url=url, filename=filename,
928 metadata=metadata)
932 metadata=metadata)
929
933
@@ -933,6 +937,9 b' class Image(DisplayObject):'
933 if self.height is None and self.metadata.get('height', {}):
937 if self.height is None and self.metadata.get('height', {}):
934 self.height = metadata['height']
938 self.height = metadata['height']
935
939
940 if self.alt is None and self.metadata.get('alt', {}):
941 self.alt = metadata['alt']
942
936 if retina:
943 if retina:
937 self._retina_shape()
944 self._retina_shape()
938
945
@@ -962,18 +969,21 b' class Image(DisplayObject):'
962
969
963 def _repr_html_(self):
970 def _repr_html_(self):
964 if not self.embed:
971 if not self.embed:
965 width = height = klass = ''
972 width = height = klass = alt = ''
966 if self.width:
973 if self.width:
967 width = ' width="%d"' % self.width
974 width = ' width="%d"' % self.width
968 if self.height:
975 if self.height:
969 height = ' height="%d"' % self.height
976 height = ' height="%d"' % self.height
970 if self.unconfined:
977 if self.unconfined:
971 klass = ' class="unconfined"'
978 klass = ' class="unconfined"'
972 return u'<img src="{url}"{width}{height}{klass}/>'.format(
979 if self.alt:
980 alt = ' alt="%s"' % html.escape(self.alt)
981 return u'<img src="{url}"{width}{height}{klass}{alt}/>'.format(
973 url=self.url,
982 url=self.url,
974 width=width,
983 width=width,
975 height=height,
984 height=height,
976 klass=klass,
985 klass=klass,
986 alt=alt,
977 )
987 )
978
988
979 def _repr_mimebundle_(self, include=None, exclude=None):
989 def _repr_mimebundle_(self, include=None, exclude=None):
@@ -1006,6 +1016,8 b' class Image(DisplayObject):'
1006 md['height'] = self.height
1016 md['height'] = self.height
1007 if self.unconfined:
1017 if self.unconfined:
1008 md['unconfined'] = self.unconfined
1018 md['unconfined'] = self.unconfined
1019 if self.alt:
1020 md['alt'] = self.alt
1009 if md or always_both:
1021 if md or always_both:
1010 return b64_data, md
1022 return b64_data, md
1011 else:
1023 else:
@@ -77,12 +77,12 b' def test_embed_svg_url():'
77 from io import BytesIO
77 from io import BytesIO
78 svg_data = b'<svg><circle x="0" y="0" r="1"/></svg>'
78 svg_data = b'<svg><circle x="0" y="0" r="1"/></svg>'
79 url = 'http://test.com/circle.svg'
79 url = 'http://test.com/circle.svg'
80
80
81 gzip_svg = BytesIO()
81 gzip_svg = BytesIO()
82 with gzip.open(gzip_svg, 'wb') as fp:
82 with gzip.open(gzip_svg, 'wb') as fp:
83 fp.write(svg_data)
83 fp.write(svg_data)
84 gzip_svg = gzip_svg.getvalue()
84 gzip_svg = gzip_svg.getvalue()
85
85
86 def mocked_urlopen(*args, **kwargs):
86 def mocked_urlopen(*args, **kwargs):
87 class MockResponse:
87 class MockResponse:
88 def __init__(self, svg):
88 def __init__(self, svg):
@@ -94,7 +94,7 b' def test_embed_svg_url():'
94
94
95 if args[0] == url:
95 if args[0] == url:
96 return MockResponse(svg_data)
96 return MockResponse(svg_data)
97 elif args[0] == url + 'z':
97 elif args[0] == url + 'z':
98 ret= MockResponse(gzip_svg)
98 ret= MockResponse(gzip_svg)
99 ret.headers['content-encoding']= 'gzip'
99 ret.headers['content-encoding']= 'gzip'
100 return ret
100 return ret
@@ -105,7 +105,7 b' def test_embed_svg_url():'
105 nt.assert_true(svg._repr_svg_().startswith('<svg'))
105 nt.assert_true(svg._repr_svg_().startswith('<svg'))
106 svg = display.SVG(url=url + 'z')
106 svg = display.SVG(url=url + 'z')
107 nt.assert_true(svg._repr_svg_().startswith('<svg'))
107 nt.assert_true(svg._repr_svg_().startswith('<svg'))
108
108
109 def test_retina_jpeg():
109 def test_retina_jpeg():
110 here = os.path.dirname(__file__)
110 here = os.path.dirname(__file__)
111 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
111 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
@@ -457,3 +457,21 b' def test_display_handle():'
457 'update': True,
457 'update': True,
458 })
458 })
459
459
460 def test_image_alt_tag():
461 """Simple test for display.Image(args, alt=x,)"""
462 thisurl = 'http://example.com/image.png'
463 img = display.Image(url=thisurl, alt='an image')
464 nt.assert_equal(u'<img src="%s" alt="an image"/>' % (thisurl), img._repr_html_())
465 img = display.Image(url=thisurl, unconfined=True, alt='an image')
466 nt.assert_equal(u'<img src="%s" class="unconfined" alt="an image"/>' % (thisurl), img._repr_html_())
467 img = display.Image(url=thisurl, alt='>"& <')
468 nt.assert_equal(u'<img src="%s" alt="&gt;&quot;&amp; &lt;"/>' % (thisurl), img._repr_html_())
469
470 img = display.Image(url=thisurl, metadata={'alt':'an image'})
471 nt.assert_equal(img.alt, 'an image')
472
473 here = os.path.dirname(__file__)
474 img = display.Image(os.path.join(here, "2x2.png"), alt='an image')
475 nt.assert_equal(img.alt, 'an image')
476 _, md = img._repr_png_()
477 nt.assert_equal(md['alt'], 'an image')
General Comments 0
You need to be logged in to leave comments. Login now