##// END OF EJS Templates
Merge pull request #3082 from minrk/image_garbage...
Matthias Bussonnier -
r10060:b4484c7a merge
parent child Browse files
Show More
@@ -1,557 +1,586 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) 2008-2011 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
23
22 from .displaypub import (
24 from .displaypub import (
23 publish_pretty, publish_html,
25 publish_pretty, publish_html,
24 publish_latex, publish_svg,
26 publish_latex, publish_svg,
25 publish_png, publish_json,
27 publish_png, publish_json,
26 publish_javascript, publish_jpeg
28 publish_javascript, publish_jpeg
27 )
29 )
28
30
29 from IPython.utils.py3compat import string_types
31 from IPython.utils.py3compat import string_types
30
32
31 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # utility functions
35 #-----------------------------------------------------------------------------
36
37 def _safe_exists(path):
38 """check path, but don't let exceptions raise"""
39 try:
40 return os.path.exists(path)
41 except Exception:
42 return False
43
44 #-----------------------------------------------------------------------------
32 # Main functions
45 # Main functions
33 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
34
47
35 def display(*objs, **kwargs):
48 def display(*objs, **kwargs):
36 """Display a Python object in all frontends.
49 """Display a Python object in all frontends.
37
50
38 By default all representations will be computed and sent to the frontends.
51 By default all representations will be computed and sent to the frontends.
39 Frontends can decide which representation is used and how.
52 Frontends can decide which representation is used and how.
40
53
41 Parameters
54 Parameters
42 ----------
55 ----------
43 objs : tuple of objects
56 objs : tuple of objects
44 The Python objects to display.
57 The Python objects to display.
45 include : list or tuple, optional
58 include : list or tuple, optional
46 A list of format type strings (MIME types) to include in the
59 A list of format type strings (MIME types) to include in the
47 format data dict. If this is set *only* the format types included
60 format data dict. If this is set *only* the format types included
48 in this list will be computed.
61 in this list will be computed.
49 exclude : list or tuple, optional
62 exclude : list or tuple, optional
50 A list of format type string (MIME types) to exclue in the format
63 A list of format type string (MIME types) to exclue in the format
51 data dict. If this is set all format types will be computed,
64 data dict. If this is set all format types will be computed,
52 except for those included in this argument.
65 except for those included in this argument.
53 """
66 """
54 include = kwargs.get('include')
67 include = kwargs.get('include')
55 exclude = kwargs.get('exclude')
68 exclude = kwargs.get('exclude')
56
69
57 from IPython.core.interactiveshell import InteractiveShell
70 from IPython.core.interactiveshell import InteractiveShell
58 inst = InteractiveShell.instance()
71 inst = InteractiveShell.instance()
59 format = inst.display_formatter.format
72 format = inst.display_formatter.format
60 publish = inst.display_pub.publish
73 publish = inst.display_pub.publish
61
74
62 for obj in objs:
75 for obj in objs:
63 format_dict = format(obj, include=include, exclude=exclude)
76 format_dict = format(obj, include=include, exclude=exclude)
64 publish('IPython.core.display.display', format_dict)
77 publish('IPython.core.display.display', format_dict)
65
78
66
79
67 def display_pretty(*objs, **kwargs):
80 def display_pretty(*objs, **kwargs):
68 """Display the pretty (default) representation of an object.
81 """Display the pretty (default) representation of an object.
69
82
70 Parameters
83 Parameters
71 ----------
84 ----------
72 objs : tuple of objects
85 objs : tuple of objects
73 The Python objects to display, or if raw=True raw text data to
86 The Python objects to display, or if raw=True raw text data to
74 display.
87 display.
75 raw : bool
88 raw : bool
76 Are the data objects raw data or Python objects that need to be
89 Are the data objects raw data or Python objects that need to be
77 formatted before display? [default: False]
90 formatted before display? [default: False]
78 """
91 """
79 raw = kwargs.pop('raw',False)
92 raw = kwargs.pop('raw',False)
80 if raw:
93 if raw:
81 for obj in objs:
94 for obj in objs:
82 publish_pretty(obj)
95 publish_pretty(obj)
83 else:
96 else:
84 display(*objs, include=['text/plain'])
97 display(*objs, include=['text/plain'])
85
98
86
99
87 def display_html(*objs, **kwargs):
100 def display_html(*objs, **kwargs):
88 """Display the HTML representation of an object.
101 """Display the HTML representation of an object.
89
102
90 Parameters
103 Parameters
91 ----------
104 ----------
92 objs : tuple of objects
105 objs : tuple of objects
93 The Python objects to display, or if raw=True raw HTML data to
106 The Python objects to display, or if raw=True raw HTML data to
94 display.
107 display.
95 raw : bool
108 raw : bool
96 Are the data objects raw data or Python objects that need to be
109 Are the data objects raw data or Python objects that need to be
97 formatted before display? [default: False]
110 formatted before display? [default: False]
98 """
111 """
99 raw = kwargs.pop('raw',False)
112 raw = kwargs.pop('raw',False)
100 if raw:
113 if raw:
101 for obj in objs:
114 for obj in objs:
102 publish_html(obj)
115 publish_html(obj)
103 else:
116 else:
104 display(*objs, include=['text/plain','text/html'])
117 display(*objs, include=['text/plain','text/html'])
105
118
106
119
107 def display_svg(*objs, **kwargs):
120 def display_svg(*objs, **kwargs):
108 """Display the SVG representation of an object.
121 """Display the SVG representation of an object.
109
122
110 Parameters
123 Parameters
111 ----------
124 ----------
112 objs : tuple of objects
125 objs : tuple of objects
113 The Python objects to display, or if raw=True raw svg data to
126 The Python objects to display, or if raw=True raw svg data to
114 display.
127 display.
115 raw : bool
128 raw : bool
116 Are the data objects raw data or Python objects that need to be
129 Are the data objects raw data or Python objects that need to be
117 formatted before display? [default: False]
130 formatted before display? [default: False]
118 """
131 """
119 raw = kwargs.pop('raw',False)
132 raw = kwargs.pop('raw',False)
120 if raw:
133 if raw:
121 for obj in objs:
134 for obj in objs:
122 publish_svg(obj)
135 publish_svg(obj)
123 else:
136 else:
124 display(*objs, include=['text/plain','image/svg+xml'])
137 display(*objs, include=['text/plain','image/svg+xml'])
125
138
126
139
127 def display_png(*objs, **kwargs):
140 def display_png(*objs, **kwargs):
128 """Display the PNG representation of an object.
141 """Display the PNG representation of an object.
129
142
130 Parameters
143 Parameters
131 ----------
144 ----------
132 objs : tuple of objects
145 objs : tuple of objects
133 The Python objects to display, or if raw=True raw png data to
146 The Python objects to display, or if raw=True raw png data to
134 display.
147 display.
135 raw : bool
148 raw : bool
136 Are the data objects raw data or Python objects that need to be
149 Are the data objects raw data or Python objects that need to be
137 formatted before display? [default: False]
150 formatted before display? [default: False]
138 """
151 """
139 raw = kwargs.pop('raw',False)
152 raw = kwargs.pop('raw',False)
140 if raw:
153 if raw:
141 for obj in objs:
154 for obj in objs:
142 publish_png(obj)
155 publish_png(obj)
143 else:
156 else:
144 display(*objs, include=['text/plain','image/png'])
157 display(*objs, include=['text/plain','image/png'])
145
158
146
159
147 def display_jpeg(*objs, **kwargs):
160 def display_jpeg(*objs, **kwargs):
148 """Display the JPEG representation of an object.
161 """Display the JPEG representation of an object.
149
162
150 Parameters
163 Parameters
151 ----------
164 ----------
152 objs : tuple of objects
165 objs : tuple of objects
153 The Python objects to display, or if raw=True raw JPEG data to
166 The Python objects to display, or if raw=True raw JPEG data to
154 display.
167 display.
155 raw : bool
168 raw : bool
156 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
157 formatted before display? [default: False]
170 formatted before display? [default: False]
158 """
171 """
159 raw = kwargs.pop('raw',False)
172 raw = kwargs.pop('raw',False)
160 if raw:
173 if raw:
161 for obj in objs:
174 for obj in objs:
162 publish_jpeg(obj)
175 publish_jpeg(obj)
163 else:
176 else:
164 display(*objs, include=['text/plain','image/jpeg'])
177 display(*objs, include=['text/plain','image/jpeg'])
165
178
166
179
167 def display_latex(*objs, **kwargs):
180 def display_latex(*objs, **kwargs):
168 """Display the LaTeX representation of an object.
181 """Display the LaTeX representation of an object.
169
182
170 Parameters
183 Parameters
171 ----------
184 ----------
172 objs : tuple of objects
185 objs : tuple of objects
173 The Python objects to display, or if raw=True raw latex data to
186 The Python objects to display, or if raw=True raw latex data to
174 display.
187 display.
175 raw : bool
188 raw : bool
176 Are the data objects raw data or Python objects that need to be
189 Are the data objects raw data or Python objects that need to be
177 formatted before display? [default: False]
190 formatted before display? [default: False]
178 """
191 """
179 raw = kwargs.pop('raw',False)
192 raw = kwargs.pop('raw',False)
180 if raw:
193 if raw:
181 for obj in objs:
194 for obj in objs:
182 publish_latex(obj)
195 publish_latex(obj)
183 else:
196 else:
184 display(*objs, include=['text/plain','text/latex'])
197 display(*objs, include=['text/plain','text/latex'])
185
198
186
199
187 def display_json(*objs, **kwargs):
200 def display_json(*objs, **kwargs):
188 """Display the JSON representation of an object.
201 """Display the JSON representation of an object.
189
202
190 Note that not many frontends support displaying JSON.
203 Note that not many frontends support displaying JSON.
191
204
192 Parameters
205 Parameters
193 ----------
206 ----------
194 objs : tuple of objects
207 objs : tuple of objects
195 The Python objects to display, or if raw=True raw json data to
208 The Python objects to display, or if raw=True raw json data to
196 display.
209 display.
197 raw : bool
210 raw : bool
198 Are the data objects raw data or Python objects that need to be
211 Are the data objects raw data or Python objects that need to be
199 formatted before display? [default: False]
212 formatted before display? [default: False]
200 """
213 """
201 raw = kwargs.pop('raw',False)
214 raw = kwargs.pop('raw',False)
202 if raw:
215 if raw:
203 for obj in objs:
216 for obj in objs:
204 publish_json(obj)
217 publish_json(obj)
205 else:
218 else:
206 display(*objs, include=['text/plain','application/json'])
219 display(*objs, include=['text/plain','application/json'])
207
220
208
221
209 def display_javascript(*objs, **kwargs):
222 def display_javascript(*objs, **kwargs):
210 """Display the Javascript representation of an object.
223 """Display the Javascript representation of an object.
211
224
212 Parameters
225 Parameters
213 ----------
226 ----------
214 objs : tuple of objects
227 objs : tuple of objects
215 The Python objects to display, or if raw=True raw javascript data to
228 The Python objects to display, or if raw=True raw javascript data to
216 display.
229 display.
217 raw : bool
230 raw : bool
218 Are the data objects raw data or Python objects that need to be
231 Are the data objects raw data or Python objects that need to be
219 formatted before display? [default: False]
232 formatted before display? [default: False]
220 """
233 """
221 raw = kwargs.pop('raw',False)
234 raw = kwargs.pop('raw',False)
222 if raw:
235 if raw:
223 for obj in objs:
236 for obj in objs:
224 publish_javascript(obj)
237 publish_javascript(obj)
225 else:
238 else:
226 display(*objs, include=['text/plain','application/javascript'])
239 display(*objs, include=['text/plain','application/javascript'])
227
240
228 #-----------------------------------------------------------------------------
241 #-----------------------------------------------------------------------------
229 # Smart classes
242 # Smart classes
230 #-----------------------------------------------------------------------------
243 #-----------------------------------------------------------------------------
231
244
232
245
233 class DisplayObject(object):
246 class DisplayObject(object):
234 """An object that wraps data to be displayed."""
247 """An object that wraps data to be displayed."""
235
248
236 _read_flags = 'r'
249 _read_flags = 'r'
237
250
238 def __init__(self, data=None, url=None, filename=None):
251 def __init__(self, data=None, url=None, filename=None):
239 """Create a display object given raw data.
252 """Create a display object given raw data.
240
253
241 When this object is returned by an expression or passed to the
254 When this object is returned by an expression or passed to the
242 display function, it will result in the data being displayed
255 display function, it will result in the data being displayed
243 in the frontend. The MIME type of the data should match the
256 in the frontend. The MIME type of the data should match the
244 subclasses used, so the Png subclass should be used for 'image/png'
257 subclasses used, so the Png subclass should be used for 'image/png'
245 data. If the data is a URL, the data will first be downloaded
258 data. If the data is a URL, the data will first be downloaded
246 and then displayed. If
259 and then displayed. If
247
260
248 Parameters
261 Parameters
249 ----------
262 ----------
250 data : unicode, str or bytes
263 data : unicode, str or bytes
251 The raw data or a URL to download the data from.
264 The raw data or a URL or file to load the data from
252 url : unicode
265 url : unicode
253 A URL to download the data from.
266 A URL to download the data from.
254 filename : unicode
267 filename : unicode
255 Path to a local file to load the data from.
268 Path to a local file to load the data from.
256 """
269 """
257 if data is not None and isinstance(data, string_types) and data.startswith('http'):
270 if data is not None and isinstance(data, string_types):
258 self.url = data
271 if data.startswith('http') and url is None:
259 self.filename = None
272 url = data
260 self.data = None
273 filename = None
261 else:
274 data = None
262 self.data = data
275 elif _safe_exists(data) and filename is None:
263 self.url = url
276 url = None
264 self.filename = None if filename is None else unicode(filename)
277 filename = data
278 data = None
279
280 self.data = data
281 self.url = url
282 self.filename = None if filename is None else unicode(filename)
283
265 self.reload()
284 self.reload()
266
285
267 def reload(self):
286 def reload(self):
268 """Reload the raw data from file or URL."""
287 """Reload the raw data from file or URL."""
269 if self.filename is not None:
288 if self.filename is not None:
270 with open(self.filename, self._read_flags) as f:
289 with open(self.filename, self._read_flags) as f:
271 self.data = f.read()
290 self.data = f.read()
272 elif self.url is not None:
291 elif self.url is not None:
273 try:
292 try:
274 import urllib2
293 import urllib2
275 response = urllib2.urlopen(self.url)
294 response = urllib2.urlopen(self.url)
276 self.data = response.read()
295 self.data = response.read()
277 # extract encoding from header, if there is one:
296 # extract encoding from header, if there is one:
278 encoding = None
297 encoding = None
279 for sub in response.headers['content-type'].split(';'):
298 for sub in response.headers['content-type'].split(';'):
280 sub = sub.strip()
299 sub = sub.strip()
281 if sub.startswith('charset'):
300 if sub.startswith('charset'):
282 encoding = sub.split('=')[-1].strip()
301 encoding = sub.split('=')[-1].strip()
283 break
302 break
284 # decode data, if an encoding was specified
303 # decode data, if an encoding was specified
285 if encoding:
304 if encoding:
286 self.data = self.data.decode(encoding, 'replace')
305 self.data = self.data.decode(encoding, 'replace')
287 except:
306 except:
288 self.data = None
307 self.data = None
289
308
290 class Pretty(DisplayObject):
309 class Pretty(DisplayObject):
291
310
292 def _repr_pretty_(self):
311 def _repr_pretty_(self):
293 return self.data
312 return self.data
294
313
295
314
296 class HTML(DisplayObject):
315 class HTML(DisplayObject):
297
316
298 def _repr_html_(self):
317 def _repr_html_(self):
299 return self.data
318 return self.data
300
319
301
320
302 class Math(DisplayObject):
321 class Math(DisplayObject):
303
322
304 def _repr_latex_(self):
323 def _repr_latex_(self):
305 s = self.data.strip('$')
324 s = self.data.strip('$')
306 return "$$%s$$" % s
325 return "$$%s$$" % s
307
326
308
327
309 class Latex(DisplayObject):
328 class Latex(DisplayObject):
310
329
311 def _repr_latex_(self):
330 def _repr_latex_(self):
312 return self.data
331 return self.data
313
332
314
333
315 class SVG(DisplayObject):
334 class SVG(DisplayObject):
316
335
317 # wrap data in a property, which extracts the <svg> tag, discarding
336 # wrap data in a property, which extracts the <svg> tag, discarding
318 # document headers
337 # document headers
319 _data = None
338 _data = None
320
339
321 @property
340 @property
322 def data(self):
341 def data(self):
323 return self._data
342 return self._data
324
343
325 @data.setter
344 @data.setter
326 def data(self, svg):
345 def data(self, svg):
327 if svg is None:
346 if svg is None:
328 self._data = None
347 self._data = None
329 return
348 return
330 # parse into dom object
349 # parse into dom object
331 from xml.dom import minidom
350 from xml.dom import minidom
332 x = minidom.parseString(svg)
351 x = minidom.parseString(svg)
333 # get svg tag (should be 1)
352 # get svg tag (should be 1)
334 found_svg = x.getElementsByTagName('svg')
353 found_svg = x.getElementsByTagName('svg')
335 if found_svg:
354 if found_svg:
336 svg = found_svg[0].toxml()
355 svg = found_svg[0].toxml()
337 else:
356 else:
338 # fallback on the input, trust the user
357 # fallback on the input, trust the user
339 # but this is probably an error.
358 # but this is probably an error.
340 pass
359 pass
341 self._data = svg
360 self._data = svg
342
361
343 def _repr_svg_(self):
362 def _repr_svg_(self):
344 return self.data
363 return self.data
345
364
346
365
347 class JSON(DisplayObject):
366 class JSON(DisplayObject):
348
367
349 def _repr_json_(self):
368 def _repr_json_(self):
350 return self.data
369 return self.data
351
370
352 css_t = """$("head").append($("<link/>").attr({
371 css_t = """$("head").append($("<link/>").attr({
353 rel: "stylesheet",
372 rel: "stylesheet",
354 type: "text/css",
373 type: "text/css",
355 href: "%s"
374 href: "%s"
356 }));
375 }));
357 """
376 """
358
377
359 lib_t1 = """$.getScript("%s", function () {
378 lib_t1 = """$.getScript("%s", function () {
360 """
379 """
361 lib_t2 = """});
380 lib_t2 = """});
362 """
381 """
363
382
364 class Javascript(DisplayObject):
383 class Javascript(DisplayObject):
365
384
366 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
385 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
367 """Create a Javascript display object given raw data.
386 """Create a Javascript display object given raw data.
368
387
369 When this object is returned by an expression or passed to the
388 When this object is returned by an expression or passed to the
370 display function, it will result in the data being displayed
389 display function, it will result in the data being displayed
371 in the frontend. If the data is a URL, the data will first be
390 in the frontend. If the data is a URL, the data will first be
372 downloaded and then displayed.
391 downloaded and then displayed.
373
392
374 In the Notebook, the containing element will be available as `element`,
393 In the Notebook, the containing element will be available as `element`,
375 and jQuery will be available. The output area starts hidden, so if
394 and jQuery will be available. The output area starts hidden, so if
376 the js appends content to `element` that should be visible, then
395 the js appends content to `element` that should be visible, then
377 it must call `container.show()` to unhide the area.
396 it must call `container.show()` to unhide the area.
378
397
379 Parameters
398 Parameters
380 ----------
399 ----------
381 data : unicode, str or bytes
400 data : unicode, str or bytes
382 The Javascript source code or a URL to download it from.
401 The Javascript source code or a URL to download it from.
383 url : unicode
402 url : unicode
384 A URL to download the data from.
403 A URL to download the data from.
385 filename : unicode
404 filename : unicode
386 Path to a local file to load the data from.
405 Path to a local file to load the data from.
387 lib : list or str
406 lib : list or str
388 A sequence of Javascript library URLs to load asynchronously before
407 A sequence of Javascript library URLs to load asynchronously before
389 running the source code. The full URLs of the libraries should
408 running the source code. The full URLs of the libraries should
390 be given. A single Javascript library URL can also be given as a
409 be given. A single Javascript library URL can also be given as a
391 string.
410 string.
392 css: : list or str
411 css: : list or str
393 A sequence of css files to load before running the source code.
412 A sequence of css files to load before running the source code.
394 The full URLs of the css files should be give. A single css URL
413 The full URLs of the css files should be give. A single css URL
395 can also be given as a string.
414 can also be given as a string.
396 """
415 """
397 if isinstance(lib, basestring):
416 if isinstance(lib, basestring):
398 lib = [lib]
417 lib = [lib]
399 elif lib is None:
418 elif lib is None:
400 lib = []
419 lib = []
401 if isinstance(css, basestring):
420 if isinstance(css, basestring):
402 css = [css]
421 css = [css]
403 elif css is None:
422 elif css is None:
404 css = []
423 css = []
405 if not isinstance(lib, (list,tuple)):
424 if not isinstance(lib, (list,tuple)):
406 raise TypeError('expected sequence, got: %r' % lib)
425 raise TypeError('expected sequence, got: %r' % lib)
407 if not isinstance(css, (list,tuple)):
426 if not isinstance(css, (list,tuple)):
408 raise TypeError('expected sequence, got: %r' % css)
427 raise TypeError('expected sequence, got: %r' % css)
409 self.lib = lib
428 self.lib = lib
410 self.css = css
429 self.css = css
411 super(Javascript, self).__init__(data=data, url=url, filename=filename)
430 super(Javascript, self).__init__(data=data, url=url, filename=filename)
412
431
413 def _repr_javascript_(self):
432 def _repr_javascript_(self):
414 r = ''
433 r = ''
415 for c in self.css:
434 for c in self.css:
416 r += css_t % c
435 r += css_t % c
417 for l in self.lib:
436 for l in self.lib:
418 r += lib_t1 % l
437 r += lib_t1 % l
419 r += self.data
438 r += self.data
420 r += lib_t2*len(self.lib)
439 r += lib_t2*len(self.lib)
421 return r
440 return r
422
441
442 # constants for identifying png/jpeg data
443 _PNG = b'\x89PNG\r\n\x1a\n'
444 _JPEG = b'\xff\xd8'
423
445
424 class Image(DisplayObject):
446 class Image(DisplayObject):
425
447
426 _read_flags = 'rb'
448 _read_flags = 'rb'
427 _FMT_JPEG = u'jpeg'
449 _FMT_JPEG = u'jpeg'
428 _FMT_PNG = u'png'
450 _FMT_PNG = u'png'
429 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
451 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
430
452
431 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None):
453 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None):
432 """Create a display an PNG/JPEG image given raw data.
454 """Create a display an PNG/JPEG image given raw data.
433
455
434 When this object is returned by an expression or passed to the
456 When this object is returned by an expression or passed to the
435 display function, it will result in the image being displayed
457 display function, it will result in the image being displayed
436 in the frontend.
458 in the frontend.
437
459
438 Parameters
460 Parameters
439 ----------
461 ----------
440 data : unicode, str or bytes
462 data : unicode, str or bytes
441 The raw data or a URL to download the data from.
463 The raw data or a URL to download the data from.
442 url : unicode
464 url : unicode
443 A URL to download the data from.
465 A URL to download the data from.
444 filename : unicode
466 filename : unicode
445 Path to a local file to load the data from.
467 Path to a local file to load the data from.
446 format : unicode
468 format : unicode
447 The format of the image data (png/jpeg/jpg). If a filename or URL is given
469 The format of the image data (png/jpeg/jpg). If a filename or URL is given
448 for format will be inferred from the filename extension.
470 for format will be inferred from the filename extension.
449 embed : bool
471 embed : bool
450 Should the image data be embedded using a data URI (True) or be
472 Should the image data be embedded using a data URI (True) or be
451 loaded using an <img> tag. Set this to True if you want the image
473 loaded using an <img> tag. Set this to True if you want the image
452 to be viewable later with no internet connection in the notebook.
474 to be viewable later with no internet connection in the notebook.
453
475
454 Default is `True`, unless the keyword argument `url` is set, then
476 Default is `True`, unless the keyword argument `url` is set, then
455 default value is `False`.
477 default value is `False`.
456
478
457 Note that QtConsole is not able to display images if `embed` is set to `False`
479 Note that QtConsole is not able to display images if `embed` is set to `False`
458 width : int
480 width : int
459 Width to which to constrain the image in html
481 Width to which to constrain the image in html
460 height : int
482 height : int
461 Height to which to constrain the image in html
483 Height to which to constrain the image in html
462
484
463 Examples
485 Examples
464 --------
486 --------
465 # embed implicitly True, works in qtconsole and notebook
487 # embed implicitly True, works in qtconsole and notebook
466 Image('http://www.google.fr/images/srpr/logo3w.png')
488 Image('http://www.google.fr/images/srpr/logo3w.png')
467
489
468 # embed implicitly False, does not works in qtconsole but works in notebook if
490 # embed implicitly False, does not works in qtconsole but works in notebook if
469 # internet connection available
491 # internet connection available
470 Image(url='http://www.google.fr/images/srpr/logo3w.png')
492 Image(url='http://www.google.fr/images/srpr/logo3w.png')
471
493
472 """
494 """
473 if filename is not None:
495 if filename is not None:
474 ext = self._find_ext(filename)
496 ext = self._find_ext(filename)
475 elif url is not None:
497 elif url is not None:
476 ext = self._find_ext(url)
498 ext = self._find_ext(url)
477 elif data is None:
499 elif data is None:
478 raise ValueError("No image data found. Expecting filename, url, or data.")
500 raise ValueError("No image data found. Expecting filename, url, or data.")
479 elif isinstance(data, string_types) and data.startswith('http'):
501 elif isinstance(data, string_types) and (
502 data.startswith('http') or _safe_exists(data)
503 ):
480 ext = self._find_ext(data)
504 ext = self._find_ext(data)
481 else:
505 else:
482 ext = None
506 ext = None
483
507
484 if ext is not None:
508 if ext is not None:
485 format = ext.lower()
509 format = ext.lower()
486 if ext == u'jpg' or ext == u'jpeg':
510 if ext == u'jpg' or ext == u'jpeg':
487 format = self._FMT_JPEG
511 format = self._FMT_JPEG
488 if ext == u'png':
512 if ext == u'png':
489 format = self._FMT_PNG
513 format = self._FMT_PNG
514 elif isinstance(data, bytes) and format == 'png':
515 # infer image type from image data header,
516 # only if format might not have been specified.
517 if data[:2] == _JPEG:
518 format = 'jpeg'
490
519
491 self.format = unicode(format).lower()
520 self.format = unicode(format).lower()
492 self.embed = embed if embed is not None else (url is None)
521 self.embed = embed if embed is not None else (url is None)
493
522
494 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
523 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
495 raise ValueError("Cannot embed the '%s' image format" % (self.format))
524 raise ValueError("Cannot embed the '%s' image format" % (self.format))
496 self.width = width
525 self.width = width
497 self.height = height
526 self.height = height
498 super(Image, self).__init__(data=data, url=url, filename=filename)
527 super(Image, self).__init__(data=data, url=url, filename=filename)
499
528
500 def reload(self):
529 def reload(self):
501 """Reload the raw data from file or URL."""
530 """Reload the raw data from file or URL."""
502 if self.embed:
531 if self.embed:
503 super(Image,self).reload()
532 super(Image,self).reload()
504
533
505 def _repr_html_(self):
534 def _repr_html_(self):
506 if not self.embed:
535 if not self.embed:
507 width = height = ''
536 width = height = ''
508 if self.width:
537 if self.width:
509 width = ' width="%d"' % self.width
538 width = ' width="%d"' % self.width
510 if self.height:
539 if self.height:
511 height = ' height="%d"' % self.height
540 height = ' height="%d"' % self.height
512 return u'<img src="%s"%s%s/>' % (self.url, width, height)
541 return u'<img src="%s"%s%s/>' % (self.url, width, height)
513
542
514 def _repr_png_(self):
543 def _repr_png_(self):
515 if self.embed and self.format == u'png':
544 if self.embed and self.format == u'png':
516 return self.data
545 return self.data
517
546
518 def _repr_jpeg_(self):
547 def _repr_jpeg_(self):
519 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
548 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
520 return self.data
549 return self.data
521
550
522 def _find_ext(self, s):
551 def _find_ext(self, s):
523 return unicode(s.split('.')[-1].lower())
552 return unicode(s.split('.')[-1].lower())
524
553
525
554
526 def clear_output(stdout=True, stderr=True, other=True):
555 def clear_output(stdout=True, stderr=True, other=True):
527 """Clear the output of the current cell receiving output.
556 """Clear the output of the current cell receiving output.
528
557
529 Optionally, each of stdout/stderr or other non-stream data (e.g. anything
558 Optionally, each of stdout/stderr or other non-stream data (e.g. anything
530 produced by display()) can be excluded from the clear event.
559 produced by display()) can be excluded from the clear event.
531
560
532 By default, everything is cleared.
561 By default, everything is cleared.
533
562
534 Parameters
563 Parameters
535 ----------
564 ----------
536 stdout : bool [default: True]
565 stdout : bool [default: True]
537 Whether to clear stdout.
566 Whether to clear stdout.
538 stderr : bool [default: True]
567 stderr : bool [default: True]
539 Whether to clear stderr.
568 Whether to clear stderr.
540 other : bool [default: True]
569 other : bool [default: True]
541 Whether to clear everything else that is not stdout/stderr
570 Whether to clear everything else that is not stdout/stderr
542 (e.g. figures,images,HTML, any result of display()).
571 (e.g. figures,images,HTML, any result of display()).
543 """
572 """
544 from IPython.core.interactiveshell import InteractiveShell
573 from IPython.core.interactiveshell import InteractiveShell
545 if InteractiveShell.initialized():
574 if InteractiveShell.initialized():
546 InteractiveShell.instance().display_pub.clear_output(
575 InteractiveShell.instance().display_pub.clear_output(
547 stdout=stdout, stderr=stderr, other=other,
576 stdout=stdout, stderr=stderr, other=other,
548 )
577 )
549 else:
578 else:
550 from IPython.utils import io
579 from IPython.utils import io
551 if stdout:
580 if stdout:
552 print('\033[2K\r', file=io.stdout, end='')
581 print('\033[2K\r', file=io.stdout, end='')
553 io.stdout.flush()
582 io.stdout.flush()
554 if stderr:
583 if stderr:
555 print('\033[2K\r', file=io.stderr, end='')
584 print('\033[2K\r', file=io.stderr, end='')
556 io.stderr.flush()
585 io.stderr.flush()
557
586
@@ -1,211 +1,224 b''
1 """Utilities to manipulate JSON objects.
1 """Utilities to manipulate JSON objects.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2010-2011 The IPython Development Team
4 # Copyright (C) 2010-2011 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING.txt, distributed as part of this software.
7 # the file COPYING.txt, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # stdlib
13 # stdlib
14 import math
14 import math
15 import re
15 import re
16 import types
16 import types
17 from datetime import datetime
17 from datetime import datetime
18
18
19 try:
19 try:
20 # base64.encodestring is deprecated in Python 3.x
20 # base64.encodestring is deprecated in Python 3.x
21 from base64 import encodebytes
21 from base64 import encodebytes
22 except ImportError:
22 except ImportError:
23 # Python 2.x
23 # Python 2.x
24 from base64 import encodestring as encodebytes
24 from base64 import encodestring as encodebytes
25
25
26 from IPython.utils import py3compat
26 from IPython.utils import py3compat
27 from IPython.utils.encoding import DEFAULT_ENCODING
27 from IPython.utils.encoding import DEFAULT_ENCODING
28 next_attr_name = '__next__' if py3compat.PY3 else 'next'
28 next_attr_name = '__next__' if py3compat.PY3 else 'next'
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Globals and constants
31 # Globals and constants
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33
33
34 # timestamp formats
34 # timestamp formats
35 ISO8601="%Y-%m-%dT%H:%M:%S.%f"
35 ISO8601="%Y-%m-%dT%H:%M:%S.%f"
36 ISO8601_PAT=re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+$")
36 ISO8601_PAT=re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+$")
37
37
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39 # Classes and functions
39 # Classes and functions
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 def rekey(dikt):
42 def rekey(dikt):
43 """Rekey a dict that has been forced to use str keys where there should be
43 """Rekey a dict that has been forced to use str keys where there should be
44 ints by json."""
44 ints by json."""
45 for k in dikt.iterkeys():
45 for k in dikt.iterkeys():
46 if isinstance(k, basestring):
46 if isinstance(k, basestring):
47 ik=fk=None
47 ik=fk=None
48 try:
48 try:
49 ik = int(k)
49 ik = int(k)
50 except ValueError:
50 except ValueError:
51 try:
51 try:
52 fk = float(k)
52 fk = float(k)
53 except ValueError:
53 except ValueError:
54 continue
54 continue
55 if ik is not None:
55 if ik is not None:
56 nk = ik
56 nk = ik
57 else:
57 else:
58 nk = fk
58 nk = fk
59 if nk in dikt:
59 if nk in dikt:
60 raise KeyError("already have key %r"%nk)
60 raise KeyError("already have key %r"%nk)
61 dikt[nk] = dikt.pop(k)
61 dikt[nk] = dikt.pop(k)
62 return dikt
62 return dikt
63
63
64
64
65 def extract_dates(obj):
65 def extract_dates(obj):
66 """extract ISO8601 dates from unpacked JSON"""
66 """extract ISO8601 dates from unpacked JSON"""
67 if isinstance(obj, dict):
67 if isinstance(obj, dict):
68 obj = dict(obj) # don't clobber
68 obj = dict(obj) # don't clobber
69 for k,v in obj.iteritems():
69 for k,v in obj.iteritems():
70 obj[k] = extract_dates(v)
70 obj[k] = extract_dates(v)
71 elif isinstance(obj, (list, tuple)):
71 elif isinstance(obj, (list, tuple)):
72 obj = [ extract_dates(o) for o in obj ]
72 obj = [ extract_dates(o) for o in obj ]
73 elif isinstance(obj, basestring):
73 elif isinstance(obj, basestring):
74 if ISO8601_PAT.match(obj):
74 if ISO8601_PAT.match(obj):
75 obj = datetime.strptime(obj, ISO8601)
75 obj = datetime.strptime(obj, ISO8601)
76 return obj
76 return obj
77
77
78 def squash_dates(obj):
78 def squash_dates(obj):
79 """squash datetime objects into ISO8601 strings"""
79 """squash datetime objects into ISO8601 strings"""
80 if isinstance(obj, dict):
80 if isinstance(obj, dict):
81 obj = dict(obj) # don't clobber
81 obj = dict(obj) # don't clobber
82 for k,v in obj.iteritems():
82 for k,v in obj.iteritems():
83 obj[k] = squash_dates(v)
83 obj[k] = squash_dates(v)
84 elif isinstance(obj, (list, tuple)):
84 elif isinstance(obj, (list, tuple)):
85 obj = [ squash_dates(o) for o in obj ]
85 obj = [ squash_dates(o) for o in obj ]
86 elif isinstance(obj, datetime):
86 elif isinstance(obj, datetime):
87 obj = obj.strftime(ISO8601)
87 obj = obj.strftime(ISO8601)
88 return obj
88 return obj
89
89
90 def date_default(obj):
90 def date_default(obj):
91 """default function for packing datetime objects in JSON."""
91 """default function for packing datetime objects in JSON."""
92 if isinstance(obj, datetime):
92 if isinstance(obj, datetime):
93 return obj.strftime(ISO8601)
93 return obj.strftime(ISO8601)
94 else:
94 else:
95 raise TypeError("%r is not JSON serializable"%obj)
95 raise TypeError("%r is not JSON serializable"%obj)
96
96
97
97
98 # constants for identifying png/jpeg data
98 # constants for identifying png/jpeg data
99 PNG = b'\x89PNG\r\n\x1a\n'
99 PNG = b'\x89PNG\r\n\x1a\n'
100 # front of PNG base64-encoded
101 PNG64 = b'iVBORw0KG'
100 JPEG = b'\xff\xd8'
102 JPEG = b'\xff\xd8'
103 # front of JPEG base64-encoded
104 JPEG64 = b'/9'
101
105
102 def encode_images(format_dict):
106 def encode_images(format_dict):
103 """b64-encodes images in a displaypub format dict
107 """b64-encodes images in a displaypub format dict
104
108
105 Perhaps this should be handled in json_clean itself?
109 Perhaps this should be handled in json_clean itself?
106
110
107 Parameters
111 Parameters
108 ----------
112 ----------
109
113
110 format_dict : dict
114 format_dict : dict
111 A dictionary of display data keyed by mime-type
115 A dictionary of display data keyed by mime-type
112
116
113 Returns
117 Returns
114 -------
118 -------
115
119
116 format_dict : dict
120 format_dict : dict
117 A copy of the same dictionary,
121 A copy of the same dictionary,
118 but binary image data ('image/png' or 'image/jpeg')
122 but binary image data ('image/png' or 'image/jpeg')
119 is base64-encoded.
123 is base64-encoded.
120
124
121 """
125 """
122 encoded = format_dict.copy()
126 encoded = format_dict.copy()
127
123 pngdata = format_dict.get('image/png')
128 pngdata = format_dict.get('image/png')
124 if isinstance(pngdata, bytes) and pngdata[:8] == PNG:
129 if isinstance(pngdata, bytes):
125 encoded['image/png'] = encodebytes(pngdata).decode('ascii')
130 # make sure we don't double-encode
131 if not pngdata.startswith(PNG64):
132 pngdata = encodebytes(pngdata)
133 encoded['image/png'] = pngdata.decode('ascii')
134
126 jpegdata = format_dict.get('image/jpeg')
135 jpegdata = format_dict.get('image/jpeg')
127 if isinstance(jpegdata, bytes) and jpegdata[:2] == JPEG:
136 if isinstance(jpegdata, bytes):
128 encoded['image/jpeg'] = encodebytes(jpegdata).decode('ascii')
137 # make sure we don't double-encode
138 if not jpegdata.startswith(JPEG64):
139 jpegdata = encodebytes(jpegdata)
140 encoded['image/jpeg'] = jpegdata.decode('ascii')
141
129 return encoded
142 return encoded
130
143
131
144
132 def json_clean(obj):
145 def json_clean(obj):
133 """Clean an object to ensure it's safe to encode in JSON.
146 """Clean an object to ensure it's safe to encode in JSON.
134
147
135 Atomic, immutable objects are returned unmodified. Sets and tuples are
148 Atomic, immutable objects are returned unmodified. Sets and tuples are
136 converted to lists, lists are copied and dicts are also copied.
149 converted to lists, lists are copied and dicts are also copied.
137
150
138 Note: dicts whose keys could cause collisions upon encoding (such as a dict
151 Note: dicts whose keys could cause collisions upon encoding (such as a dict
139 with both the number 1 and the string '1' as keys) will cause a ValueError
152 with both the number 1 and the string '1' as keys) will cause a ValueError
140 to be raised.
153 to be raised.
141
154
142 Parameters
155 Parameters
143 ----------
156 ----------
144 obj : any python object
157 obj : any python object
145
158
146 Returns
159 Returns
147 -------
160 -------
148 out : object
161 out : object
149
162
150 A version of the input which will not cause an encoding error when
163 A version of the input which will not cause an encoding error when
151 encoded as JSON. Note that this function does not *encode* its inputs,
164 encoded as JSON. Note that this function does not *encode* its inputs,
152 it simply sanitizes it so that there will be no encoding errors later.
165 it simply sanitizes it so that there will be no encoding errors later.
153
166
154 Examples
167 Examples
155 --------
168 --------
156 >>> json_clean(4)
169 >>> json_clean(4)
157 4
170 4
158 >>> json_clean(range(10))
171 >>> json_clean(range(10))
159 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
172 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
160 >>> sorted(json_clean(dict(x=1, y=2)).items())
173 >>> sorted(json_clean(dict(x=1, y=2)).items())
161 [('x', 1), ('y', 2)]
174 [('x', 1), ('y', 2)]
162 >>> sorted(json_clean(dict(x=1, y=2, z=[1,2,3])).items())
175 >>> sorted(json_clean(dict(x=1, y=2, z=[1,2,3])).items())
163 [('x', 1), ('y', 2), ('z', [1, 2, 3])]
176 [('x', 1), ('y', 2), ('z', [1, 2, 3])]
164 >>> json_clean(True)
177 >>> json_clean(True)
165 True
178 True
166 """
179 """
167 # types that are 'atomic' and ok in json as-is. bool doesn't need to be
180 # types that are 'atomic' and ok in json as-is. bool doesn't need to be
168 # listed explicitly because bools pass as int instances
181 # listed explicitly because bools pass as int instances
169 atomic_ok = (unicode, int, types.NoneType)
182 atomic_ok = (unicode, int, types.NoneType)
170
183
171 # containers that we need to convert into lists
184 # containers that we need to convert into lists
172 container_to_list = (tuple, set, types.GeneratorType)
185 container_to_list = (tuple, set, types.GeneratorType)
173
186
174 if isinstance(obj, float):
187 if isinstance(obj, float):
175 # cast out-of-range floats to their reprs
188 # cast out-of-range floats to their reprs
176 if math.isnan(obj) or math.isinf(obj):
189 if math.isnan(obj) or math.isinf(obj):
177 return repr(obj)
190 return repr(obj)
178 return obj
191 return obj
179
192
180 if isinstance(obj, atomic_ok):
193 if isinstance(obj, atomic_ok):
181 return obj
194 return obj
182
195
183 if isinstance(obj, bytes):
196 if isinstance(obj, bytes):
184 return obj.decode(DEFAULT_ENCODING, 'replace')
197 return obj.decode(DEFAULT_ENCODING, 'replace')
185
198
186 if isinstance(obj, container_to_list) or (
199 if isinstance(obj, container_to_list) or (
187 hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)):
200 hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)):
188 obj = list(obj)
201 obj = list(obj)
189
202
190 if isinstance(obj, list):
203 if isinstance(obj, list):
191 return [json_clean(x) for x in obj]
204 return [json_clean(x) for x in obj]
192
205
193 if isinstance(obj, dict):
206 if isinstance(obj, dict):
194 # First, validate that the dict won't lose data in conversion due to
207 # First, validate that the dict won't lose data in conversion due to
195 # key collisions after stringification. This can happen with keys like
208 # key collisions after stringification. This can happen with keys like
196 # True and 'true' or 1 and '1', which collide in JSON.
209 # True and 'true' or 1 and '1', which collide in JSON.
197 nkeys = len(obj)
210 nkeys = len(obj)
198 nkeys_collapsed = len(set(map(str, obj)))
211 nkeys_collapsed = len(set(map(str, obj)))
199 if nkeys != nkeys_collapsed:
212 if nkeys != nkeys_collapsed:
200 raise ValueError('dict can not be safely converted to JSON: '
213 raise ValueError('dict can not be safely converted to JSON: '
201 'key collision would lead to dropped values')
214 'key collision would lead to dropped values')
202 # If all OK, proceed by making the new dict that will be json-safe
215 # If all OK, proceed by making the new dict that will be json-safe
203 out = {}
216 out = {}
204 for k,v in obj.iteritems():
217 for k,v in obj.iteritems():
205 out[str(k)] = json_clean(v)
218 out[str(k)] = json_clean(v)
206 return out
219 return out
207
220
208 # If we get here, we don't know how to handle the object, so we just get
221 # If we get here, we don't know how to handle the object, so we just get
209 # its repr and return that. This will catch lambdas, open sockets, class
222 # its repr and return that. This will catch lambdas, open sockets, class
210 # objects, and any other complicated contraption that json can't encode
223 # objects, and any other complicated contraption that json can't encode
211 return repr(obj)
224 return repr(obj)
General Comments 0
You need to be logged in to leave comments. Login now