##// END OF EJS Templates
Document IPython.display API correctly
Thomas Kluyver -
Show More
@@ -1,811 +1,817 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.core.formatters import _safe_get_formatter_method
25 from IPython.core.formatters import _safe_get_formatter_method
26 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
26 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
27 unicode_type)
27 unicode_type)
28 from IPython.testing.skipdoctest import skip_doctest
28 from IPython.testing.skipdoctest import skip_doctest
29 from .displaypub import publish_display_data
29 from .displaypub import publish_display_data
30
30
31 __all__ = ['display', 'display_pretty', 'display_html', 'display_markdown',
32 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
33 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
34 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'JSON', 'Javascript',
35 'clear_output', 'set_matplotlib_formats', 'set_matplotlib_close']
36
31 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
32 # utility functions
38 # utility functions
33 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
34
40
35 def _safe_exists(path):
41 def _safe_exists(path):
36 """Check path, but don't let exceptions raise"""
42 """Check path, but don't let exceptions raise"""
37 try:
43 try:
38 return os.path.exists(path)
44 return os.path.exists(path)
39 except Exception:
45 except Exception:
40 return False
46 return False
41
47
42 def _merge(d1, d2):
48 def _merge(d1, d2):
43 """Like update, but merges sub-dicts instead of clobbering at the top level.
49 """Like update, but merges sub-dicts instead of clobbering at the top level.
44
50
45 Updates d1 in-place
51 Updates d1 in-place
46 """
52 """
47
53
48 if not isinstance(d2, dict) or not isinstance(d1, dict):
54 if not isinstance(d2, dict) or not isinstance(d1, dict):
49 return d2
55 return d2
50 for key, value in d2.items():
56 for key, value in d2.items():
51 d1[key] = _merge(d1.get(key), value)
57 d1[key] = _merge(d1.get(key), value)
52 return d1
58 return d1
53
59
54 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
60 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
55 """internal implementation of all display_foo methods
61 """internal implementation of all display_foo methods
56
62
57 Parameters
63 Parameters
58 ----------
64 ----------
59 mimetype : str
65 mimetype : str
60 The mimetype to be published (e.g. 'image/png')
66 The mimetype to be published (e.g. 'image/png')
61 objs : tuple of objects
67 objs : tuple of objects
62 The Python objects to display, or if raw=True raw text data to
68 The Python objects to display, or if raw=True raw text data to
63 display.
69 display.
64 raw : bool
70 raw : bool
65 Are the data objects raw data or Python objects that need to be
71 Are the data objects raw data or Python objects that need to be
66 formatted before display? [default: False]
72 formatted before display? [default: False]
67 metadata : dict (optional)
73 metadata : dict (optional)
68 Metadata to be associated with the specific mimetype output.
74 Metadata to be associated with the specific mimetype output.
69 """
75 """
70 if metadata:
76 if metadata:
71 metadata = {mimetype: metadata}
77 metadata = {mimetype: metadata}
72 if raw:
78 if raw:
73 # turn list of pngdata into list of { 'image/png': pngdata }
79 # turn list of pngdata into list of { 'image/png': pngdata }
74 objs = [ {mimetype: obj} for obj in objs ]
80 objs = [ {mimetype: obj} for obj in objs ]
75 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
81 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
76
82
77 #-----------------------------------------------------------------------------
83 #-----------------------------------------------------------------------------
78 # Main functions
84 # Main functions
79 #-----------------------------------------------------------------------------
85 #-----------------------------------------------------------------------------
80
86
81 def display(*objs, **kwargs):
87 def display(*objs, **kwargs):
82 """Display a Python object in all frontends.
88 """Display a Python object in all frontends.
83
89
84 By default all representations will be computed and sent to the frontends.
90 By default all representations will be computed and sent to the frontends.
85 Frontends can decide which representation is used and how.
91 Frontends can decide which representation is used and how.
86
92
87 Parameters
93 Parameters
88 ----------
94 ----------
89 objs : tuple of objects
95 objs : tuple of objects
90 The Python objects to display.
96 The Python objects to display.
91 raw : bool, optional
97 raw : bool, optional
92 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
98 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
93 or Python objects that need to be formatted before display? [default: False]
99 or Python objects that need to be formatted before display? [default: False]
94 include : list or tuple, optional
100 include : list or tuple, optional
95 A list of format type strings (MIME types) to include in the
101 A list of format type strings (MIME types) to include in the
96 format data dict. If this is set *only* the format types included
102 format data dict. If this is set *only* the format types included
97 in this list will be computed.
103 in this list will be computed.
98 exclude : list or tuple, optional
104 exclude : list or tuple, optional
99 A list of format type strings (MIME types) to exclude in the format
105 A list of format type strings (MIME types) to exclude in the format
100 data dict. If this is set all format types will be computed,
106 data dict. If this is set all format types will be computed,
101 except for those included in this argument.
107 except for those included in this argument.
102 metadata : dict, optional
108 metadata : dict, optional
103 A dictionary of metadata to associate with the output.
109 A dictionary of metadata to associate with the output.
104 mime-type keys in this dictionary will be associated with the individual
110 mime-type keys in this dictionary will be associated with the individual
105 representation formats, if they exist.
111 representation formats, if they exist.
106 """
112 """
107 raw = kwargs.get('raw', False)
113 raw = kwargs.get('raw', False)
108 include = kwargs.get('include')
114 include = kwargs.get('include')
109 exclude = kwargs.get('exclude')
115 exclude = kwargs.get('exclude')
110 metadata = kwargs.get('metadata')
116 metadata = kwargs.get('metadata')
111
117
112 from IPython.core.interactiveshell import InteractiveShell
118 from IPython.core.interactiveshell import InteractiveShell
113
119
114 if not raw:
120 if not raw:
115 format = InteractiveShell.instance().display_formatter.format
121 format = InteractiveShell.instance().display_formatter.format
116
122
117 for obj in objs:
123 for obj in objs:
118
124
119 # If _ipython_display_ is defined, use that to display this object.
125 # If _ipython_display_ is defined, use that to display this object.
120 display_method = _safe_get_formatter_method(obj, '_ipython_display_')
126 display_method = _safe_get_formatter_method(obj, '_ipython_display_')
121 if display_method is not None:
127 if display_method is not None:
122 try:
128 try:
123 display_method(**kwargs)
129 display_method(**kwargs)
124 except NotImplementedError:
130 except NotImplementedError:
125 pass
131 pass
126 else:
132 else:
127 continue
133 continue
128 if raw:
134 if raw:
129 publish_display_data(data=obj, metadata=metadata)
135 publish_display_data(data=obj, metadata=metadata)
130 else:
136 else:
131 format_dict, md_dict = format(obj, include=include, exclude=exclude)
137 format_dict, md_dict = format(obj, include=include, exclude=exclude)
132 if metadata:
138 if metadata:
133 # kwarg-specified metadata gets precedence
139 # kwarg-specified metadata gets precedence
134 _merge(md_dict, metadata)
140 _merge(md_dict, metadata)
135 publish_display_data(data=format_dict, metadata=md_dict)
141 publish_display_data(data=format_dict, metadata=md_dict)
136
142
137
143
138 def display_pretty(*objs, **kwargs):
144 def display_pretty(*objs, **kwargs):
139 """Display the pretty (default) representation of an object.
145 """Display the pretty (default) representation of an object.
140
146
141 Parameters
147 Parameters
142 ----------
148 ----------
143 objs : tuple of objects
149 objs : tuple of objects
144 The Python objects to display, or if raw=True raw text data to
150 The Python objects to display, or if raw=True raw text data to
145 display.
151 display.
146 raw : bool
152 raw : bool
147 Are the data objects raw data or Python objects that need to be
153 Are the data objects raw data or Python objects that need to be
148 formatted before display? [default: False]
154 formatted before display? [default: False]
149 metadata : dict (optional)
155 metadata : dict (optional)
150 Metadata to be associated with the specific mimetype output.
156 Metadata to be associated with the specific mimetype output.
151 """
157 """
152 _display_mimetype('text/plain', objs, **kwargs)
158 _display_mimetype('text/plain', objs, **kwargs)
153
159
154
160
155 def display_html(*objs, **kwargs):
161 def display_html(*objs, **kwargs):
156 """Display the HTML representation of an object.
162 """Display the HTML representation of an object.
157
163
158 Parameters
164 Parameters
159 ----------
165 ----------
160 objs : tuple of objects
166 objs : tuple of objects
161 The Python objects to display, or if raw=True raw HTML data to
167 The Python objects to display, or if raw=True raw HTML data to
162 display.
168 display.
163 raw : bool
169 raw : bool
164 Are the data objects raw data or Python objects that need to be
170 Are the data objects raw data or Python objects that need to be
165 formatted before display? [default: False]
171 formatted before display? [default: False]
166 metadata : dict (optional)
172 metadata : dict (optional)
167 Metadata to be associated with the specific mimetype output.
173 Metadata to be associated with the specific mimetype output.
168 """
174 """
169 _display_mimetype('text/html', objs, **kwargs)
175 _display_mimetype('text/html', objs, **kwargs)
170
176
171
177
172 def display_markdown(*objs, **kwargs):
178 def display_markdown(*objs, **kwargs):
173 """Displays the Markdown representation of an object.
179 """Displays the Markdown representation of an object.
174
180
175 Parameters
181 Parameters
176 ----------
182 ----------
177 objs : tuple of objects
183 objs : tuple of objects
178 The Python objects to display, or if raw=True raw markdown data to
184 The Python objects to display, or if raw=True raw markdown data to
179 display.
185 display.
180 raw : bool
186 raw : bool
181 Are the data objects raw data or Python objects that need to be
187 Are the data objects raw data or Python objects that need to be
182 formatted before display? [default: False]
188 formatted before display? [default: False]
183 metadata : dict (optional)
189 metadata : dict (optional)
184 Metadata to be associated with the specific mimetype output.
190 Metadata to be associated with the specific mimetype output.
185 """
191 """
186
192
187 _display_mimetype('text/markdown', objs, **kwargs)
193 _display_mimetype('text/markdown', objs, **kwargs)
188
194
189
195
190 def display_svg(*objs, **kwargs):
196 def display_svg(*objs, **kwargs):
191 """Display the SVG representation of an object.
197 """Display the SVG representation of an object.
192
198
193 Parameters
199 Parameters
194 ----------
200 ----------
195 objs : tuple of objects
201 objs : tuple of objects
196 The Python objects to display, or if raw=True raw svg data to
202 The Python objects to display, or if raw=True raw svg data to
197 display.
203 display.
198 raw : bool
204 raw : bool
199 Are the data objects raw data or Python objects that need to be
205 Are the data objects raw data or Python objects that need to be
200 formatted before display? [default: False]
206 formatted before display? [default: False]
201 metadata : dict (optional)
207 metadata : dict (optional)
202 Metadata to be associated with the specific mimetype output.
208 Metadata to be associated with the specific mimetype output.
203 """
209 """
204 _display_mimetype('image/svg+xml', objs, **kwargs)
210 _display_mimetype('image/svg+xml', objs, **kwargs)
205
211
206
212
207 def display_png(*objs, **kwargs):
213 def display_png(*objs, **kwargs):
208 """Display the PNG representation of an object.
214 """Display the PNG representation of an object.
209
215
210 Parameters
216 Parameters
211 ----------
217 ----------
212 objs : tuple of objects
218 objs : tuple of objects
213 The Python objects to display, or if raw=True raw png data to
219 The Python objects to display, or if raw=True raw png data to
214 display.
220 display.
215 raw : bool
221 raw : bool
216 Are the data objects raw data or Python objects that need to be
222 Are the data objects raw data or Python objects that need to be
217 formatted before display? [default: False]
223 formatted before display? [default: False]
218 metadata : dict (optional)
224 metadata : dict (optional)
219 Metadata to be associated with the specific mimetype output.
225 Metadata to be associated with the specific mimetype output.
220 """
226 """
221 _display_mimetype('image/png', objs, **kwargs)
227 _display_mimetype('image/png', objs, **kwargs)
222
228
223
229
224 def display_jpeg(*objs, **kwargs):
230 def display_jpeg(*objs, **kwargs):
225 """Display the JPEG representation of an object.
231 """Display the JPEG representation of an object.
226
232
227 Parameters
233 Parameters
228 ----------
234 ----------
229 objs : tuple of objects
235 objs : tuple of objects
230 The Python objects to display, or if raw=True raw JPEG data to
236 The Python objects to display, or if raw=True raw JPEG data to
231 display.
237 display.
232 raw : bool
238 raw : bool
233 Are the data objects raw data or Python objects that need to be
239 Are the data objects raw data or Python objects that need to be
234 formatted before display? [default: False]
240 formatted before display? [default: False]
235 metadata : dict (optional)
241 metadata : dict (optional)
236 Metadata to be associated with the specific mimetype output.
242 Metadata to be associated with the specific mimetype output.
237 """
243 """
238 _display_mimetype('image/jpeg', objs, **kwargs)
244 _display_mimetype('image/jpeg', objs, **kwargs)
239
245
240
246
241 def display_latex(*objs, **kwargs):
247 def display_latex(*objs, **kwargs):
242 """Display the LaTeX representation of an object.
248 """Display the LaTeX representation of an object.
243
249
244 Parameters
250 Parameters
245 ----------
251 ----------
246 objs : tuple of objects
252 objs : tuple of objects
247 The Python objects to display, or if raw=True raw latex data to
253 The Python objects to display, or if raw=True raw latex data to
248 display.
254 display.
249 raw : bool
255 raw : bool
250 Are the data objects raw data or Python objects that need to be
256 Are the data objects raw data or Python objects that need to be
251 formatted before display? [default: False]
257 formatted before display? [default: False]
252 metadata : dict (optional)
258 metadata : dict (optional)
253 Metadata to be associated with the specific mimetype output.
259 Metadata to be associated with the specific mimetype output.
254 """
260 """
255 _display_mimetype('text/latex', objs, **kwargs)
261 _display_mimetype('text/latex', objs, **kwargs)
256
262
257
263
258 def display_json(*objs, **kwargs):
264 def display_json(*objs, **kwargs):
259 """Display the JSON representation of an object.
265 """Display the JSON representation of an object.
260
266
261 Note that not many frontends support displaying JSON.
267 Note that not many frontends support displaying JSON.
262
268
263 Parameters
269 Parameters
264 ----------
270 ----------
265 objs : tuple of objects
271 objs : tuple of objects
266 The Python objects to display, or if raw=True raw json data to
272 The Python objects to display, or if raw=True raw json data to
267 display.
273 display.
268 raw : bool
274 raw : bool
269 Are the data objects raw data or Python objects that need to be
275 Are the data objects raw data or Python objects that need to be
270 formatted before display? [default: False]
276 formatted before display? [default: False]
271 metadata : dict (optional)
277 metadata : dict (optional)
272 Metadata to be associated with the specific mimetype output.
278 Metadata to be associated with the specific mimetype output.
273 """
279 """
274 _display_mimetype('application/json', objs, **kwargs)
280 _display_mimetype('application/json', objs, **kwargs)
275
281
276
282
277 def display_javascript(*objs, **kwargs):
283 def display_javascript(*objs, **kwargs):
278 """Display the Javascript representation of an object.
284 """Display the Javascript representation of an object.
279
285
280 Parameters
286 Parameters
281 ----------
287 ----------
282 objs : tuple of objects
288 objs : tuple of objects
283 The Python objects to display, or if raw=True raw javascript data to
289 The Python objects to display, or if raw=True raw javascript data to
284 display.
290 display.
285 raw : bool
291 raw : bool
286 Are the data objects raw data or Python objects that need to be
292 Are the data objects raw data or Python objects that need to be
287 formatted before display? [default: False]
293 formatted before display? [default: False]
288 metadata : dict (optional)
294 metadata : dict (optional)
289 Metadata to be associated with the specific mimetype output.
295 Metadata to be associated with the specific mimetype output.
290 """
296 """
291 _display_mimetype('application/javascript', objs, **kwargs)
297 _display_mimetype('application/javascript', objs, **kwargs)
292
298
293
299
294 def display_pdf(*objs, **kwargs):
300 def display_pdf(*objs, **kwargs):
295 """Display the PDF representation of an object.
301 """Display the PDF representation of an object.
296
302
297 Parameters
303 Parameters
298 ----------
304 ----------
299 objs : tuple of objects
305 objs : tuple of objects
300 The Python objects to display, or if raw=True raw javascript data to
306 The Python objects to display, or if raw=True raw javascript data to
301 display.
307 display.
302 raw : bool
308 raw : bool
303 Are the data objects raw data or Python objects that need to be
309 Are the data objects raw data or Python objects that need to be
304 formatted before display? [default: False]
310 formatted before display? [default: False]
305 metadata : dict (optional)
311 metadata : dict (optional)
306 Metadata to be associated with the specific mimetype output.
312 Metadata to be associated with the specific mimetype output.
307 """
313 """
308 _display_mimetype('application/pdf', objs, **kwargs)
314 _display_mimetype('application/pdf', objs, **kwargs)
309
315
310
316
311 #-----------------------------------------------------------------------------
317 #-----------------------------------------------------------------------------
312 # Smart classes
318 # Smart classes
313 #-----------------------------------------------------------------------------
319 #-----------------------------------------------------------------------------
314
320
315
321
316 class DisplayObject(object):
322 class DisplayObject(object):
317 """An object that wraps data to be displayed."""
323 """An object that wraps data to be displayed."""
318
324
319 _read_flags = 'r'
325 _read_flags = 'r'
320 _show_mem_addr = False
326 _show_mem_addr = False
321
327
322 def __init__(self, data=None, url=None, filename=None):
328 def __init__(self, data=None, url=None, filename=None):
323 """Create a display object given raw data.
329 """Create a display object given raw data.
324
330
325 When this object is returned by an expression or passed to the
331 When this object is returned by an expression or passed to the
326 display function, it will result in the data being displayed
332 display function, it will result in the data being displayed
327 in the frontend. The MIME type of the data should match the
333 in the frontend. The MIME type of the data should match the
328 subclasses used, so the Png subclass should be used for 'image/png'
334 subclasses used, so the Png subclass should be used for 'image/png'
329 data. If the data is a URL, the data will first be downloaded
335 data. If the data is a URL, the data will first be downloaded
330 and then displayed. If
336 and then displayed. If
331
337
332 Parameters
338 Parameters
333 ----------
339 ----------
334 data : unicode, str or bytes
340 data : unicode, str or bytes
335 The raw data or a URL or file to load the data from
341 The raw data or a URL or file to load the data from
336 url : unicode
342 url : unicode
337 A URL to download the data from.
343 A URL to download the data from.
338 filename : unicode
344 filename : unicode
339 Path to a local file to load the data from.
345 Path to a local file to load the data from.
340 """
346 """
341 if data is not None and isinstance(data, string_types):
347 if data is not None and isinstance(data, string_types):
342 if data.startswith('http') and url is None:
348 if data.startswith('http') and url is None:
343 url = data
349 url = data
344 filename = None
350 filename = None
345 data = None
351 data = None
346 elif _safe_exists(data) and filename is None:
352 elif _safe_exists(data) and filename is None:
347 url = None
353 url = None
348 filename = data
354 filename = data
349 data = None
355 data = None
350
356
351 self.data = data
357 self.data = data
352 self.url = url
358 self.url = url
353 self.filename = None if filename is None else unicode_type(filename)
359 self.filename = None if filename is None else unicode_type(filename)
354
360
355 self.reload()
361 self.reload()
356 self._check_data()
362 self._check_data()
357
363
358 def __repr__(self):
364 def __repr__(self):
359 if not self._show_mem_addr:
365 if not self._show_mem_addr:
360 cls = self.__class__
366 cls = self.__class__
361 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
367 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
362 else:
368 else:
363 r = super(DisplayObject, self).__repr__()
369 r = super(DisplayObject, self).__repr__()
364 return r
370 return r
365
371
366 def _check_data(self):
372 def _check_data(self):
367 """Override in subclasses if there's something to check."""
373 """Override in subclasses if there's something to check."""
368 pass
374 pass
369
375
370 def reload(self):
376 def reload(self):
371 """Reload the raw data from file or URL."""
377 """Reload the raw data from file or URL."""
372 if self.filename is not None:
378 if self.filename is not None:
373 with open(self.filename, self._read_flags) as f:
379 with open(self.filename, self._read_flags) as f:
374 self.data = f.read()
380 self.data = f.read()
375 elif self.url is not None:
381 elif self.url is not None:
376 try:
382 try:
377 try:
383 try:
378 from urllib.request import urlopen # Py3
384 from urllib.request import urlopen # Py3
379 except ImportError:
385 except ImportError:
380 from urllib2 import urlopen
386 from urllib2 import urlopen
381 response = urlopen(self.url)
387 response = urlopen(self.url)
382 self.data = response.read()
388 self.data = response.read()
383 # extract encoding from header, if there is one:
389 # extract encoding from header, if there is one:
384 encoding = None
390 encoding = None
385 for sub in response.headers['content-type'].split(';'):
391 for sub in response.headers['content-type'].split(';'):
386 sub = sub.strip()
392 sub = sub.strip()
387 if sub.startswith('charset'):
393 if sub.startswith('charset'):
388 encoding = sub.split('=')[-1].strip()
394 encoding = sub.split('=')[-1].strip()
389 break
395 break
390 # decode data, if an encoding was specified
396 # decode data, if an encoding was specified
391 if encoding:
397 if encoding:
392 self.data = self.data.decode(encoding, 'replace')
398 self.data = self.data.decode(encoding, 'replace')
393 except:
399 except:
394 self.data = None
400 self.data = None
395
401
396 class TextDisplayObject(DisplayObject):
402 class TextDisplayObject(DisplayObject):
397 """Validate that display data is text"""
403 """Validate that display data is text"""
398 def _check_data(self):
404 def _check_data(self):
399 if self.data is not None and not isinstance(self.data, string_types):
405 if self.data is not None and not isinstance(self.data, string_types):
400 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
406 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
401
407
402 class Pretty(TextDisplayObject):
408 class Pretty(TextDisplayObject):
403
409
404 def _repr_pretty_(self):
410 def _repr_pretty_(self):
405 return self.data
411 return self.data
406
412
407
413
408 class HTML(TextDisplayObject):
414 class HTML(TextDisplayObject):
409
415
410 def _repr_html_(self):
416 def _repr_html_(self):
411 return self.data
417 return self.data
412
418
413 def __html__(self):
419 def __html__(self):
414 """
420 """
415 This method exists to inform other HTML-using modules (e.g. Markupsafe,
421 This method exists to inform other HTML-using modules (e.g. Markupsafe,
416 htmltag, etc) that this object is HTML and does not need things like
422 htmltag, etc) that this object is HTML and does not need things like
417 special characters (<>&) escaped.
423 special characters (<>&) escaped.
418 """
424 """
419 return self._repr_html_()
425 return self._repr_html_()
420
426
421
427
422 class Markdown(TextDisplayObject):
428 class Markdown(TextDisplayObject):
423
429
424 def _repr_markdown_(self):
430 def _repr_markdown_(self):
425 return self.data
431 return self.data
426
432
427
433
428 class Math(TextDisplayObject):
434 class Math(TextDisplayObject):
429
435
430 def _repr_latex_(self):
436 def _repr_latex_(self):
431 s = self.data.strip('$')
437 s = self.data.strip('$')
432 return "$$%s$$" % s
438 return "$$%s$$" % s
433
439
434
440
435 class Latex(TextDisplayObject):
441 class Latex(TextDisplayObject):
436
442
437 def _repr_latex_(self):
443 def _repr_latex_(self):
438 return self.data
444 return self.data
439
445
440
446
441 class SVG(DisplayObject):
447 class SVG(DisplayObject):
442
448
443 # wrap data in a property, which extracts the <svg> tag, discarding
449 # wrap data in a property, which extracts the <svg> tag, discarding
444 # document headers
450 # document headers
445 _data = None
451 _data = None
446
452
447 @property
453 @property
448 def data(self):
454 def data(self):
449 return self._data
455 return self._data
450
456
451 @data.setter
457 @data.setter
452 def data(self, svg):
458 def data(self, svg):
453 if svg is None:
459 if svg is None:
454 self._data = None
460 self._data = None
455 return
461 return
456 # parse into dom object
462 # parse into dom object
457 from xml.dom import minidom
463 from xml.dom import minidom
458 svg = cast_bytes_py2(svg)
464 svg = cast_bytes_py2(svg)
459 x = minidom.parseString(svg)
465 x = minidom.parseString(svg)
460 # get svg tag (should be 1)
466 # get svg tag (should be 1)
461 found_svg = x.getElementsByTagName('svg')
467 found_svg = x.getElementsByTagName('svg')
462 if found_svg:
468 if found_svg:
463 svg = found_svg[0].toxml()
469 svg = found_svg[0].toxml()
464 else:
470 else:
465 # fallback on the input, trust the user
471 # fallback on the input, trust the user
466 # but this is probably an error.
472 # but this is probably an error.
467 pass
473 pass
468 svg = cast_unicode(svg)
474 svg = cast_unicode(svg)
469 self._data = svg
475 self._data = svg
470
476
471 def _repr_svg_(self):
477 def _repr_svg_(self):
472 return self.data
478 return self.data
473
479
474
480
475 class JSON(TextDisplayObject):
481 class JSON(TextDisplayObject):
476
482
477 def _repr_json_(self):
483 def _repr_json_(self):
478 return self.data
484 return self.data
479
485
480 css_t = """$("head").append($("<link/>").attr({
486 css_t = """$("head").append($("<link/>").attr({
481 rel: "stylesheet",
487 rel: "stylesheet",
482 type: "text/css",
488 type: "text/css",
483 href: "%s"
489 href: "%s"
484 }));
490 }));
485 """
491 """
486
492
487 lib_t1 = """$.getScript("%s", function () {
493 lib_t1 = """$.getScript("%s", function () {
488 """
494 """
489 lib_t2 = """});
495 lib_t2 = """});
490 """
496 """
491
497
492 class Javascript(TextDisplayObject):
498 class Javascript(TextDisplayObject):
493
499
494 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
500 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
495 """Create a Javascript display object given raw data.
501 """Create a Javascript display object given raw data.
496
502
497 When this object is returned by an expression or passed to the
503 When this object is returned by an expression or passed to the
498 display function, it will result in the data being displayed
504 display function, it will result in the data being displayed
499 in the frontend. If the data is a URL, the data will first be
505 in the frontend. If the data is a URL, the data will first be
500 downloaded and then displayed.
506 downloaded and then displayed.
501
507
502 In the Notebook, the containing element will be available as `element`,
508 In the Notebook, the containing element will be available as `element`,
503 and jQuery will be available. Content appended to `element` will be
509 and jQuery will be available. Content appended to `element` will be
504 visible in the output area.
510 visible in the output area.
505
511
506 Parameters
512 Parameters
507 ----------
513 ----------
508 data : unicode, str or bytes
514 data : unicode, str or bytes
509 The Javascript source code or a URL to download it from.
515 The Javascript source code or a URL to download it from.
510 url : unicode
516 url : unicode
511 A URL to download the data from.
517 A URL to download the data from.
512 filename : unicode
518 filename : unicode
513 Path to a local file to load the data from.
519 Path to a local file to load the data from.
514 lib : list or str
520 lib : list or str
515 A sequence of Javascript library URLs to load asynchronously before
521 A sequence of Javascript library URLs to load asynchronously before
516 running the source code. The full URLs of the libraries should
522 running the source code. The full URLs of the libraries should
517 be given. A single Javascript library URL can also be given as a
523 be given. A single Javascript library URL can also be given as a
518 string.
524 string.
519 css: : list or str
525 css: : list or str
520 A sequence of css files to load before running the source code.
526 A sequence of css files to load before running the source code.
521 The full URLs of the css files should be given. A single css URL
527 The full URLs of the css files should be given. A single css URL
522 can also be given as a string.
528 can also be given as a string.
523 """
529 """
524 if isinstance(lib, string_types):
530 if isinstance(lib, string_types):
525 lib = [lib]
531 lib = [lib]
526 elif lib is None:
532 elif lib is None:
527 lib = []
533 lib = []
528 if isinstance(css, string_types):
534 if isinstance(css, string_types):
529 css = [css]
535 css = [css]
530 elif css is None:
536 elif css is None:
531 css = []
537 css = []
532 if not isinstance(lib, (list,tuple)):
538 if not isinstance(lib, (list,tuple)):
533 raise TypeError('expected sequence, got: %r' % lib)
539 raise TypeError('expected sequence, got: %r' % lib)
534 if not isinstance(css, (list,tuple)):
540 if not isinstance(css, (list,tuple)):
535 raise TypeError('expected sequence, got: %r' % css)
541 raise TypeError('expected sequence, got: %r' % css)
536 self.lib = lib
542 self.lib = lib
537 self.css = css
543 self.css = css
538 super(Javascript, self).__init__(data=data, url=url, filename=filename)
544 super(Javascript, self).__init__(data=data, url=url, filename=filename)
539
545
540 def _repr_javascript_(self):
546 def _repr_javascript_(self):
541 r = ''
547 r = ''
542 for c in self.css:
548 for c in self.css:
543 r += css_t % c
549 r += css_t % c
544 for l in self.lib:
550 for l in self.lib:
545 r += lib_t1 % l
551 r += lib_t1 % l
546 r += self.data
552 r += self.data
547 r += lib_t2*len(self.lib)
553 r += lib_t2*len(self.lib)
548 return r
554 return r
549
555
550 # constants for identifying png/jpeg data
556 # constants for identifying png/jpeg data
551 _PNG = b'\x89PNG\r\n\x1a\n'
557 _PNG = b'\x89PNG\r\n\x1a\n'
552 _JPEG = b'\xff\xd8'
558 _JPEG = b'\xff\xd8'
553
559
554 def _pngxy(data):
560 def _pngxy(data):
555 """read the (width, height) from a PNG header"""
561 """read the (width, height) from a PNG header"""
556 ihdr = data.index(b'IHDR')
562 ihdr = data.index(b'IHDR')
557 # next 8 bytes are width/height
563 # next 8 bytes are width/height
558 w4h4 = data[ihdr+4:ihdr+12]
564 w4h4 = data[ihdr+4:ihdr+12]
559 return struct.unpack('>ii', w4h4)
565 return struct.unpack('>ii', w4h4)
560
566
561 def _jpegxy(data):
567 def _jpegxy(data):
562 """read the (width, height) from a JPEG header"""
568 """read the (width, height) from a JPEG header"""
563 # adapted from http://www.64lines.com/jpeg-width-height
569 # adapted from http://www.64lines.com/jpeg-width-height
564
570
565 idx = 4
571 idx = 4
566 while True:
572 while True:
567 block_size = struct.unpack('>H', data[idx:idx+2])[0]
573 block_size = struct.unpack('>H', data[idx:idx+2])[0]
568 idx = idx + block_size
574 idx = idx + block_size
569 if data[idx:idx+2] == b'\xFF\xC0':
575 if data[idx:idx+2] == b'\xFF\xC0':
570 # found Start of Frame
576 # found Start of Frame
571 iSOF = idx
577 iSOF = idx
572 break
578 break
573 else:
579 else:
574 # read another block
580 # read another block
575 idx += 2
581 idx += 2
576
582
577 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
583 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
578 return w, h
584 return w, h
579
585
580 class Image(DisplayObject):
586 class Image(DisplayObject):
581
587
582 _read_flags = 'rb'
588 _read_flags = 'rb'
583 _FMT_JPEG = u'jpeg'
589 _FMT_JPEG = u'jpeg'
584 _FMT_PNG = u'png'
590 _FMT_PNG = u'png'
585 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
591 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
586
592
587 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
593 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
588 """Create a PNG/JPEG image object given raw data.
594 """Create a PNG/JPEG image object given raw data.
589
595
590 When this object is returned by an input cell or passed to the
596 When this object is returned by an input cell or passed to the
591 display function, it will result in the image being displayed
597 display function, it will result in the image being displayed
592 in the frontend.
598 in the frontend.
593
599
594 Parameters
600 Parameters
595 ----------
601 ----------
596 data : unicode, str or bytes
602 data : unicode, str or bytes
597 The raw image data or a URL or filename to load the data from.
603 The raw image data or a URL or filename to load the data from.
598 This always results in embedded image data.
604 This always results in embedded image data.
599 url : unicode
605 url : unicode
600 A URL to download the data from. If you specify `url=`,
606 A URL to download the data from. If you specify `url=`,
601 the image data will not be embedded unless you also specify `embed=True`.
607 the image data will not be embedded unless you also specify `embed=True`.
602 filename : unicode
608 filename : unicode
603 Path to a local file to load the data from.
609 Path to a local file to load the data from.
604 Images from a file are always embedded.
610 Images from a file are always embedded.
605 format : unicode
611 format : unicode
606 The format of the image data (png/jpeg/jpg). If a filename or URL is given
612 The format of the image data (png/jpeg/jpg). If a filename or URL is given
607 for format will be inferred from the filename extension.
613 for format will be inferred from the filename extension.
608 embed : bool
614 embed : bool
609 Should the image data be embedded using a data URI (True) or be
615 Should the image data be embedded using a data URI (True) or be
610 loaded using an <img> tag. Set this to True if you want the image
616 loaded using an <img> tag. Set this to True if you want the image
611 to be viewable later with no internet connection in the notebook.
617 to be viewable later with no internet connection in the notebook.
612
618
613 Default is `True`, unless the keyword argument `url` is set, then
619 Default is `True`, unless the keyword argument `url` is set, then
614 default value is `False`.
620 default value is `False`.
615
621
616 Note that QtConsole is not able to display images if `embed` is set to `False`
622 Note that QtConsole is not able to display images if `embed` is set to `False`
617 width : int
623 width : int
618 Width to which to constrain the image in html
624 Width to which to constrain the image in html
619 height : int
625 height : int
620 Height to which to constrain the image in html
626 Height to which to constrain the image in html
621 retina : bool
627 retina : bool
622 Automatically set the width and height to half of the measured
628 Automatically set the width and height to half of the measured
623 width and height.
629 width and height.
624 This only works for embedded images because it reads the width/height
630 This only works for embedded images because it reads the width/height
625 from image data.
631 from image data.
626 For non-embedded images, you can just set the desired display width
632 For non-embedded images, you can just set the desired display width
627 and height directly.
633 and height directly.
628
634
629 Examples
635 Examples
630 --------
636 --------
631 # embedded image data, works in qtconsole and notebook
637 # embedded image data, works in qtconsole and notebook
632 # when passed positionally, the first arg can be any of raw image data,
638 # when passed positionally, the first arg can be any of raw image data,
633 # a URL, or a filename from which to load image data.
639 # a URL, or a filename from which to load image data.
634 # The result is always embedding image data for inline images.
640 # The result is always embedding image data for inline images.
635 Image('http://www.google.fr/images/srpr/logo3w.png')
641 Image('http://www.google.fr/images/srpr/logo3w.png')
636 Image('/path/to/image.jpg')
642 Image('/path/to/image.jpg')
637 Image(b'RAW_PNG_DATA...')
643 Image(b'RAW_PNG_DATA...')
638
644
639 # Specifying Image(url=...) does not embed the image data,
645 # Specifying Image(url=...) does not embed the image data,
640 # it only generates `<img>` tag with a link to the source.
646 # it only generates `<img>` tag with a link to the source.
641 # This will not work in the qtconsole or offline.
647 # This will not work in the qtconsole or offline.
642 Image(url='http://www.google.fr/images/srpr/logo3w.png')
648 Image(url='http://www.google.fr/images/srpr/logo3w.png')
643
649
644 """
650 """
645 if filename is not None:
651 if filename is not None:
646 ext = self._find_ext(filename)
652 ext = self._find_ext(filename)
647 elif url is not None:
653 elif url is not None:
648 ext = self._find_ext(url)
654 ext = self._find_ext(url)
649 elif data is None:
655 elif data is None:
650 raise ValueError("No image data found. Expecting filename, url, or data.")
656 raise ValueError("No image data found. Expecting filename, url, or data.")
651 elif isinstance(data, string_types) and (
657 elif isinstance(data, string_types) and (
652 data.startswith('http') or _safe_exists(data)
658 data.startswith('http') or _safe_exists(data)
653 ):
659 ):
654 ext = self._find_ext(data)
660 ext = self._find_ext(data)
655 else:
661 else:
656 ext = None
662 ext = None
657
663
658 if ext is not None:
664 if ext is not None:
659 format = ext.lower()
665 format = ext.lower()
660 if ext == u'jpg' or ext == u'jpeg':
666 if ext == u'jpg' or ext == u'jpeg':
661 format = self._FMT_JPEG
667 format = self._FMT_JPEG
662 if ext == u'png':
668 if ext == u'png':
663 format = self._FMT_PNG
669 format = self._FMT_PNG
664 elif isinstance(data, bytes) and format == 'png':
670 elif isinstance(data, bytes) and format == 'png':
665 # infer image type from image data header,
671 # infer image type from image data header,
666 # only if format might not have been specified.
672 # only if format might not have been specified.
667 if data[:2] == _JPEG:
673 if data[:2] == _JPEG:
668 format = 'jpeg'
674 format = 'jpeg'
669
675
670 self.format = unicode_type(format).lower()
676 self.format = unicode_type(format).lower()
671 self.embed = embed if embed is not None else (url is None)
677 self.embed = embed if embed is not None else (url is None)
672
678
673 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
679 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
674 raise ValueError("Cannot embed the '%s' image format" % (self.format))
680 raise ValueError("Cannot embed the '%s' image format" % (self.format))
675 self.width = width
681 self.width = width
676 self.height = height
682 self.height = height
677 self.retina = retina
683 self.retina = retina
678 super(Image, self).__init__(data=data, url=url, filename=filename)
684 super(Image, self).__init__(data=data, url=url, filename=filename)
679
685
680 if retina:
686 if retina:
681 self._retina_shape()
687 self._retina_shape()
682
688
683 def _retina_shape(self):
689 def _retina_shape(self):
684 """load pixel-doubled width and height from image data"""
690 """load pixel-doubled width and height from image data"""
685 if not self.embed:
691 if not self.embed:
686 return
692 return
687 if self.format == 'png':
693 if self.format == 'png':
688 w, h = _pngxy(self.data)
694 w, h = _pngxy(self.data)
689 elif self.format == 'jpeg':
695 elif self.format == 'jpeg':
690 w, h = _jpegxy(self.data)
696 w, h = _jpegxy(self.data)
691 else:
697 else:
692 # retina only supports png
698 # retina only supports png
693 return
699 return
694 self.width = w // 2
700 self.width = w // 2
695 self.height = h // 2
701 self.height = h // 2
696
702
697 def reload(self):
703 def reload(self):
698 """Reload the raw data from file or URL."""
704 """Reload the raw data from file or URL."""
699 if self.embed:
705 if self.embed:
700 super(Image,self).reload()
706 super(Image,self).reload()
701 if self.retina:
707 if self.retina:
702 self._retina_shape()
708 self._retina_shape()
703
709
704 def _repr_html_(self):
710 def _repr_html_(self):
705 if not self.embed:
711 if not self.embed:
706 width = height = ''
712 width = height = ''
707 if self.width:
713 if self.width:
708 width = ' width="%d"' % self.width
714 width = ' width="%d"' % self.width
709 if self.height:
715 if self.height:
710 height = ' height="%d"' % self.height
716 height = ' height="%d"' % self.height
711 return u'<img src="%s"%s%s/>' % (self.url, width, height)
717 return u'<img src="%s"%s%s/>' % (self.url, width, height)
712
718
713 def _data_and_metadata(self):
719 def _data_and_metadata(self):
714 """shortcut for returning metadata with shape information, if defined"""
720 """shortcut for returning metadata with shape information, if defined"""
715 md = {}
721 md = {}
716 if self.width:
722 if self.width:
717 md['width'] = self.width
723 md['width'] = self.width
718 if self.height:
724 if self.height:
719 md['height'] = self.height
725 md['height'] = self.height
720 if md:
726 if md:
721 return self.data, md
727 return self.data, md
722 else:
728 else:
723 return self.data
729 return self.data
724
730
725 def _repr_png_(self):
731 def _repr_png_(self):
726 if self.embed and self.format == u'png':
732 if self.embed and self.format == u'png':
727 return self._data_and_metadata()
733 return self._data_and_metadata()
728
734
729 def _repr_jpeg_(self):
735 def _repr_jpeg_(self):
730 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
736 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
731 return self._data_and_metadata()
737 return self._data_and_metadata()
732
738
733 def _find_ext(self, s):
739 def _find_ext(self, s):
734 return unicode_type(s.split('.')[-1].lower())
740 return unicode_type(s.split('.')[-1].lower())
735
741
736
742
737 def clear_output(wait=False):
743 def clear_output(wait=False):
738 """Clear the output of the current cell receiving output.
744 """Clear the output of the current cell receiving output.
739
745
740 Parameters
746 Parameters
741 ----------
747 ----------
742 wait : bool [default: false]
748 wait : bool [default: false]
743 Wait to clear the output until new output is available to replace it."""
749 Wait to clear the output until new output is available to replace it."""
744 from IPython.core.interactiveshell import InteractiveShell
750 from IPython.core.interactiveshell import InteractiveShell
745 if InteractiveShell.initialized():
751 if InteractiveShell.initialized():
746 InteractiveShell.instance().display_pub.clear_output(wait)
752 InteractiveShell.instance().display_pub.clear_output(wait)
747 else:
753 else:
748 from IPython.utils import io
754 from IPython.utils import io
749 print('\033[2K\r', file=io.stdout, end='')
755 print('\033[2K\r', file=io.stdout, end='')
750 io.stdout.flush()
756 io.stdout.flush()
751 print('\033[2K\r', file=io.stderr, end='')
757 print('\033[2K\r', file=io.stderr, end='')
752 io.stderr.flush()
758 io.stderr.flush()
753
759
754
760
755 @skip_doctest
761 @skip_doctest
756 def set_matplotlib_formats(*formats, **kwargs):
762 def set_matplotlib_formats(*formats, **kwargs):
757 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
763 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
758
764
759 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
765 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
760
766
761 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
767 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
762
768
763 To set this in your config files use the following::
769 To set this in your config files use the following::
764
770
765 c.InlineBackend.figure_formats = {'png', 'jpeg'}
771 c.InlineBackend.figure_formats = {'png', 'jpeg'}
766 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
772 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
767
773
768 Parameters
774 Parameters
769 ----------
775 ----------
770 *formats : strs
776 *formats : strs
771 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
777 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
772 **kwargs :
778 **kwargs :
773 Keyword args will be relayed to ``figure.canvas.print_figure``.
779 Keyword args will be relayed to ``figure.canvas.print_figure``.
774 """
780 """
775 from IPython.core.interactiveshell import InteractiveShell
781 from IPython.core.interactiveshell import InteractiveShell
776 from IPython.core.pylabtools import select_figure_formats
782 from IPython.core.pylabtools import select_figure_formats
777 from IPython.kernel.zmq.pylab.config import InlineBackend
783 from IPython.kernel.zmq.pylab.config import InlineBackend
778 # build kwargs, starting with InlineBackend config
784 # build kwargs, starting with InlineBackend config
779 kw = {}
785 kw = {}
780 cfg = InlineBackend.instance()
786 cfg = InlineBackend.instance()
781 kw.update(cfg.print_figure_kwargs)
787 kw.update(cfg.print_figure_kwargs)
782 kw.update(**kwargs)
788 kw.update(**kwargs)
783 shell = InteractiveShell.instance()
789 shell = InteractiveShell.instance()
784 select_figure_formats(shell, formats, **kw)
790 select_figure_formats(shell, formats, **kw)
785
791
786 @skip_doctest
792 @skip_doctest
787 def set_matplotlib_close(close=True):
793 def set_matplotlib_close(close=True):
788 """Set whether the inline backend closes all figures automatically or not.
794 """Set whether the inline backend closes all figures automatically or not.
789
795
790 By default, the inline backend used in the IPython Notebook will close all
796 By default, the inline backend used in the IPython Notebook will close all
791 matplotlib figures automatically after each cell is run. This means that
797 matplotlib figures automatically after each cell is run. This means that
792 plots in different cells won't interfere. Sometimes, you may want to make
798 plots in different cells won't interfere. Sometimes, you may want to make
793 a plot in one cell and then refine it in later cells. This can be accomplished
799 a plot in one cell and then refine it in later cells. This can be accomplished
794 by::
800 by::
795
801
796 In [1]: set_matplotlib_close(False)
802 In [1]: set_matplotlib_close(False)
797
803
798 To set this in your config files use the following::
804 To set this in your config files use the following::
799
805
800 c.InlineBackend.close_figures = False
806 c.InlineBackend.close_figures = False
801
807
802 Parameters
808 Parameters
803 ----------
809 ----------
804 close : bool
810 close : bool
805 Should all matplotlib figures be automatically closed after each cell is
811 Should all matplotlib figures be automatically closed after each cell is
806 run?
812 run?
807 """
813 """
808 from IPython.kernel.zmq.pylab.config import InlineBackend
814 from IPython.kernel.zmq.pylab.config import InlineBackend
809 cfg = InlineBackend.instance()
815 cfg = InlineBackend.instance()
810 cfg.close_figures = close
816 cfg.close_figures = close
811
817
@@ -1,530 +1,533 b''
1 """Various display related classes.
1 """Various display related classes.
2
2
3 Authors : MinRK, gregcaporaso, dannystaple
3 Authors : MinRK, gregcaporaso, dannystaple
4 """
4 """
5 from os.path import exists, isfile, splitext, abspath, join, isdir
5 from os.path import exists, isfile, splitext, abspath, join, isdir
6 from os import walk, sep
6 from os import walk, sep
7
7
8 from IPython.core.display import DisplayObject
8 from IPython.core.display import DisplayObject
9
9
10 __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument',
11 'FileLink', 'FileLinks']
12
10
13
11 class Audio(DisplayObject):
14 class Audio(DisplayObject):
12 """Create an audio object.
15 """Create an audio object.
13
16
14 When this object is returned by an input cell or passed to the
17 When this object is returned by an input cell or passed to the
15 display function, it will result in Audio controls being displayed
18 display function, it will result in Audio controls being displayed
16 in the frontend (only works in the notebook).
19 in the frontend (only works in the notebook).
17
20
18 Parameters
21 Parameters
19 ----------
22 ----------
20 data : numpy array, list, unicode, str or bytes
23 data : numpy array, list, unicode, str or bytes
21 Can be one of
24 Can be one of
22
25
23 * Numpy 1d array containing the desired waveform (mono)
26 * Numpy 1d array containing the desired waveform (mono)
24 * Numpy 2d array containing waveforms for each channel.
27 * Numpy 2d array containing waveforms for each channel.
25 Shape=(NCHAN, NSAMPLES). For the standard channel order, see
28 Shape=(NCHAN, NSAMPLES). For the standard channel order, see
26 http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
29 http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
27 * List of float or integer representing the waveform (mono)
30 * List of float or integer representing the waveform (mono)
28 * String containing the filename
31 * String containing the filename
29 * Bytestring containing raw PCM data or
32 * Bytestring containing raw PCM data or
30 * URL pointing to a file on the web.
33 * URL pointing to a file on the web.
31
34
32 If the array option is used the waveform will be normalized.
35 If the array option is used the waveform will be normalized.
33
36
34 If a filename or url is used the format support will be browser
37 If a filename or url is used the format support will be browser
35 dependent.
38 dependent.
36 url : unicode
39 url : unicode
37 A URL to download the data from.
40 A URL to download the data from.
38 filename : unicode
41 filename : unicode
39 Path to a local file to load the data from.
42 Path to a local file to load the data from.
40 embed : boolean
43 embed : boolean
41 Should the image data be embedded using a data URI (True) or should
44 Should the image data be embedded using a data URI (True) or should
42 the original source be referenced. Set this to True if you want the
45 the original source be referenced. Set this to True if you want the
43 audio to playable later with no internet connection in the notebook.
46 audio to playable later with no internet connection in the notebook.
44
47
45 Default is `True`, unless the keyword argument `url` is set, then
48 Default is `True`, unless the keyword argument `url` is set, then
46 default value is `False`.
49 default value is `False`.
47 rate : integer
50 rate : integer
48 The sampling rate of the raw data.
51 The sampling rate of the raw data.
49 Only required when data parameter is being used as an array
52 Only required when data parameter is being used as an array
50 autoplay : bool
53 autoplay : bool
51 Set to True if the audio should immediately start playing.
54 Set to True if the audio should immediately start playing.
52 Default is `False`.
55 Default is `False`.
53
56
54 Examples
57 Examples
55 --------
58 --------
56 ::
59 ::
57
60
58 # Generate a sound
61 # Generate a sound
59 import numpy as np
62 import numpy as np
60 framerate = 44100
63 framerate = 44100
61 t = np.linspace(0,5,framerate*5)
64 t = np.linspace(0,5,framerate*5)
62 data = np.sin(2*np.pi*220*t) + np.sin(2*np.pi*224*t))
65 data = np.sin(2*np.pi*220*t) + np.sin(2*np.pi*224*t))
63 Audio(data,rate=framerate)
66 Audio(data,rate=framerate)
64
67
65 # Can also do stereo or more channels
68 # Can also do stereo or more channels
66 dataleft = np.sin(2*np.pi*220*t)
69 dataleft = np.sin(2*np.pi*220*t)
67 dataright = np.sin(2*np.pi*224*t)
70 dataright = np.sin(2*np.pi*224*t)
68 Audio([dataleft, dataright],rate=framerate)
71 Audio([dataleft, dataright],rate=framerate)
69
72
70 Audio("http://www.nch.com.au/acm/8k16bitpcm.wav") # From URL
73 Audio("http://www.nch.com.au/acm/8k16bitpcm.wav") # From URL
71 Audio(url="http://www.w3schools.com/html/horse.ogg")
74 Audio(url="http://www.w3schools.com/html/horse.ogg")
72
75
73 Audio('/path/to/sound.wav') # From file
76 Audio('/path/to/sound.wav') # From file
74 Audio(filename='/path/to/sound.ogg')
77 Audio(filename='/path/to/sound.ogg')
75
78
76 Audio(b'RAW_WAV_DATA..) # From bytes
79 Audio(b'RAW_WAV_DATA..) # From bytes
77 Audio(data=b'RAW_WAV_DATA..)
80 Audio(data=b'RAW_WAV_DATA..)
78
81
79 """
82 """
80 _read_flags = 'rb'
83 _read_flags = 'rb'
81
84
82 def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, autoplay=False):
85 def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, autoplay=False):
83 if filename is None and url is None and data is None:
86 if filename is None and url is None and data is None:
84 raise ValueError("No image data found. Expecting filename, url, or data.")
87 raise ValueError("No image data found. Expecting filename, url, or data.")
85 if embed is False and url is None:
88 if embed is False and url is None:
86 raise ValueError("No url found. Expecting url when embed=False")
89 raise ValueError("No url found. Expecting url when embed=False")
87
90
88 if url is not None and embed is not True:
91 if url is not None and embed is not True:
89 self.embed = False
92 self.embed = False
90 else:
93 else:
91 self.embed = True
94 self.embed = True
92 self.autoplay = autoplay
95 self.autoplay = autoplay
93 super(Audio, self).__init__(data=data, url=url, filename=filename)
96 super(Audio, self).__init__(data=data, url=url, filename=filename)
94
97
95 if self.data is not None and not isinstance(self.data, bytes):
98 if self.data is not None and not isinstance(self.data, bytes):
96 self.data = self._make_wav(data,rate)
99 self.data = self._make_wav(data,rate)
97
100
98 def reload(self):
101 def reload(self):
99 """Reload the raw data from file or URL."""
102 """Reload the raw data from file or URL."""
100 import mimetypes
103 import mimetypes
101 if self.embed:
104 if self.embed:
102 super(Audio, self).reload()
105 super(Audio, self).reload()
103
106
104 if self.filename is not None:
107 if self.filename is not None:
105 self.mimetype = mimetypes.guess_type(self.filename)[0]
108 self.mimetype = mimetypes.guess_type(self.filename)[0]
106 elif self.url is not None:
109 elif self.url is not None:
107 self.mimetype = mimetypes.guess_type(self.url)[0]
110 self.mimetype = mimetypes.guess_type(self.url)[0]
108 else:
111 else:
109 self.mimetype = "audio/wav"
112 self.mimetype = "audio/wav"
110
113
111 def _make_wav(self, data, rate):
114 def _make_wav(self, data, rate):
112 """ Transform a numpy array to a PCM bytestring """
115 """ Transform a numpy array to a PCM bytestring """
113 import struct
116 import struct
114 from io import BytesIO
117 from io import BytesIO
115 import wave
118 import wave
116
119
117 try:
120 try:
118 import numpy as np
121 import numpy as np
119
122
120 data = np.array(data, dtype=float)
123 data = np.array(data, dtype=float)
121 if len(data.shape) == 1:
124 if len(data.shape) == 1:
122 nchan = 1
125 nchan = 1
123 elif len(data.shape) == 2:
126 elif len(data.shape) == 2:
124 # In wave files,channels are interleaved. E.g.,
127 # In wave files,channels are interleaved. E.g.,
125 # "L1R1L2R2..." for stereo. See
128 # "L1R1L2R2..." for stereo. See
126 # http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
129 # http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
127 # for channel ordering
130 # for channel ordering
128 nchan = data.shape[0]
131 nchan = data.shape[0]
129 data = data.T.ravel()
132 data = data.T.ravel()
130 else:
133 else:
131 raise ValueError('Array audio input must be a 1D or 2D array')
134 raise ValueError('Array audio input must be a 1D or 2D array')
132 scaled = np.int16(data/np.max(np.abs(data))*32767).tolist()
135 scaled = np.int16(data/np.max(np.abs(data))*32767).tolist()
133 except ImportError:
136 except ImportError:
134 # check that it is a "1D" list
137 # check that it is a "1D" list
135 idata = iter(data) # fails if not an iterable
138 idata = iter(data) # fails if not an iterable
136 try:
139 try:
137 iter(idata.next())
140 iter(idata.next())
138 raise TypeError('Only lists of mono audio are '
141 raise TypeError('Only lists of mono audio are '
139 'supported if numpy is not installed')
142 'supported if numpy is not installed')
140 except TypeError:
143 except TypeError:
141 # this means it's not a nested list, which is what we want
144 # this means it's not a nested list, which is what we want
142 pass
145 pass
143 maxabsvalue = float(max([abs(x) for x in data]))
146 maxabsvalue = float(max([abs(x) for x in data]))
144 scaled = [int(x/maxabsvalue*32767) for x in data]
147 scaled = [int(x/maxabsvalue*32767) for x in data]
145 nchan = 1
148 nchan = 1
146
149
147 fp = BytesIO()
150 fp = BytesIO()
148 waveobj = wave.open(fp,mode='wb')
151 waveobj = wave.open(fp,mode='wb')
149 waveobj.setnchannels(nchan)
152 waveobj.setnchannels(nchan)
150 waveobj.setframerate(rate)
153 waveobj.setframerate(rate)
151 waveobj.setsampwidth(2)
154 waveobj.setsampwidth(2)
152 waveobj.setcomptype('NONE','NONE')
155 waveobj.setcomptype('NONE','NONE')
153 waveobj.writeframes(b''.join([struct.pack('<h',x) for x in scaled]))
156 waveobj.writeframes(b''.join([struct.pack('<h',x) for x in scaled]))
154 val = fp.getvalue()
157 val = fp.getvalue()
155 waveobj.close()
158 waveobj.close()
156
159
157 return val
160 return val
158
161
159 def _data_and_metadata(self):
162 def _data_and_metadata(self):
160 """shortcut for returning metadata with url information, if defined"""
163 """shortcut for returning metadata with url information, if defined"""
161 md = {}
164 md = {}
162 if self.url:
165 if self.url:
163 md['url'] = self.url
166 md['url'] = self.url
164 if md:
167 if md:
165 return self.data, md
168 return self.data, md
166 else:
169 else:
167 return self.data
170 return self.data
168
171
169 def _repr_html_(self):
172 def _repr_html_(self):
170 src = """
173 src = """
171 <audio controls="controls" {autoplay}>
174 <audio controls="controls" {autoplay}>
172 <source src="{src}" type="{type}" />
175 <source src="{src}" type="{type}" />
173 Your browser does not support the audio element.
176 Your browser does not support the audio element.
174 </audio>
177 </audio>
175 """
178 """
176 return src.format(src=self.src_attr(),type=self.mimetype, autoplay=self.autoplay_attr())
179 return src.format(src=self.src_attr(),type=self.mimetype, autoplay=self.autoplay_attr())
177
180
178 def src_attr(self):
181 def src_attr(self):
179 import base64
182 import base64
180 if self.embed and (self.data is not None):
183 if self.embed and (self.data is not None):
181 data = base64=base64.b64encode(self.data).decode('ascii')
184 data = base64=base64.b64encode(self.data).decode('ascii')
182 return """data:{type};base64,{base64}""".format(type=self.mimetype,
185 return """data:{type};base64,{base64}""".format(type=self.mimetype,
183 base64=data)
186 base64=data)
184 elif self.url is not None:
187 elif self.url is not None:
185 return self.url
188 return self.url
186 else:
189 else:
187 return ""
190 return ""
188
191
189 def autoplay_attr(self):
192 def autoplay_attr(self):
190 if(self.autoplay):
193 if(self.autoplay):
191 return 'autoplay="autoplay"'
194 return 'autoplay="autoplay"'
192 else:
195 else:
193 return ''
196 return ''
194
197
195 class IFrame(object):
198 class IFrame(object):
196 """
199 """
197 Generic class to embed an iframe in an IPython notebook
200 Generic class to embed an iframe in an IPython notebook
198 """
201 """
199
202
200 iframe = """
203 iframe = """
201 <iframe
204 <iframe
202 width="{width}"
205 width="{width}"
203 height={height}"
206 height={height}"
204 src="{src}{params}"
207 src="{src}{params}"
205 frameborder="0"
208 frameborder="0"
206 allowfullscreen
209 allowfullscreen
207 ></iframe>
210 ></iframe>
208 """
211 """
209
212
210 def __init__(self, src, width, height, **kwargs):
213 def __init__(self, src, width, height, **kwargs):
211 self.src = src
214 self.src = src
212 self.width = width
215 self.width = width
213 self.height = height
216 self.height = height
214 self.params = kwargs
217 self.params = kwargs
215
218
216 def _repr_html_(self):
219 def _repr_html_(self):
217 """return the embed iframe"""
220 """return the embed iframe"""
218 if self.params:
221 if self.params:
219 try:
222 try:
220 from urllib.parse import urlencode # Py 3
223 from urllib.parse import urlencode # Py 3
221 except ImportError:
224 except ImportError:
222 from urllib import urlencode
225 from urllib import urlencode
223 params = "?" + urlencode(self.params)
226 params = "?" + urlencode(self.params)
224 else:
227 else:
225 params = ""
228 params = ""
226 return self.iframe.format(src=self.src,
229 return self.iframe.format(src=self.src,
227 width=self.width,
230 width=self.width,
228 height=self.height,
231 height=self.height,
229 params=params)
232 params=params)
230
233
231 class YouTubeVideo(IFrame):
234 class YouTubeVideo(IFrame):
232 """Class for embedding a YouTube Video in an IPython session, based on its video id.
235 """Class for embedding a YouTube Video in an IPython session, based on its video id.
233
236
234 e.g. to embed the video from https://www.youtube.com/watch?v=foo , you would
237 e.g. to embed the video from https://www.youtube.com/watch?v=foo , you would
235 do::
238 do::
236
239
237 vid = YouTubeVideo("foo")
240 vid = YouTubeVideo("foo")
238 display(vid)
241 display(vid)
239
242
240 To start from 30 seconds::
243 To start from 30 seconds::
241
244
242 vid = YouTubeVideo("abc", start=30)
245 vid = YouTubeVideo("abc", start=30)
243 display(vid)
246 display(vid)
244
247
245 To calculate seconds from time as hours, minutes, seconds use
248 To calculate seconds from time as hours, minutes, seconds use
246 :class:`datetime.timedelta`::
249 :class:`datetime.timedelta`::
247
250
248 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
251 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
249
252
250 Other parameters can be provided as documented at
253 Other parameters can be provided as documented at
251 https://developers.google.com/youtube/player_parameters#parameter-subheader
254 https://developers.google.com/youtube/player_parameters#parameter-subheader
252 """
255 """
253
256
254 def __init__(self, id, width=400, height=300, **kwargs):
257 def __init__(self, id, width=400, height=300, **kwargs):
255 src = "https://www.youtube.com/embed/{0}".format(id)
258 src = "https://www.youtube.com/embed/{0}".format(id)
256 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
259 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
257
260
258 class VimeoVideo(IFrame):
261 class VimeoVideo(IFrame):
259 """
262 """
260 Class for embedding a Vimeo video in an IPython session, based on its video id.
263 Class for embedding a Vimeo video in an IPython session, based on its video id.
261 """
264 """
262
265
263 def __init__(self, id, width=400, height=300, **kwargs):
266 def __init__(self, id, width=400, height=300, **kwargs):
264 src="https://player.vimeo.com/video/{0}".format(id)
267 src="https://player.vimeo.com/video/{0}".format(id)
265 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
268 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
266
269
267 class ScribdDocument(IFrame):
270 class ScribdDocument(IFrame):
268 """
271 """
269 Class for embedding a Scribd document in an IPython session
272 Class for embedding a Scribd document in an IPython session
270
273
271 Use the start_page params to specify a starting point in the document
274 Use the start_page params to specify a starting point in the document
272 Use the view_mode params to specify display type one off scroll | slideshow | book
275 Use the view_mode params to specify display type one off scroll | slideshow | book
273
276
274 e.g to Display Wes' foundational paper about PANDAS in book mode from page 3
277 e.g to Display Wes' foundational paper about PANDAS in book mode from page 3
275
278
276 ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book")
279 ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book")
277 """
280 """
278
281
279 def __init__(self, id, width=400, height=300, **kwargs):
282 def __init__(self, id, width=400, height=300, **kwargs):
280 src="https://www.scribd.com/embeds/{0}/content".format(id)
283 src="https://www.scribd.com/embeds/{0}/content".format(id)
281 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
284 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
282
285
283 class FileLink(object):
286 class FileLink(object):
284 """Class for embedding a local file link in an IPython session, based on path
287 """Class for embedding a local file link in an IPython session, based on path
285
288
286 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
289 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
287
290
288 you would do::
291 you would do::
289
292
290 local_file = FileLink("my/data.txt")
293 local_file = FileLink("my/data.txt")
291 display(local_file)
294 display(local_file)
292
295
293 or in the HTML notebook, just::
296 or in the HTML notebook, just::
294
297
295 FileLink("my/data.txt")
298 FileLink("my/data.txt")
296 """
299 """
297
300
298 html_link_str = "<a href='%s' target='_blank'>%s</a>"
301 html_link_str = "<a href='%s' target='_blank'>%s</a>"
299
302
300 def __init__(self,
303 def __init__(self,
301 path,
304 path,
302 url_prefix='',
305 url_prefix='',
303 result_html_prefix='',
306 result_html_prefix='',
304 result_html_suffix='<br>'):
307 result_html_suffix='<br>'):
305 """
308 """
306 Parameters
309 Parameters
307 ----------
310 ----------
308 path : str
311 path : str
309 path to the file or directory that should be formatted
312 path to the file or directory that should be formatted
310 directory_prefix : str
313 directory_prefix : str
311 prefix to be prepended to all files to form a working link [default:
314 prefix to be prepended to all files to form a working link [default:
312 'files']
315 'files']
313 result_html_prefix : str
316 result_html_prefix : str
314 text to append to beginning to link [default: none]
317 text to append to beginning to link [default: none]
315 result_html_suffix : str
318 result_html_suffix : str
316 text to append at the end of link [default: '<br>']
319 text to append at the end of link [default: '<br>']
317 """
320 """
318 if isdir(path):
321 if isdir(path):
319 raise ValueError("Cannot display a directory using FileLink. "
322 raise ValueError("Cannot display a directory using FileLink. "
320 "Use FileLinks to display '%s'." % path)
323 "Use FileLinks to display '%s'." % path)
321 self.path = path
324 self.path = path
322 self.url_prefix = url_prefix
325 self.url_prefix = url_prefix
323 self.result_html_prefix = result_html_prefix
326 self.result_html_prefix = result_html_prefix
324 self.result_html_suffix = result_html_suffix
327 self.result_html_suffix = result_html_suffix
325
328
326 def _format_path(self):
329 def _format_path(self):
327 fp = ''.join([self.url_prefix,self.path])
330 fp = ''.join([self.url_prefix,self.path])
328 return ''.join([self.result_html_prefix,
331 return ''.join([self.result_html_prefix,
329 self.html_link_str % (fp, self.path),
332 self.html_link_str % (fp, self.path),
330 self.result_html_suffix])
333 self.result_html_suffix])
331
334
332 def _repr_html_(self):
335 def _repr_html_(self):
333 """return html link to file
336 """return html link to file
334 """
337 """
335 if not exists(self.path):
338 if not exists(self.path):
336 return ("Path (<tt>%s</tt>) doesn't exist. "
339 return ("Path (<tt>%s</tt>) doesn't exist. "
337 "It may still be in the process of "
340 "It may still be in the process of "
338 "being generated, or you may have the "
341 "being generated, or you may have the "
339 "incorrect path." % self.path)
342 "incorrect path." % self.path)
340
343
341 return self._format_path()
344 return self._format_path()
342
345
343 def __repr__(self):
346 def __repr__(self):
344 """return absolute path to file
347 """return absolute path to file
345 """
348 """
346 return abspath(self.path)
349 return abspath(self.path)
347
350
348 class FileLinks(FileLink):
351 class FileLinks(FileLink):
349 """Class for embedding local file links in an IPython session, based on path
352 """Class for embedding local file links in an IPython session, based on path
350
353
351 e.g. to embed links to files that were generated in the IPython notebook
354 e.g. to embed links to files that were generated in the IPython notebook
352 under ``my/data``, you would do::
355 under ``my/data``, you would do::
353
356
354 local_files = FileLinks("my/data")
357 local_files = FileLinks("my/data")
355 display(local_files)
358 display(local_files)
356
359
357 or in the HTML notebook, just::
360 or in the HTML notebook, just::
358
361
359 FileLinks("my/data")
362 FileLinks("my/data")
360 """
363 """
361 def __init__(self,
364 def __init__(self,
362 path,
365 path,
363 url_prefix='',
366 url_prefix='',
364 included_suffixes=None,
367 included_suffixes=None,
365 result_html_prefix='',
368 result_html_prefix='',
366 result_html_suffix='<br>',
369 result_html_suffix='<br>',
367 notebook_display_formatter=None,
370 notebook_display_formatter=None,
368 terminal_display_formatter=None):
371 terminal_display_formatter=None):
369 """
372 """
370 See :class:`FileLink` for the ``path``, ``url_prefix``,
373 See :class:`FileLink` for the ``path``, ``url_prefix``,
371 ``result_html_prefix`` and ``result_html_suffix`` parameters.
374 ``result_html_prefix`` and ``result_html_suffix`` parameters.
372
375
373 included_suffixes : list
376 included_suffixes : list
374 Filename suffixes to include when formatting output [default: include
377 Filename suffixes to include when formatting output [default: include
375 all files]
378 all files]
376
379
377 notebook_display_formatter : function
380 notebook_display_formatter : function
378 Used to format links for display in the notebook. See discussion of
381 Used to format links for display in the notebook. See discussion of
379 formatter functions below.
382 formatter functions below.
380
383
381 terminal_display_formatter : function
384 terminal_display_formatter : function
382 Used to format links for display in the terminal. See discussion of
385 Used to format links for display in the terminal. See discussion of
383 formatter functions below.
386 formatter functions below.
384
387
385 Formatter functions must be of the form::
388 Formatter functions must be of the form::
386
389
387 f(dirname, fnames, included_suffixes)
390 f(dirname, fnames, included_suffixes)
388
391
389 dirname : str
392 dirname : str
390 The name of a directory
393 The name of a directory
391 fnames : list
394 fnames : list
392 The files in that directory
395 The files in that directory
393 included_suffixes : list
396 included_suffixes : list
394 The file suffixes that should be included in the output (passing None
397 The file suffixes that should be included in the output (passing None
395 meansto include all suffixes in the output in the built-in formatters)
398 meansto include all suffixes in the output in the built-in formatters)
396
399
397 The function should return a list of lines that will be printed in the
400 The function should return a list of lines that will be printed in the
398 notebook (if passing notebook_display_formatter) or the terminal (if
401 notebook (if passing notebook_display_formatter) or the terminal (if
399 passing terminal_display_formatter). This function is iterated over for
402 passing terminal_display_formatter). This function is iterated over for
400 each directory in self.path. Default formatters are in place, can be
403 each directory in self.path. Default formatters are in place, can be
401 passed here to support alternative formatting.
404 passed here to support alternative formatting.
402
405
403 """
406 """
404 if isfile(path):
407 if isfile(path):
405 raise ValueError("Cannot display a file using FileLinks. "
408 raise ValueError("Cannot display a file using FileLinks. "
406 "Use FileLink to display '%s'." % path)
409 "Use FileLink to display '%s'." % path)
407 self.included_suffixes = included_suffixes
410 self.included_suffixes = included_suffixes
408 # remove trailing slashs for more consistent output formatting
411 # remove trailing slashs for more consistent output formatting
409 path = path.rstrip('/')
412 path = path.rstrip('/')
410
413
411 self.path = path
414 self.path = path
412 self.url_prefix = url_prefix
415 self.url_prefix = url_prefix
413 self.result_html_prefix = result_html_prefix
416 self.result_html_prefix = result_html_prefix
414 self.result_html_suffix = result_html_suffix
417 self.result_html_suffix = result_html_suffix
415
418
416 self.notebook_display_formatter = \
419 self.notebook_display_formatter = \
417 notebook_display_formatter or self._get_notebook_display_formatter()
420 notebook_display_formatter or self._get_notebook_display_formatter()
418 self.terminal_display_formatter = \
421 self.terminal_display_formatter = \
419 terminal_display_formatter or self._get_terminal_display_formatter()
422 terminal_display_formatter or self._get_terminal_display_formatter()
420
423
421 def _get_display_formatter(self,
424 def _get_display_formatter(self,
422 dirname_output_format,
425 dirname_output_format,
423 fname_output_format,
426 fname_output_format,
424 fp_format,
427 fp_format,
425 fp_cleaner=None):
428 fp_cleaner=None):
426 """ generate built-in formatter function
429 """ generate built-in formatter function
427
430
428 this is used to define both the notebook and terminal built-in
431 this is used to define both the notebook and terminal built-in
429 formatters as they only differ by some wrapper text for each entry
432 formatters as they only differ by some wrapper text for each entry
430
433
431 dirname_output_format: string to use for formatting directory
434 dirname_output_format: string to use for formatting directory
432 names, dirname will be substituted for a single "%s" which
435 names, dirname will be substituted for a single "%s" which
433 must appear in this string
436 must appear in this string
434 fname_output_format: string to use for formatting file names,
437 fname_output_format: string to use for formatting file names,
435 if a single "%s" appears in the string, fname will be substituted
438 if a single "%s" appears in the string, fname will be substituted
436 if two "%s" appear in the string, the path to fname will be
439 if two "%s" appear in the string, the path to fname will be
437 substituted for the first and fname will be substituted for the
440 substituted for the first and fname will be substituted for the
438 second
441 second
439 fp_format: string to use for formatting filepaths, must contain
442 fp_format: string to use for formatting filepaths, must contain
440 exactly two "%s" and the dirname will be subsituted for the first
443 exactly two "%s" and the dirname will be subsituted for the first
441 and fname will be substituted for the second
444 and fname will be substituted for the second
442 """
445 """
443 def f(dirname, fnames, included_suffixes=None):
446 def f(dirname, fnames, included_suffixes=None):
444 result = []
447 result = []
445 # begin by figuring out which filenames, if any,
448 # begin by figuring out which filenames, if any,
446 # are going to be displayed
449 # are going to be displayed
447 display_fnames = []
450 display_fnames = []
448 for fname in fnames:
451 for fname in fnames:
449 if (isfile(join(dirname,fname)) and
452 if (isfile(join(dirname,fname)) and
450 (included_suffixes == None or
453 (included_suffixes == None or
451 splitext(fname)[1] in included_suffixes)):
454 splitext(fname)[1] in included_suffixes)):
452 display_fnames.append(fname)
455 display_fnames.append(fname)
453
456
454 if len(display_fnames) == 0:
457 if len(display_fnames) == 0:
455 # if there are no filenames to display, don't print anything
458 # if there are no filenames to display, don't print anything
456 # (not even the directory name)
459 # (not even the directory name)
457 pass
460 pass
458 else:
461 else:
459 # otherwise print the formatted directory name followed by
462 # otherwise print the formatted directory name followed by
460 # the formatted filenames
463 # the formatted filenames
461 dirname_output_line = dirname_output_format % dirname
464 dirname_output_line = dirname_output_format % dirname
462 result.append(dirname_output_line)
465 result.append(dirname_output_line)
463 for fname in display_fnames:
466 for fname in display_fnames:
464 fp = fp_format % (dirname,fname)
467 fp = fp_format % (dirname,fname)
465 if fp_cleaner is not None:
468 if fp_cleaner is not None:
466 fp = fp_cleaner(fp)
469 fp = fp_cleaner(fp)
467 try:
470 try:
468 # output can include both a filepath and a filename...
471 # output can include both a filepath and a filename...
469 fname_output_line = fname_output_format % (fp, fname)
472 fname_output_line = fname_output_format % (fp, fname)
470 except TypeError:
473 except TypeError:
471 # ... or just a single filepath
474 # ... or just a single filepath
472 fname_output_line = fname_output_format % fname
475 fname_output_line = fname_output_format % fname
473 result.append(fname_output_line)
476 result.append(fname_output_line)
474 return result
477 return result
475 return f
478 return f
476
479
477 def _get_notebook_display_formatter(self,
480 def _get_notebook_display_formatter(self,
478 spacer="&nbsp;&nbsp;"):
481 spacer="&nbsp;&nbsp;"):
479 """ generate function to use for notebook formatting
482 """ generate function to use for notebook formatting
480 """
483 """
481 dirname_output_format = \
484 dirname_output_format = \
482 self.result_html_prefix + "%s/" + self.result_html_suffix
485 self.result_html_prefix + "%s/" + self.result_html_suffix
483 fname_output_format = \
486 fname_output_format = \
484 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
487 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
485 fp_format = self.url_prefix + '%s/%s'
488 fp_format = self.url_prefix + '%s/%s'
486 if sep == "\\":
489 if sep == "\\":
487 # Working on a platform where the path separator is "\", so
490 # Working on a platform where the path separator is "\", so
488 # must convert these to "/" for generating a URI
491 # must convert these to "/" for generating a URI
489 def fp_cleaner(fp):
492 def fp_cleaner(fp):
490 # Replace all occurences of backslash ("\") with a forward
493 # Replace all occurences of backslash ("\") with a forward
491 # slash ("/") - this is necessary on windows when a path is
494 # slash ("/") - this is necessary on windows when a path is
492 # provided as input, but we must link to a URI
495 # provided as input, but we must link to a URI
493 return fp.replace('\\','/')
496 return fp.replace('\\','/')
494 else:
497 else:
495 fp_cleaner = None
498 fp_cleaner = None
496
499
497 return self._get_display_formatter(dirname_output_format,
500 return self._get_display_formatter(dirname_output_format,
498 fname_output_format,
501 fname_output_format,
499 fp_format,
502 fp_format,
500 fp_cleaner)
503 fp_cleaner)
501
504
502 def _get_terminal_display_formatter(self,
505 def _get_terminal_display_formatter(self,
503 spacer=" "):
506 spacer=" "):
504 """ generate function to use for terminal formatting
507 """ generate function to use for terminal formatting
505 """
508 """
506 dirname_output_format = "%s/"
509 dirname_output_format = "%s/"
507 fname_output_format = spacer + "%s"
510 fname_output_format = spacer + "%s"
508 fp_format = '%s/%s'
511 fp_format = '%s/%s'
509
512
510 return self._get_display_formatter(dirname_output_format,
513 return self._get_display_formatter(dirname_output_format,
511 fname_output_format,
514 fname_output_format,
512 fp_format)
515 fp_format)
513
516
514 def _format_path(self):
517 def _format_path(self):
515 result_lines = []
518 result_lines = []
516 walked_dir = list(walk(self.path))
519 walked_dir = list(walk(self.path))
517 walked_dir.sort()
520 walked_dir.sort()
518 for dirname, subdirs, fnames in walked_dir:
521 for dirname, subdirs, fnames in walked_dir:
519 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
522 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
520 return '\n'.join(result_lines)
523 return '\n'.join(result_lines)
521
524
522 def __repr__(self):
525 def __repr__(self):
523 """return newline-separated absolute paths
526 """return newline-separated absolute paths
524 """
527 """
525 result_lines = []
528 result_lines = []
526 walked_dir = list(walk(self.path))
529 walked_dir = list(walk(self.path))
527 walked_dir.sort()
530 walked_dir.sort()
528 for dirname, subdirs, fnames in walked_dir:
531 for dirname, subdirs, fnames in walked_dir:
529 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
532 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
530 return '\n'.join(result_lines)
533 return '\n'.join(result_lines)
@@ -1,58 +1,62 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Script to auto-generate our API docs.
2 """Script to auto-generate our API docs.
3 """
3 """
4 # stdlib imports
4 # stdlib imports
5 import os
5 import os
6 import sys
6 import sys
7
7
8 # local imports
8 # local imports
9 sys.path.append(os.path.abspath('sphinxext'))
9 sys.path.append(os.path.abspath('sphinxext'))
10 from apigen import ApiDocWriter
10 from apigen import ApiDocWriter
11
11
12 #*****************************************************************************
12 #*****************************************************************************
13 if __name__ == '__main__':
13 if __name__ == '__main__':
14 pjoin = os.path.join
14 pjoin = os.path.join
15 package = 'IPython'
15 package = 'IPython'
16 outdir = pjoin('source','api','generated')
16 outdir = pjoin('source','api','generated')
17 docwriter = ApiDocWriter(package,rst_extension='.rst')
17 docwriter = ApiDocWriter(package,rst_extension='.rst')
18 # You have to escape the . here because . is a special char for regexps.
18 # You have to escape the . here because . is a special char for regexps.
19 # You must do make clean if you change this!
19 # You must do make clean if you change this!
20 docwriter.package_skip_patterns += [r'\.external$',
20 docwriter.package_skip_patterns += [r'\.external$',
21 # Extensions are documented elsewhere.
21 # Extensions are documented elsewhere.
22 r'\.extensions',
22 r'\.extensions',
23 r'\.config\.profile',
23 r'\.config\.profile',
24 # These should be accessed via nbformat.current
24 # These should be accessed via nbformat.current
25 r'\.nbformat\.v\d+',
25 r'\.nbformat\.v\d+',
26 ]
26 ]
27
27
28 # The inputhook* modules often cause problems on import, such as trying to
28 # The inputhook* modules often cause problems on import, such as trying to
29 # load incompatible Qt bindings. It's easiest to leave them all out. The
29 # load incompatible Qt bindings. It's easiest to leave them all out. The
30 # main API is in the inputhook module, which is documented.
30 # main API is in the inputhook module, which is documented.
31 docwriter.module_skip_patterns += [ r'\.lib\.inputhook.+',
31 docwriter.module_skip_patterns += [ r'\.lib\.inputhook.+',
32 r'\.ipdoctest',
32 r'\.ipdoctest',
33 r'\.testing\.plugin',
33 r'\.testing\.plugin',
34 # This just prints a deprecation msg:
34 # This just prints a deprecation msg:
35 r'\.frontend$',
35 r'\.frontend$',
36 # We document this manually.
36 # We document this manually.
37 r'\.utils\.py3compat',
37 r'\.utils\.py3compat',
38 # These are exposed by nbformat.current
38 # These are exposed by nbformat.current
39 r'\.nbformat\.convert',
39 r'\.nbformat\.convert',
40 r'\.nbformat\.validator',
40 r'\.nbformat\.validator',
41 # These are exposed in display
42 r'\.core\.display',
43 r'\.lib\.display',
41 ]
44 ]
42
45
43 # These modules import functions and classes from other places to expose
46 # These modules import functions and classes from other places to expose
44 # them as part of the public API. They must have __all__ defined. The
47 # them as part of the public API. They must have __all__ defined. The
45 # non-API modules they import from should be excluded by the skip patterns
48 # non-API modules they import from should be excluded by the skip patterns
46 # above.
49 # above.
47 docwriter.names_from__all__.update({
50 docwriter.names_from__all__.update({
48 'IPython.nbformat.current',
51 'IPython.nbformat.current',
52 'IPython.display',
49 })
53 })
50
54
51 # Now, generate the outputs
55 # Now, generate the outputs
52 docwriter.write_api_docs(outdir)
56 docwriter.write_api_docs(outdir)
53 # Write index with .txt extension - we can include it, but Sphinx won't try
57 # Write index with .txt extension - we can include it, but Sphinx won't try
54 # to compile it
58 # to compile it
55 docwriter.write_index(outdir, 'gen.txt',
59 docwriter.write_index(outdir, 'gen.txt',
56 relative_to = pjoin('source','api')
60 relative_to = pjoin('source','api')
57 )
61 )
58 print ('%d files written' % len(docwriter.written_modules))
62 print ('%d files written' % len(docwriter.written_modules))
General Comments 0
You need to be logged in to leave comments. Login now