##// END OF EJS Templates
Adding PDFFormatter and kernel side handling of PDF display data.
Brian E. Granger -
Show More
@@ -1,701 +1,719 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 not raw:
113 if not raw:
114 format = InteractiveShell.instance().display_formatter.format
114 format = InteractiveShell.instance().display_formatter.format
115
115
116 for obj in objs:
116 for obj in objs:
117
117
118 # If _ipython_display_ is defined, use that to display this object.
118 # If _ipython_display_ is defined, use that to display this object.
119 display_method = getattr(obj, '_ipython_display_', None)
119 display_method = getattr(obj, '_ipython_display_', None)
120 if display_method is not None:
120 if display_method is not None:
121 try:
121 try:
122 display_method(**kwargs)
122 display_method(**kwargs)
123 except NotImplementedError:
123 except NotImplementedError:
124 pass
124 pass
125 else:
125 else:
126 continue
126 continue
127 if raw:
127 if raw:
128 publish_display_data('display', obj, metadata)
128 publish_display_data('display', obj, metadata)
129 else:
129 else:
130 format_dict, md_dict = format(obj, include=include, exclude=exclude)
130 format_dict, md_dict = format(obj, include=include, exclude=exclude)
131 if metadata:
131 if metadata:
132 # kwarg-specified metadata gets precedence
132 # kwarg-specified metadata gets precedence
133 _merge(md_dict, metadata)
133 _merge(md_dict, metadata)
134 publish_display_data('display', format_dict, md_dict)
134 publish_display_data('display', format_dict, md_dict)
135
135
136
136
137 def display_pretty(*objs, **kwargs):
137 def display_pretty(*objs, **kwargs):
138 """Display the pretty (default) representation of an object.
138 """Display the pretty (default) representation of an object.
139
139
140 Parameters
140 Parameters
141 ----------
141 ----------
142 objs : tuple of objects
142 objs : tuple of objects
143 The Python objects to display, or if raw=True raw text data to
143 The Python objects to display, or if raw=True raw text data to
144 display.
144 display.
145 raw : bool
145 raw : bool
146 Are the data objects raw data or Python objects that need to be
146 Are the data objects raw data or Python objects that need to be
147 formatted before display? [default: False]
147 formatted before display? [default: False]
148 metadata : dict (optional)
148 metadata : dict (optional)
149 Metadata to be associated with the specific mimetype output.
149 Metadata to be associated with the specific mimetype output.
150 """
150 """
151 _display_mimetype('text/plain', objs, **kwargs)
151 _display_mimetype('text/plain', objs, **kwargs)
152
152
153
153
154 def display_html(*objs, **kwargs):
154 def display_html(*objs, **kwargs):
155 """Display the HTML representation of an object.
155 """Display the HTML representation of an object.
156
156
157 Parameters
157 Parameters
158 ----------
158 ----------
159 objs : tuple of objects
159 objs : tuple of objects
160 The Python objects to display, or if raw=True raw HTML data to
160 The Python objects to display, or if raw=True raw HTML data to
161 display.
161 display.
162 raw : bool
162 raw : bool
163 Are the data objects raw data or Python objects that need to be
163 Are the data objects raw data or Python objects that need to be
164 formatted before display? [default: False]
164 formatted before display? [default: False]
165 metadata : dict (optional)
165 metadata : dict (optional)
166 Metadata to be associated with the specific mimetype output.
166 Metadata to be associated with the specific mimetype output.
167 """
167 """
168 _display_mimetype('text/html', objs, **kwargs)
168 _display_mimetype('text/html', objs, **kwargs)
169
169
170
170
171 def display_svg(*objs, **kwargs):
171 def display_svg(*objs, **kwargs):
172 """Display the SVG representation of an object.
172 """Display the SVG representation of an object.
173
173
174 Parameters
174 Parameters
175 ----------
175 ----------
176 objs : tuple of objects
176 objs : tuple of objects
177 The Python objects to display, or if raw=True raw svg data to
177 The Python objects to display, or if raw=True raw svg data to
178 display.
178 display.
179 raw : bool
179 raw : bool
180 Are the data objects raw data or Python objects that need to be
180 Are the data objects raw data or Python objects that need to be
181 formatted before display? [default: False]
181 formatted before display? [default: False]
182 metadata : dict (optional)
182 metadata : dict (optional)
183 Metadata to be associated with the specific mimetype output.
183 Metadata to be associated with the specific mimetype output.
184 """
184 """
185 _display_mimetype('image/svg+xml', objs, **kwargs)
185 _display_mimetype('image/svg+xml', objs, **kwargs)
186
186
187
187
188 def display_png(*objs, **kwargs):
188 def display_png(*objs, **kwargs):
189 """Display the PNG representation of an object.
189 """Display the PNG representation of an object.
190
190
191 Parameters
191 Parameters
192 ----------
192 ----------
193 objs : tuple of objects
193 objs : tuple of objects
194 The Python objects to display, or if raw=True raw png data to
194 The Python objects to display, or if raw=True raw png data to
195 display.
195 display.
196 raw : bool
196 raw : bool
197 Are the data objects raw data or Python objects that need to be
197 Are the data objects raw data or Python objects that need to be
198 formatted before display? [default: False]
198 formatted before display? [default: False]
199 metadata : dict (optional)
199 metadata : dict (optional)
200 Metadata to be associated with the specific mimetype output.
200 Metadata to be associated with the specific mimetype output.
201 """
201 """
202 _display_mimetype('image/png', objs, **kwargs)
202 _display_mimetype('image/png', objs, **kwargs)
203
203
204
204
205 def display_jpeg(*objs, **kwargs):
205 def display_jpeg(*objs, **kwargs):
206 """Display the JPEG representation of an object.
206 """Display the JPEG representation of an object.
207
207
208 Parameters
208 Parameters
209 ----------
209 ----------
210 objs : tuple of objects
210 objs : tuple of objects
211 The Python objects to display, or if raw=True raw JPEG data to
211 The Python objects to display, or if raw=True raw JPEG data to
212 display.
212 display.
213 raw : bool
213 raw : bool
214 Are the data objects raw data or Python objects that need to be
214 Are the data objects raw data or Python objects that need to be
215 formatted before display? [default: False]
215 formatted before display? [default: False]
216 metadata : dict (optional)
216 metadata : dict (optional)
217 Metadata to be associated with the specific mimetype output.
217 Metadata to be associated with the specific mimetype output.
218 """
218 """
219 _display_mimetype('image/jpeg', objs, **kwargs)
219 _display_mimetype('image/jpeg', objs, **kwargs)
220
220
221
221
222 def display_latex(*objs, **kwargs):
222 def display_latex(*objs, **kwargs):
223 """Display the LaTeX representation of an object.
223 """Display the LaTeX representation of an object.
224
224
225 Parameters
225 Parameters
226 ----------
226 ----------
227 objs : tuple of objects
227 objs : tuple of objects
228 The Python objects to display, or if raw=True raw latex data to
228 The Python objects to display, or if raw=True raw latex data to
229 display.
229 display.
230 raw : bool
230 raw : bool
231 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
232 formatted before display? [default: False]
232 formatted before display? [default: False]
233 metadata : dict (optional)
233 metadata : dict (optional)
234 Metadata to be associated with the specific mimetype output.
234 Metadata to be associated with the specific mimetype output.
235 """
235 """
236 _display_mimetype('text/latex', objs, **kwargs)
236 _display_mimetype('text/latex', objs, **kwargs)
237
237
238
238
239 def display_json(*objs, **kwargs):
239 def display_json(*objs, **kwargs):
240 """Display the JSON representation of an object.
240 """Display the JSON representation of an object.
241
241
242 Note that not many frontends support displaying JSON.
242 Note that not many frontends support displaying JSON.
243
243
244 Parameters
244 Parameters
245 ----------
245 ----------
246 objs : tuple of objects
246 objs : tuple of objects
247 The Python objects to display, or if raw=True raw json data to
247 The Python objects to display, or if raw=True raw json data to
248 display.
248 display.
249 raw : bool
249 raw : bool
250 Are the data objects raw data or Python objects that need to be
250 Are the data objects raw data or Python objects that need to be
251 formatted before display? [default: False]
251 formatted before display? [default: False]
252 metadata : dict (optional)
252 metadata : dict (optional)
253 Metadata to be associated with the specific mimetype output.
253 Metadata to be associated with the specific mimetype output.
254 """
254 """
255 _display_mimetype('application/json', objs, **kwargs)
255 _display_mimetype('application/json', objs, **kwargs)
256
256
257
257
258 def display_javascript(*objs, **kwargs):
258 def display_javascript(*objs, **kwargs):
259 """Display the Javascript representation of an object.
259 """Display the Javascript representation of an object.
260
260
261 Parameters
261 Parameters
262 ----------
262 ----------
263 objs : tuple of objects
263 objs : tuple of objects
264 The Python objects to display, or if raw=True raw javascript data to
264 The Python objects to display, or if raw=True raw javascript data to
265 display.
265 display.
266 raw : bool
266 raw : bool
267 Are the data objects raw data or Python objects that need to be
267 Are the data objects raw data or Python objects that need to be
268 formatted before display? [default: False]
268 formatted before display? [default: False]
269 metadata : dict (optional)
269 metadata : dict (optional)
270 Metadata to be associated with the specific mimetype output.
270 Metadata to be associated with the specific mimetype output.
271 """
271 """
272 _display_mimetype('application/javascript', objs, **kwargs)
272 _display_mimetype('application/javascript', objs, **kwargs)
273
273
274
275 def display_pdf(*objs, **kwargs):
276 """Display the PDF representation of an object.
277
278 Parameters
279 ----------
280 objs : tuple of objects
281 The Python objects to display, or if raw=True raw javascript data to
282 display.
283 raw : bool
284 Are the data objects raw data or Python objects that need to be
285 formatted before display? [default: False]
286 metadata : dict (optional)
287 Metadata to be associated with the specific mimetype output.
288 """
289 _display_mimetype('application/pdf', objs, **kwargs)
290
291
274 #-----------------------------------------------------------------------------
292 #-----------------------------------------------------------------------------
275 # Smart classes
293 # Smart classes
276 #-----------------------------------------------------------------------------
294 #-----------------------------------------------------------------------------
277
295
278
296
279 class DisplayObject(object):
297 class DisplayObject(object):
280 """An object that wraps data to be displayed."""
298 """An object that wraps data to be displayed."""
281
299
282 _read_flags = 'r'
300 _read_flags = 'r'
283
301
284 def __init__(self, data=None, url=None, filename=None):
302 def __init__(self, data=None, url=None, filename=None):
285 """Create a display object given raw data.
303 """Create a display object given raw data.
286
304
287 When this object is returned by an expression or passed to the
305 When this object is returned by an expression or passed to the
288 display function, it will result in the data being displayed
306 display function, it will result in the data being displayed
289 in the frontend. The MIME type of the data should match the
307 in the frontend. The MIME type of the data should match the
290 subclasses used, so the Png subclass should be used for 'image/png'
308 subclasses used, so the Png subclass should be used for 'image/png'
291 data. If the data is a URL, the data will first be downloaded
309 data. If the data is a URL, the data will first be downloaded
292 and then displayed. If
310 and then displayed. If
293
311
294 Parameters
312 Parameters
295 ----------
313 ----------
296 data : unicode, str or bytes
314 data : unicode, str or bytes
297 The raw data or a URL or file to load the data from
315 The raw data or a URL or file to load the data from
298 url : unicode
316 url : unicode
299 A URL to download the data from.
317 A URL to download the data from.
300 filename : unicode
318 filename : unicode
301 Path to a local file to load the data from.
319 Path to a local file to load the data from.
302 """
320 """
303 if data is not None and isinstance(data, string_types):
321 if data is not None and isinstance(data, string_types):
304 if data.startswith('http') and url is None:
322 if data.startswith('http') and url is None:
305 url = data
323 url = data
306 filename = None
324 filename = None
307 data = None
325 data = None
308 elif _safe_exists(data) and filename is None:
326 elif _safe_exists(data) and filename is None:
309 url = None
327 url = None
310 filename = data
328 filename = data
311 data = None
329 data = None
312
330
313 self.data = data
331 self.data = data
314 self.url = url
332 self.url = url
315 self.filename = None if filename is None else unicode_type(filename)
333 self.filename = None if filename is None else unicode_type(filename)
316
334
317 self.reload()
335 self.reload()
318 self._check_data()
336 self._check_data()
319
337
320 def _check_data(self):
338 def _check_data(self):
321 """Override in subclasses if there's something to check."""
339 """Override in subclasses if there's something to check."""
322 pass
340 pass
323
341
324 def reload(self):
342 def reload(self):
325 """Reload the raw data from file or URL."""
343 """Reload the raw data from file or URL."""
326 if self.filename is not None:
344 if self.filename is not None:
327 with open(self.filename, self._read_flags) as f:
345 with open(self.filename, self._read_flags) as f:
328 self.data = f.read()
346 self.data = f.read()
329 elif self.url is not None:
347 elif self.url is not None:
330 try:
348 try:
331 try:
349 try:
332 from urllib.request import urlopen # Py3
350 from urllib.request import urlopen # Py3
333 except ImportError:
351 except ImportError:
334 from urllib2 import urlopen
352 from urllib2 import urlopen
335 response = urlopen(self.url)
353 response = urlopen(self.url)
336 self.data = response.read()
354 self.data = response.read()
337 # extract encoding from header, if there is one:
355 # extract encoding from header, if there is one:
338 encoding = None
356 encoding = None
339 for sub in response.headers['content-type'].split(';'):
357 for sub in response.headers['content-type'].split(';'):
340 sub = sub.strip()
358 sub = sub.strip()
341 if sub.startswith('charset'):
359 if sub.startswith('charset'):
342 encoding = sub.split('=')[-1].strip()
360 encoding = sub.split('=')[-1].strip()
343 break
361 break
344 # decode data, if an encoding was specified
362 # decode data, if an encoding was specified
345 if encoding:
363 if encoding:
346 self.data = self.data.decode(encoding, 'replace')
364 self.data = self.data.decode(encoding, 'replace')
347 except:
365 except:
348 self.data = None
366 self.data = None
349
367
350 class TextDisplayObject(DisplayObject):
368 class TextDisplayObject(DisplayObject):
351 """Validate that display data is text"""
369 """Validate that display data is text"""
352 def _check_data(self):
370 def _check_data(self):
353 if self.data is not None and not isinstance(self.data, string_types):
371 if self.data is not None and not isinstance(self.data, string_types):
354 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
372 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
355
373
356 class Pretty(TextDisplayObject):
374 class Pretty(TextDisplayObject):
357
375
358 def _repr_pretty_(self):
376 def _repr_pretty_(self):
359 return self.data
377 return self.data
360
378
361
379
362 class HTML(TextDisplayObject):
380 class HTML(TextDisplayObject):
363
381
364 def _repr_html_(self):
382 def _repr_html_(self):
365 return self.data
383 return self.data
366
384
367 def __html__(self):
385 def __html__(self):
368 """
386 """
369 This method exists to inform other HTML-using modules (e.g. Markupsafe,
387 This method exists to inform other HTML-using modules (e.g. Markupsafe,
370 htmltag, etc) that this object is HTML and does not need things like
388 htmltag, etc) that this object is HTML and does not need things like
371 special characters (<>&) escaped.
389 special characters (<>&) escaped.
372 """
390 """
373 return self._repr_html_()
391 return self._repr_html_()
374
392
375
393
376 class Math(TextDisplayObject):
394 class Math(TextDisplayObject):
377
395
378 def _repr_latex_(self):
396 def _repr_latex_(self):
379 s = self.data.strip('$')
397 s = self.data.strip('$')
380 return "$$%s$$" % s
398 return "$$%s$$" % s
381
399
382
400
383 class Latex(TextDisplayObject):
401 class Latex(TextDisplayObject):
384
402
385 def _repr_latex_(self):
403 def _repr_latex_(self):
386 return self.data
404 return self.data
387
405
388
406
389 class SVG(DisplayObject):
407 class SVG(DisplayObject):
390
408
391 # wrap data in a property, which extracts the <svg> tag, discarding
409 # wrap data in a property, which extracts the <svg> tag, discarding
392 # document headers
410 # document headers
393 _data = None
411 _data = None
394
412
395 @property
413 @property
396 def data(self):
414 def data(self):
397 return self._data
415 return self._data
398
416
399 @data.setter
417 @data.setter
400 def data(self, svg):
418 def data(self, svg):
401 if svg is None:
419 if svg is None:
402 self._data = None
420 self._data = None
403 return
421 return
404 # parse into dom object
422 # parse into dom object
405 from xml.dom import minidom
423 from xml.dom import minidom
406 svg = cast_bytes_py2(svg)
424 svg = cast_bytes_py2(svg)
407 x = minidom.parseString(svg)
425 x = minidom.parseString(svg)
408 # get svg tag (should be 1)
426 # get svg tag (should be 1)
409 found_svg = x.getElementsByTagName('svg')
427 found_svg = x.getElementsByTagName('svg')
410 if found_svg:
428 if found_svg:
411 svg = found_svg[0].toxml()
429 svg = found_svg[0].toxml()
412 else:
430 else:
413 # fallback on the input, trust the user
431 # fallback on the input, trust the user
414 # but this is probably an error.
432 # but this is probably an error.
415 pass
433 pass
416 svg = cast_unicode(svg)
434 svg = cast_unicode(svg)
417 self._data = svg
435 self._data = svg
418
436
419 def _repr_svg_(self):
437 def _repr_svg_(self):
420 return self.data
438 return self.data
421
439
422
440
423 class JSON(TextDisplayObject):
441 class JSON(TextDisplayObject):
424
442
425 def _repr_json_(self):
443 def _repr_json_(self):
426 return self.data
444 return self.data
427
445
428 css_t = """$("head").append($("<link/>").attr({
446 css_t = """$("head").append($("<link/>").attr({
429 rel: "stylesheet",
447 rel: "stylesheet",
430 type: "text/css",
448 type: "text/css",
431 href: "%s"
449 href: "%s"
432 }));
450 }));
433 """
451 """
434
452
435 lib_t1 = """$.getScript("%s", function () {
453 lib_t1 = """$.getScript("%s", function () {
436 """
454 """
437 lib_t2 = """});
455 lib_t2 = """});
438 """
456 """
439
457
440 class Javascript(TextDisplayObject):
458 class Javascript(TextDisplayObject):
441
459
442 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
460 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
443 """Create a Javascript display object given raw data.
461 """Create a Javascript display object given raw data.
444
462
445 When this object is returned by an expression or passed to the
463 When this object is returned by an expression or passed to the
446 display function, it will result in the data being displayed
464 display function, it will result in the data being displayed
447 in the frontend. If the data is a URL, the data will first be
465 in the frontend. If the data is a URL, the data will first be
448 downloaded and then displayed.
466 downloaded and then displayed.
449
467
450 In the Notebook, the containing element will be available as `element`,
468 In the Notebook, the containing element will be available as `element`,
451 and jQuery will be available. The output area starts hidden, so if
469 and jQuery will be available. The output area starts hidden, so if
452 the js appends content to `element` that should be visible, then
470 the js appends content to `element` that should be visible, then
453 it must call `container.show()` to unhide the area.
471 it must call `container.show()` to unhide the area.
454
472
455 Parameters
473 Parameters
456 ----------
474 ----------
457 data : unicode, str or bytes
475 data : unicode, str or bytes
458 The Javascript source code or a URL to download it from.
476 The Javascript source code or a URL to download it from.
459 url : unicode
477 url : unicode
460 A URL to download the data from.
478 A URL to download the data from.
461 filename : unicode
479 filename : unicode
462 Path to a local file to load the data from.
480 Path to a local file to load the data from.
463 lib : list or str
481 lib : list or str
464 A sequence of Javascript library URLs to load asynchronously before
482 A sequence of Javascript library URLs to load asynchronously before
465 running the source code. The full URLs of the libraries should
483 running the source code. The full URLs of the libraries should
466 be given. A single Javascript library URL can also be given as a
484 be given. A single Javascript library URL can also be given as a
467 string.
485 string.
468 css: : list or str
486 css: : list or str
469 A sequence of css files to load before running the source code.
487 A sequence of css files to load before running the source code.
470 The full URLs of the css files should be given. A single css URL
488 The full URLs of the css files should be given. A single css URL
471 can also be given as a string.
489 can also be given as a string.
472 """
490 """
473 if isinstance(lib, string_types):
491 if isinstance(lib, string_types):
474 lib = [lib]
492 lib = [lib]
475 elif lib is None:
493 elif lib is None:
476 lib = []
494 lib = []
477 if isinstance(css, string_types):
495 if isinstance(css, string_types):
478 css = [css]
496 css = [css]
479 elif css is None:
497 elif css is None:
480 css = []
498 css = []
481 if not isinstance(lib, (list,tuple)):
499 if not isinstance(lib, (list,tuple)):
482 raise TypeError('expected sequence, got: %r' % lib)
500 raise TypeError('expected sequence, got: %r' % lib)
483 if not isinstance(css, (list,tuple)):
501 if not isinstance(css, (list,tuple)):
484 raise TypeError('expected sequence, got: %r' % css)
502 raise TypeError('expected sequence, got: %r' % css)
485 self.lib = lib
503 self.lib = lib
486 self.css = css
504 self.css = css
487 super(Javascript, self).__init__(data=data, url=url, filename=filename)
505 super(Javascript, self).__init__(data=data, url=url, filename=filename)
488
506
489 def _repr_javascript_(self):
507 def _repr_javascript_(self):
490 r = ''
508 r = ''
491 for c in self.css:
509 for c in self.css:
492 r += css_t % c
510 r += css_t % c
493 for l in self.lib:
511 for l in self.lib:
494 r += lib_t1 % l
512 r += lib_t1 % l
495 r += self.data
513 r += self.data
496 r += lib_t2*len(self.lib)
514 r += lib_t2*len(self.lib)
497 return r
515 return r
498
516
499 # constants for identifying png/jpeg data
517 # constants for identifying png/jpeg data
500 _PNG = b'\x89PNG\r\n\x1a\n'
518 _PNG = b'\x89PNG\r\n\x1a\n'
501 _JPEG = b'\xff\xd8'
519 _JPEG = b'\xff\xd8'
502
520
503 def _pngxy(data):
521 def _pngxy(data):
504 """read the (width, height) from a PNG header"""
522 """read the (width, height) from a PNG header"""
505 ihdr = data.index(b'IHDR')
523 ihdr = data.index(b'IHDR')
506 # next 8 bytes are width/height
524 # next 8 bytes are width/height
507 w4h4 = data[ihdr+4:ihdr+12]
525 w4h4 = data[ihdr+4:ihdr+12]
508 return struct.unpack('>ii', w4h4)
526 return struct.unpack('>ii', w4h4)
509
527
510 def _jpegxy(data):
528 def _jpegxy(data):
511 """read the (width, height) from a JPEG header"""
529 """read the (width, height) from a JPEG header"""
512 # adapted from http://www.64lines.com/jpeg-width-height
530 # adapted from http://www.64lines.com/jpeg-width-height
513
531
514 idx = 4
532 idx = 4
515 while True:
533 while True:
516 block_size = struct.unpack('>H', data[idx:idx+2])[0]
534 block_size = struct.unpack('>H', data[idx:idx+2])[0]
517 idx = idx + block_size
535 idx = idx + block_size
518 if data[idx:idx+2] == b'\xFF\xC0':
536 if data[idx:idx+2] == b'\xFF\xC0':
519 # found Start of Frame
537 # found Start of Frame
520 iSOF = idx
538 iSOF = idx
521 break
539 break
522 else:
540 else:
523 # read another block
541 # read another block
524 idx += 2
542 idx += 2
525
543
526 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
544 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
527 return w, h
545 return w, h
528
546
529 class Image(DisplayObject):
547 class Image(DisplayObject):
530
548
531 _read_flags = 'rb'
549 _read_flags = 'rb'
532 _FMT_JPEG = u'jpeg'
550 _FMT_JPEG = u'jpeg'
533 _FMT_PNG = u'png'
551 _FMT_PNG = u'png'
534 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
552 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
535
553
536 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
554 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
537 """Create a PNG/JPEG image object given raw data.
555 """Create a PNG/JPEG image object given raw data.
538
556
539 When this object is returned by an input cell or passed to the
557 When this object is returned by an input cell or passed to the
540 display function, it will result in the image being displayed
558 display function, it will result in the image being displayed
541 in the frontend.
559 in the frontend.
542
560
543 Parameters
561 Parameters
544 ----------
562 ----------
545 data : unicode, str or bytes
563 data : unicode, str or bytes
546 The raw image data or a URL or filename to load the data from.
564 The raw image data or a URL or filename to load the data from.
547 This always results in embedded image data.
565 This always results in embedded image data.
548 url : unicode
566 url : unicode
549 A URL to download the data from. If you specify `url=`,
567 A URL to download the data from. If you specify `url=`,
550 the image data will not be embedded unless you also specify `embed=True`.
568 the image data will not be embedded unless you also specify `embed=True`.
551 filename : unicode
569 filename : unicode
552 Path to a local file to load the data from.
570 Path to a local file to load the data from.
553 Images from a file are always embedded.
571 Images from a file are always embedded.
554 format : unicode
572 format : unicode
555 The format of the image data (png/jpeg/jpg). If a filename or URL is given
573 The format of the image data (png/jpeg/jpg). If a filename or URL is given
556 for format will be inferred from the filename extension.
574 for format will be inferred from the filename extension.
557 embed : bool
575 embed : bool
558 Should the image data be embedded using a data URI (True) or be
576 Should the image data be embedded using a data URI (True) or be
559 loaded using an <img> tag. Set this to True if you want the image
577 loaded using an <img> tag. Set this to True if you want the image
560 to be viewable later with no internet connection in the notebook.
578 to be viewable later with no internet connection in the notebook.
561
579
562 Default is `True`, unless the keyword argument `url` is set, then
580 Default is `True`, unless the keyword argument `url` is set, then
563 default value is `False`.
581 default value is `False`.
564
582
565 Note that QtConsole is not able to display images if `embed` is set to `False`
583 Note that QtConsole is not able to display images if `embed` is set to `False`
566 width : int
584 width : int
567 Width to which to constrain the image in html
585 Width to which to constrain the image in html
568 height : int
586 height : int
569 Height to which to constrain the image in html
587 Height to which to constrain the image in html
570 retina : bool
588 retina : bool
571 Automatically set the width and height to half of the measured
589 Automatically set the width and height to half of the measured
572 width and height.
590 width and height.
573 This only works for embedded images because it reads the width/height
591 This only works for embedded images because it reads the width/height
574 from image data.
592 from image data.
575 For non-embedded images, you can just set the desired display width
593 For non-embedded images, you can just set the desired display width
576 and height directly.
594 and height directly.
577
595
578 Examples
596 Examples
579 --------
597 --------
580 # embedded image data, works in qtconsole and notebook
598 # embedded image data, works in qtconsole and notebook
581 # when passed positionally, the first arg can be any of raw image data,
599 # when passed positionally, the first arg can be any of raw image data,
582 # a URL, or a filename from which to load image data.
600 # a URL, or a filename from which to load image data.
583 # The result is always embedding image data for inline images.
601 # The result is always embedding image data for inline images.
584 Image('http://www.google.fr/images/srpr/logo3w.png')
602 Image('http://www.google.fr/images/srpr/logo3w.png')
585 Image('/path/to/image.jpg')
603 Image('/path/to/image.jpg')
586 Image(b'RAW_PNG_DATA...')
604 Image(b'RAW_PNG_DATA...')
587
605
588 # Specifying Image(url=...) does not embed the image data,
606 # Specifying Image(url=...) does not embed the image data,
589 # it only generates `<img>` tag with a link to the source.
607 # it only generates `<img>` tag with a link to the source.
590 # This will not work in the qtconsole or offline.
608 # This will not work in the qtconsole or offline.
591 Image(url='http://www.google.fr/images/srpr/logo3w.png')
609 Image(url='http://www.google.fr/images/srpr/logo3w.png')
592
610
593 """
611 """
594 if filename is not None:
612 if filename is not None:
595 ext = self._find_ext(filename)
613 ext = self._find_ext(filename)
596 elif url is not None:
614 elif url is not None:
597 ext = self._find_ext(url)
615 ext = self._find_ext(url)
598 elif data is None:
616 elif data is None:
599 raise ValueError("No image data found. Expecting filename, url, or data.")
617 raise ValueError("No image data found. Expecting filename, url, or data.")
600 elif isinstance(data, string_types) and (
618 elif isinstance(data, string_types) and (
601 data.startswith('http') or _safe_exists(data)
619 data.startswith('http') or _safe_exists(data)
602 ):
620 ):
603 ext = self._find_ext(data)
621 ext = self._find_ext(data)
604 else:
622 else:
605 ext = None
623 ext = None
606
624
607 if ext is not None:
625 if ext is not None:
608 format = ext.lower()
626 format = ext.lower()
609 if ext == u'jpg' or ext == u'jpeg':
627 if ext == u'jpg' or ext == u'jpeg':
610 format = self._FMT_JPEG
628 format = self._FMT_JPEG
611 if ext == u'png':
629 if ext == u'png':
612 format = self._FMT_PNG
630 format = self._FMT_PNG
613 elif isinstance(data, bytes) and format == 'png':
631 elif isinstance(data, bytes) and format == 'png':
614 # infer image type from image data header,
632 # infer image type from image data header,
615 # only if format might not have been specified.
633 # only if format might not have been specified.
616 if data[:2] == _JPEG:
634 if data[:2] == _JPEG:
617 format = 'jpeg'
635 format = 'jpeg'
618
636
619 self.format = unicode_type(format).lower()
637 self.format = unicode_type(format).lower()
620 self.embed = embed if embed is not None else (url is None)
638 self.embed = embed if embed is not None else (url is None)
621
639
622 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
640 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
623 raise ValueError("Cannot embed the '%s' image format" % (self.format))
641 raise ValueError("Cannot embed the '%s' image format" % (self.format))
624 self.width = width
642 self.width = width
625 self.height = height
643 self.height = height
626 self.retina = retina
644 self.retina = retina
627 super(Image, self).__init__(data=data, url=url, filename=filename)
645 super(Image, self).__init__(data=data, url=url, filename=filename)
628
646
629 if retina:
647 if retina:
630 self._retina_shape()
648 self._retina_shape()
631
649
632 def _retina_shape(self):
650 def _retina_shape(self):
633 """load pixel-doubled width and height from image data"""
651 """load pixel-doubled width and height from image data"""
634 if not self.embed:
652 if not self.embed:
635 return
653 return
636 if self.format == 'png':
654 if self.format == 'png':
637 w, h = _pngxy(self.data)
655 w, h = _pngxy(self.data)
638 elif self.format == 'jpeg':
656 elif self.format == 'jpeg':
639 w, h = _jpegxy(self.data)
657 w, h = _jpegxy(self.data)
640 else:
658 else:
641 # retina only supports png
659 # retina only supports png
642 return
660 return
643 self.width = w // 2
661 self.width = w // 2
644 self.height = h // 2
662 self.height = h // 2
645
663
646 def reload(self):
664 def reload(self):
647 """Reload the raw data from file or URL."""
665 """Reload the raw data from file or URL."""
648 if self.embed:
666 if self.embed:
649 super(Image,self).reload()
667 super(Image,self).reload()
650 if self.retina:
668 if self.retina:
651 self._retina_shape()
669 self._retina_shape()
652
670
653 def _repr_html_(self):
671 def _repr_html_(self):
654 if not self.embed:
672 if not self.embed:
655 width = height = ''
673 width = height = ''
656 if self.width:
674 if self.width:
657 width = ' width="%d"' % self.width
675 width = ' width="%d"' % self.width
658 if self.height:
676 if self.height:
659 height = ' height="%d"' % self.height
677 height = ' height="%d"' % self.height
660 return u'<img src="%s"%s%s/>' % (self.url, width, height)
678 return u'<img src="%s"%s%s/>' % (self.url, width, height)
661
679
662 def _data_and_metadata(self):
680 def _data_and_metadata(self):
663 """shortcut for returning metadata with shape information, if defined"""
681 """shortcut for returning metadata with shape information, if defined"""
664 md = {}
682 md = {}
665 if self.width:
683 if self.width:
666 md['width'] = self.width
684 md['width'] = self.width
667 if self.height:
685 if self.height:
668 md['height'] = self.height
686 md['height'] = self.height
669 if md:
687 if md:
670 return self.data, md
688 return self.data, md
671 else:
689 else:
672 return self.data
690 return self.data
673
691
674 def _repr_png_(self):
692 def _repr_png_(self):
675 if self.embed and self.format == u'png':
693 if self.embed and self.format == u'png':
676 return self._data_and_metadata()
694 return self._data_and_metadata()
677
695
678 def _repr_jpeg_(self):
696 def _repr_jpeg_(self):
679 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
697 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
680 return self._data_and_metadata()
698 return self._data_and_metadata()
681
699
682 def _find_ext(self, s):
700 def _find_ext(self, s):
683 return unicode_type(s.split('.')[-1].lower())
701 return unicode_type(s.split('.')[-1].lower())
684
702
685
703
686 def clear_output(wait=False):
704 def clear_output(wait=False):
687 """Clear the output of the current cell receiving output.
705 """Clear the output of the current cell receiving output.
688
706
689 Parameters
707 Parameters
690 ----------
708 ----------
691 wait : bool [default: false]
709 wait : bool [default: false]
692 Wait to clear the output until new output is available to replace it."""
710 Wait to clear the output until new output is available to replace it."""
693 from IPython.core.interactiveshell import InteractiveShell
711 from IPython.core.interactiveshell import InteractiveShell
694 if InteractiveShell.initialized():
712 if InteractiveShell.initialized():
695 InteractiveShell.instance().display_pub.clear_output(wait)
713 InteractiveShell.instance().display_pub.clear_output(wait)
696 else:
714 else:
697 from IPython.utils import io
715 from IPython.utils import io
698 print('\033[2K\r', file=io.stdout, end='')
716 print('\033[2K\r', file=io.stdout, end='')
699 io.stdout.flush()
717 io.stdout.flush()
700 print('\033[2K\r', file=io.stderr, end='')
718 print('\033[2K\r', file=io.stderr, end='')
701 io.stderr.flush()
719 io.stderr.flush()
@@ -1,825 +1,846 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Display formatters.
2 """Display formatters.
3
3
4 Inheritance diagram:
4 Inheritance diagram:
5
5
6 .. inheritance-diagram:: IPython.core.formatters
6 .. inheritance-diagram:: IPython.core.formatters
7 :parts: 3
7 :parts: 3
8
8
9 Authors:
9 Authors:
10
10
11 * Robert Kern
11 * Robert Kern
12 * Brian Granger
12 * Brian Granger
13 """
13 """
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Copyright (C) 2010-2011, IPython Development Team.
15 # Copyright (C) 2010-2011, IPython Development Team.
16 #
16 #
17 # Distributed under the terms of the Modified BSD License.
17 # Distributed under the terms of the Modified BSD License.
18 #
18 #
19 # The full license is in the file COPYING.txt, distributed with this software.
19 # The full license is in the file COPYING.txt, distributed with this software.
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Imports
23 # Imports
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 # Stdlib imports
26 # Stdlib imports
27 import abc
27 import abc
28 import sys
28 import sys
29 import warnings
29 import warnings
30
30
31 from IPython.external.decorator import decorator
31 from IPython.external.decorator import decorator
32
32
33 # Our own imports
33 # Our own imports
34 from IPython.config.configurable import Configurable
34 from IPython.config.configurable import Configurable
35 from IPython.lib import pretty
35 from IPython.lib import pretty
36 from IPython.utils import io
36 from IPython.utils import io
37 from IPython.utils.traitlets import (
37 from IPython.utils.traitlets import (
38 Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List,
38 Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List,
39 )
39 )
40 from IPython.utils.warn import warn
40 from IPython.utils.warn import warn
41 from IPython.utils.py3compat import (
41 from IPython.utils.py3compat import (
42 unicode_to_str, with_metaclass, PY3, string_types, unicode_type,
42 unicode_to_str, with_metaclass, PY3, string_types, unicode_type,
43 )
43 )
44
44
45 if PY3:
45 if PY3:
46 from io import StringIO
46 from io import StringIO
47 else:
47 else:
48 from StringIO import StringIO
48 from StringIO import StringIO
49
49
50
50
51 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
52 # The main DisplayFormatter class
52 # The main DisplayFormatter class
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54
54
55 class DisplayFormatter(Configurable):
55 class DisplayFormatter(Configurable):
56
56
57 # When set to true only the default plain text formatter will be used.
57 # When set to true only the default plain text formatter will be used.
58 plain_text_only = Bool(False, config=True)
58 plain_text_only = Bool(False, config=True)
59 def _plain_text_only_changed(self, name, old, new):
59 def _plain_text_only_changed(self, name, old, new):
60 warnings.warn("""DisplayFormatter.plain_text_only is deprecated.
60 warnings.warn("""DisplayFormatter.plain_text_only is deprecated.
61
61
62 Use DisplayFormatter.active_types = ['text/plain']
62 Use DisplayFormatter.active_types = ['text/plain']
63 for the same effect.
63 for the same effect.
64 """, DeprecationWarning)
64 """, DeprecationWarning)
65 if new:
65 if new:
66 self.active_types = ['text/plain']
66 self.active_types = ['text/plain']
67 else:
67 else:
68 self.active_types = self.format_types
68 self.active_types = self.format_types
69
69
70 active_types = List(Unicode, config=True,
70 active_types = List(Unicode, config=True,
71 help="""List of currently active mime-types to display.
71 help="""List of currently active mime-types to display.
72 You can use this to set a white-list for formats to display.
72 You can use this to set a white-list for formats to display.
73
73
74 Most users will not need to change this value.
74 Most users will not need to change this value.
75 """)
75 """)
76 def _active_types_default(self):
76 def _active_types_default(self):
77 return self.format_types
77 return self.format_types
78
78
79 def _active_types_changed(self, name, old, new):
79 def _active_types_changed(self, name, old, new):
80 for key, formatter in self.formatters.items():
80 for key, formatter in self.formatters.items():
81 if key in new:
81 if key in new:
82 formatter.enabled = True
82 formatter.enabled = True
83 else:
83 else:
84 formatter.enabled = False
84 formatter.enabled = False
85
85
86 # A dict of formatter whose keys are format types (MIME types) and whose
86 # A dict of formatter whose keys are format types (MIME types) and whose
87 # values are subclasses of BaseFormatter.
87 # values are subclasses of BaseFormatter.
88 formatters = Dict()
88 formatters = Dict()
89 def _formatters_default(self):
89 def _formatters_default(self):
90 """Activate the default formatters."""
90 """Activate the default formatters."""
91 formatter_classes = [
91 formatter_classes = [
92 PlainTextFormatter,
92 PlainTextFormatter,
93 HTMLFormatter,
93 HTMLFormatter,
94 SVGFormatter,
94 SVGFormatter,
95 PNGFormatter,
95 PNGFormatter,
96 PDFFormatter,
96 JPEGFormatter,
97 JPEGFormatter,
97 LatexFormatter,
98 LatexFormatter,
98 JSONFormatter,
99 JSONFormatter,
99 JavascriptFormatter
100 JavascriptFormatter
100 ]
101 ]
101 d = {}
102 d = {}
102 for cls in formatter_classes:
103 for cls in formatter_classes:
103 f = cls(parent=self)
104 f = cls(parent=self)
104 d[f.format_type] = f
105 d[f.format_type] = f
105 return d
106 return d
106
107
107 def format(self, obj, include=None, exclude=None):
108 def format(self, obj, include=None, exclude=None):
108 """Return a format data dict for an object.
109 """Return a format data dict for an object.
109
110
110 By default all format types will be computed.
111 By default all format types will be computed.
111
112
112 The following MIME types are currently implemented:
113 The following MIME types are currently implemented:
113
114
114 * text/plain
115 * text/plain
115 * text/html
116 * text/html
116 * text/latex
117 * text/latex
117 * application/json
118 * application/json
118 * application/javascript
119 * application/javascript
120 * application/pdf
119 * image/png
121 * image/png
120 * image/jpeg
122 * image/jpeg
121 * image/svg+xml
123 * image/svg+xml
122
124
123 Parameters
125 Parameters
124 ----------
126 ----------
125 obj : object
127 obj : object
126 The Python object whose format data will be computed.
128 The Python object whose format data will be computed.
127 include : list or tuple, optional
129 include : list or tuple, optional
128 A list of format type strings (MIME types) to include in the
130 A list of format type strings (MIME types) to include in the
129 format data dict. If this is set *only* the format types included
131 format data dict. If this is set *only* the format types included
130 in this list will be computed.
132 in this list will be computed.
131 exclude : list or tuple, optional
133 exclude : list or tuple, optional
132 A list of format type string (MIME types) to exclude in the format
134 A list of format type string (MIME types) to exclude in the format
133 data dict. If this is set all format types will be computed,
135 data dict. If this is set all format types will be computed,
134 except for those included in this argument.
136 except for those included in this argument.
135
137
136 Returns
138 Returns
137 -------
139 -------
138 (format_dict, metadata_dict) : tuple of two dicts
140 (format_dict, metadata_dict) : tuple of two dicts
139
141
140 format_dict is a dictionary of key/value pairs, one of each format that was
142 format_dict is a dictionary of key/value pairs, one of each format that was
141 generated for the object. The keys are the format types, which
143 generated for the object. The keys are the format types, which
142 will usually be MIME type strings and the values and JSON'able
144 will usually be MIME type strings and the values and JSON'able
143 data structure containing the raw data for the representation in
145 data structure containing the raw data for the representation in
144 that format.
146 that format.
145
147
146 metadata_dict is a dictionary of metadata about each mime-type output.
148 metadata_dict is a dictionary of metadata about each mime-type output.
147 Its keys will be a strict subset of the keys in format_dict.
149 Its keys will be a strict subset of the keys in format_dict.
148 """
150 """
149 format_dict = {}
151 format_dict = {}
150 md_dict = {}
152 md_dict = {}
151
153
152 for format_type, formatter in self.formatters.items():
154 for format_type, formatter in self.formatters.items():
153 if include and format_type not in include:
155 if include and format_type not in include:
154 continue
156 continue
155 if exclude and format_type in exclude:
157 if exclude and format_type in exclude:
156 continue
158 continue
157
159
158 md = None
160 md = None
159 try:
161 try:
160 data = formatter(obj)
162 data = formatter(obj)
161 except:
163 except:
162 # FIXME: log the exception
164 # FIXME: log the exception
163 raise
165 raise
164
166
165 # formatters can return raw data or (data, metadata)
167 # formatters can return raw data or (data, metadata)
166 if isinstance(data, tuple) and len(data) == 2:
168 if isinstance(data, tuple) and len(data) == 2:
167 data, md = data
169 data, md = data
168
170
169 if data is not None:
171 if data is not None:
170 format_dict[format_type] = data
172 format_dict[format_type] = data
171 if md is not None:
173 if md is not None:
172 md_dict[format_type] = md
174 md_dict[format_type] = md
173
175
174 return format_dict, md_dict
176 return format_dict, md_dict
175
177
176 @property
178 @property
177 def format_types(self):
179 def format_types(self):
178 """Return the format types (MIME types) of the active formatters."""
180 """Return the format types (MIME types) of the active formatters."""
179 return list(self.formatters.keys())
181 return list(self.formatters.keys())
180
182
181
183
182 #-----------------------------------------------------------------------------
184 #-----------------------------------------------------------------------------
183 # Formatters for specific format types (text, html, svg, etc.)
185 # Formatters for specific format types (text, html, svg, etc.)
184 #-----------------------------------------------------------------------------
186 #-----------------------------------------------------------------------------
185
187
186 class FormatterWarning(UserWarning):
188 class FormatterWarning(UserWarning):
187 """Warning class for errors in formatters"""
189 """Warning class for errors in formatters"""
188
190
189 @decorator
191 @decorator
190 def warn_format_error(method, self, *args, **kwargs):
192 def warn_format_error(method, self, *args, **kwargs):
191 """decorator for warning on failed format call"""
193 """decorator for warning on failed format call"""
192 try:
194 try:
193 r = method(self, *args, **kwargs)
195 r = method(self, *args, **kwargs)
194 except NotImplementedError as e:
196 except NotImplementedError as e:
195 # don't warn on NotImplementedErrors
197 # don't warn on NotImplementedErrors
196 return None
198 return None
197 except Exception as e:
199 except Exception as e:
198 warnings.warn("Exception in %s formatter: %s" % (self.format_type, e),
200 warnings.warn("Exception in %s formatter: %s" % (self.format_type, e),
199 FormatterWarning,
201 FormatterWarning,
200 )
202 )
201 return None
203 return None
202 if r is None or isinstance(r, self._return_type) or \
204 if r is None or isinstance(r, self._return_type) or \
203 (isinstance(r, tuple) and r and isinstance(r[0], self._return_type)):
205 (isinstance(r, tuple) and r and isinstance(r[0], self._return_type)):
204 return r
206 return r
205 else:
207 else:
206 warnings.warn(
208 warnings.warn(
207 "%s formatter returned invalid type %s (expected %s) for object: %s" % \
209 "%s formatter returned invalid type %s (expected %s) for object: %s" % \
208 (self.format_type, type(r), self._return_type, pretty._safe_repr(args[0])),
210 (self.format_type, type(r), self._return_type, pretty._safe_repr(args[0])),
209 FormatterWarning
211 FormatterWarning
210 )
212 )
211
213
212
214
213 class FormatterABC(with_metaclass(abc.ABCMeta, object)):
215 class FormatterABC(with_metaclass(abc.ABCMeta, object)):
214 """ Abstract base class for Formatters.
216 """ Abstract base class for Formatters.
215
217
216 A formatter is a callable class that is responsible for computing the
218 A formatter is a callable class that is responsible for computing the
217 raw format data for a particular format type (MIME type). For example,
219 raw format data for a particular format type (MIME type). For example,
218 an HTML formatter would have a format type of `text/html` and would return
220 an HTML formatter would have a format type of `text/html` and would return
219 the HTML representation of the object when called.
221 the HTML representation of the object when called.
220 """
222 """
221
223
222 # The format type of the data returned, usually a MIME type.
224 # The format type of the data returned, usually a MIME type.
223 format_type = 'text/plain'
225 format_type = 'text/plain'
224
226
225 # Is the formatter enabled...
227 # Is the formatter enabled...
226 enabled = True
228 enabled = True
227
229
228 @abc.abstractmethod
230 @abc.abstractmethod
229 @warn_format_error
231 @warn_format_error
230 def __call__(self, obj):
232 def __call__(self, obj):
231 """Return a JSON'able representation of the object.
233 """Return a JSON'able representation of the object.
232
234
233 If the object cannot be formatted by this formatter,
235 If the object cannot be formatted by this formatter,
234 warn and return None.
236 warn and return None.
235 """
237 """
236 return repr(obj)
238 return repr(obj)
237
239
238
240
239 def _mod_name_key(typ):
241 def _mod_name_key(typ):
240 """Return a (__module__, __name__) tuple for a type.
242 """Return a (__module__, __name__) tuple for a type.
241
243
242 Used as key in Formatter.deferred_printers.
244 Used as key in Formatter.deferred_printers.
243 """
245 """
244 module = getattr(typ, '__module__', None)
246 module = getattr(typ, '__module__', None)
245 name = getattr(typ, '__name__', None)
247 name = getattr(typ, '__name__', None)
246 return (module, name)
248 return (module, name)
247
249
248
250
249 def _get_type(obj):
251 def _get_type(obj):
250 """Return the type of an instance (old and new-style)"""
252 """Return the type of an instance (old and new-style)"""
251 return getattr(obj, '__class__', None) or type(obj)
253 return getattr(obj, '__class__', None) or type(obj)
252
254
253 _raise_key_error = object()
255 _raise_key_error = object()
254
256
255
257
256 class BaseFormatter(Configurable):
258 class BaseFormatter(Configurable):
257 """A base formatter class that is configurable.
259 """A base formatter class that is configurable.
258
260
259 This formatter should usually be used as the base class of all formatters.
261 This formatter should usually be used as the base class of all formatters.
260 It is a traited :class:`Configurable` class and includes an extensible
262 It is a traited :class:`Configurable` class and includes an extensible
261 API for users to determine how their objects are formatted. The following
263 API for users to determine how their objects are formatted. The following
262 logic is used to find a function to format an given object.
264 logic is used to find a function to format an given object.
263
265
264 1. The object is introspected to see if it has a method with the name
266 1. The object is introspected to see if it has a method with the name
265 :attr:`print_method`. If is does, that object is passed to that method
267 :attr:`print_method`. If is does, that object is passed to that method
266 for formatting.
268 for formatting.
267 2. If no print method is found, three internal dictionaries are consulted
269 2. If no print method is found, three internal dictionaries are consulted
268 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
270 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
269 and :attr:`deferred_printers`.
271 and :attr:`deferred_printers`.
270
272
271 Users should use these dictionaries to register functions that will be
273 Users should use these dictionaries to register functions that will be
272 used to compute the format data for their objects (if those objects don't
274 used to compute the format data for their objects (if those objects don't
273 have the special print methods). The easiest way of using these
275 have the special print methods). The easiest way of using these
274 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
276 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
275 methods.
277 methods.
276
278
277 If no function/callable is found to compute the format data, ``None`` is
279 If no function/callable is found to compute the format data, ``None`` is
278 returned and this format type is not used.
280 returned and this format type is not used.
279 """
281 """
280
282
281 format_type = Unicode('text/plain')
283 format_type = Unicode('text/plain')
282 _return_type = string_types
284 _return_type = string_types
283
285
284 enabled = Bool(True, config=True)
286 enabled = Bool(True, config=True)
285
287
286 print_method = ObjectName('__repr__')
288 print_method = ObjectName('__repr__')
287
289
288 # The singleton printers.
290 # The singleton printers.
289 # Maps the IDs of the builtin singleton objects to the format functions.
291 # Maps the IDs of the builtin singleton objects to the format functions.
290 singleton_printers = Dict(config=True)
292 singleton_printers = Dict(config=True)
291
293
292 # The type-specific printers.
294 # The type-specific printers.
293 # Map type objects to the format functions.
295 # Map type objects to the format functions.
294 type_printers = Dict(config=True)
296 type_printers = Dict(config=True)
295
297
296 # The deferred-import type-specific printers.
298 # The deferred-import type-specific printers.
297 # Map (modulename, classname) pairs to the format functions.
299 # Map (modulename, classname) pairs to the format functions.
298 deferred_printers = Dict(config=True)
300 deferred_printers = Dict(config=True)
299
301
300 @warn_format_error
302 @warn_format_error
301 def __call__(self, obj):
303 def __call__(self, obj):
302 """Compute the format for an object."""
304 """Compute the format for an object."""
303 if self.enabled:
305 if self.enabled:
304 # lookup registered printer
306 # lookup registered printer
305 try:
307 try:
306 printer = self.lookup(obj)
308 printer = self.lookup(obj)
307 except KeyError:
309 except KeyError:
308 pass
310 pass
309 else:
311 else:
310 return printer(obj)
312 return printer(obj)
311 # Finally look for special method names
313 # Finally look for special method names
312 method = pretty._safe_getattr(obj, self.print_method, None)
314 method = pretty._safe_getattr(obj, self.print_method, None)
313 if method is not None:
315 if method is not None:
314 return method()
316 return method()
315 return None
317 return None
316 else:
318 else:
317 return None
319 return None
318
320
319 def __contains__(self, typ):
321 def __contains__(self, typ):
320 """map in to lookup_by_type"""
322 """map in to lookup_by_type"""
321 try:
323 try:
322 self.lookup_by_type(typ)
324 self.lookup_by_type(typ)
323 except KeyError:
325 except KeyError:
324 return False
326 return False
325 else:
327 else:
326 return True
328 return True
327
329
328 def lookup(self, obj):
330 def lookup(self, obj):
329 """Look up the formatter for a given instance.
331 """Look up the formatter for a given instance.
330
332
331 Parameters
333 Parameters
332 ----------
334 ----------
333 obj : object instance
335 obj : object instance
334
336
335 Returns
337 Returns
336 -------
338 -------
337 f : callable
339 f : callable
338 The registered formatting callable for the type.
340 The registered formatting callable for the type.
339
341
340 Raises
342 Raises
341 ------
343 ------
342 KeyError if the type has not been registered.
344 KeyError if the type has not been registered.
343 """
345 """
344 # look for singleton first
346 # look for singleton first
345 obj_id = id(obj)
347 obj_id = id(obj)
346 if obj_id in self.singleton_printers:
348 if obj_id in self.singleton_printers:
347 return self.singleton_printers[obj_id]
349 return self.singleton_printers[obj_id]
348 # then lookup by type
350 # then lookup by type
349 return self.lookup_by_type(_get_type(obj))
351 return self.lookup_by_type(_get_type(obj))
350
352
351 def lookup_by_type(self, typ):
353 def lookup_by_type(self, typ):
352 """Look up the registered formatter for a type.
354 """Look up the registered formatter for a type.
353
355
354 Parameters
356 Parameters
355 ----------
357 ----------
356 typ : type or '__module__.__name__' string for a type
358 typ : type or '__module__.__name__' string for a type
357
359
358 Returns
360 Returns
359 -------
361 -------
360 f : callable
362 f : callable
361 The registered formatting callable for the type.
363 The registered formatting callable for the type.
362
364
363 Raises
365 Raises
364 ------
366 ------
365 KeyError if the type has not been registered.
367 KeyError if the type has not been registered.
366 """
368 """
367 if isinstance(typ, string_types):
369 if isinstance(typ, string_types):
368 typ_key = tuple(typ.rsplit('.',1))
370 typ_key = tuple(typ.rsplit('.',1))
369 if typ_key not in self.deferred_printers:
371 if typ_key not in self.deferred_printers:
370 # We may have it cached in the type map. We will have to
372 # We may have it cached in the type map. We will have to
371 # iterate over all of the types to check.
373 # iterate over all of the types to check.
372 for cls in self.type_printers:
374 for cls in self.type_printers:
373 if _mod_name_key(cls) == typ_key:
375 if _mod_name_key(cls) == typ_key:
374 return self.type_printers[cls]
376 return self.type_printers[cls]
375 else:
377 else:
376 return self.deferred_printers[typ_key]
378 return self.deferred_printers[typ_key]
377 else:
379 else:
378 for cls in pretty._get_mro(typ):
380 for cls in pretty._get_mro(typ):
379 if cls in self.type_printers or self._in_deferred_types(cls):
381 if cls in self.type_printers or self._in_deferred_types(cls):
380 return self.type_printers[cls]
382 return self.type_printers[cls]
381
383
382 # If we have reached here, the lookup failed.
384 # If we have reached here, the lookup failed.
383 raise KeyError("No registered printer for {0!r}".format(typ))
385 raise KeyError("No registered printer for {0!r}".format(typ))
384
386
385 def for_type(self, typ, func=None):
387 def for_type(self, typ, func=None):
386 """Add a format function for a given type.
388 """Add a format function for a given type.
387
389
388 Parameters
390 Parameters
389 -----------
391 -----------
390 typ : type or '__module__.__name__' string for a type
392 typ : type or '__module__.__name__' string for a type
391 The class of the object that will be formatted using `func`.
393 The class of the object that will be formatted using `func`.
392 func : callable
394 func : callable
393 A callable for computing the format data.
395 A callable for computing the format data.
394 `func` will be called with the object to be formatted,
396 `func` will be called with the object to be formatted,
395 and will return the raw data in this formatter's format.
397 and will return the raw data in this formatter's format.
396 Subclasses may use a different call signature for the
398 Subclasses may use a different call signature for the
397 `func` argument.
399 `func` argument.
398
400
399 If `func` is None or not specified, there will be no change,
401 If `func` is None or not specified, there will be no change,
400 only returning the current value.
402 only returning the current value.
401
403
402 Returns
404 Returns
403 -------
405 -------
404 oldfunc : callable
406 oldfunc : callable
405 The currently registered callable.
407 The currently registered callable.
406 If you are registering a new formatter,
408 If you are registering a new formatter,
407 this will be the previous value (to enable restoring later).
409 this will be the previous value (to enable restoring later).
408 """
410 """
409 # if string given, interpret as 'pkg.module.class_name'
411 # if string given, interpret as 'pkg.module.class_name'
410 if isinstance(typ, string_types):
412 if isinstance(typ, string_types):
411 type_module, type_name = typ.rsplit('.', 1)
413 type_module, type_name = typ.rsplit('.', 1)
412 return self.for_type_by_name(type_module, type_name, func)
414 return self.for_type_by_name(type_module, type_name, func)
413
415
414 try:
416 try:
415 oldfunc = self.lookup_by_type(typ)
417 oldfunc = self.lookup_by_type(typ)
416 except KeyError:
418 except KeyError:
417 oldfunc = None
419 oldfunc = None
418
420
419 if func is not None:
421 if func is not None:
420 self.type_printers[typ] = func
422 self.type_printers[typ] = func
421
423
422 return oldfunc
424 return oldfunc
423
425
424 def for_type_by_name(self, type_module, type_name, func=None):
426 def for_type_by_name(self, type_module, type_name, func=None):
425 """Add a format function for a type specified by the full dotted
427 """Add a format function for a type specified by the full dotted
426 module and name of the type, rather than the type of the object.
428 module and name of the type, rather than the type of the object.
427
429
428 Parameters
430 Parameters
429 ----------
431 ----------
430 type_module : str
432 type_module : str
431 The full dotted name of the module the type is defined in, like
433 The full dotted name of the module the type is defined in, like
432 ``numpy``.
434 ``numpy``.
433 type_name : str
435 type_name : str
434 The name of the type (the class name), like ``dtype``
436 The name of the type (the class name), like ``dtype``
435 func : callable
437 func : callable
436 A callable for computing the format data.
438 A callable for computing the format data.
437 `func` will be called with the object to be formatted,
439 `func` will be called with the object to be formatted,
438 and will return the raw data in this formatter's format.
440 and will return the raw data in this formatter's format.
439 Subclasses may use a different call signature for the
441 Subclasses may use a different call signature for the
440 `func` argument.
442 `func` argument.
441
443
442 If `func` is None or unspecified, there will be no change,
444 If `func` is None or unspecified, there will be no change,
443 only returning the current value.
445 only returning the current value.
444
446
445 Returns
447 Returns
446 -------
448 -------
447 oldfunc : callable
449 oldfunc : callable
448 The currently registered callable.
450 The currently registered callable.
449 If you are registering a new formatter,
451 If you are registering a new formatter,
450 this will be the previous value (to enable restoring later).
452 this will be the previous value (to enable restoring later).
451 """
453 """
452 key = (type_module, type_name)
454 key = (type_module, type_name)
453
455
454 try:
456 try:
455 oldfunc = self.lookup_by_type("%s.%s" % key)
457 oldfunc = self.lookup_by_type("%s.%s" % key)
456 except KeyError:
458 except KeyError:
457 oldfunc = None
459 oldfunc = None
458
460
459 if func is not None:
461 if func is not None:
460 self.deferred_printers[key] = func
462 self.deferred_printers[key] = func
461 return oldfunc
463 return oldfunc
462
464
463 def pop(self, typ, default=_raise_key_error):
465 def pop(self, typ, default=_raise_key_error):
464 """Pop a formatter for the given type.
466 """Pop a formatter for the given type.
465
467
466 Parameters
468 Parameters
467 ----------
469 ----------
468 typ : type or '__module__.__name__' string for a type
470 typ : type or '__module__.__name__' string for a type
469 default : object
471 default : object
470 value to be returned if no formatter is registered for typ.
472 value to be returned if no formatter is registered for typ.
471
473
472 Returns
474 Returns
473 -------
475 -------
474 obj : object
476 obj : object
475 The last registered object for the type.
477 The last registered object for the type.
476
478
477 Raises
479 Raises
478 ------
480 ------
479 KeyError if the type is not registered and default is not specified.
481 KeyError if the type is not registered and default is not specified.
480 """
482 """
481
483
482 if isinstance(typ, string_types):
484 if isinstance(typ, string_types):
483 typ_key = tuple(typ.rsplit('.',1))
485 typ_key = tuple(typ.rsplit('.',1))
484 if typ_key not in self.deferred_printers:
486 if typ_key not in self.deferred_printers:
485 # We may have it cached in the type map. We will have to
487 # We may have it cached in the type map. We will have to
486 # iterate over all of the types to check.
488 # iterate over all of the types to check.
487 for cls in self.type_printers:
489 for cls in self.type_printers:
488 if _mod_name_key(cls) == typ_key:
490 if _mod_name_key(cls) == typ_key:
489 old = self.type_printers.pop(cls)
491 old = self.type_printers.pop(cls)
490 break
492 break
491 else:
493 else:
492 old = default
494 old = default
493 else:
495 else:
494 old = self.deferred_printers.pop(typ_key)
496 old = self.deferred_printers.pop(typ_key)
495 else:
497 else:
496 if typ in self.type_printers:
498 if typ in self.type_printers:
497 old = self.type_printers.pop(typ)
499 old = self.type_printers.pop(typ)
498 else:
500 else:
499 old = self.deferred_printers.pop(_mod_name_key(typ), default)
501 old = self.deferred_printers.pop(_mod_name_key(typ), default)
500 if old is _raise_key_error:
502 if old is _raise_key_error:
501 raise KeyError("No registered value for {0!r}".format(typ))
503 raise KeyError("No registered value for {0!r}".format(typ))
502 return old
504 return old
503
505
504 def _in_deferred_types(self, cls):
506 def _in_deferred_types(self, cls):
505 """
507 """
506 Check if the given class is specified in the deferred type registry.
508 Check if the given class is specified in the deferred type registry.
507
509
508 Successful matches will be moved to the regular type registry for future use.
510 Successful matches will be moved to the regular type registry for future use.
509 """
511 """
510 mod = getattr(cls, '__module__', None)
512 mod = getattr(cls, '__module__', None)
511 name = getattr(cls, '__name__', None)
513 name = getattr(cls, '__name__', None)
512 key = (mod, name)
514 key = (mod, name)
513 if key in self.deferred_printers:
515 if key in self.deferred_printers:
514 # Move the printer over to the regular registry.
516 # Move the printer over to the regular registry.
515 printer = self.deferred_printers.pop(key)
517 printer = self.deferred_printers.pop(key)
516 self.type_printers[cls] = printer
518 self.type_printers[cls] = printer
517 return True
519 return True
518 return False
520 return False
519
521
520
522
521 class PlainTextFormatter(BaseFormatter):
523 class PlainTextFormatter(BaseFormatter):
522 """The default pretty-printer.
524 """The default pretty-printer.
523
525
524 This uses :mod:`IPython.lib.pretty` to compute the format data of
526 This uses :mod:`IPython.lib.pretty` to compute the format data of
525 the object. If the object cannot be pretty printed, :func:`repr` is used.
527 the object. If the object cannot be pretty printed, :func:`repr` is used.
526 See the documentation of :mod:`IPython.lib.pretty` for details on
528 See the documentation of :mod:`IPython.lib.pretty` for details on
527 how to write pretty printers. Here is a simple example::
529 how to write pretty printers. Here is a simple example::
528
530
529 def dtype_pprinter(obj, p, cycle):
531 def dtype_pprinter(obj, p, cycle):
530 if cycle:
532 if cycle:
531 return p.text('dtype(...)')
533 return p.text('dtype(...)')
532 if hasattr(obj, 'fields'):
534 if hasattr(obj, 'fields'):
533 if obj.fields is None:
535 if obj.fields is None:
534 p.text(repr(obj))
536 p.text(repr(obj))
535 else:
537 else:
536 p.begin_group(7, 'dtype([')
538 p.begin_group(7, 'dtype([')
537 for i, field in enumerate(obj.descr):
539 for i, field in enumerate(obj.descr):
538 if i > 0:
540 if i > 0:
539 p.text(',')
541 p.text(',')
540 p.breakable()
542 p.breakable()
541 p.pretty(field)
543 p.pretty(field)
542 p.end_group(7, '])')
544 p.end_group(7, '])')
543 """
545 """
544
546
545 # The format type of data returned.
547 # The format type of data returned.
546 format_type = Unicode('text/plain')
548 format_type = Unicode('text/plain')
547
549
548 # This subclass ignores this attribute as it always need to return
550 # This subclass ignores this attribute as it always need to return
549 # something.
551 # something.
550 enabled = Bool(True, config=False)
552 enabled = Bool(True, config=False)
551
553
552 # Look for a _repr_pretty_ methods to use for pretty printing.
554 # Look for a _repr_pretty_ methods to use for pretty printing.
553 print_method = ObjectName('_repr_pretty_')
555 print_method = ObjectName('_repr_pretty_')
554
556
555 # Whether to pretty-print or not.
557 # Whether to pretty-print or not.
556 pprint = Bool(True, config=True)
558 pprint = Bool(True, config=True)
557
559
558 # Whether to be verbose or not.
560 # Whether to be verbose or not.
559 verbose = Bool(False, config=True)
561 verbose = Bool(False, config=True)
560
562
561 # The maximum width.
563 # The maximum width.
562 max_width = Integer(79, config=True)
564 max_width = Integer(79, config=True)
563
565
564 # The newline character.
566 # The newline character.
565 newline = Unicode('\n', config=True)
567 newline = Unicode('\n', config=True)
566
568
567 # format-string for pprinting floats
569 # format-string for pprinting floats
568 float_format = Unicode('%r')
570 float_format = Unicode('%r')
569 # setter for float precision, either int or direct format-string
571 # setter for float precision, either int or direct format-string
570 float_precision = CUnicode('', config=True)
572 float_precision = CUnicode('', config=True)
571
573
572 def _float_precision_changed(self, name, old, new):
574 def _float_precision_changed(self, name, old, new):
573 """float_precision changed, set float_format accordingly.
575 """float_precision changed, set float_format accordingly.
574
576
575 float_precision can be set by int or str.
577 float_precision can be set by int or str.
576 This will set float_format, after interpreting input.
578 This will set float_format, after interpreting input.
577 If numpy has been imported, numpy print precision will also be set.
579 If numpy has been imported, numpy print precision will also be set.
578
580
579 integer `n` sets format to '%.nf', otherwise, format set directly.
581 integer `n` sets format to '%.nf', otherwise, format set directly.
580
582
581 An empty string returns to defaults (repr for float, 8 for numpy).
583 An empty string returns to defaults (repr for float, 8 for numpy).
582
584
583 This parameter can be set via the '%precision' magic.
585 This parameter can be set via the '%precision' magic.
584 """
586 """
585
587
586 if '%' in new:
588 if '%' in new:
587 # got explicit format string
589 # got explicit format string
588 fmt = new
590 fmt = new
589 try:
591 try:
590 fmt%3.14159
592 fmt%3.14159
591 except Exception:
593 except Exception:
592 raise ValueError("Precision must be int or format string, not %r"%new)
594 raise ValueError("Precision must be int or format string, not %r"%new)
593 elif new:
595 elif new:
594 # otherwise, should be an int
596 # otherwise, should be an int
595 try:
597 try:
596 i = int(new)
598 i = int(new)
597 assert i >= 0
599 assert i >= 0
598 except ValueError:
600 except ValueError:
599 raise ValueError("Precision must be int or format string, not %r"%new)
601 raise ValueError("Precision must be int or format string, not %r"%new)
600 except AssertionError:
602 except AssertionError:
601 raise ValueError("int precision must be non-negative, not %r"%i)
603 raise ValueError("int precision must be non-negative, not %r"%i)
602
604
603 fmt = '%%.%if'%i
605 fmt = '%%.%if'%i
604 if 'numpy' in sys.modules:
606 if 'numpy' in sys.modules:
605 # set numpy precision if it has been imported
607 # set numpy precision if it has been imported
606 import numpy
608 import numpy
607 numpy.set_printoptions(precision=i)
609 numpy.set_printoptions(precision=i)
608 else:
610 else:
609 # default back to repr
611 # default back to repr
610 fmt = '%r'
612 fmt = '%r'
611 if 'numpy' in sys.modules:
613 if 'numpy' in sys.modules:
612 import numpy
614 import numpy
613 # numpy default is 8
615 # numpy default is 8
614 numpy.set_printoptions(precision=8)
616 numpy.set_printoptions(precision=8)
615 self.float_format = fmt
617 self.float_format = fmt
616
618
617 # Use the default pretty printers from IPython.lib.pretty.
619 # Use the default pretty printers from IPython.lib.pretty.
618 def _singleton_printers_default(self):
620 def _singleton_printers_default(self):
619 return pretty._singleton_pprinters.copy()
621 return pretty._singleton_pprinters.copy()
620
622
621 def _type_printers_default(self):
623 def _type_printers_default(self):
622 d = pretty._type_pprinters.copy()
624 d = pretty._type_pprinters.copy()
623 d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
625 d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
624 return d
626 return d
625
627
626 def _deferred_printers_default(self):
628 def _deferred_printers_default(self):
627 return pretty._deferred_type_pprinters.copy()
629 return pretty._deferred_type_pprinters.copy()
628
630
629 #### FormatterABC interface ####
631 #### FormatterABC interface ####
630
632
631 @warn_format_error
633 @warn_format_error
632 def __call__(self, obj):
634 def __call__(self, obj):
633 """Compute the pretty representation of the object."""
635 """Compute the pretty representation of the object."""
634 if not self.pprint:
636 if not self.pprint:
635 return pretty._safe_repr(obj)
637 return pretty._safe_repr(obj)
636 else:
638 else:
637 # This uses use StringIO, as cStringIO doesn't handle unicode.
639 # This uses use StringIO, as cStringIO doesn't handle unicode.
638 stream = StringIO()
640 stream = StringIO()
639 # self.newline.encode() is a quick fix for issue gh-597. We need to
641 # self.newline.encode() is a quick fix for issue gh-597. We need to
640 # ensure that stream does not get a mix of unicode and bytestrings,
642 # ensure that stream does not get a mix of unicode and bytestrings,
641 # or it will cause trouble.
643 # or it will cause trouble.
642 printer = pretty.RepresentationPrinter(stream, self.verbose,
644 printer = pretty.RepresentationPrinter(stream, self.verbose,
643 self.max_width, unicode_to_str(self.newline),
645 self.max_width, unicode_to_str(self.newline),
644 singleton_pprinters=self.singleton_printers,
646 singleton_pprinters=self.singleton_printers,
645 type_pprinters=self.type_printers,
647 type_pprinters=self.type_printers,
646 deferred_pprinters=self.deferred_printers)
648 deferred_pprinters=self.deferred_printers)
647 printer.pretty(obj)
649 printer.pretty(obj)
648 printer.flush()
650 printer.flush()
649 return stream.getvalue()
651 return stream.getvalue()
650
652
651
653
652 class HTMLFormatter(BaseFormatter):
654 class HTMLFormatter(BaseFormatter):
653 """An HTML formatter.
655 """An HTML formatter.
654
656
655 To define the callables that compute the HTML representation of your
657 To define the callables that compute the HTML representation of your
656 objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
658 objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
657 or :meth:`for_type_by_name` methods to register functions that handle
659 or :meth:`for_type_by_name` methods to register functions that handle
658 this.
660 this.
659
661
660 The return value of this formatter should be a valid HTML snippet that
662 The return value of this formatter should be a valid HTML snippet that
661 could be injected into an existing DOM. It should *not* include the
663 could be injected into an existing DOM. It should *not* include the
662 ```<html>`` or ```<body>`` tags.
664 ```<html>`` or ```<body>`` tags.
663 """
665 """
664 format_type = Unicode('text/html')
666 format_type = Unicode('text/html')
665
667
666 print_method = ObjectName('_repr_html_')
668 print_method = ObjectName('_repr_html_')
667
669
668
670
669 class SVGFormatter(BaseFormatter):
671 class SVGFormatter(BaseFormatter):
670 """An SVG formatter.
672 """An SVG formatter.
671
673
672 To define the callables that compute the SVG representation of your
674 To define the callables that compute the SVG representation of your
673 objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
675 objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
674 or :meth:`for_type_by_name` methods to register functions that handle
676 or :meth:`for_type_by_name` methods to register functions that handle
675 this.
677 this.
676
678
677 The return value of this formatter should be valid SVG enclosed in
679 The return value of this formatter should be valid SVG enclosed in
678 ```<svg>``` tags, that could be injected into an existing DOM. It should
680 ```<svg>``` tags, that could be injected into an existing DOM. It should
679 *not* include the ```<html>`` or ```<body>`` tags.
681 *not* include the ```<html>`` or ```<body>`` tags.
680 """
682 """
681 format_type = Unicode('image/svg+xml')
683 format_type = Unicode('image/svg+xml')
682
684
683 print_method = ObjectName('_repr_svg_')
685 print_method = ObjectName('_repr_svg_')
684
686
685
687
686 class PNGFormatter(BaseFormatter):
688 class PNGFormatter(BaseFormatter):
687 """A PNG formatter.
689 """A PNG formatter.
688
690
689 To define the callables that compute the PNG representation of your
691 To define the callables that compute the PNG representation of your
690 objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
692 objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
691 or :meth:`for_type_by_name` methods to register functions that handle
693 or :meth:`for_type_by_name` methods to register functions that handle
692 this.
694 this.
693
695
694 The return value of this formatter should be raw PNG data, *not*
696 The return value of this formatter should be raw PNG data, *not*
695 base64 encoded.
697 base64 encoded.
696 """
698 """
697 format_type = Unicode('image/png')
699 format_type = Unicode('image/png')
698
700
699 print_method = ObjectName('_repr_png_')
701 print_method = ObjectName('_repr_png_')
700
702
701 _return_type = (bytes, unicode_type)
703 _return_type = (bytes, unicode_type)
702
704
703
705
704 class JPEGFormatter(BaseFormatter):
706 class JPEGFormatter(BaseFormatter):
705 """A JPEG formatter.
707 """A JPEG formatter.
706
708
707 To define the callables that compute the JPEG representation of your
709 To define the callables that compute the JPEG representation of your
708 objects, define a :meth:`_repr_jpeg_` method or use the :meth:`for_type`
710 objects, define a :meth:`_repr_jpeg_` method or use the :meth:`for_type`
709 or :meth:`for_type_by_name` methods to register functions that handle
711 or :meth:`for_type_by_name` methods to register functions that handle
710 this.
712 this.
711
713
712 The return value of this formatter should be raw JPEG data, *not*
714 The return value of this formatter should be raw JPEG data, *not*
713 base64 encoded.
715 base64 encoded.
714 """
716 """
715 format_type = Unicode('image/jpeg')
717 format_type = Unicode('image/jpeg')
716
718
717 print_method = ObjectName('_repr_jpeg_')
719 print_method = ObjectName('_repr_jpeg_')
718
720
719 _return_type = (bytes, unicode_type)
721 _return_type = (bytes, unicode_type)
720
722
721
723
722 class LatexFormatter(BaseFormatter):
724 class LatexFormatter(BaseFormatter):
723 """A LaTeX formatter.
725 """A LaTeX formatter.
724
726
725 To define the callables that compute the LaTeX representation of your
727 To define the callables that compute the LaTeX representation of your
726 objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
728 objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
727 or :meth:`for_type_by_name` methods to register functions that handle
729 or :meth:`for_type_by_name` methods to register functions that handle
728 this.
730 this.
729
731
730 The return value of this formatter should be a valid LaTeX equation,
732 The return value of this formatter should be a valid LaTeX equation,
731 enclosed in either ```$```, ```$$``` or another LaTeX equation
733 enclosed in either ```$```, ```$$``` or another LaTeX equation
732 environment.
734 environment.
733 """
735 """
734 format_type = Unicode('text/latex')
736 format_type = Unicode('text/latex')
735
737
736 print_method = ObjectName('_repr_latex_')
738 print_method = ObjectName('_repr_latex_')
737
739
738
740
739 class JSONFormatter(BaseFormatter):
741 class JSONFormatter(BaseFormatter):
740 """A JSON string formatter.
742 """A JSON string formatter.
741
743
742 To define the callables that compute the JSON string representation of
744 To define the callables that compute the JSON string representation of
743 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
745 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
744 or :meth:`for_type_by_name` methods to register functions that handle
746 or :meth:`for_type_by_name` methods to register functions that handle
745 this.
747 this.
746
748
747 The return value of this formatter should be a valid JSON string.
749 The return value of this formatter should be a valid JSON string.
748 """
750 """
749 format_type = Unicode('application/json')
751 format_type = Unicode('application/json')
750
752
751 print_method = ObjectName('_repr_json_')
753 print_method = ObjectName('_repr_json_')
752
754
753
755
754 class JavascriptFormatter(BaseFormatter):
756 class JavascriptFormatter(BaseFormatter):
755 """A Javascript formatter.
757 """A Javascript formatter.
756
758
757 To define the callables that compute the Javascript representation of
759 To define the callables that compute the Javascript representation of
758 your objects, define a :meth:`_repr_javascript_` method or use the
760 your objects, define a :meth:`_repr_javascript_` method or use the
759 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
761 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
760 that handle this.
762 that handle this.
761
763
762 The return value of this formatter should be valid Javascript code and
764 The return value of this formatter should be valid Javascript code and
763 should *not* be enclosed in ```<script>``` tags.
765 should *not* be enclosed in ```<script>``` tags.
764 """
766 """
765 format_type = Unicode('application/javascript')
767 format_type = Unicode('application/javascript')
766
768
767 print_method = ObjectName('_repr_javascript_')
769 print_method = ObjectName('_repr_javascript_')
768
770
771
772 class PDFFormatter(BaseFormatter):
773 """A PDF formatter.
774
775 To defined the callables that compute to PDF representation of your
776 objects, define a :meth:`_repr_pdf_` method or use the :meth:`for_type`
777 or :meth:`for_type_by_name` methods to register functions that handle
778 this.
779
780 The return value of this formatter should be raw PDF data, *not*
781 base64 encoded.
782 """
783 format_type = Unicode('application/pdf')
784
785 print_method = ObjectName('_repr_pdf_')
786
787
769 FormatterABC.register(BaseFormatter)
788 FormatterABC.register(BaseFormatter)
770 FormatterABC.register(PlainTextFormatter)
789 FormatterABC.register(PlainTextFormatter)
771 FormatterABC.register(HTMLFormatter)
790 FormatterABC.register(HTMLFormatter)
772 FormatterABC.register(SVGFormatter)
791 FormatterABC.register(SVGFormatter)
773 FormatterABC.register(PNGFormatter)
792 FormatterABC.register(PNGFormatter)
793 FormatterABC.register(PDFFormatter)
774 FormatterABC.register(JPEGFormatter)
794 FormatterABC.register(JPEGFormatter)
775 FormatterABC.register(LatexFormatter)
795 FormatterABC.register(LatexFormatter)
776 FormatterABC.register(JSONFormatter)
796 FormatterABC.register(JSONFormatter)
777 FormatterABC.register(JavascriptFormatter)
797 FormatterABC.register(JavascriptFormatter)
778
798
779
799
780 def format_display_data(obj, include=None, exclude=None):
800 def format_display_data(obj, include=None, exclude=None):
781 """Return a format data dict for an object.
801 """Return a format data dict for an object.
782
802
783 By default all format types will be computed.
803 By default all format types will be computed.
784
804
785 The following MIME types are currently implemented:
805 The following MIME types are currently implemented:
786
806
787 * text/plain
807 * text/plain
788 * text/html
808 * text/html
789 * text/latex
809 * text/latex
790 * application/json
810 * application/json
791 * application/javascript
811 * application/javascript
812 * application/pdf
792 * image/png
813 * image/png
793 * image/jpeg
814 * image/jpeg
794 * image/svg+xml
815 * image/svg+xml
795
816
796 Parameters
817 Parameters
797 ----------
818 ----------
798 obj : object
819 obj : object
799 The Python object whose format data will be computed.
820 The Python object whose format data will be computed.
800
821
801 Returns
822 Returns
802 -------
823 -------
803 format_dict : dict
824 format_dict : dict
804 A dictionary of key/value pairs, one or each format that was
825 A dictionary of key/value pairs, one or each format that was
805 generated for the object. The keys are the format types, which
826 generated for the object. The keys are the format types, which
806 will usually be MIME type strings and the values and JSON'able
827 will usually be MIME type strings and the values and JSON'able
807 data structure containing the raw data for the representation in
828 data structure containing the raw data for the representation in
808 that format.
829 that format.
809 include : list or tuple, optional
830 include : list or tuple, optional
810 A list of format type strings (MIME types) to include in the
831 A list of format type strings (MIME types) to include in the
811 format data dict. If this is set *only* the format types included
832 format data dict. If this is set *only* the format types included
812 in this list will be computed.
833 in this list will be computed.
813 exclude : list or tuple, optional
834 exclude : list or tuple, optional
814 A list of format type string (MIME types) to exclue in the format
835 A list of format type string (MIME types) to exclue in the format
815 data dict. If this is set all format types will be computed,
836 data dict. If this is set all format types will be computed,
816 except for those included in this argument.
837 except for those included in this argument.
817 """
838 """
818 from IPython.core.interactiveshell import InteractiveShell
839 from IPython.core.interactiveshell import InteractiveShell
819
840
820 InteractiveShell.instance().display_formatter.format(
841 InteractiveShell.instance().display_formatter.format(
821 obj,
842 obj,
822 include,
843 include,
823 exclude
844 exclude
824 )
845 )
825
846
@@ -1,282 +1,291 b''
1 """Tests for the Formatters."""
1 """Tests for the Formatters."""
2
2
3 from math import pi
3 from math import pi
4
4
5 try:
5 try:
6 import numpy
6 import numpy
7 except:
7 except:
8 numpy = None
8 numpy = None
9 import nose.tools as nt
9 import nose.tools as nt
10
10
11 from IPython.core.formatters import PlainTextFormatter, HTMLFormatter, _mod_name_key
11 from IPython.core.formatters import (
12 PlainTextFormatter, HTMLFormatter, PDFFormatter, _mod_name_key
13 )
12 from IPython.utils.io import capture_output
14 from IPython.utils.io import capture_output
13
15
14 class A(object):
16 class A(object):
15 def __repr__(self):
17 def __repr__(self):
16 return 'A()'
18 return 'A()'
17
19
18 class B(A):
20 class B(A):
19 def __repr__(self):
21 def __repr__(self):
20 return 'B()'
22 return 'B()'
21
23
22 class C:
24 class C:
23 pass
25 pass
24
26
25 class BadPretty(object):
27 class BadPretty(object):
26 _repr_pretty_ = None
28 _repr_pretty_ = None
27
29
28 class GoodPretty(object):
30 class GoodPretty(object):
29 def _repr_pretty_(self, pp, cycle):
31 def _repr_pretty_(self, pp, cycle):
30 pp.text('foo')
32 pp.text('foo')
31
33
32 def __repr__(self):
34 def __repr__(self):
33 return 'GoodPretty()'
35 return 'GoodPretty()'
34
36
35 def foo_printer(obj, pp, cycle):
37 def foo_printer(obj, pp, cycle):
36 pp.text('foo')
38 pp.text('foo')
37
39
38 def test_pretty():
40 def test_pretty():
39 f = PlainTextFormatter()
41 f = PlainTextFormatter()
40 f.for_type(A, foo_printer)
42 f.for_type(A, foo_printer)
41 nt.assert_equal(f(A()), 'foo')
43 nt.assert_equal(f(A()), 'foo')
42 nt.assert_equal(f(B()), 'foo')
44 nt.assert_equal(f(B()), 'foo')
43 nt.assert_equal(f(GoodPretty()), 'foo')
45 nt.assert_equal(f(GoodPretty()), 'foo')
44 # Just don't raise an exception for the following:
46 # Just don't raise an exception for the following:
45 f(BadPretty())
47 f(BadPretty())
46
48
47 f.pprint = False
49 f.pprint = False
48 nt.assert_equal(f(A()), 'A()')
50 nt.assert_equal(f(A()), 'A()')
49 nt.assert_equal(f(B()), 'B()')
51 nt.assert_equal(f(B()), 'B()')
50 nt.assert_equal(f(GoodPretty()), 'GoodPretty()')
52 nt.assert_equal(f(GoodPretty()), 'GoodPretty()')
51
53
52
54
53 def test_deferred():
55 def test_deferred():
54 f = PlainTextFormatter()
56 f = PlainTextFormatter()
55
57
56 def test_precision():
58 def test_precision():
57 """test various values for float_precision."""
59 """test various values for float_precision."""
58 f = PlainTextFormatter()
60 f = PlainTextFormatter()
59 nt.assert_equal(f(pi), repr(pi))
61 nt.assert_equal(f(pi), repr(pi))
60 f.float_precision = 0
62 f.float_precision = 0
61 if numpy:
63 if numpy:
62 po = numpy.get_printoptions()
64 po = numpy.get_printoptions()
63 nt.assert_equal(po['precision'], 0)
65 nt.assert_equal(po['precision'], 0)
64 nt.assert_equal(f(pi), '3')
66 nt.assert_equal(f(pi), '3')
65 f.float_precision = 2
67 f.float_precision = 2
66 if numpy:
68 if numpy:
67 po = numpy.get_printoptions()
69 po = numpy.get_printoptions()
68 nt.assert_equal(po['precision'], 2)
70 nt.assert_equal(po['precision'], 2)
69 nt.assert_equal(f(pi), '3.14')
71 nt.assert_equal(f(pi), '3.14')
70 f.float_precision = '%g'
72 f.float_precision = '%g'
71 if numpy:
73 if numpy:
72 po = numpy.get_printoptions()
74 po = numpy.get_printoptions()
73 nt.assert_equal(po['precision'], 2)
75 nt.assert_equal(po['precision'], 2)
74 nt.assert_equal(f(pi), '3.14159')
76 nt.assert_equal(f(pi), '3.14159')
75 f.float_precision = '%e'
77 f.float_precision = '%e'
76 nt.assert_equal(f(pi), '3.141593e+00')
78 nt.assert_equal(f(pi), '3.141593e+00')
77 f.float_precision = ''
79 f.float_precision = ''
78 if numpy:
80 if numpy:
79 po = numpy.get_printoptions()
81 po = numpy.get_printoptions()
80 nt.assert_equal(po['precision'], 8)
82 nt.assert_equal(po['precision'], 8)
81 nt.assert_equal(f(pi), repr(pi))
83 nt.assert_equal(f(pi), repr(pi))
82
84
83 def test_bad_precision():
85 def test_bad_precision():
84 """test various invalid values for float_precision."""
86 """test various invalid values for float_precision."""
85 f = PlainTextFormatter()
87 f = PlainTextFormatter()
86 def set_fp(p):
88 def set_fp(p):
87 f.float_precision=p
89 f.float_precision=p
88 nt.assert_raises(ValueError, set_fp, '%')
90 nt.assert_raises(ValueError, set_fp, '%')
89 nt.assert_raises(ValueError, set_fp, '%.3f%i')
91 nt.assert_raises(ValueError, set_fp, '%.3f%i')
90 nt.assert_raises(ValueError, set_fp, 'foo')
92 nt.assert_raises(ValueError, set_fp, 'foo')
91 nt.assert_raises(ValueError, set_fp, -1)
93 nt.assert_raises(ValueError, set_fp, -1)
92
94
93 def test_for_type():
95 def test_for_type():
94 f = PlainTextFormatter()
96 f = PlainTextFormatter()
95
97
96 # initial return, None
98 # initial return, None
97 nt.assert_is(f.for_type(C, foo_printer), None)
99 nt.assert_is(f.for_type(C, foo_printer), None)
98 # no func queries
100 # no func queries
99 nt.assert_is(f.for_type(C), foo_printer)
101 nt.assert_is(f.for_type(C), foo_printer)
100 # shouldn't change anything
102 # shouldn't change anything
101 nt.assert_is(f.for_type(C), foo_printer)
103 nt.assert_is(f.for_type(C), foo_printer)
102 # None should do the same
104 # None should do the same
103 nt.assert_is(f.for_type(C, None), foo_printer)
105 nt.assert_is(f.for_type(C, None), foo_printer)
104 nt.assert_is(f.for_type(C, None), foo_printer)
106 nt.assert_is(f.for_type(C, None), foo_printer)
105
107
106 def test_for_type_string():
108 def test_for_type_string():
107 f = PlainTextFormatter()
109 f = PlainTextFormatter()
108
110
109 mod = C.__module__
111 mod = C.__module__
110
112
111 type_str = '%s.%s' % (C.__module__, 'C')
113 type_str = '%s.%s' % (C.__module__, 'C')
112
114
113 # initial return, None
115 # initial return, None
114 nt.assert_is(f.for_type(type_str, foo_printer), None)
116 nt.assert_is(f.for_type(type_str, foo_printer), None)
115 # no func queries
117 # no func queries
116 nt.assert_is(f.for_type(type_str), foo_printer)
118 nt.assert_is(f.for_type(type_str), foo_printer)
117 nt.assert_in(_mod_name_key(C), f.deferred_printers)
119 nt.assert_in(_mod_name_key(C), f.deferred_printers)
118 nt.assert_is(f.for_type(C), foo_printer)
120 nt.assert_is(f.for_type(C), foo_printer)
119 nt.assert_not_in(_mod_name_key(C), f.deferred_printers)
121 nt.assert_not_in(_mod_name_key(C), f.deferred_printers)
120 nt.assert_in(C, f.type_printers)
122 nt.assert_in(C, f.type_printers)
121
123
122 def test_for_type_by_name():
124 def test_for_type_by_name():
123 f = PlainTextFormatter()
125 f = PlainTextFormatter()
124
126
125 mod = C.__module__
127 mod = C.__module__
126
128
127 # initial return, None
129 # initial return, None
128 nt.assert_is(f.for_type_by_name(mod, 'C', foo_printer), None)
130 nt.assert_is(f.for_type_by_name(mod, 'C', foo_printer), None)
129 # no func queries
131 # no func queries
130 nt.assert_is(f.for_type_by_name(mod, 'C'), foo_printer)
132 nt.assert_is(f.for_type_by_name(mod, 'C'), foo_printer)
131 # shouldn't change anything
133 # shouldn't change anything
132 nt.assert_is(f.for_type_by_name(mod, 'C'), foo_printer)
134 nt.assert_is(f.for_type_by_name(mod, 'C'), foo_printer)
133 # None should do the same
135 # None should do the same
134 nt.assert_is(f.for_type_by_name(mod, 'C', None), foo_printer)
136 nt.assert_is(f.for_type_by_name(mod, 'C', None), foo_printer)
135 nt.assert_is(f.for_type_by_name(mod, 'C', None), foo_printer)
137 nt.assert_is(f.for_type_by_name(mod, 'C', None), foo_printer)
136
138
137 def test_lookup():
139 def test_lookup():
138 f = PlainTextFormatter()
140 f = PlainTextFormatter()
139
141
140 f.for_type(C, foo_printer)
142 f.for_type(C, foo_printer)
141 nt.assert_is(f.lookup(C()), foo_printer)
143 nt.assert_is(f.lookup(C()), foo_printer)
142 with nt.assert_raises(KeyError):
144 with nt.assert_raises(KeyError):
143 f.lookup(A())
145 f.lookup(A())
144
146
145 def test_lookup_string():
147 def test_lookup_string():
146 f = PlainTextFormatter()
148 f = PlainTextFormatter()
147 type_str = '%s.%s' % (C.__module__, 'C')
149 type_str = '%s.%s' % (C.__module__, 'C')
148
150
149 f.for_type(type_str, foo_printer)
151 f.for_type(type_str, foo_printer)
150 nt.assert_is(f.lookup(C()), foo_printer)
152 nt.assert_is(f.lookup(C()), foo_printer)
151 # should move from deferred to imported dict
153 # should move from deferred to imported dict
152 nt.assert_not_in(_mod_name_key(C), f.deferred_printers)
154 nt.assert_not_in(_mod_name_key(C), f.deferred_printers)
153 nt.assert_in(C, f.type_printers)
155 nt.assert_in(C, f.type_printers)
154
156
155 def test_lookup_by_type():
157 def test_lookup_by_type():
156 f = PlainTextFormatter()
158 f = PlainTextFormatter()
157 f.for_type(C, foo_printer)
159 f.for_type(C, foo_printer)
158 nt.assert_is(f.lookup_by_type(C), foo_printer)
160 nt.assert_is(f.lookup_by_type(C), foo_printer)
159 type_str = '%s.%s' % (C.__module__, 'C')
161 type_str = '%s.%s' % (C.__module__, 'C')
160 with nt.assert_raises(KeyError):
162 with nt.assert_raises(KeyError):
161 f.lookup_by_type(A)
163 f.lookup_by_type(A)
162
164
163 def test_lookup_by_type_string():
165 def test_lookup_by_type_string():
164 f = PlainTextFormatter()
166 f = PlainTextFormatter()
165 type_str = '%s.%s' % (C.__module__, 'C')
167 type_str = '%s.%s' % (C.__module__, 'C')
166 f.for_type(type_str, foo_printer)
168 f.for_type(type_str, foo_printer)
167
169
168 # verify insertion
170 # verify insertion
169 nt.assert_in(_mod_name_key(C), f.deferred_printers)
171 nt.assert_in(_mod_name_key(C), f.deferred_printers)
170 nt.assert_not_in(C, f.type_printers)
172 nt.assert_not_in(C, f.type_printers)
171
173
172 nt.assert_is(f.lookup_by_type(type_str), foo_printer)
174 nt.assert_is(f.lookup_by_type(type_str), foo_printer)
173 # lookup by string doesn't cause import
175 # lookup by string doesn't cause import
174 nt.assert_in(_mod_name_key(C), f.deferred_printers)
176 nt.assert_in(_mod_name_key(C), f.deferred_printers)
175 nt.assert_not_in(C, f.type_printers)
177 nt.assert_not_in(C, f.type_printers)
176
178
177 nt.assert_is(f.lookup_by_type(C), foo_printer)
179 nt.assert_is(f.lookup_by_type(C), foo_printer)
178 # should move from deferred to imported dict
180 # should move from deferred to imported dict
179 nt.assert_not_in(_mod_name_key(C), f.deferred_printers)
181 nt.assert_not_in(_mod_name_key(C), f.deferred_printers)
180 nt.assert_in(C, f.type_printers)
182 nt.assert_in(C, f.type_printers)
181
183
182 def test_in_formatter():
184 def test_in_formatter():
183 f = PlainTextFormatter()
185 f = PlainTextFormatter()
184 f.for_type(C, foo_printer)
186 f.for_type(C, foo_printer)
185 type_str = '%s.%s' % (C.__module__, 'C')
187 type_str = '%s.%s' % (C.__module__, 'C')
186 nt.assert_in(C, f)
188 nt.assert_in(C, f)
187 nt.assert_in(type_str, f)
189 nt.assert_in(type_str, f)
188
190
189 def test_string_in_formatter():
191 def test_string_in_formatter():
190 f = PlainTextFormatter()
192 f = PlainTextFormatter()
191 type_str = '%s.%s' % (C.__module__, 'C')
193 type_str = '%s.%s' % (C.__module__, 'C')
192 f.for_type(type_str, foo_printer)
194 f.for_type(type_str, foo_printer)
193 nt.assert_in(type_str, f)
195 nt.assert_in(type_str, f)
194 nt.assert_in(C, f)
196 nt.assert_in(C, f)
195
197
196 def test_pop():
198 def test_pop():
197 f = PlainTextFormatter()
199 f = PlainTextFormatter()
198 f.for_type(C, foo_printer)
200 f.for_type(C, foo_printer)
199 nt.assert_is(f.lookup_by_type(C), foo_printer)
201 nt.assert_is(f.lookup_by_type(C), foo_printer)
200 nt.assert_is(f.pop(C, None), foo_printer)
202 nt.assert_is(f.pop(C, None), foo_printer)
201 f.for_type(C, foo_printer)
203 f.for_type(C, foo_printer)
202 nt.assert_is(f.pop(C), foo_printer)
204 nt.assert_is(f.pop(C), foo_printer)
203 with nt.assert_raises(KeyError):
205 with nt.assert_raises(KeyError):
204 f.lookup_by_type(C)
206 f.lookup_by_type(C)
205 with nt.assert_raises(KeyError):
207 with nt.assert_raises(KeyError):
206 f.pop(C)
208 f.pop(C)
207 with nt.assert_raises(KeyError):
209 with nt.assert_raises(KeyError):
208 f.pop(A)
210 f.pop(A)
209 nt.assert_is(f.pop(A, None), None)
211 nt.assert_is(f.pop(A, None), None)
210
212
211 def test_pop_string():
213 def test_pop_string():
212 f = PlainTextFormatter()
214 f = PlainTextFormatter()
213 type_str = '%s.%s' % (C.__module__, 'C')
215 type_str = '%s.%s' % (C.__module__, 'C')
214
216
215 with nt.assert_raises(KeyError):
217 with nt.assert_raises(KeyError):
216 f.pop(type_str)
218 f.pop(type_str)
217
219
218 f.for_type(type_str, foo_printer)
220 f.for_type(type_str, foo_printer)
219 f.pop(type_str)
221 f.pop(type_str)
220 with nt.assert_raises(KeyError):
222 with nt.assert_raises(KeyError):
221 f.lookup_by_type(C)
223 f.lookup_by_type(C)
222 with nt.assert_raises(KeyError):
224 with nt.assert_raises(KeyError):
223 f.pop(type_str)
225 f.pop(type_str)
224
226
225 f.for_type(C, foo_printer)
227 f.for_type(C, foo_printer)
226 nt.assert_is(f.pop(type_str, None), foo_printer)
228 nt.assert_is(f.pop(type_str, None), foo_printer)
227 with nt.assert_raises(KeyError):
229 with nt.assert_raises(KeyError):
228 f.lookup_by_type(C)
230 f.lookup_by_type(C)
229 with nt.assert_raises(KeyError):
231 with nt.assert_raises(KeyError):
230 f.pop(type_str)
232 f.pop(type_str)
231 nt.assert_is(f.pop(type_str, None), None)
233 nt.assert_is(f.pop(type_str, None), None)
232
234
233
235
234 def test_warn_error_method():
236 def test_warn_error_method():
235 f = HTMLFormatter()
237 f = HTMLFormatter()
236 class BadHTML(object):
238 class BadHTML(object):
237 def _repr_html_(self):
239 def _repr_html_(self):
238 return 1/0
240 return 1/0
239 bad = BadHTML()
241 bad = BadHTML()
240 with capture_output() as captured:
242 with capture_output() as captured:
241 result = f(bad)
243 result = f(bad)
242 nt.assert_is(result, None)
244 nt.assert_is(result, None)
243 nt.assert_in("FormatterWarning", captured.stderr)
245 nt.assert_in("FormatterWarning", captured.stderr)
244 nt.assert_in("text/html", captured.stderr)
246 nt.assert_in("text/html", captured.stderr)
245 nt.assert_in("zero", captured.stderr)
247 nt.assert_in("zero", captured.stderr)
246
248
247 def test_nowarn_notimplemented():
249 def test_nowarn_notimplemented():
248 f = HTMLFormatter()
250 f = HTMLFormatter()
249 class HTMLNotImplemented(object):
251 class HTMLNotImplemented(object):
250 def _repr_html_(self):
252 def _repr_html_(self):
251 raise NotImplementedError
253 raise NotImplementedError
252 return 1/0
254 return 1/0
253 h = HTMLNotImplemented()
255 h = HTMLNotImplemented()
254 with capture_output() as captured:
256 with capture_output() as captured:
255 result = f(h)
257 result = f(h)
256 nt.assert_is(result, None)
258 nt.assert_is(result, None)
257 nt.assert_not_in("FormatterWarning", captured.stderr)
259 nt.assert_not_in("FormatterWarning", captured.stderr)
258
260
259 def test_warn_error_for_type():
261 def test_warn_error_for_type():
260 f = HTMLFormatter()
262 f = HTMLFormatter()
261 f.for_type(int, lambda i: name_error)
263 f.for_type(int, lambda i: name_error)
262 with capture_output() as captured:
264 with capture_output() as captured:
263 result = f(5)
265 result = f(5)
264 nt.assert_is(result, None)
266 nt.assert_is(result, None)
265 nt.assert_in("FormatterWarning", captured.stderr)
267 nt.assert_in("FormatterWarning", captured.stderr)
266 nt.assert_in("text/html", captured.stderr)
268 nt.assert_in("text/html", captured.stderr)
267 nt.assert_in("name_error", captured.stderr)
269 nt.assert_in("name_error", captured.stderr)
268
270
269 def test_warn_error_pretty_method():
271 def test_warn_error_pretty_method():
270 f = PlainTextFormatter()
272 f = PlainTextFormatter()
271 class BadPretty(object):
273 class BadPretty(object):
272 def _repr_pretty_(self):
274 def _repr_pretty_(self):
273 return "hello"
275 return "hello"
274 bad = BadPretty()
276 bad = BadPretty()
275 with capture_output() as captured:
277 with capture_output() as captured:
276 result = f(bad)
278 result = f(bad)
277 nt.assert_is(result, None)
279 nt.assert_is(result, None)
278 nt.assert_in("FormatterWarning", captured.stderr)
280 nt.assert_in("FormatterWarning", captured.stderr)
279 nt.assert_in("text/plain", captured.stderr)
281 nt.assert_in("text/plain", captured.stderr)
280 nt.assert_in("argument", captured.stderr)
282 nt.assert_in("argument", captured.stderr)
281
283
284 class MakePDF(object):
285 def _repr_pdf_(self):
286 return 'PDF'
282
287
288 def test_pdf_formatter():
289 pdf = MakePDF()
290 f = PDFFormatter()
291 nt.assert_equal(f(pdf), 'PDF')
@@ -1,247 +1,256 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.py3compat import string_types, unicode_type, iteritems
27 from IPython.utils.py3compat import string_types, unicode_type, iteritems
28 from IPython.utils.encoding import DEFAULT_ENCODING
28 from IPython.utils.encoding import DEFAULT_ENCODING
29 next_attr_name = '__next__' if py3compat.PY3 else 'next'
29 next_attr_name = '__next__' if py3compat.PY3 else 'next'
30
30
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32 # Globals and constants
32 # Globals and constants
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34
34
35 # timestamp formats
35 # timestamp formats
36 ISO8601 = "%Y-%m-%dT%H:%M:%S.%f"
36 ISO8601 = "%Y-%m-%dT%H:%M:%S.%f"
37 ISO8601_PAT=re.compile(r"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,6})Z?([\+\-]\d{2}:?\d{2})?$")
37 ISO8601_PAT=re.compile(r"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,6})Z?([\+\-]\d{2}:?\d{2})?$")
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Classes and functions
40 # Classes and functions
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42
42
43 def rekey(dikt):
43 def rekey(dikt):
44 """Rekey a dict that has been forced to use str keys where there should be
44 """Rekey a dict that has been forced to use str keys where there should be
45 ints by json."""
45 ints by json."""
46 for k in dikt:
46 for k in dikt:
47 if isinstance(k, string_types):
47 if isinstance(k, string_types):
48 ik=fk=None
48 ik=fk=None
49 try:
49 try:
50 ik = int(k)
50 ik = int(k)
51 except ValueError:
51 except ValueError:
52 try:
52 try:
53 fk = float(k)
53 fk = float(k)
54 except ValueError:
54 except ValueError:
55 continue
55 continue
56 if ik is not None:
56 if ik is not None:
57 nk = ik
57 nk = ik
58 else:
58 else:
59 nk = fk
59 nk = fk
60 if nk in dikt:
60 if nk in dikt:
61 raise KeyError("already have key %r"%nk)
61 raise KeyError("already have key %r"%nk)
62 dikt[nk] = dikt.pop(k)
62 dikt[nk] = dikt.pop(k)
63 return dikt
63 return dikt
64
64
65 def parse_date(s):
65 def parse_date(s):
66 """parse an ISO8601 date string
66 """parse an ISO8601 date string
67
67
68 If it is None or not a valid ISO8601 timestamp,
68 If it is None or not a valid ISO8601 timestamp,
69 it will be returned unmodified.
69 it will be returned unmodified.
70 Otherwise, it will return a datetime object.
70 Otherwise, it will return a datetime object.
71 """
71 """
72 if s is None:
72 if s is None:
73 return s
73 return s
74 m = ISO8601_PAT.match(s)
74 m = ISO8601_PAT.match(s)
75 if m:
75 if m:
76 # FIXME: add actual timezone support
76 # FIXME: add actual timezone support
77 # this just drops the timezone info
77 # this just drops the timezone info
78 notz = m.groups()[0]
78 notz = m.groups()[0]
79 return datetime.strptime(notz, ISO8601)
79 return datetime.strptime(notz, ISO8601)
80 return s
80 return s
81
81
82 def extract_dates(obj):
82 def extract_dates(obj):
83 """extract ISO8601 dates from unpacked JSON"""
83 """extract ISO8601 dates from unpacked JSON"""
84 if isinstance(obj, dict):
84 if isinstance(obj, dict):
85 new_obj = {} # don't clobber
85 new_obj = {} # don't clobber
86 for k,v in iteritems(obj):
86 for k,v in iteritems(obj):
87 new_obj[k] = extract_dates(v)
87 new_obj[k] = extract_dates(v)
88 obj = new_obj
88 obj = new_obj
89 elif isinstance(obj, (list, tuple)):
89 elif isinstance(obj, (list, tuple)):
90 obj = [ extract_dates(o) for o in obj ]
90 obj = [ extract_dates(o) for o in obj ]
91 elif isinstance(obj, string_types):
91 elif isinstance(obj, string_types):
92 obj = parse_date(obj)
92 obj = parse_date(obj)
93 return obj
93 return obj
94
94
95 def squash_dates(obj):
95 def squash_dates(obj):
96 """squash datetime objects into ISO8601 strings"""
96 """squash datetime objects into ISO8601 strings"""
97 if isinstance(obj, dict):
97 if isinstance(obj, dict):
98 obj = dict(obj) # don't clobber
98 obj = dict(obj) # don't clobber
99 for k,v in iteritems(obj):
99 for k,v in iteritems(obj):
100 obj[k] = squash_dates(v)
100 obj[k] = squash_dates(v)
101 elif isinstance(obj, (list, tuple)):
101 elif isinstance(obj, (list, tuple)):
102 obj = [ squash_dates(o) for o in obj ]
102 obj = [ squash_dates(o) for o in obj ]
103 elif isinstance(obj, datetime):
103 elif isinstance(obj, datetime):
104 obj = obj.isoformat()
104 obj = obj.isoformat()
105 return obj
105 return obj
106
106
107 def date_default(obj):
107 def date_default(obj):
108 """default function for packing datetime objects in JSON."""
108 """default function for packing datetime objects in JSON."""
109 if isinstance(obj, datetime):
109 if isinstance(obj, datetime):
110 return obj.isoformat()
110 return obj.isoformat()
111 else:
111 else:
112 raise TypeError("%r is not JSON serializable"%obj)
112 raise TypeError("%r is not JSON serializable"%obj)
113
113
114
114
115 # constants for identifying png/jpeg data
115 # constants for identifying png/jpeg data
116 PNG = b'\x89PNG\r\n\x1a\n'
116 PNG = b'\x89PNG\r\n\x1a\n'
117 # front of PNG base64-encoded
117 # front of PNG base64-encoded
118 PNG64 = b'iVBORw0KG'
118 PNG64 = b'iVBORw0KG'
119 JPEG = b'\xff\xd8'
119 JPEG = b'\xff\xd8'
120 # front of JPEG base64-encoded
120 # front of JPEG base64-encoded
121 JPEG64 = b'/9'
121 JPEG64 = b'/9'
122 # front of PDF base64-encoded
123 PDF64 = b'JVBER'
122
124
123 def encode_images(format_dict):
125 def encode_images(format_dict):
124 """b64-encodes images in a displaypub format dict
126 """b64-encodes images in a displaypub format dict
125
127
126 Perhaps this should be handled in json_clean itself?
128 Perhaps this should be handled in json_clean itself?
127
129
128 Parameters
130 Parameters
129 ----------
131 ----------
130
132
131 format_dict : dict
133 format_dict : dict
132 A dictionary of display data keyed by mime-type
134 A dictionary of display data keyed by mime-type
133
135
134 Returns
136 Returns
135 -------
137 -------
136
138
137 format_dict : dict
139 format_dict : dict
138 A copy of the same dictionary,
140 A copy of the same dictionary,
139 but binary image data ('image/png' or 'image/jpeg')
141 but binary image data ('image/png', 'image/jpeg' or 'application/pdf')
140 is base64-encoded.
142 is base64-encoded.
141
143
142 """
144 """
143 encoded = format_dict.copy()
145 encoded = format_dict.copy()
144
146
145 pngdata = format_dict.get('image/png')
147 pngdata = format_dict.get('image/png')
146 if isinstance(pngdata, bytes):
148 if isinstance(pngdata, bytes):
147 # make sure we don't double-encode
149 # make sure we don't double-encode
148 if not pngdata.startswith(PNG64):
150 if not pngdata.startswith(PNG64):
149 pngdata = encodebytes(pngdata)
151 pngdata = encodebytes(pngdata)
150 encoded['image/png'] = pngdata.decode('ascii')
152 encoded['image/png'] = pngdata.decode('ascii')
151
153
152 jpegdata = format_dict.get('image/jpeg')
154 jpegdata = format_dict.get('image/jpeg')
153 if isinstance(jpegdata, bytes):
155 if isinstance(jpegdata, bytes):
154 # make sure we don't double-encode
156 # make sure we don't double-encode
155 if not jpegdata.startswith(JPEG64):
157 if not jpegdata.startswith(JPEG64):
156 jpegdata = encodebytes(jpegdata)
158 jpegdata = encodebytes(jpegdata)
157 encoded['image/jpeg'] = jpegdata.decode('ascii')
159 encoded['image/jpeg'] = jpegdata.decode('ascii')
158
160
161 pdfdata = format_dict.get('application/pdf')
162 if isinstance(pdfdata, bytes):
163 # make sure we don't double-encode
164 if not pdfdata.startswith(PDF64):
165 pdfdata = encodebytes(pdfdata)
166 encoded['application/pdf'] = pdfdata.decode('ascii')
167
159 return encoded
168 return encoded
160
169
161
170
162 def json_clean(obj):
171 def json_clean(obj):
163 """Clean an object to ensure it's safe to encode in JSON.
172 """Clean an object to ensure it's safe to encode in JSON.
164
173
165 Atomic, immutable objects are returned unmodified. Sets and tuples are
174 Atomic, immutable objects are returned unmodified. Sets and tuples are
166 converted to lists, lists are copied and dicts are also copied.
175 converted to lists, lists are copied and dicts are also copied.
167
176
168 Note: dicts whose keys could cause collisions upon encoding (such as a dict
177 Note: dicts whose keys could cause collisions upon encoding (such as a dict
169 with both the number 1 and the string '1' as keys) will cause a ValueError
178 with both the number 1 and the string '1' as keys) will cause a ValueError
170 to be raised.
179 to be raised.
171
180
172 Parameters
181 Parameters
173 ----------
182 ----------
174 obj : any python object
183 obj : any python object
175
184
176 Returns
185 Returns
177 -------
186 -------
178 out : object
187 out : object
179
188
180 A version of the input which will not cause an encoding error when
189 A version of the input which will not cause an encoding error when
181 encoded as JSON. Note that this function does not *encode* its inputs,
190 encoded as JSON. Note that this function does not *encode* its inputs,
182 it simply sanitizes it so that there will be no encoding errors later.
191 it simply sanitizes it so that there will be no encoding errors later.
183
192
184 Examples
193 Examples
185 --------
194 --------
186 >>> json_clean(4)
195 >>> json_clean(4)
187 4
196 4
188 >>> json_clean(list(range(10)))
197 >>> json_clean(list(range(10)))
189 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
198 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
190 >>> sorted(json_clean(dict(x=1, y=2)).items())
199 >>> sorted(json_clean(dict(x=1, y=2)).items())
191 [('x', 1), ('y', 2)]
200 [('x', 1), ('y', 2)]
192 >>> sorted(json_clean(dict(x=1, y=2, z=[1,2,3])).items())
201 >>> sorted(json_clean(dict(x=1, y=2, z=[1,2,3])).items())
193 [('x', 1), ('y', 2), ('z', [1, 2, 3])]
202 [('x', 1), ('y', 2), ('z', [1, 2, 3])]
194 >>> json_clean(True)
203 >>> json_clean(True)
195 True
204 True
196 """
205 """
197 # types that are 'atomic' and ok in json as-is.
206 # types that are 'atomic' and ok in json as-is.
198 atomic_ok = (unicode_type, type(None))
207 atomic_ok = (unicode_type, type(None))
199
208
200 # containers that we need to convert into lists
209 # containers that we need to convert into lists
201 container_to_list = (tuple, set, types.GeneratorType)
210 container_to_list = (tuple, set, types.GeneratorType)
202
211
203 if isinstance(obj, float):
212 if isinstance(obj, float):
204 # cast out-of-range floats to their reprs
213 # cast out-of-range floats to their reprs
205 if math.isnan(obj) or math.isinf(obj):
214 if math.isnan(obj) or math.isinf(obj):
206 return repr(obj)
215 return repr(obj)
207 return float(obj)
216 return float(obj)
208
217
209 if isinstance(obj, int):
218 if isinstance(obj, int):
210 # cast int to int, in case subclasses override __str__ (e.g. boost enum, #4598)
219 # cast int to int, in case subclasses override __str__ (e.g. boost enum, #4598)
211 if isinstance(obj, bool):
220 if isinstance(obj, bool):
212 # bools are ints, but we don't want to cast them to 0,1
221 # bools are ints, but we don't want to cast them to 0,1
213 return obj
222 return obj
214 return int(obj)
223 return int(obj)
215
224
216 if isinstance(obj, atomic_ok):
225 if isinstance(obj, atomic_ok):
217 return obj
226 return obj
218
227
219 if isinstance(obj, bytes):
228 if isinstance(obj, bytes):
220 return obj.decode(DEFAULT_ENCODING, 'replace')
229 return obj.decode(DEFAULT_ENCODING, 'replace')
221
230
222 if isinstance(obj, container_to_list) or (
231 if isinstance(obj, container_to_list) or (
223 hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)):
232 hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)):
224 obj = list(obj)
233 obj = list(obj)
225
234
226 if isinstance(obj, list):
235 if isinstance(obj, list):
227 return [json_clean(x) for x in obj]
236 return [json_clean(x) for x in obj]
228
237
229 if isinstance(obj, dict):
238 if isinstance(obj, dict):
230 # First, validate that the dict won't lose data in conversion due to
239 # First, validate that the dict won't lose data in conversion due to
231 # key collisions after stringification. This can happen with keys like
240 # key collisions after stringification. This can happen with keys like
232 # True and 'true' or 1 and '1', which collide in JSON.
241 # True and 'true' or 1 and '1', which collide in JSON.
233 nkeys = len(obj)
242 nkeys = len(obj)
234 nkeys_collapsed = len(set(map(str, obj)))
243 nkeys_collapsed = len(set(map(str, obj)))
235 if nkeys != nkeys_collapsed:
244 if nkeys != nkeys_collapsed:
236 raise ValueError('dict can not be safely converted to JSON: '
245 raise ValueError('dict can not be safely converted to JSON: '
237 'key collision would lead to dropped values')
246 'key collision would lead to dropped values')
238 # If all OK, proceed by making the new dict that will be json-safe
247 # If all OK, proceed by making the new dict that will be json-safe
239 out = {}
248 out = {}
240 for k,v in iteritems(obj):
249 for k,v in iteritems(obj):
241 out[str(k)] = json_clean(v)
250 out[str(k)] = json_clean(v)
242 return out
251 return out
243
252
244 # If we get here, we don't know how to handle the object, so we just get
253 # If we get here, we don't know how to handle the object, so we just get
245 # its repr and return that. This will catch lambdas, open sockets, class
254 # its repr and return that. This will catch lambdas, open sockets, class
246 # objects, and any other complicated contraption that json can't encode
255 # objects, and any other complicated contraption that json can't encode
247 return repr(obj)
256 return repr(obj)
@@ -1,147 +1,149 b''
1 """Test suite for our JSON utilities.
1 """Test suite for our JSON utilities.
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 datetime
14 import datetime
15 import json
15 import json
16 from base64 import decodestring
16 from base64 import decodestring
17
17
18 # third party
18 # third party
19 import nose.tools as nt
19 import nose.tools as nt
20
20
21 # our own
21 # our own
22 from IPython.utils import jsonutil, tz
22 from IPython.utils import jsonutil, tz
23 from ..jsonutil import json_clean, encode_images
23 from ..jsonutil import json_clean, encode_images
24 from ..py3compat import unicode_to_str, str_to_bytes, iteritems
24 from ..py3compat import unicode_to_str, str_to_bytes, iteritems
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Test functions
27 # Test functions
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 class Int(int):
29 class Int(int):
30 def __str__(self):
30 def __str__(self):
31 return 'Int(%i)' % self
31 return 'Int(%i)' % self
32
32
33 def test():
33 def test():
34 # list of input/expected output. Use None for the expected output if it
34 # list of input/expected output. Use None for the expected output if it
35 # can be the same as the input.
35 # can be the same as the input.
36 pairs = [(1, None), # start with scalars
36 pairs = [(1, None), # start with scalars
37 (1.0, None),
37 (1.0, None),
38 ('a', None),
38 ('a', None),
39 (True, None),
39 (True, None),
40 (False, None),
40 (False, None),
41 (None, None),
41 (None, None),
42 # complex numbers for now just go to strings, as otherwise they
42 # complex numbers for now just go to strings, as otherwise they
43 # are unserializable
43 # are unserializable
44 (1j, '1j'),
44 (1j, '1j'),
45 # Containers
45 # Containers
46 ([1, 2], None),
46 ([1, 2], None),
47 ((1, 2), [1, 2]),
47 ((1, 2), [1, 2]),
48 (set([1, 2]), [1, 2]),
48 (set([1, 2]), [1, 2]),
49 (dict(x=1), None),
49 (dict(x=1), None),
50 ({'x': 1, 'y':[1,2,3], '1':'int'}, None),
50 ({'x': 1, 'y':[1,2,3], '1':'int'}, None),
51 # More exotic objects
51 # More exotic objects
52 ((x for x in range(3)), [0, 1, 2]),
52 ((x for x in range(3)), [0, 1, 2]),
53 (iter([1, 2]), [1, 2]),
53 (iter([1, 2]), [1, 2]),
54 (Int(5), 5),
54 (Int(5), 5),
55 ]
55 ]
56
56
57 for val, jval in pairs:
57 for val, jval in pairs:
58 if jval is None:
58 if jval is None:
59 jval = val
59 jval = val
60 out = json_clean(val)
60 out = json_clean(val)
61 # validate our cleanup
61 # validate our cleanup
62 nt.assert_equal(out, jval)
62 nt.assert_equal(out, jval)
63 # and ensure that what we return, indeed encodes cleanly
63 # and ensure that what we return, indeed encodes cleanly
64 json.loads(json.dumps(out))
64 json.loads(json.dumps(out))
65
65
66
66
67
67
68 def test_encode_images():
68 def test_encode_images():
69 # invalid data, but the header and footer are from real files
69 # invalid data, but the header and footer are from real files
70 pngdata = b'\x89PNG\r\n\x1a\nblahblahnotactuallyvalidIEND\xaeB`\x82'
70 pngdata = b'\x89PNG\r\n\x1a\nblahblahnotactuallyvalidIEND\xaeB`\x82'
71 jpegdata = b'\xff\xd8\xff\xe0\x00\x10JFIFblahblahjpeg(\xa0\x0f\xff\xd9'
71 jpegdata = b'\xff\xd8\xff\xe0\x00\x10JFIFblahblahjpeg(\xa0\x0f\xff\xd9'
72 pdfdata = b'%PDF-1.\ntrailer<</Root<</Pages<</Kids[<</MediaBox[0 0 3 3]>>]>>>>>>'
72
73
73 fmt = {
74 fmt = {
74 'image/png' : pngdata,
75 'image/png' : pngdata,
75 'image/jpeg' : jpegdata,
76 'image/jpeg' : jpegdata,
77 'application/pdf' : pdfdata
76 }
78 }
77 encoded = encode_images(fmt)
79 encoded = encode_images(fmt)
78 for key, value in iteritems(fmt):
80 for key, value in iteritems(fmt):
79 # encoded has unicode, want bytes
81 # encoded has unicode, want bytes
80 decoded = decodestring(encoded[key].encode('ascii'))
82 decoded = decodestring(encoded[key].encode('ascii'))
81 nt.assert_equal(decoded, value)
83 nt.assert_equal(decoded, value)
82 encoded2 = encode_images(encoded)
84 encoded2 = encode_images(encoded)
83 nt.assert_equal(encoded, encoded2)
85 nt.assert_equal(encoded, encoded2)
84
86
85 b64_str = {}
87 b64_str = {}
86 for key, encoded in iteritems(encoded):
88 for key, encoded in iteritems(encoded):
87 b64_str[key] = unicode_to_str(encoded)
89 b64_str[key] = unicode_to_str(encoded)
88 encoded3 = encode_images(b64_str)
90 encoded3 = encode_images(b64_str)
89 nt.assert_equal(encoded3, b64_str)
91 nt.assert_equal(encoded3, b64_str)
90 for key, value in iteritems(fmt):
92 for key, value in iteritems(fmt):
91 # encoded3 has str, want bytes
93 # encoded3 has str, want bytes
92 decoded = decodestring(str_to_bytes(encoded3[key]))
94 decoded = decodestring(str_to_bytes(encoded3[key]))
93 nt.assert_equal(decoded, value)
95 nt.assert_equal(decoded, value)
94
96
95 def test_lambda():
97 def test_lambda():
96 jc = json_clean(lambda : 1)
98 jc = json_clean(lambda : 1)
97 assert isinstance(jc, str)
99 assert isinstance(jc, str)
98 assert '<lambda>' in jc
100 assert '<lambda>' in jc
99 json.dumps(jc)
101 json.dumps(jc)
100
102
101 def test_extract_dates():
103 def test_extract_dates():
102 timestamps = [
104 timestamps = [
103 '2013-07-03T16:34:52.249482',
105 '2013-07-03T16:34:52.249482',
104 '2013-07-03T16:34:52.249482Z',
106 '2013-07-03T16:34:52.249482Z',
105 '2013-07-03T16:34:52.249482Z-0800',
107 '2013-07-03T16:34:52.249482Z-0800',
106 '2013-07-03T16:34:52.249482Z+0800',
108 '2013-07-03T16:34:52.249482Z+0800',
107 '2013-07-03T16:34:52.249482Z+08:00',
109 '2013-07-03T16:34:52.249482Z+08:00',
108 '2013-07-03T16:34:52.249482Z-08:00',
110 '2013-07-03T16:34:52.249482Z-08:00',
109 '2013-07-03T16:34:52.249482-0800',
111 '2013-07-03T16:34:52.249482-0800',
110 '2013-07-03T16:34:52.249482+0800',
112 '2013-07-03T16:34:52.249482+0800',
111 '2013-07-03T16:34:52.249482+08:00',
113 '2013-07-03T16:34:52.249482+08:00',
112 '2013-07-03T16:34:52.249482-08:00',
114 '2013-07-03T16:34:52.249482-08:00',
113 ]
115 ]
114 extracted = jsonutil.extract_dates(timestamps)
116 extracted = jsonutil.extract_dates(timestamps)
115 ref = extracted[0]
117 ref = extracted[0]
116 for dt in extracted:
118 for dt in extracted:
117 nt.assert_true(isinstance(dt, datetime.datetime))
119 nt.assert_true(isinstance(dt, datetime.datetime))
118 nt.assert_equal(dt, ref)
120 nt.assert_equal(dt, ref)
119
121
120 def test_parse_ms_precision():
122 def test_parse_ms_precision():
121 base = '2013-07-03T16:34:52.'
123 base = '2013-07-03T16:34:52.'
122 digits = '1234567890'
124 digits = '1234567890'
123
125
124 for i in range(len(digits)):
126 for i in range(len(digits)):
125 ts = base + digits[:i]
127 ts = base + digits[:i]
126 parsed = jsonutil.parse_date(ts)
128 parsed = jsonutil.parse_date(ts)
127 if i >= 1 and i <= 6:
129 if i >= 1 and i <= 6:
128 assert isinstance(parsed, datetime.datetime)
130 assert isinstance(parsed, datetime.datetime)
129 else:
131 else:
130 assert isinstance(parsed, str)
132 assert isinstance(parsed, str)
131
133
132 def test_date_default():
134 def test_date_default():
133 data = dict(today=datetime.datetime.now(), utcnow=tz.utcnow())
135 data = dict(today=datetime.datetime.now(), utcnow=tz.utcnow())
134 jsondata = json.dumps(data, default=jsonutil.date_default)
136 jsondata = json.dumps(data, default=jsonutil.date_default)
135 nt.assert_in("+00", jsondata)
137 nt.assert_in("+00", jsondata)
136 nt.assert_equal(jsondata.count("+00"), 1)
138 nt.assert_equal(jsondata.count("+00"), 1)
137 extracted = jsonutil.extract_dates(json.loads(jsondata))
139 extracted = jsonutil.extract_dates(json.loads(jsondata))
138 for dt in extracted.values():
140 for dt in extracted.values():
139 nt.assert_true(isinstance(dt, datetime.datetime))
141 nt.assert_true(isinstance(dt, datetime.datetime))
140
142
141 def test_exception():
143 def test_exception():
142 bad_dicts = [{1:'number', '1':'string'},
144 bad_dicts = [{1:'number', '1':'string'},
143 {True:'bool', 'True':'string'},
145 {True:'bool', 'True':'string'},
144 ]
146 ]
145 for d in bad_dicts:
147 for d in bad_dicts:
146 nt.assert_raises(ValueError, json_clean, d)
148 nt.assert_raises(ValueError, json_clean, d)
147
149
General Comments 0
You need to be logged in to leave comments. Login now