##// END OF EJS Templates
back out metadata copying for every mime-type
Paul Ivanov -
Show More
@@ -1,685 +1,679 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Top-level display functions for displaying object in different formats.
2 """Top-level display functions for displaying object in different formats.
3
3
4 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2013 The IPython Development Team
10 # Copyright (C) 2013 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from __future__ import print_function
20 from __future__ import print_function
21
21
22 import os
22 import os
23 import struct
23 import struct
24
24
25 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
25 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
26 unicode_type)
26 unicode_type)
27
27
28 from .displaypub import publish_display_data
28 from .displaypub import publish_display_data
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # utility functions
31 # utility functions
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33
33
34 def _safe_exists(path):
34 def _safe_exists(path):
35 """Check path, but don't let exceptions raise"""
35 """Check path, but don't let exceptions raise"""
36 try:
36 try:
37 return os.path.exists(path)
37 return os.path.exists(path)
38 except Exception:
38 except Exception:
39 return False
39 return False
40
40
41 def _merge(d1, d2):
41 def _merge(d1, d2):
42 """Like update, but merges sub-dicts instead of clobbering at the top level.
42 """Like update, but merges sub-dicts instead of clobbering at the top level.
43
43
44 Updates d1 in-place
44 Updates d1 in-place
45 """
45 """
46
46
47 if not isinstance(d2, dict) or not isinstance(d1, dict):
47 if not isinstance(d2, dict) or not isinstance(d1, dict):
48 return d2
48 return d2
49 for key, value in d2.items():
49 for key, value in d2.items():
50 d1[key] = _merge(d1.get(key), value)
50 d1[key] = _merge(d1.get(key), value)
51 return d1
51 return d1
52
52
53 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
53 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
54 """internal implementation of all display_foo methods
54 """internal implementation of all display_foo methods
55
55
56 Parameters
56 Parameters
57 ----------
57 ----------
58 mimetype : str
58 mimetype : str
59 The mimetype to be published (e.g. 'image/png')
59 The mimetype to be published (e.g. 'image/png')
60 objs : tuple of objects
60 objs : tuple of objects
61 The Python objects to display, or if raw=True raw text data to
61 The Python objects to display, or if raw=True raw text data to
62 display.
62 display.
63 raw : bool
63 raw : bool
64 Are the data objects raw data or Python objects that need to be
64 Are the data objects raw data or Python objects that need to be
65 formatted before display? [default: False]
65 formatted before display? [default: False]
66 metadata : dict (optional)
66 metadata : dict (optional)
67 Metadata to be associated with the specific mimetype output.
67 Metadata to be associated with the specific mimetype output.
68 """
68 """
69 if metadata:
69 if metadata:
70 metadata = {mimetype: metadata}
70 metadata = {mimetype: metadata}
71 if raw:
71 if raw:
72 # turn list of pngdata into list of { 'image/png': pngdata }
72 # turn list of pngdata into list of { 'image/png': pngdata }
73 objs = [ {mimetype: obj} for obj in objs ]
73 objs = [ {mimetype: obj} for obj in objs ]
74 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
74 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
75
75
76 #-----------------------------------------------------------------------------
76 #-----------------------------------------------------------------------------
77 # Main functions
77 # Main functions
78 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
79
79
80 def display(*objs, **kwargs):
80 def display(*objs, **kwargs):
81 """Display a Python object in all frontends.
81 """Display a Python object in all frontends.
82
82
83 By default all representations will be computed and sent to the frontends.
83 By default all representations will be computed and sent to the frontends.
84 Frontends can decide which representation is used and how.
84 Frontends can decide which representation is used and how.
85
85
86 Parameters
86 Parameters
87 ----------
87 ----------
88 objs : tuple of objects
88 objs : tuple of objects
89 The Python objects to display.
89 The Python objects to display.
90 raw : bool, optional
90 raw : bool, optional
91 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
91 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
92 or Python objects that need to be formatted before display? [default: False]
92 or Python objects that need to be formatted before display? [default: False]
93 include : list or tuple, optional
93 include : list or tuple, optional
94 A list of format type strings (MIME types) to include in the
94 A list of format type strings (MIME types) to include in the
95 format data dict. If this is set *only* the format types included
95 format data dict. If this is set *only* the format types included
96 in this list will be computed.
96 in this list will be computed.
97 exclude : list or tuple, optional
97 exclude : list or tuple, optional
98 A list of format type strings (MIME types) to exclude in the format
98 A list of format type strings (MIME types) to exclude in the format
99 data dict. If this is set all format types will be computed,
99 data dict. If this is set all format types will be computed,
100 except for those included in this argument.
100 except for those included in this argument.
101 metadata : dict, optional
101 metadata : dict, optional
102 A dictionary of metadata to associate with the output.
102 A dictionary of metadata to associate with the output.
103 mime-type keys in this dictionary will be associated with the individual
103 mime-type keys in this dictionary will be associated with the individual
104 representation formats, if they exist.
104 representation formats, if they exist.
105 """
105 """
106 raw = kwargs.get('raw', False)
106 raw = kwargs.get('raw', False)
107 include = kwargs.get('include')
107 include = kwargs.get('include')
108 exclude = kwargs.get('exclude')
108 exclude = kwargs.get('exclude')
109 metadata = kwargs.get('metadata')
109 metadata = kwargs.get('metadata')
110
110
111 from IPython.core.interactiveshell import InteractiveShell
111 from IPython.core.interactiveshell import InteractiveShell
112
112
113 if raw:
113 if raw:
114 for obj in objs:
114 for obj in objs:
115 publish_display_data('display', obj, metadata)
115 publish_display_data('display', obj, metadata)
116 else:
116 else:
117 format = InteractiveShell.instance().display_formatter.format
117 format = InteractiveShell.instance().display_formatter.format
118 for obj in objs:
118 for obj in objs:
119 format_dict, md_dict = format(obj, include=include, exclude=exclude)
119 format_dict, md_dict = format(obj, include=include, exclude=exclude)
120 if metadata:
120 if metadata:
121 # publish_display_data exepects metadata dict that is keyed by
122 # mimetype, so this ensures we recreate such a dict for it, if
123 # one is not already passed to us
124 if set(metadata.keys()) != set(format_dict.keys()):
125 _md = metadata.copy()
126 metadata = {mt: _md for mt in format_dict}
127 # kwarg-specified metadata gets precedence
121 # kwarg-specified metadata gets precedence
128 _merge(md_dict, metadata)
122 _merge(md_dict, metadata)
129 publish_display_data('display', format_dict, md_dict)
123 publish_display_data('display', format_dict, md_dict)
130
124
131
125
132 def display_pretty(*objs, **kwargs):
126 def display_pretty(*objs, **kwargs):
133 """Display the pretty (default) representation of an object.
127 """Display the pretty (default) representation of an object.
134
128
135 Parameters
129 Parameters
136 ----------
130 ----------
137 objs : tuple of objects
131 objs : tuple of objects
138 The Python objects to display, or if raw=True raw text data to
132 The Python objects to display, or if raw=True raw text data to
139 display.
133 display.
140 raw : bool
134 raw : bool
141 Are the data objects raw data or Python objects that need to be
135 Are the data objects raw data or Python objects that need to be
142 formatted before display? [default: False]
136 formatted before display? [default: False]
143 metadata : dict (optional)
137 metadata : dict (optional)
144 Metadata to be associated with the specific mimetype output.
138 Metadata to be associated with the specific mimetype output.
145 """
139 """
146 _display_mimetype('text/plain', objs, **kwargs)
140 _display_mimetype('text/plain', objs, **kwargs)
147
141
148
142
149 def display_html(*objs, **kwargs):
143 def display_html(*objs, **kwargs):
150 """Display the HTML representation of an object.
144 """Display the HTML representation of an object.
151
145
152 Parameters
146 Parameters
153 ----------
147 ----------
154 objs : tuple of objects
148 objs : tuple of objects
155 The Python objects to display, or if raw=True raw HTML data to
149 The Python objects to display, or if raw=True raw HTML data to
156 display.
150 display.
157 raw : bool
151 raw : bool
158 Are the data objects raw data or Python objects that need to be
152 Are the data objects raw data or Python objects that need to be
159 formatted before display? [default: False]
153 formatted before display? [default: False]
160 metadata : dict (optional)
154 metadata : dict (optional)
161 Metadata to be associated with the specific mimetype output.
155 Metadata to be associated with the specific mimetype output.
162 """
156 """
163 _display_mimetype('text/html', objs, **kwargs)
157 _display_mimetype('text/html', objs, **kwargs)
164
158
165
159
166 def display_svg(*objs, **kwargs):
160 def display_svg(*objs, **kwargs):
167 """Display the SVG representation of an object.
161 """Display the SVG representation of an object.
168
162
169 Parameters
163 Parameters
170 ----------
164 ----------
171 objs : tuple of objects
165 objs : tuple of objects
172 The Python objects to display, or if raw=True raw svg data to
166 The Python objects to display, or if raw=True raw svg data to
173 display.
167 display.
174 raw : bool
168 raw : bool
175 Are the data objects raw data or Python objects that need to be
169 Are the data objects raw data or Python objects that need to be
176 formatted before display? [default: False]
170 formatted before display? [default: False]
177 metadata : dict (optional)
171 metadata : dict (optional)
178 Metadata to be associated with the specific mimetype output.
172 Metadata to be associated with the specific mimetype output.
179 """
173 """
180 _display_mimetype('image/svg+xml', objs, **kwargs)
174 _display_mimetype('image/svg+xml', objs, **kwargs)
181
175
182
176
183 def display_png(*objs, **kwargs):
177 def display_png(*objs, **kwargs):
184 """Display the PNG representation of an object.
178 """Display the PNG representation of an object.
185
179
186 Parameters
180 Parameters
187 ----------
181 ----------
188 objs : tuple of objects
182 objs : tuple of objects
189 The Python objects to display, or if raw=True raw png data to
183 The Python objects to display, or if raw=True raw png data to
190 display.
184 display.
191 raw : bool
185 raw : bool
192 Are the data objects raw data or Python objects that need to be
186 Are the data objects raw data or Python objects that need to be
193 formatted before display? [default: False]
187 formatted before display? [default: False]
194 metadata : dict (optional)
188 metadata : dict (optional)
195 Metadata to be associated with the specific mimetype output.
189 Metadata to be associated with the specific mimetype output.
196 """
190 """
197 _display_mimetype('image/png', objs, **kwargs)
191 _display_mimetype('image/png', objs, **kwargs)
198
192
199
193
200 def display_jpeg(*objs, **kwargs):
194 def display_jpeg(*objs, **kwargs):
201 """Display the JPEG representation of an object.
195 """Display the JPEG representation of an object.
202
196
203 Parameters
197 Parameters
204 ----------
198 ----------
205 objs : tuple of objects
199 objs : tuple of objects
206 The Python objects to display, or if raw=True raw JPEG data to
200 The Python objects to display, or if raw=True raw JPEG data to
207 display.
201 display.
208 raw : bool
202 raw : bool
209 Are the data objects raw data or Python objects that need to be
203 Are the data objects raw data or Python objects that need to be
210 formatted before display? [default: False]
204 formatted before display? [default: False]
211 metadata : dict (optional)
205 metadata : dict (optional)
212 Metadata to be associated with the specific mimetype output.
206 Metadata to be associated with the specific mimetype output.
213 """
207 """
214 _display_mimetype('image/jpeg', objs, **kwargs)
208 _display_mimetype('image/jpeg', objs, **kwargs)
215
209
216
210
217 def display_latex(*objs, **kwargs):
211 def display_latex(*objs, **kwargs):
218 """Display the LaTeX representation of an object.
212 """Display the LaTeX representation of an object.
219
213
220 Parameters
214 Parameters
221 ----------
215 ----------
222 objs : tuple of objects
216 objs : tuple of objects
223 The Python objects to display, or if raw=True raw latex data to
217 The Python objects to display, or if raw=True raw latex data to
224 display.
218 display.
225 raw : bool
219 raw : bool
226 Are the data objects raw data or Python objects that need to be
220 Are the data objects raw data or Python objects that need to be
227 formatted before display? [default: False]
221 formatted before display? [default: False]
228 metadata : dict (optional)
222 metadata : dict (optional)
229 Metadata to be associated with the specific mimetype output.
223 Metadata to be associated with the specific mimetype output.
230 """
224 """
231 _display_mimetype('text/latex', objs, **kwargs)
225 _display_mimetype('text/latex', objs, **kwargs)
232
226
233
227
234 def display_json(*objs, **kwargs):
228 def display_json(*objs, **kwargs):
235 """Display the JSON representation of an object.
229 """Display the JSON representation of an object.
236
230
237 Note that not many frontends support displaying JSON.
231 Note that not many frontends support displaying JSON.
238
232
239 Parameters
233 Parameters
240 ----------
234 ----------
241 objs : tuple of objects
235 objs : tuple of objects
242 The Python objects to display, or if raw=True raw json data to
236 The Python objects to display, or if raw=True raw json data to
243 display.
237 display.
244 raw : bool
238 raw : bool
245 Are the data objects raw data or Python objects that need to be
239 Are the data objects raw data or Python objects that need to be
246 formatted before display? [default: False]
240 formatted before display? [default: False]
247 metadata : dict (optional)
241 metadata : dict (optional)
248 Metadata to be associated with the specific mimetype output.
242 Metadata to be associated with the specific mimetype output.
249 """
243 """
250 _display_mimetype('application/json', objs, **kwargs)
244 _display_mimetype('application/json', objs, **kwargs)
251
245
252
246
253 def display_javascript(*objs, **kwargs):
247 def display_javascript(*objs, **kwargs):
254 """Display the Javascript representation of an object.
248 """Display the Javascript representation of an object.
255
249
256 Parameters
250 Parameters
257 ----------
251 ----------
258 objs : tuple of objects
252 objs : tuple of objects
259 The Python objects to display, or if raw=True raw javascript data to
253 The Python objects to display, or if raw=True raw javascript data to
260 display.
254 display.
261 raw : bool
255 raw : bool
262 Are the data objects raw data or Python objects that need to be
256 Are the data objects raw data or Python objects that need to be
263 formatted before display? [default: False]
257 formatted before display? [default: False]
264 metadata : dict (optional)
258 metadata : dict (optional)
265 Metadata to be associated with the specific mimetype output.
259 Metadata to be associated with the specific mimetype output.
266 """
260 """
267 _display_mimetype('application/javascript', objs, **kwargs)
261 _display_mimetype('application/javascript', objs, **kwargs)
268
262
269 #-----------------------------------------------------------------------------
263 #-----------------------------------------------------------------------------
270 # Smart classes
264 # Smart classes
271 #-----------------------------------------------------------------------------
265 #-----------------------------------------------------------------------------
272
266
273
267
274 class DisplayObject(object):
268 class DisplayObject(object):
275 """An object that wraps data to be displayed."""
269 """An object that wraps data to be displayed."""
276
270
277 _read_flags = 'r'
271 _read_flags = 'r'
278
272
279 def __init__(self, data=None, url=None, filename=None):
273 def __init__(self, data=None, url=None, filename=None):
280 """Create a display object given raw data.
274 """Create a display object given raw data.
281
275
282 When this object is returned by an expression or passed to the
276 When this object is returned by an expression or passed to the
283 display function, it will result in the data being displayed
277 display function, it will result in the data being displayed
284 in the frontend. The MIME type of the data should match the
278 in the frontend. The MIME type of the data should match the
285 subclasses used, so the Png subclass should be used for 'image/png'
279 subclasses used, so the Png subclass should be used for 'image/png'
286 data. If the data is a URL, the data will first be downloaded
280 data. If the data is a URL, the data will first be downloaded
287 and then displayed. If
281 and then displayed. If
288
282
289 Parameters
283 Parameters
290 ----------
284 ----------
291 data : unicode, str or bytes
285 data : unicode, str or bytes
292 The raw data or a URL or file to load the data from
286 The raw data or a URL or file to load the data from
293 url : unicode
287 url : unicode
294 A URL to download the data from.
288 A URL to download the data from.
295 filename : unicode
289 filename : unicode
296 Path to a local file to load the data from.
290 Path to a local file to load the data from.
297 """
291 """
298 if data is not None and isinstance(data, string_types):
292 if data is not None and isinstance(data, string_types):
299 if data.startswith('http') and url is None:
293 if data.startswith('http') and url is None:
300 url = data
294 url = data
301 filename = None
295 filename = None
302 data = None
296 data = None
303 elif _safe_exists(data) and filename is None:
297 elif _safe_exists(data) and filename is None:
304 url = None
298 url = None
305 filename = data
299 filename = data
306 data = None
300 data = None
307
301
308 self.data = data
302 self.data = data
309 self.url = url
303 self.url = url
310 self.filename = None if filename is None else unicode_type(filename)
304 self.filename = None if filename is None else unicode_type(filename)
311
305
312 self.reload()
306 self.reload()
313
307
314 def reload(self):
308 def reload(self):
315 """Reload the raw data from file or URL."""
309 """Reload the raw data from file or URL."""
316 if self.filename is not None:
310 if self.filename is not None:
317 with open(self.filename, self._read_flags) as f:
311 with open(self.filename, self._read_flags) as f:
318 self.data = f.read()
312 self.data = f.read()
319 elif self.url is not None:
313 elif self.url is not None:
320 try:
314 try:
321 try:
315 try:
322 from urllib.request import urlopen # Py3
316 from urllib.request import urlopen # Py3
323 except ImportError:
317 except ImportError:
324 from urllib2 import urlopen
318 from urllib2 import urlopen
325 response = urlopen(self.url)
319 response = urlopen(self.url)
326 self.data = response.read()
320 self.data = response.read()
327 # extract encoding from header, if there is one:
321 # extract encoding from header, if there is one:
328 encoding = None
322 encoding = None
329 for sub in response.headers['content-type'].split(';'):
323 for sub in response.headers['content-type'].split(';'):
330 sub = sub.strip()
324 sub = sub.strip()
331 if sub.startswith('charset'):
325 if sub.startswith('charset'):
332 encoding = sub.split('=')[-1].strip()
326 encoding = sub.split('=')[-1].strip()
333 break
327 break
334 # decode data, if an encoding was specified
328 # decode data, if an encoding was specified
335 if encoding:
329 if encoding:
336 self.data = self.data.decode(encoding, 'replace')
330 self.data = self.data.decode(encoding, 'replace')
337 except:
331 except:
338 self.data = None
332 self.data = None
339
333
340 class Pretty(DisplayObject):
334 class Pretty(DisplayObject):
341
335
342 def _repr_pretty_(self):
336 def _repr_pretty_(self):
343 return self.data
337 return self.data
344
338
345
339
346 class HTML(DisplayObject):
340 class HTML(DisplayObject):
347
341
348 def _repr_html_(self):
342 def _repr_html_(self):
349 return self.data
343 return self.data
350
344
351 def __html__(self):
345 def __html__(self):
352 """
346 """
353 This method exists to inform other HTML-using modules (e.g. Markupsafe,
347 This method exists to inform other HTML-using modules (e.g. Markupsafe,
354 htmltag, etc) that this object is HTML and does not need things like
348 htmltag, etc) that this object is HTML and does not need things like
355 special characters (<>&) escaped.
349 special characters (<>&) escaped.
356 """
350 """
357 return self._repr_html_()
351 return self._repr_html_()
358
352
359
353
360 class Math(DisplayObject):
354 class Math(DisplayObject):
361
355
362 def _repr_latex_(self):
356 def _repr_latex_(self):
363 s = self.data.strip('$')
357 s = self.data.strip('$')
364 return "$$%s$$" % s
358 return "$$%s$$" % s
365
359
366
360
367 class Latex(DisplayObject):
361 class Latex(DisplayObject):
368
362
369 def _repr_latex_(self):
363 def _repr_latex_(self):
370 return self.data
364 return self.data
371
365
372
366
373 class SVG(DisplayObject):
367 class SVG(DisplayObject):
374
368
375 # wrap data in a property, which extracts the <svg> tag, discarding
369 # wrap data in a property, which extracts the <svg> tag, discarding
376 # document headers
370 # document headers
377 _data = None
371 _data = None
378
372
379 @property
373 @property
380 def data(self):
374 def data(self):
381 return self._data
375 return self._data
382
376
383 @data.setter
377 @data.setter
384 def data(self, svg):
378 def data(self, svg):
385 if svg is None:
379 if svg is None:
386 self._data = None
380 self._data = None
387 return
381 return
388 # parse into dom object
382 # parse into dom object
389 from xml.dom import minidom
383 from xml.dom import minidom
390 svg = cast_bytes_py2(svg)
384 svg = cast_bytes_py2(svg)
391 x = minidom.parseString(svg)
385 x = minidom.parseString(svg)
392 # get svg tag (should be 1)
386 # get svg tag (should be 1)
393 found_svg = x.getElementsByTagName('svg')
387 found_svg = x.getElementsByTagName('svg')
394 if found_svg:
388 if found_svg:
395 svg = found_svg[0].toxml()
389 svg = found_svg[0].toxml()
396 else:
390 else:
397 # fallback on the input, trust the user
391 # fallback on the input, trust the user
398 # but this is probably an error.
392 # but this is probably an error.
399 pass
393 pass
400 svg = cast_unicode(svg)
394 svg = cast_unicode(svg)
401 self._data = svg
395 self._data = svg
402
396
403 def _repr_svg_(self):
397 def _repr_svg_(self):
404 return self.data
398 return self.data
405
399
406
400
407 class JSON(DisplayObject):
401 class JSON(DisplayObject):
408
402
409 def _repr_json_(self):
403 def _repr_json_(self):
410 return self.data
404 return self.data
411
405
412 css_t = """$("head").append($("<link/>").attr({
406 css_t = """$("head").append($("<link/>").attr({
413 rel: "stylesheet",
407 rel: "stylesheet",
414 type: "text/css",
408 type: "text/css",
415 href: "%s"
409 href: "%s"
416 }));
410 }));
417 """
411 """
418
412
419 lib_t1 = """$.getScript("%s", function () {
413 lib_t1 = """$.getScript("%s", function () {
420 """
414 """
421 lib_t2 = """});
415 lib_t2 = """});
422 """
416 """
423
417
424 class Javascript(DisplayObject):
418 class Javascript(DisplayObject):
425
419
426 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
420 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
427 """Create a Javascript display object given raw data.
421 """Create a Javascript display object given raw data.
428
422
429 When this object is returned by an expression or passed to the
423 When this object is returned by an expression or passed to the
430 display function, it will result in the data being displayed
424 display function, it will result in the data being displayed
431 in the frontend. If the data is a URL, the data will first be
425 in the frontend. If the data is a URL, the data will first be
432 downloaded and then displayed.
426 downloaded and then displayed.
433
427
434 In the Notebook, the containing element will be available as `element`,
428 In the Notebook, the containing element will be available as `element`,
435 and jQuery will be available. The output area starts hidden, so if
429 and jQuery will be available. The output area starts hidden, so if
436 the js appends content to `element` that should be visible, then
430 the js appends content to `element` that should be visible, then
437 it must call `container.show()` to unhide the area.
431 it must call `container.show()` to unhide the area.
438
432
439 Parameters
433 Parameters
440 ----------
434 ----------
441 data : unicode, str or bytes
435 data : unicode, str or bytes
442 The Javascript source code or a URL to download it from.
436 The Javascript source code or a URL to download it from.
443 url : unicode
437 url : unicode
444 A URL to download the data from.
438 A URL to download the data from.
445 filename : unicode
439 filename : unicode
446 Path to a local file to load the data from.
440 Path to a local file to load the data from.
447 lib : list or str
441 lib : list or str
448 A sequence of Javascript library URLs to load asynchronously before
442 A sequence of Javascript library URLs to load asynchronously before
449 running the source code. The full URLs of the libraries should
443 running the source code. The full URLs of the libraries should
450 be given. A single Javascript library URL can also be given as a
444 be given. A single Javascript library URL can also be given as a
451 string.
445 string.
452 css: : list or str
446 css: : list or str
453 A sequence of css files to load before running the source code.
447 A sequence of css files to load before running the source code.
454 The full URLs of the css files should be given. A single css URL
448 The full URLs of the css files should be given. A single css URL
455 can also be given as a string.
449 can also be given as a string.
456 """
450 """
457 if isinstance(lib, string_types):
451 if isinstance(lib, string_types):
458 lib = [lib]
452 lib = [lib]
459 elif lib is None:
453 elif lib is None:
460 lib = []
454 lib = []
461 if isinstance(css, string_types):
455 if isinstance(css, string_types):
462 css = [css]
456 css = [css]
463 elif css is None:
457 elif css is None:
464 css = []
458 css = []
465 if not isinstance(lib, (list,tuple)):
459 if not isinstance(lib, (list,tuple)):
466 raise TypeError('expected sequence, got: %r' % lib)
460 raise TypeError('expected sequence, got: %r' % lib)
467 if not isinstance(css, (list,tuple)):
461 if not isinstance(css, (list,tuple)):
468 raise TypeError('expected sequence, got: %r' % css)
462 raise TypeError('expected sequence, got: %r' % css)
469 self.lib = lib
463 self.lib = lib
470 self.css = css
464 self.css = css
471 super(Javascript, self).__init__(data=data, url=url, filename=filename)
465 super(Javascript, self).__init__(data=data, url=url, filename=filename)
472
466
473 def _repr_javascript_(self):
467 def _repr_javascript_(self):
474 r = ''
468 r = ''
475 for c in self.css:
469 for c in self.css:
476 r += css_t % c
470 r += css_t % c
477 for l in self.lib:
471 for l in self.lib:
478 r += lib_t1 % l
472 r += lib_t1 % l
479 r += self.data
473 r += self.data
480 r += lib_t2*len(self.lib)
474 r += lib_t2*len(self.lib)
481 return r
475 return r
482
476
483 # constants for identifying png/jpeg data
477 # constants for identifying png/jpeg data
484 _PNG = b'\x89PNG\r\n\x1a\n'
478 _PNG = b'\x89PNG\r\n\x1a\n'
485 _JPEG = b'\xff\xd8'
479 _JPEG = b'\xff\xd8'
486
480
487 def _pngxy(data):
481 def _pngxy(data):
488 """read the (width, height) from a PNG header"""
482 """read the (width, height) from a PNG header"""
489 ihdr = data.index(b'IHDR')
483 ihdr = data.index(b'IHDR')
490 # next 8 bytes are width/height
484 # next 8 bytes are width/height
491 w4h4 = data[ihdr+4:ihdr+12]
485 w4h4 = data[ihdr+4:ihdr+12]
492 return struct.unpack('>ii', w4h4)
486 return struct.unpack('>ii', w4h4)
493
487
494 def _jpegxy(data):
488 def _jpegxy(data):
495 """read the (width, height) from a JPEG header"""
489 """read the (width, height) from a JPEG header"""
496 # adapted from http://www.64lines.com/jpeg-width-height
490 # adapted from http://www.64lines.com/jpeg-width-height
497
491
498 idx = 4
492 idx = 4
499 while True:
493 while True:
500 block_size = struct.unpack('>H', data[idx:idx+2])[0]
494 block_size = struct.unpack('>H', data[idx:idx+2])[0]
501 idx = idx + block_size
495 idx = idx + block_size
502 if data[idx:idx+2] == b'\xFF\xC0':
496 if data[idx:idx+2] == b'\xFF\xC0':
503 # found Start of Frame
497 # found Start of Frame
504 iSOF = idx
498 iSOF = idx
505 break
499 break
506 else:
500 else:
507 # read another block
501 # read another block
508 idx += 2
502 idx += 2
509
503
510 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
504 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
511 return w, h
505 return w, h
512
506
513 class Image(DisplayObject):
507 class Image(DisplayObject):
514
508
515 _read_flags = 'rb'
509 _read_flags = 'rb'
516 _FMT_JPEG = u'jpeg'
510 _FMT_JPEG = u'jpeg'
517 _FMT_PNG = u'png'
511 _FMT_PNG = u'png'
518 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
512 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
519
513
520 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
514 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
521 """Create a PNG/JPEG image object given raw data.
515 """Create a PNG/JPEG image object given raw data.
522
516
523 When this object is returned by an input cell or passed to the
517 When this object is returned by an input cell or passed to the
524 display function, it will result in the image being displayed
518 display function, it will result in the image being displayed
525 in the frontend.
519 in the frontend.
526
520
527 Parameters
521 Parameters
528 ----------
522 ----------
529 data : unicode, str or bytes
523 data : unicode, str or bytes
530 The raw image data or a URL or filename to load the data from.
524 The raw image data or a URL or filename to load the data from.
531 This always results in embedded image data.
525 This always results in embedded image data.
532 url : unicode
526 url : unicode
533 A URL to download the data from. If you specify `url=`,
527 A URL to download the data from. If you specify `url=`,
534 the image data will not be embedded unless you also specify `embed=True`.
528 the image data will not be embedded unless you also specify `embed=True`.
535 filename : unicode
529 filename : unicode
536 Path to a local file to load the data from.
530 Path to a local file to load the data from.
537 Images from a file are always embedded.
531 Images from a file are always embedded.
538 format : unicode
532 format : unicode
539 The format of the image data (png/jpeg/jpg). If a filename or URL is given
533 The format of the image data (png/jpeg/jpg). If a filename or URL is given
540 for format will be inferred from the filename extension.
534 for format will be inferred from the filename extension.
541 embed : bool
535 embed : bool
542 Should the image data be embedded using a data URI (True) or be
536 Should the image data be embedded using a data URI (True) or be
543 loaded using an <img> tag. Set this to True if you want the image
537 loaded using an <img> tag. Set this to True if you want the image
544 to be viewable later with no internet connection in the notebook.
538 to be viewable later with no internet connection in the notebook.
545
539
546 Default is `True`, unless the keyword argument `url` is set, then
540 Default is `True`, unless the keyword argument `url` is set, then
547 default value is `False`.
541 default value is `False`.
548
542
549 Note that QtConsole is not able to display images if `embed` is set to `False`
543 Note that QtConsole is not able to display images if `embed` is set to `False`
550 width : int
544 width : int
551 Width to which to constrain the image in html
545 Width to which to constrain the image in html
552 height : int
546 height : int
553 Height to which to constrain the image in html
547 Height to which to constrain the image in html
554 retina : bool
548 retina : bool
555 Automatically set the width and height to half of the measured
549 Automatically set the width and height to half of the measured
556 width and height.
550 width and height.
557 This only works for embedded images because it reads the width/height
551 This only works for embedded images because it reads the width/height
558 from image data.
552 from image data.
559 For non-embedded images, you can just set the desired display width
553 For non-embedded images, you can just set the desired display width
560 and height directly.
554 and height directly.
561
555
562 Examples
556 Examples
563 --------
557 --------
564 # embedded image data, works in qtconsole and notebook
558 # embedded image data, works in qtconsole and notebook
565 # when passed positionally, the first arg can be any of raw image data,
559 # when passed positionally, the first arg can be any of raw image data,
566 # a URL, or a filename from which to load image data.
560 # a URL, or a filename from which to load image data.
567 # The result is always embedding image data for inline images.
561 # The result is always embedding image data for inline images.
568 Image('http://www.google.fr/images/srpr/logo3w.png')
562 Image('http://www.google.fr/images/srpr/logo3w.png')
569 Image('/path/to/image.jpg')
563 Image('/path/to/image.jpg')
570 Image(b'RAW_PNG_DATA...')
564 Image(b'RAW_PNG_DATA...')
571
565
572 # Specifying Image(url=...) does not embed the image data,
566 # Specifying Image(url=...) does not embed the image data,
573 # it only generates `<img>` tag with a link to the source.
567 # it only generates `<img>` tag with a link to the source.
574 # This will not work in the qtconsole or offline.
568 # This will not work in the qtconsole or offline.
575 Image(url='http://www.google.fr/images/srpr/logo3w.png')
569 Image(url='http://www.google.fr/images/srpr/logo3w.png')
576
570
577 """
571 """
578 if filename is not None:
572 if filename is not None:
579 ext = self._find_ext(filename)
573 ext = self._find_ext(filename)
580 elif url is not None:
574 elif url is not None:
581 ext = self._find_ext(url)
575 ext = self._find_ext(url)
582 elif data is None:
576 elif data is None:
583 raise ValueError("No image data found. Expecting filename, url, or data.")
577 raise ValueError("No image data found. Expecting filename, url, or data.")
584 elif isinstance(data, string_types) and (
578 elif isinstance(data, string_types) and (
585 data.startswith('http') or _safe_exists(data)
579 data.startswith('http') or _safe_exists(data)
586 ):
580 ):
587 ext = self._find_ext(data)
581 ext = self._find_ext(data)
588 else:
582 else:
589 ext = None
583 ext = None
590
584
591 if ext is not None:
585 if ext is not None:
592 format = ext.lower()
586 format = ext.lower()
593 if ext == u'jpg' or ext == u'jpeg':
587 if ext == u'jpg' or ext == u'jpeg':
594 format = self._FMT_JPEG
588 format = self._FMT_JPEG
595 if ext == u'png':
589 if ext == u'png':
596 format = self._FMT_PNG
590 format = self._FMT_PNG
597 elif isinstance(data, bytes) and format == 'png':
591 elif isinstance(data, bytes) and format == 'png':
598 # infer image type from image data header,
592 # infer image type from image data header,
599 # only if format might not have been specified.
593 # only if format might not have been specified.
600 if data[:2] == _JPEG:
594 if data[:2] == _JPEG:
601 format = 'jpeg'
595 format = 'jpeg'
602
596
603 self.format = unicode_type(format).lower()
597 self.format = unicode_type(format).lower()
604 self.embed = embed if embed is not None else (url is None)
598 self.embed = embed if embed is not None else (url is None)
605
599
606 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
600 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
607 raise ValueError("Cannot embed the '%s' image format" % (self.format))
601 raise ValueError("Cannot embed the '%s' image format" % (self.format))
608 self.width = width
602 self.width = width
609 self.height = height
603 self.height = height
610 self.retina = retina
604 self.retina = retina
611 super(Image, self).__init__(data=data, url=url, filename=filename)
605 super(Image, self).__init__(data=data, url=url, filename=filename)
612
606
613 if retina:
607 if retina:
614 self._retina_shape()
608 self._retina_shape()
615
609
616 def _retina_shape(self):
610 def _retina_shape(self):
617 """load pixel-doubled width and height from image data"""
611 """load pixel-doubled width and height from image data"""
618 if not self.embed:
612 if not self.embed:
619 return
613 return
620 if self.format == 'png':
614 if self.format == 'png':
621 w, h = _pngxy(self.data)
615 w, h = _pngxy(self.data)
622 elif self.format == 'jpeg':
616 elif self.format == 'jpeg':
623 w, h = _jpegxy(self.data)
617 w, h = _jpegxy(self.data)
624 else:
618 else:
625 # retina only supports png
619 # retina only supports png
626 return
620 return
627 self.width = w // 2
621 self.width = w // 2
628 self.height = h // 2
622 self.height = h // 2
629
623
630 def reload(self):
624 def reload(self):
631 """Reload the raw data from file or URL."""
625 """Reload the raw data from file or URL."""
632 if self.embed:
626 if self.embed:
633 super(Image,self).reload()
627 super(Image,self).reload()
634 if self.retina:
628 if self.retina:
635 self._retina_shape()
629 self._retina_shape()
636
630
637 def _repr_html_(self):
631 def _repr_html_(self):
638 if not self.embed:
632 if not self.embed:
639 width = height = ''
633 width = height = ''
640 if self.width:
634 if self.width:
641 width = ' width="%d"' % self.width
635 width = ' width="%d"' % self.width
642 if self.height:
636 if self.height:
643 height = ' height="%d"' % self.height
637 height = ' height="%d"' % self.height
644 return u'<img src="%s"%s%s/>' % (self.url, width, height)
638 return u'<img src="%s"%s%s/>' % (self.url, width, height)
645
639
646 def _data_and_metadata(self):
640 def _data_and_metadata(self):
647 """shortcut for returning metadata with shape information, if defined"""
641 """shortcut for returning metadata with shape information, if defined"""
648 md = {}
642 md = {}
649 if self.width:
643 if self.width:
650 md['width'] = self.width
644 md['width'] = self.width
651 if self.height:
645 if self.height:
652 md['height'] = self.height
646 md['height'] = self.height
653 if md:
647 if md:
654 return self.data, md
648 return self.data, md
655 else:
649 else:
656 return self.data
650 return self.data
657
651
658 def _repr_png_(self):
652 def _repr_png_(self):
659 if self.embed and self.format == u'png':
653 if self.embed and self.format == u'png':
660 return self._data_and_metadata()
654 return self._data_and_metadata()
661
655
662 def _repr_jpeg_(self):
656 def _repr_jpeg_(self):
663 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
657 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
664 return self._data_and_metadata()
658 return self._data_and_metadata()
665
659
666 def _find_ext(self, s):
660 def _find_ext(self, s):
667 return unicode_type(s.split('.')[-1].lower())
661 return unicode_type(s.split('.')[-1].lower())
668
662
669
663
670 def clear_output(wait=False):
664 def clear_output(wait=False):
671 """Clear the output of the current cell receiving output.
665 """Clear the output of the current cell receiving output.
672
666
673 Parameters
667 Parameters
674 ----------
668 ----------
675 wait : bool [default: false]
669 wait : bool [default: false]
676 Wait to clear the output until new output is available to replace it."""
670 Wait to clear the output until new output is available to replace it."""
677 from IPython.core.interactiveshell import InteractiveShell
671 from IPython.core.interactiveshell import InteractiveShell
678 if InteractiveShell.initialized():
672 if InteractiveShell.initialized():
679 InteractiveShell.instance().display_pub.clear_output(wait)
673 InteractiveShell.instance().display_pub.clear_output(wait)
680 else:
674 else:
681 from IPython.utils import io
675 from IPython.utils import io
682 print('\033[2K\r', file=io.stdout, end='')
676 print('\033[2K\r', file=io.stdout, end='')
683 io.stdout.flush()
677 io.stdout.flush()
684 print('\033[2K\r', file=io.stderr, end='')
678 print('\033[2K\r', file=io.stderr, end='')
685 io.stderr.flush()
679 io.stderr.flush()
General Comments 0
You need to be logged in to leave comments. Login now