1 # Copyright (c) IPython Development Team. 2 # Distributed under the terms of the Modified BSD License. 3 4 import json 5 import os 6 import warnings 7 8 from unittest import mock 9 10 import pytest 11 12 from IPython import display 13 from IPython.core.getipython import get_ipython 14 from IPython.utils.io import capture_output 15 from IPython.utils.tempdir import NamedFileInTemporaryDirectory 16 from IPython import paths as ipath 17 from IPython.testing.tools import AssertNotPrints 18 19 import IPython.testing.decorators as dec 20 21 def test_image_size(): 22 """Simple test for display.Image(args, width=x,height=y)""" 23 thisurl = 'http://www.google.fr/images/srpr/logo3w.png' 24 img = display.Image(url=thisurl, width=200, height=200) 25 assert '<img src="%s" width="200" height="200"/>' % (thisurl) == img._repr_html_() 26 img = display.Image(url=thisurl, metadata={'width':200, 'height':200}) 27 assert '<img src="%s" width="200" height="200"/>' % (thisurl) == img._repr_html_() 28 img = display.Image(url=thisurl, width=200) 29 assert '<img src="%s" width="200"/>' % (thisurl) == img._repr_html_() 30 img = display.Image(url=thisurl) 31 assert '<img src="%s"/>' % (thisurl) == img._repr_html_() 32 img = display.Image(url=thisurl, unconfined=True) 33 assert '<img src="%s" class="unconfined"/>' % (thisurl) == img._repr_html_() 34 35 36 def test_image_mimes(): 37 fmt = get_ipython().display_formatter.format 38 for format in display.Image._ACCEPTABLE_EMBEDDINGS: 39 mime = display.Image._MIMETYPES[format] 40 img = display.Image(b'garbage', format=format) 41 data, metadata = fmt(img) 42 assert sorted(data) == sorted([mime, "text/plain"]) 43 44 45 def test_geojson(): 46 47 gj = display.GeoJSON(data={ 48 "type": "Feature", 49 "geometry": { 50 "type": "Point", 51 "coordinates": [-81.327, 296.038] 52 }, 53 "properties": { 54 "name": "Inca City" 55 } 56 }, 57 url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png", 58 layer_options={ 59 "basemap_id": "celestia_mars-shaded-16k_global", 60 "attribution": "Celestia/praesepe", 61 "minZoom": 0, 62 "maxZoom": 18, 63 }, 64 ) 65 assert "<IPython.core.display.GeoJSON object>" == str(gj) 66 67 68 def test_retina_png(): 69 here = os.path.dirname(__file__) 70 img = display.Image(os.path.join(here, "2x2.png"), retina=True) 71 assert img.height == 1 72 assert img.width == 1 73 data, md = img._repr_png_() 74 assert md["width"] == 1 75 assert md["height"] == 1 76 77 78 def test_embed_svg_url(): 79 import gzip 80 from io import BytesIO 81 svg_data = b'<svg><circle x="0" y="0" r="1"/></svg>' 82 url = 'http://test.com/circle.svg' 83 84 gzip_svg = BytesIO() 85 with gzip.open(gzip_svg, 'wb') as fp: 86 fp.write(svg_data) 87 gzip_svg = gzip_svg.getvalue() 88 89 def mocked_urlopen(*args, **kwargs): 90 class MockResponse: 91 def __init__(self, svg): 92 self._svg_data = svg 93 self.headers = {'content-type': 'image/svg+xml'} 94 95 def read(self): 96 return self._svg_data 97 98 if args[0] == url: 99 return MockResponse(svg_data) 100 elif args[0] == url + "z": 101 ret = MockResponse(gzip_svg) 102 ret.headers["content-encoding"] = "gzip" 103 return ret 104 return MockResponse(None) 105 106 with mock.patch('urllib.request.urlopen', side_effect=mocked_urlopen): 107 svg = display.SVG(url=url) 108 assert svg._repr_svg_().startswith("<svg") is True 109 svg = display.SVG(url=url + "z") 110 assert svg._repr_svg_().startswith("<svg") is True 111 112 113 def test_retina_jpeg(): 114 here = os.path.dirname(__file__) 115 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True) 116 assert img.height == 1 117 assert img.width == 1 118 data, md = img._repr_jpeg_() 119 assert md["width"] == 1 120 assert md["height"] == 1 121 122 123 def test_base64image(): 124 display.Image("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AAAAACAAHiIbwzAAAAAElFTkSuQmCC") 125 126 def test_image_filename_defaults(): 127 '''test format constraint, and validity of jpeg and png''' 128 tpath = ipath.get_ipython_package_dir() 129 pytest.raises( 130 ValueError, 131 display.Image, 132 filename=os.path.join(tpath, "testing/tests/badformat.zip"), 133 embed=True, 134 ) 135 pytest.raises(ValueError, display.Image) 136 pytest.raises( 137 ValueError, 138 display.Image, 139 data="this is not an image", 140 format="badformat", 141 embed=True, 142 ) 143 # check both paths to allow packages to test at build and install time 144 imgfile = os.path.join(tpath, 'core/tests/2x2.png') 145 img = display.Image(filename=imgfile) 146 assert "png" == img.format 147 assert img._repr_png_() is not None 148 img = display.Image( 149 filename=os.path.join(tpath, "testing/tests/logo.jpg"), embed=False 150 ) 151 assert "jpeg" == img.format 152 assert img._repr_jpeg_() is None 153 154 def _get_inline_config(): 155 from matplotlib_inline.config import InlineBackend 156 return InlineBackend.instance() 157 158 159 @dec.skip_without("matplotlib") 160 def test_set_matplotlib_close(): 161 cfg = _get_inline_config() 162 cfg.close_figures = False 163 with pytest.deprecated_call(): 164 display.set_matplotlib_close() 165 assert cfg.close_figures 166 with pytest.deprecated_call(): 167 display.set_matplotlib_close(False) 168 assert not cfg.close_figures 169 170 _fmt_mime_map = { 171 'png': 'image/png', 172 'jpeg': 'image/jpeg', 173 'pdf': 'application/pdf', 174 'retina': 'image/png', 175 'svg': 'image/svg+xml', 176 } 177 178 @dec.skip_without('matplotlib') 179 def test_set_matplotlib_formats(): 180 from matplotlib.figure import Figure 181 formatters = get_ipython().display_formatter.formatters 182 for formats in [ 183 ('png',), 184 ('pdf', 'svg'), 185 ('jpeg', 'retina', 'png'), 186 (), 187 ]: 188 active_mimes = {_fmt_mime_map[fmt] for fmt in formats} 189 with pytest.deprecated_call(): 190 display.set_matplotlib_formats(*formats) 191 for mime, f in formatters.items(): 192 if mime in active_mimes: 193 assert Figure in f 194 else: 195 assert Figure not in f 196 197 198 @dec.skip_without("matplotlib") 199 def test_set_matplotlib_formats_kwargs(): 200 from matplotlib.figure import Figure 201 ip = get_ipython() 202 cfg = _get_inline_config() 203 cfg.print_figure_kwargs.update(dict(foo='bar')) 204 kwargs = dict(dpi=150) 205 with pytest.deprecated_call(): 206 display.set_matplotlib_formats("png", **kwargs) 207 formatter = ip.display_formatter.formatters["image/png"] 208 f = formatter.lookup_by_type(Figure) 209 formatter_kwargs = f.keywords 210 expected = kwargs 211 expected["base64"] = True 212 expected["fmt"] = "png" 213 expected.update(cfg.print_figure_kwargs) 214 assert formatter_kwargs == expected 215 216 def test_display_available(): 217 """ 218 Test that display is available without import 219 220 We don't really care if it's in builtin or anything else, but it should 221 always be available. 222 """ 223 ip = get_ipython() 224 with AssertNotPrints('NameError'): 225 ip.run_cell('display') 226 try: 227 ip.run_cell('del display') 228 except NameError: 229 pass # it's ok, it might be in builtins 230 # even if deleted it should be back 231 with AssertNotPrints('NameError'): 232 ip.run_cell('display') 233 234 def test_textdisplayobj_pretty_repr(): 235 p = display.Pretty("This is a simple test") 236 assert repr(p) == "<IPython.core.display.Pretty object>" 237 assert p.data == "This is a simple test" 238 239 p._show_mem_addr = True 240 assert repr(p) == object.__repr__(p) 241 242 243 def test_displayobject_repr(): 244 h = display.HTML("<br />") 245 assert repr(h) == "<IPython.core.display.HTML object>" 246 h._show_mem_addr = True 247 assert repr(h) == object.__repr__(h) 248 h._show_mem_addr = False 249 assert repr(h) == "<IPython.core.display.HTML object>" 250 251 j = display.Javascript("") 252 assert repr(j) == "<IPython.core.display.Javascript object>" 253 j._show_mem_addr = True 254 assert repr(j) == object.__repr__(j) 255 j._show_mem_addr = False 256 assert repr(j) == "<IPython.core.display.Javascript object>" 257 258 @mock.patch('warnings.warn') 259 def test_encourage_iframe_over_html(m_warn): 260 display.HTML() 261 m_warn.assert_not_called() 262 263 display.HTML('<br />') 264 m_warn.assert_not_called() 265 266 display.HTML('<html><p>Lots of content here</p><iframe src="http://a.com"></iframe>') 267 m_warn.assert_not_called() 268 269 display.HTML('<iframe src="http://a.com"></iframe>') 270 m_warn.assert_called_with('Consider using IPython.display.IFrame instead') 271 272 m_warn.reset_mock() 273 display.HTML('<IFRAME SRC="http://a.com"></IFRAME>') 274 m_warn.assert_called_with('Consider using IPython.display.IFrame instead') 275 276 def test_progress(): 277 p = display.ProgressBar(10) 278 assert "0/10" in repr(p) 279 p.html_width = "100%" 280 p.progress = 5 281 assert ( 282 p._repr_html_() == "<progress style='width:100%' max='10' value='5'></progress>" 283 ) 284 285 286 def test_progress_iter(): 287 with capture_output(display=False) as captured: 288 for i in display.ProgressBar(5): 289 out = captured.stdout 290 assert "{0}/5".format(i) in out 291 out = captured.stdout 292 assert "5/5" in out 293 294 295 def test_json(): 296 d = {'a': 5} 297 lis = [d] 298 metadata = [ 299 {'expanded': False, 'root': 'root'}, 300 {'expanded': True, 'root': 'root'}, 301 {'expanded': False, 'root': 'custom'}, 302 {'expanded': True, 'root': 'custom'}, 303 ] 304 json_objs = [ 305 display.JSON(d), 306 display.JSON(d, expanded=True), 307 display.JSON(d, root='custom'), 308 display.JSON(d, expanded=True, root='custom'), 309 ] 310 for j, md in zip(json_objs, metadata): 311 assert j._repr_json_() == (d, md) 312 313 with warnings.catch_warnings(record=True) as w: 314 warnings.simplefilter("always") 315 j = display.JSON(json.dumps(d)) 316 assert len(w) == 1 317 assert j._repr_json_() == (d, metadata[0]) 318 319 json_objs = [ 320 display.JSON(lis), 321 display.JSON(lis, expanded=True), 322 display.JSON(lis, root='custom'), 323 display.JSON(lis, expanded=True, root='custom'), 324 ] 325 for j, md in zip(json_objs, metadata): 326 assert j._repr_json_() == (lis, md) 327 328 with warnings.catch_warnings(record=True) as w: 329 warnings.simplefilter("always") 330 j = display.JSON(json.dumps(lis)) 331 assert len(w) == 1 332 assert j._repr_json_() == (lis, metadata[0]) 333 334 335 def test_video_embedding(): 336 """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash""" 337 v = display.Video("http://ignored") 338 assert not v.embed 339 html = v._repr_html_() 340 assert 'src="data:' not in html 341 assert 'src="http://ignored"' in html 342 343 with pytest.raises(ValueError): 344 v = display.Video(b'abc') 345 346 with NamedFileInTemporaryDirectory('test.mp4') as f: 347 f.write(b'abc') 348 f.close() 349 350 v = display.Video(f.name) 351 assert not v.embed 352 html = v._repr_html_() 353 assert 'src="data:' not in html 354 355 v = display.Video(f.name, embed=True) 356 html = v._repr_html_() 357 assert 'src="data:video/mp4;base64,YWJj"' in html 358 359 v = display.Video(f.name, embed=True, mimetype='video/other') 360 html = v._repr_html_() 361 assert 'src="data:video/other;base64,YWJj"' in html 362 363 v = display.Video(b'abc', embed=True, mimetype='video/mp4') 364 html = v._repr_html_() 365 assert 'src="data:video/mp4;base64,YWJj"' in html 366 367 v = display.Video(u'YWJj', embed=True, mimetype='video/xyz') 368 html = v._repr_html_() 369 assert 'src="data:video/xyz;base64,YWJj"' in html 370 371 def test_html_metadata(): 372 s = "<h1>Test</h1>" 373 h = display.HTML(s, metadata={"isolated": True}) 374 assert h._repr_html_() == (s, {"isolated": True}) 375 376 377 def test_display_id(): 378 ip = get_ipython() 379 with mock.patch.object(ip.display_pub, 'publish') as pub: 380 handle = display.display('x') 381 assert handle is None 382 handle = display.display('y', display_id='secret') 383 assert isinstance(handle, display.DisplayHandle) 384 handle2 = display.display('z', display_id=True) 385 assert isinstance(handle2, display.DisplayHandle) 386 assert handle.display_id != handle2.display_id 387 388 assert pub.call_count == 3 389 args, kwargs = pub.call_args_list[0] 390 assert args == () 391 assert kwargs == { 392 'data': { 393 'text/plain': repr('x') 394 }, 395 'metadata': {}, 396 } 397 args, kwargs = pub.call_args_list[1] 398 assert args == () 399 assert kwargs == { 400 'data': { 401 'text/plain': repr('y') 402 }, 403 'metadata': {}, 404 'transient': { 405 'display_id': handle.display_id, 406 }, 407 } 408 args, kwargs = pub.call_args_list[2] 409 assert args == () 410 assert kwargs == { 411 'data': { 412 'text/plain': repr('z') 413 }, 414 'metadata': {}, 415 'transient': { 416 'display_id': handle2.display_id, 417 }, 418 } 419 420 421 def test_update_display(): 422 ip = get_ipython() 423 with mock.patch.object(ip.display_pub, 'publish') as pub: 424 with pytest.raises(TypeError): 425 display.update_display('x') 426 display.update_display('x', display_id='1') 427 display.update_display('y', display_id='2') 428 args, kwargs = pub.call_args_list[0] 429 assert args == () 430 assert kwargs == { 431 'data': { 432 'text/plain': repr('x') 433 }, 434 'metadata': {}, 435 'transient': { 436 'display_id': '1', 437 }, 438 'update': True, 439 } 440 args, kwargs = pub.call_args_list[1] 441 assert args == () 442 assert kwargs == { 443 'data': { 444 'text/plain': repr('y') 445 }, 446 'metadata': {}, 447 'transient': { 448 'display_id': '2', 449 }, 450 'update': True, 451 } 452 453 454 def test_display_handle(): 455 ip = get_ipython() 456 handle = display.DisplayHandle() 457 assert isinstance(handle.display_id, str) 458 handle = display.DisplayHandle("my-id") 459 assert handle.display_id == "my-id" 460 with mock.patch.object(ip.display_pub, "publish") as pub: 461 handle.display("x") 462 handle.update("y") 463 464 args, kwargs = pub.call_args_list[0] 465 assert args == () 466 assert kwargs == { 467 'data': { 468 'text/plain': repr('x') 469 }, 470 'metadata': {}, 471 'transient': { 472 'display_id': handle.display_id, 473 } 474 } 475 args, kwargs = pub.call_args_list[1] 476 assert args == () 477 assert kwargs == { 478 'data': { 479 'text/plain': repr('y') 480 }, 481 'metadata': {}, 482 'transient': { 483 'display_id': handle.display_id, 484 }, 485 'update': True, 486 } 487 488 489 def test_image_alt_tag(): 490 """Simple test for display.Image(args, alt=x,)""" 491 thisurl = "http://example.com/image.png" 492 img = display.Image(url=thisurl, alt="an image") 493 assert '<img src="%s" alt="an image"/>' % (thisurl) == img._repr_html_() 494 img = display.Image(url=thisurl, unconfined=True, alt="an image") 495 assert ( 496 '<img src="%s" class="unconfined" alt="an image"/>' % (thisurl) 497 == img._repr_html_() 498 ) 499 img = display.Image(url=thisurl, alt='>"& <') 500 assert '<img src="%s" alt=">"& <"/>' % (thisurl) == img._repr_html_() 501 502 img = display.Image(url=thisurl, metadata={"alt": "an image"}) 503 assert img.alt == "an image" 504 here = os.path.dirname(__file__) 505 img = display.Image(os.path.join(here, "2x2.png"), alt="an image") 506 assert img.alt == "an image" 507 _, md = img._repr_png_() 508 assert md["alt"] == "an image" 509 510 511 def test_image_bad_filename_raises_proper_exception(): 512 with pytest.raises(FileNotFoundError): 513 display.Image("/this/file/does/not/exist/")._repr_png_() 514