1# Copyright (c) IPython Development Team.
2# Distributed under the terms of the Modified BSD License.
3
4import json
5import os
6import sys
7import warnings
8
9import nose.tools as nt
10
11from IPython.core import display
12from IPython.core.getipython import get_ipython
13from IPython.utils.io import capture_output
14from IPython.utils.tempdir import NamedFileInTemporaryDirectory
15from IPython import paths as ipath
16from IPython.testing.tools import AssertPrints, AssertNotPrints
17
18import IPython.testing.decorators as dec
19
20if sys.version_info < (3,):
21    import mock
22else:
23    from unittest import mock
24
25def test_image_size():
26    """Simple test for display.Image(args, width=x,height=y)"""
27    thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
28    img = display.Image(url=thisurl, width=200, height=200)
29    nt.assert_equal(u'<img src="%s" width="200" height="200"/>' % (thisurl), img._repr_html_())
30    img = display.Image(url=thisurl, width=200)
31    nt.assert_equal(u'<img src="%s" width="200"/>' % (thisurl), img._repr_html_())
32    img = display.Image(url=thisurl)
33    nt.assert_equal(u'<img src="%s"/>' % (thisurl), img._repr_html_())
34    img = display.Image(url=thisurl, unconfined=True)
35    nt.assert_equal(u'<img src="%s" class="unconfined"/>' % (thisurl), img._repr_html_())
36
37def test_retina_png():
38    here = os.path.dirname(__file__)
39    img = display.Image(os.path.join(here, "2x2.png"), retina=True)
40    nt.assert_equal(img.height, 1)
41    nt.assert_equal(img.width, 1)
42    data, md = img._repr_png_()
43    nt.assert_equal(md['width'], 1)
44    nt.assert_equal(md['height'], 1)
45
46def test_retina_jpeg():
47    here = os.path.dirname(__file__)
48    img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
49    nt.assert_equal(img.height, 1)
50    nt.assert_equal(img.width, 1)
51    data, md = img._repr_jpeg_()
52    nt.assert_equal(md['width'], 1)
53    nt.assert_equal(md['height'], 1)
54
55def test_base64image():
56    display.Image("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AAAAACAAHiIbwzAAAAAElFTkSuQmCC")
57
58def test_image_filename_defaults():
59    '''test format constraint, and validity of jpeg and png'''
60    tpath = ipath.get_ipython_package_dir()
61    nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.gif'),
62                     embed=True)
63    nt.assert_raises(ValueError, display.Image)
64    nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True)
65    # check boths paths to allow packages to test at build and install time
66    imgfile = os.path.join(tpath, 'core/tests/2x2.png')
67    img = display.Image(filename=imgfile)
68    nt.assert_equal('png', img.format)
69    nt.assert_is_not_none(img._repr_png_())
70    img = display.Image(filename=os.path.join(tpath, 'testing/tests/logo.jpg'), embed=False)
71    nt.assert_equal('jpeg', img.format)
72    nt.assert_is_none(img._repr_jpeg_())
73
74def _get_inline_config():
75    from ipykernel.pylab.config import InlineBackend
76    return InlineBackend.instance()
77
78@dec.skip_without('matplotlib')
79def test_set_matplotlib_close():
80    cfg = _get_inline_config()
81    cfg.close_figures = False
82    display.set_matplotlib_close()
83    assert cfg.close_figures
84    display.set_matplotlib_close(False)
85    assert not cfg.close_figures
86
87_fmt_mime_map = {
88    'png': 'image/png',
89    'jpeg': 'image/jpeg',
90    'pdf': 'application/pdf',
91    'retina': 'image/png',
92    'svg': 'image/svg+xml',
93}
94
95@dec.skip_without('matplotlib')
96def test_set_matplotlib_formats():
97    from matplotlib.figure import Figure
98    formatters = get_ipython().display_formatter.formatters
99    for formats in [
100        ('png',),
101        ('pdf', 'svg'),
102        ('jpeg', 'retina', 'png'),
103        (),
104    ]:
105        active_mimes = {_fmt_mime_map[fmt] for fmt in formats}
106        display.set_matplotlib_formats(*formats)
107        for mime, f in formatters.items():
108            if mime in active_mimes:
109                nt.assert_in(Figure, f)
110            else:
111                nt.assert_not_in(Figure, f)
112
113@dec.skip_without('matplotlib')
114def test_set_matplotlib_formats_kwargs():
115    from matplotlib.figure import Figure
116    ip = get_ipython()
117    cfg = _get_inline_config()
118    cfg.print_figure_kwargs.update(dict(foo='bar'))
119    kwargs = dict(quality=10)
120    display.set_matplotlib_formats('png', **kwargs)
121    formatter = ip.display_formatter.formatters['image/png']
122    f = formatter.lookup_by_type(Figure)
123    cell = f.__closure__[0].cell_contents
124    expected = kwargs
125    expected.update(cfg.print_figure_kwargs)
126    nt.assert_equal(cell, expected)
127
128def test_display_available():
129    """
130    Test that display is available without import
131
132    We don't really care if it's in builtin or anything else, but it should
133    always be available.
134    """
135    ip = get_ipython()
136    with AssertNotPrints('NameError'):
137        ip.run_cell('display')
138    try:
139        ip.run_cell('del display')
140    except NameError:
141        pass # it's ok, it might be in builtins
142    # even if deleted it should be back
143    with AssertNotPrints('NameError'):
144        ip.run_cell('display')
145
146def test_textdisplayobj_pretty_repr():
147     p = display.Pretty("This is a simple test")
148     nt.assert_equal(repr(p), '<IPython.core.display.Pretty object>')
149     nt.assert_equal(p.data, 'This is a simple test')
150
151     p._show_mem_addr = True
152     nt.assert_equal(repr(p), object.__repr__(p))
153
154def test_displayobject_repr():
155    h = display.HTML('<br />')
156    nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
157    h._show_mem_addr = True
158    nt.assert_equal(repr(h), object.__repr__(h))
159    h._show_mem_addr = False
160    nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
161
162    j = display.Javascript('')
163    nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
164    j._show_mem_addr = True
165    nt.assert_equal(repr(j), object.__repr__(j))
166    j._show_mem_addr = False
167    nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
168
169def test_progress():
170    p = display.ProgressBar(10)
171    nt.assert_in('0/10',repr(p))
172    p.html_width = '100%'
173    p.progress = 5
174    nt.assert_equal(p._repr_html_(), "<progress style='width:100%' max='10' value='5'></progress>")
175
176def test_progress_iter():
177    with capture_output(display=False) as captured:
178        for i in display.ProgressBar(5):
179            out = captured.stdout
180            nt.assert_in('{0}/5'.format(i), out)
181    out = captured.stdout
182    nt.assert_in('5/5', out)
183
184def test_json():
185    d = {'a': 5}
186    lis = [d]
187    j = display.JSON(d)
188    nt.assert_equal(j._repr_json_(), d)
189
190    with warnings.catch_warnings(record=True) as w:
191        warnings.simplefilter("always")
192        j = display.JSON(json.dumps(d))
193        nt.assert_equal(len(w), 1)
194        nt.assert_equal(j._repr_json_(), d)
195
196    j = display.JSON(lis)
197    nt.assert_equal(j._repr_json_(), lis)
198
199    with warnings.catch_warnings(record=True) as w:
200        warnings.simplefilter("always")
201        j = display.JSON(json.dumps(lis))
202        nt.assert_equal(len(w), 1)
203        nt.assert_equal(j._repr_json_(), lis)
204
205def test_video_embedding():
206    """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash"""
207    v = display.Video("http://ignored")
208    assert not v.embed
209    html = v._repr_html_()
210    nt.assert_not_in('src="data:', html)
211    nt.assert_in('src="http://ignored"', html)
212
213    with nt.assert_raises(ValueError):
214        v = display.Video(b'abc')
215
216    with NamedFileInTemporaryDirectory('test.mp4') as f:
217        f.write(b'abc')
218        f.close()
219
220        v = display.Video(f.name)
221        assert not v.embed
222        html = v._repr_html_()
223        nt.assert_not_in('src="data:', html)
224
225        v = display.Video(f.name, embed=True)
226        html = v._repr_html_()
227        nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
228
229        v = display.Video(f.name, embed=True, mimetype='video/other')
230        html = v._repr_html_()
231        nt.assert_in('src="data:video/other;base64,YWJj"',html)
232
233        v = display.Video(b'abc', embed=True, mimetype='video/mp4')
234        html = v._repr_html_()
235        nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
236
237        v = display.Video(u'YWJj', embed=True, mimetype='video/xyz')
238        html = v._repr_html_()
239        nt.assert_in('src="data:video/xyz;base64,YWJj"',html)
240
241
242def test_display_id():
243    ip = get_ipython()
244    with mock.patch.object(ip.display_pub, 'publish') as pub:
245        handle = display.display('x')
246        nt.assert_is(handle, None)
247        handle = display.display('y', display_id='secret')
248        nt.assert_is_instance(handle, display.DisplayHandle)
249        handle2 = display.display('z', display_id=True)
250        nt.assert_is_instance(handle2, display.DisplayHandle)
251    nt.assert_not_equal(handle.display_id, handle2.display_id)
252
253    nt.assert_equal(pub.call_count, 3)
254    args, kwargs = pub.call_args_list[0]
255    nt.assert_equal(args, ())
256    nt.assert_equal(kwargs, {
257        'data': {
258            'text/plain': repr('x')
259        },
260        'metadata': {},
261    })
262    args, kwargs = pub.call_args_list[1]
263    nt.assert_equal(args, ())
264    nt.assert_equal(kwargs, {
265        'data': {
266            'text/plain': repr('y')
267        },
268        'metadata': {},
269        'transient': {
270            'display_id': handle.display_id,
271        },
272    })
273    args, kwargs = pub.call_args_list[2]
274    nt.assert_equal(args, ())
275    nt.assert_equal(kwargs, {
276        'data': {
277            'text/plain': repr('z')
278        },
279        'metadata': {},
280        'transient': {
281            'display_id': handle2.display_id,
282        },
283    })
284
285
286def test_update_display():
287    ip = get_ipython()
288    with mock.patch.object(ip.display_pub, 'publish') as pub:
289        with nt.assert_raises(TypeError):
290            display.update_display('x')
291        display.update_display('x', display_id='1')
292        display.update_display('y', display_id='2')
293    args, kwargs = pub.call_args_list[0]
294    nt.assert_equal(args, ())
295    nt.assert_equal(kwargs, {
296        'data': {
297            'text/plain': repr('x')
298        },
299        'metadata': {},
300        'transient': {
301            'display_id': '1',
302        },
303        'update': True,
304    })
305    args, kwargs = pub.call_args_list[1]
306    nt.assert_equal(args, ())
307    nt.assert_equal(kwargs, {
308        'data': {
309            'text/plain': repr('y')
310        },
311        'metadata': {},
312        'transient': {
313            'display_id': '2',
314        },
315        'update': True,
316    })
317
318
319def test_display_handle():
320    ip = get_ipython()
321    handle = display.DisplayHandle()
322    if sys.version_info < (3,):
323        nt.assert_is_instance(handle.display_id, unicode)
324    else:
325        nt.assert_is_instance(handle.display_id, str)
326    handle = display.DisplayHandle('my-id')
327    nt.assert_equal(handle.display_id, 'my-id')
328    with mock.patch.object(ip.display_pub, 'publish') as pub:
329        handle.display('x')
330        handle.update('y')
331
332    args, kwargs = pub.call_args_list[0]
333    nt.assert_equal(args, ())
334    nt.assert_equal(kwargs, {
335        'data': {
336            'text/plain': repr('x')
337        },
338        'metadata': {},
339        'transient': {
340            'display_id': handle.display_id,
341        }
342    })
343    args, kwargs = pub.call_args_list[1]
344    nt.assert_equal(args, ())
345    nt.assert_equal(kwargs, {
346        'data': {
347            'text/plain': repr('y')
348        },
349        'metadata': {},
350        'transient': {
351            'display_id': handle.display_id,
352        },
353        'update': True,
354    })
355