##// END OF EJS Templates
Show raw code as plain text repr
Thomas Kluyver -
Show More
@@ -1,603 +1,606 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, TextDisplayObject
8 from IPython.core.display import DisplayObject, TextDisplayObject
9
9
10 __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument',
10 __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument',
11 'FileLink', 'FileLinks', 'Code']
11 'FileLink', 'FileLinks', 'Code']
12
12
13
13
14 class Audio(DisplayObject):
14 class Audio(DisplayObject):
15 """Create an audio object.
15 """Create an audio object.
16
16
17 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
18 display function, it will result in Audio controls being displayed
18 display function, it will result in Audio controls being displayed
19 in the frontend (only works in the notebook).
19 in the frontend (only works in the notebook).
20
20
21 Parameters
21 Parameters
22 ----------
22 ----------
23 data : numpy array, list, unicode, str or bytes
23 data : numpy array, list, unicode, str or bytes
24 Can be one of
24 Can be one of
25
25
26 * Numpy 1d array containing the desired waveform (mono)
26 * Numpy 1d array containing the desired waveform (mono)
27 * Numpy 2d array containing waveforms for each channel.
27 * Numpy 2d array containing waveforms for each channel.
28 Shape=(NCHAN, NSAMPLES). For the standard channel order, see
28 Shape=(NCHAN, NSAMPLES). For the standard channel order, see
29 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
30 * List of float or integer representing the waveform (mono)
30 * List of float or integer representing the waveform (mono)
31 * String containing the filename
31 * String containing the filename
32 * Bytestring containing raw PCM data or
32 * Bytestring containing raw PCM data or
33 * URL pointing to a file on the web.
33 * URL pointing to a file on the web.
34
34
35 If the array option is used the waveform will be normalized.
35 If the array option is used the waveform will be normalized.
36
36
37 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
38 dependent.
38 dependent.
39 url : unicode
39 url : unicode
40 A URL to download the data from.
40 A URL to download the data from.
41 filename : unicode
41 filename : unicode
42 Path to a local file to load the data from.
42 Path to a local file to load the data from.
43 embed : boolean
43 embed : boolean
44 Should the audio data be embedded using a data URI (True) or should
44 Should the audio data be embedded using a data URI (True) or should
45 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
46 audio to playable later with no internet connection in the notebook.
46 audio to playable later with no internet connection in the notebook.
47
47
48 Default is `True`, unless the keyword argument `url` is set, then
48 Default is `True`, unless the keyword argument `url` is set, then
49 default value is `False`.
49 default value is `False`.
50 rate : integer
50 rate : integer
51 The sampling rate of the raw data.
51 The sampling rate of the raw data.
52 Only required when data parameter is being used as an array
52 Only required when data parameter is being used as an array
53 autoplay : bool
53 autoplay : bool
54 Set to True if the audio should immediately start playing.
54 Set to True if the audio should immediately start playing.
55 Default is `False`.
55 Default is `False`.
56
56
57 Examples
57 Examples
58 --------
58 --------
59 ::
59 ::
60
60
61 # Generate a sound
61 # Generate a sound
62 import numpy as np
62 import numpy as np
63 framerate = 44100
63 framerate = 44100
64 t = np.linspace(0,5,framerate*5)
64 t = np.linspace(0,5,framerate*5)
65 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))
66 Audio(data,rate=framerate)
66 Audio(data,rate=framerate)
67
67
68 # Can also do stereo or more channels
68 # Can also do stereo or more channels
69 dataleft = np.sin(2*np.pi*220*t)
69 dataleft = np.sin(2*np.pi*220*t)
70 dataright = np.sin(2*np.pi*224*t)
70 dataright = np.sin(2*np.pi*224*t)
71 Audio([dataleft, dataright],rate=framerate)
71 Audio([dataleft, dataright],rate=framerate)
72
72
73 Audio("http://www.nch.com.au/acm/8k16bitpcm.wav") # From URL
73 Audio("http://www.nch.com.au/acm/8k16bitpcm.wav") # From URL
74 Audio(url="http://www.w3schools.com/html/horse.ogg")
74 Audio(url="http://www.w3schools.com/html/horse.ogg")
75
75
76 Audio('/path/to/sound.wav') # From file
76 Audio('/path/to/sound.wav') # From file
77 Audio(filename='/path/to/sound.ogg')
77 Audio(filename='/path/to/sound.ogg')
78
78
79 Audio(b'RAW_WAV_DATA..) # From bytes
79 Audio(b'RAW_WAV_DATA..) # From bytes
80 Audio(data=b'RAW_WAV_DATA..)
80 Audio(data=b'RAW_WAV_DATA..)
81
81
82 """
82 """
83 _read_flags = 'rb'
83 _read_flags = 'rb'
84
84
85 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):
86 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:
87 raise ValueError("No image data found. Expecting filename, url, or data.")
87 raise ValueError("No image data found. Expecting filename, url, or data.")
88 if embed is False and url is None:
88 if embed is False and url is None:
89 raise ValueError("No url found. Expecting url when embed=False")
89 raise ValueError("No url found. Expecting url when embed=False")
90
90
91 if url is not None and embed is not True:
91 if url is not None and embed is not True:
92 self.embed = False
92 self.embed = False
93 else:
93 else:
94 self.embed = True
94 self.embed = True
95 self.autoplay = autoplay
95 self.autoplay = autoplay
96 super(Audio, self).__init__(data=data, url=url, filename=filename)
96 super(Audio, self).__init__(data=data, url=url, filename=filename)
97
97
98 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):
99 self.data = self._make_wav(data,rate)
99 self.data = self._make_wav(data,rate)
100
100
101 def reload(self):
101 def reload(self):
102 """Reload the raw data from file or URL."""
102 """Reload the raw data from file or URL."""
103 import mimetypes
103 import mimetypes
104 if self.embed:
104 if self.embed:
105 super(Audio, self).reload()
105 super(Audio, self).reload()
106
106
107 if self.filename is not None:
107 if self.filename is not None:
108 self.mimetype = mimetypes.guess_type(self.filename)[0]
108 self.mimetype = mimetypes.guess_type(self.filename)[0]
109 elif self.url is not None:
109 elif self.url is not None:
110 self.mimetype = mimetypes.guess_type(self.url)[0]
110 self.mimetype = mimetypes.guess_type(self.url)[0]
111 else:
111 else:
112 self.mimetype = "audio/wav"
112 self.mimetype = "audio/wav"
113
113
114 def _make_wav(self, data, rate):
114 def _make_wav(self, data, rate):
115 """ Transform a numpy array to a PCM bytestring """
115 """ Transform a numpy array to a PCM bytestring """
116 import struct
116 import struct
117 from io import BytesIO
117 from io import BytesIO
118 import wave
118 import wave
119
119
120 try:
120 try:
121 import numpy as np
121 import numpy as np
122
122
123 data = np.array(data, dtype=float)
123 data = np.array(data, dtype=float)
124 if len(data.shape) == 1:
124 if len(data.shape) == 1:
125 nchan = 1
125 nchan = 1
126 elif len(data.shape) == 2:
126 elif len(data.shape) == 2:
127 # In wave files,channels are interleaved. E.g.,
127 # In wave files,channels are interleaved. E.g.,
128 # "L1R1L2R2..." for stereo. See
128 # "L1R1L2R2..." for stereo. See
129 # 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
130 # for channel ordering
130 # for channel ordering
131 nchan = data.shape[0]
131 nchan = data.shape[0]
132 data = data.T.ravel()
132 data = data.T.ravel()
133 else:
133 else:
134 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')
135 scaled = np.int16(data/np.max(np.abs(data))*32767).tolist()
135 scaled = np.int16(data/np.max(np.abs(data))*32767).tolist()
136 except ImportError:
136 except ImportError:
137 # check that it is a "1D" list
137 # check that it is a "1D" list
138 idata = iter(data) # fails if not an iterable
138 idata = iter(data) # fails if not an iterable
139 try:
139 try:
140 iter(idata.next())
140 iter(idata.next())
141 raise TypeError('Only lists of mono audio are '
141 raise TypeError('Only lists of mono audio are '
142 'supported if numpy is not installed')
142 'supported if numpy is not installed')
143 except TypeError:
143 except TypeError:
144 # 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
145 pass
145 pass
146 maxabsvalue = float(max([abs(x) for x in data]))
146 maxabsvalue = float(max([abs(x) for x in data]))
147 scaled = [int(x/maxabsvalue*32767) for x in data]
147 scaled = [int(x/maxabsvalue*32767) for x in data]
148 nchan = 1
148 nchan = 1
149
149
150 fp = BytesIO()
150 fp = BytesIO()
151 waveobj = wave.open(fp,mode='wb')
151 waveobj = wave.open(fp,mode='wb')
152 waveobj.setnchannels(nchan)
152 waveobj.setnchannels(nchan)
153 waveobj.setframerate(rate)
153 waveobj.setframerate(rate)
154 waveobj.setsampwidth(2)
154 waveobj.setsampwidth(2)
155 waveobj.setcomptype('NONE','NONE')
155 waveobj.setcomptype('NONE','NONE')
156 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]))
157 val = fp.getvalue()
157 val = fp.getvalue()
158 waveobj.close()
158 waveobj.close()
159
159
160 return val
160 return val
161
161
162 def _data_and_metadata(self):
162 def _data_and_metadata(self):
163 """shortcut for returning metadata with url information, if defined"""
163 """shortcut for returning metadata with url information, if defined"""
164 md = {}
164 md = {}
165 if self.url:
165 if self.url:
166 md['url'] = self.url
166 md['url'] = self.url
167 if md:
167 if md:
168 return self.data, md
168 return self.data, md
169 else:
169 else:
170 return self.data
170 return self.data
171
171
172 def _repr_html_(self):
172 def _repr_html_(self):
173 src = """
173 src = """
174 <audio controls="controls" {autoplay}>
174 <audio controls="controls" {autoplay}>
175 <source src="{src}" type="{type}" />
175 <source src="{src}" type="{type}" />
176 Your browser does not support the audio element.
176 Your browser does not support the audio element.
177 </audio>
177 </audio>
178 """
178 """
179 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())
180
180
181 def src_attr(self):
181 def src_attr(self):
182 import base64
182 import base64
183 if self.embed and (self.data is not None):
183 if self.embed and (self.data is not None):
184 data = base64=base64.b64encode(self.data).decode('ascii')
184 data = base64=base64.b64encode(self.data).decode('ascii')
185 return """data:{type};base64,{base64}""".format(type=self.mimetype,
185 return """data:{type};base64,{base64}""".format(type=self.mimetype,
186 base64=data)
186 base64=data)
187 elif self.url is not None:
187 elif self.url is not None:
188 return self.url
188 return self.url
189 else:
189 else:
190 return ""
190 return ""
191
191
192 def autoplay_attr(self):
192 def autoplay_attr(self):
193 if(self.autoplay):
193 if(self.autoplay):
194 return 'autoplay="autoplay"'
194 return 'autoplay="autoplay"'
195 else:
195 else:
196 return ''
196 return ''
197
197
198 class IFrame(object):
198 class IFrame(object):
199 """
199 """
200 Generic class to embed an iframe in an IPython notebook
200 Generic class to embed an iframe in an IPython notebook
201 """
201 """
202
202
203 iframe = """
203 iframe = """
204 <iframe
204 <iframe
205 width="{width}"
205 width="{width}"
206 height="{height}"
206 height="{height}"
207 src="{src}{params}"
207 src="{src}{params}"
208 frameborder="0"
208 frameborder="0"
209 allowfullscreen
209 allowfullscreen
210 ></iframe>
210 ></iframe>
211 """
211 """
212
212
213 def __init__(self, src, width, height, **kwargs):
213 def __init__(self, src, width, height, **kwargs):
214 self.src = src
214 self.src = src
215 self.width = width
215 self.width = width
216 self.height = height
216 self.height = height
217 self.params = kwargs
217 self.params = kwargs
218
218
219 def _repr_html_(self):
219 def _repr_html_(self):
220 """return the embed iframe"""
220 """return the embed iframe"""
221 if self.params:
221 if self.params:
222 try:
222 try:
223 from urllib.parse import urlencode # Py 3
223 from urllib.parse import urlencode # Py 3
224 except ImportError:
224 except ImportError:
225 from urllib import urlencode
225 from urllib import urlencode
226 params = "?" + urlencode(self.params)
226 params = "?" + urlencode(self.params)
227 else:
227 else:
228 params = ""
228 params = ""
229 return self.iframe.format(src=self.src,
229 return self.iframe.format(src=self.src,
230 width=self.width,
230 width=self.width,
231 height=self.height,
231 height=self.height,
232 params=params)
232 params=params)
233
233
234 class YouTubeVideo(IFrame):
234 class YouTubeVideo(IFrame):
235 """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.
236
236
237 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
238 do::
238 do::
239
239
240 vid = YouTubeVideo("foo")
240 vid = YouTubeVideo("foo")
241 display(vid)
241 display(vid)
242
242
243 To start from 30 seconds::
243 To start from 30 seconds::
244
244
245 vid = YouTubeVideo("abc", start=30)
245 vid = YouTubeVideo("abc", start=30)
246 display(vid)
246 display(vid)
247
247
248 To calculate seconds from time as hours, minutes, seconds use
248 To calculate seconds from time as hours, minutes, seconds use
249 :class:`datetime.timedelta`::
249 :class:`datetime.timedelta`::
250
250
251 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
251 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
252
252
253 Other parameters can be provided as documented at
253 Other parameters can be provided as documented at
254 https://developers.google.com/youtube/player_parameters#Parameters
254 https://developers.google.com/youtube/player_parameters#Parameters
255
255
256 When converting the notebook using nbconvert, a jpeg representation of the video
256 When converting the notebook using nbconvert, a jpeg representation of the video
257 will be inserted in the document.
257 will be inserted in the document.
258 """
258 """
259
259
260 def __init__(self, id, width=400, height=300, **kwargs):
260 def __init__(self, id, width=400, height=300, **kwargs):
261 self.id=id
261 self.id=id
262 src = "https://www.youtube.com/embed/{0}".format(id)
262 src = "https://www.youtube.com/embed/{0}".format(id)
263 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
263 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
264
264
265 def _repr_jpeg_(self):
265 def _repr_jpeg_(self):
266 # Deferred import
266 # Deferred import
267 from urllib.request import urlopen
267 from urllib.request import urlopen
268
268
269 try:
269 try:
270 return urlopen("https://img.youtube.com/vi/{id}/hqdefault.jpg".format(id=self.id)).read()
270 return urlopen("https://img.youtube.com/vi/{id}/hqdefault.jpg".format(id=self.id)).read()
271 except IOError:
271 except IOError:
272 return None
272 return None
273
273
274 class VimeoVideo(IFrame):
274 class VimeoVideo(IFrame):
275 """
275 """
276 Class for embedding a Vimeo video in an IPython session, based on its video id.
276 Class for embedding a Vimeo video in an IPython session, based on its video id.
277 """
277 """
278
278
279 def __init__(self, id, width=400, height=300, **kwargs):
279 def __init__(self, id, width=400, height=300, **kwargs):
280 src="https://player.vimeo.com/video/{0}".format(id)
280 src="https://player.vimeo.com/video/{0}".format(id)
281 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
281 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
282
282
283 class ScribdDocument(IFrame):
283 class ScribdDocument(IFrame):
284 """
284 """
285 Class for embedding a Scribd document in an IPython session
285 Class for embedding a Scribd document in an IPython session
286
286
287 Use the start_page params to specify a starting point in the document
287 Use the start_page params to specify a starting point in the document
288 Use the view_mode params to specify display type one off scroll | slideshow | book
288 Use the view_mode params to specify display type one off scroll | slideshow | book
289
289
290 e.g to Display Wes' foundational paper about PANDAS in book mode from page 3
290 e.g to Display Wes' foundational paper about PANDAS in book mode from page 3
291
291
292 ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book")
292 ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book")
293 """
293 """
294
294
295 def __init__(self, id, width=400, height=300, **kwargs):
295 def __init__(self, id, width=400, height=300, **kwargs):
296 src="https://www.scribd.com/embeds/{0}/content".format(id)
296 src="https://www.scribd.com/embeds/{0}/content".format(id)
297 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
297 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
298
298
299 class FileLink(object):
299 class FileLink(object):
300 """Class for embedding a local file link in an IPython session, based on path
300 """Class for embedding a local file link in an IPython session, based on path
301
301
302 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
302 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
303
303
304 you would do::
304 you would do::
305
305
306 local_file = FileLink("my/data.txt")
306 local_file = FileLink("my/data.txt")
307 display(local_file)
307 display(local_file)
308
308
309 or in the HTML notebook, just::
309 or in the HTML notebook, just::
310
310
311 FileLink("my/data.txt")
311 FileLink("my/data.txt")
312 """
312 """
313
313
314 html_link_str = "<a href='%s' target='_blank'>%s</a>"
314 html_link_str = "<a href='%s' target='_blank'>%s</a>"
315
315
316 def __init__(self,
316 def __init__(self,
317 path,
317 path,
318 url_prefix='',
318 url_prefix='',
319 result_html_prefix='',
319 result_html_prefix='',
320 result_html_suffix='<br>'):
320 result_html_suffix='<br>'):
321 """
321 """
322 Parameters
322 Parameters
323 ----------
323 ----------
324 path : str
324 path : str
325 path to the file or directory that should be formatted
325 path to the file or directory that should be formatted
326 url_prefix : str
326 url_prefix : str
327 prefix to be prepended to all files to form a working link [default:
327 prefix to be prepended to all files to form a working link [default:
328 '']
328 '']
329 result_html_prefix : str
329 result_html_prefix : str
330 text to append to beginning to link [default: '']
330 text to append to beginning to link [default: '']
331 result_html_suffix : str
331 result_html_suffix : str
332 text to append at the end of link [default: '<br>']
332 text to append at the end of link [default: '<br>']
333 """
333 """
334 if isdir(path):
334 if isdir(path):
335 raise ValueError("Cannot display a directory using FileLink. "
335 raise ValueError("Cannot display a directory using FileLink. "
336 "Use FileLinks to display '%s'." % path)
336 "Use FileLinks to display '%s'." % path)
337 self.path = path
337 self.path = path
338 self.url_prefix = url_prefix
338 self.url_prefix = url_prefix
339 self.result_html_prefix = result_html_prefix
339 self.result_html_prefix = result_html_prefix
340 self.result_html_suffix = result_html_suffix
340 self.result_html_suffix = result_html_suffix
341
341
342 def _format_path(self):
342 def _format_path(self):
343 fp = ''.join([self.url_prefix,self.path])
343 fp = ''.join([self.url_prefix,self.path])
344 return ''.join([self.result_html_prefix,
344 return ''.join([self.result_html_prefix,
345 self.html_link_str % (fp, self.path),
345 self.html_link_str % (fp, self.path),
346 self.result_html_suffix])
346 self.result_html_suffix])
347
347
348 def _repr_html_(self):
348 def _repr_html_(self):
349 """return html link to file
349 """return html link to file
350 """
350 """
351 if not exists(self.path):
351 if not exists(self.path):
352 return ("Path (<tt>%s</tt>) doesn't exist. "
352 return ("Path (<tt>%s</tt>) doesn't exist. "
353 "It may still be in the process of "
353 "It may still be in the process of "
354 "being generated, or you may have the "
354 "being generated, or you may have the "
355 "incorrect path." % self.path)
355 "incorrect path." % self.path)
356
356
357 return self._format_path()
357 return self._format_path()
358
358
359 def __repr__(self):
359 def __repr__(self):
360 """return absolute path to file
360 """return absolute path to file
361 """
361 """
362 return abspath(self.path)
362 return abspath(self.path)
363
363
364 class FileLinks(FileLink):
364 class FileLinks(FileLink):
365 """Class for embedding local file links in an IPython session, based on path
365 """Class for embedding local file links in an IPython session, based on path
366
366
367 e.g. to embed links to files that were generated in the IPython notebook
367 e.g. to embed links to files that were generated in the IPython notebook
368 under ``my/data``, you would do::
368 under ``my/data``, you would do::
369
369
370 local_files = FileLinks("my/data")
370 local_files = FileLinks("my/data")
371 display(local_files)
371 display(local_files)
372
372
373 or in the HTML notebook, just::
373 or in the HTML notebook, just::
374
374
375 FileLinks("my/data")
375 FileLinks("my/data")
376 """
376 """
377 def __init__(self,
377 def __init__(self,
378 path,
378 path,
379 url_prefix='',
379 url_prefix='',
380 included_suffixes=None,
380 included_suffixes=None,
381 result_html_prefix='',
381 result_html_prefix='',
382 result_html_suffix='<br>',
382 result_html_suffix='<br>',
383 notebook_display_formatter=None,
383 notebook_display_formatter=None,
384 terminal_display_formatter=None,
384 terminal_display_formatter=None,
385 recursive=True):
385 recursive=True):
386 """
386 """
387 See :class:`FileLink` for the ``path``, ``url_prefix``,
387 See :class:`FileLink` for the ``path``, ``url_prefix``,
388 ``result_html_prefix`` and ``result_html_suffix`` parameters.
388 ``result_html_prefix`` and ``result_html_suffix`` parameters.
389
389
390 included_suffixes : list
390 included_suffixes : list
391 Filename suffixes to include when formatting output [default: include
391 Filename suffixes to include when formatting output [default: include
392 all files]
392 all files]
393
393
394 notebook_display_formatter : function
394 notebook_display_formatter : function
395 Used to format links for display in the notebook. See discussion of
395 Used to format links for display in the notebook. See discussion of
396 formatter functions below.
396 formatter functions below.
397
397
398 terminal_display_formatter : function
398 terminal_display_formatter : function
399 Used to format links for display in the terminal. See discussion of
399 Used to format links for display in the terminal. See discussion of
400 formatter functions below.
400 formatter functions below.
401
401
402 Formatter functions must be of the form::
402 Formatter functions must be of the form::
403
403
404 f(dirname, fnames, included_suffixes)
404 f(dirname, fnames, included_suffixes)
405
405
406 dirname : str
406 dirname : str
407 The name of a directory
407 The name of a directory
408 fnames : list
408 fnames : list
409 The files in that directory
409 The files in that directory
410 included_suffixes : list
410 included_suffixes : list
411 The file suffixes that should be included in the output (passing None
411 The file suffixes that should be included in the output (passing None
412 meansto include all suffixes in the output in the built-in formatters)
412 meansto include all suffixes in the output in the built-in formatters)
413 recursive : boolean
413 recursive : boolean
414 Whether to recurse into subdirectories. Default is True.
414 Whether to recurse into subdirectories. Default is True.
415
415
416 The function should return a list of lines that will be printed in the
416 The function should return a list of lines that will be printed in the
417 notebook (if passing notebook_display_formatter) or the terminal (if
417 notebook (if passing notebook_display_formatter) or the terminal (if
418 passing terminal_display_formatter). This function is iterated over for
418 passing terminal_display_formatter). This function is iterated over for
419 each directory in self.path. Default formatters are in place, can be
419 each directory in self.path. Default formatters are in place, can be
420 passed here to support alternative formatting.
420 passed here to support alternative formatting.
421
421
422 """
422 """
423 if isfile(path):
423 if isfile(path):
424 raise ValueError("Cannot display a file using FileLinks. "
424 raise ValueError("Cannot display a file using FileLinks. "
425 "Use FileLink to display '%s'." % path)
425 "Use FileLink to display '%s'." % path)
426 self.included_suffixes = included_suffixes
426 self.included_suffixes = included_suffixes
427 # remove trailing slashs for more consistent output formatting
427 # remove trailing slashs for more consistent output formatting
428 path = path.rstrip('/')
428 path = path.rstrip('/')
429
429
430 self.path = path
430 self.path = path
431 self.url_prefix = url_prefix
431 self.url_prefix = url_prefix
432 self.result_html_prefix = result_html_prefix
432 self.result_html_prefix = result_html_prefix
433 self.result_html_suffix = result_html_suffix
433 self.result_html_suffix = result_html_suffix
434
434
435 self.notebook_display_formatter = \
435 self.notebook_display_formatter = \
436 notebook_display_formatter or self._get_notebook_display_formatter()
436 notebook_display_formatter or self._get_notebook_display_formatter()
437 self.terminal_display_formatter = \
437 self.terminal_display_formatter = \
438 terminal_display_formatter or self._get_terminal_display_formatter()
438 terminal_display_formatter or self._get_terminal_display_formatter()
439
439
440 self.recursive = recursive
440 self.recursive = recursive
441
441
442 def _get_display_formatter(self,
442 def _get_display_formatter(self,
443 dirname_output_format,
443 dirname_output_format,
444 fname_output_format,
444 fname_output_format,
445 fp_format,
445 fp_format,
446 fp_cleaner=None):
446 fp_cleaner=None):
447 """ generate built-in formatter function
447 """ generate built-in formatter function
448
448
449 this is used to define both the notebook and terminal built-in
449 this is used to define both the notebook and terminal built-in
450 formatters as they only differ by some wrapper text for each entry
450 formatters as they only differ by some wrapper text for each entry
451
451
452 dirname_output_format: string to use for formatting directory
452 dirname_output_format: string to use for formatting directory
453 names, dirname will be substituted for a single "%s" which
453 names, dirname will be substituted for a single "%s" which
454 must appear in this string
454 must appear in this string
455 fname_output_format: string to use for formatting file names,
455 fname_output_format: string to use for formatting file names,
456 if a single "%s" appears in the string, fname will be substituted
456 if a single "%s" appears in the string, fname will be substituted
457 if two "%s" appear in the string, the path to fname will be
457 if two "%s" appear in the string, the path to fname will be
458 substituted for the first and fname will be substituted for the
458 substituted for the first and fname will be substituted for the
459 second
459 second
460 fp_format: string to use for formatting filepaths, must contain
460 fp_format: string to use for formatting filepaths, must contain
461 exactly two "%s" and the dirname will be subsituted for the first
461 exactly two "%s" and the dirname will be subsituted for the first
462 and fname will be substituted for the second
462 and fname will be substituted for the second
463 """
463 """
464 def f(dirname, fnames, included_suffixes=None):
464 def f(dirname, fnames, included_suffixes=None):
465 result = []
465 result = []
466 # begin by figuring out which filenames, if any,
466 # begin by figuring out which filenames, if any,
467 # are going to be displayed
467 # are going to be displayed
468 display_fnames = []
468 display_fnames = []
469 for fname in fnames:
469 for fname in fnames:
470 if (isfile(join(dirname,fname)) and
470 if (isfile(join(dirname,fname)) and
471 (included_suffixes is None or
471 (included_suffixes is None or
472 splitext(fname)[1] in included_suffixes)):
472 splitext(fname)[1] in included_suffixes)):
473 display_fnames.append(fname)
473 display_fnames.append(fname)
474
474
475 if len(display_fnames) == 0:
475 if len(display_fnames) == 0:
476 # if there are no filenames to display, don't print anything
476 # if there are no filenames to display, don't print anything
477 # (not even the directory name)
477 # (not even the directory name)
478 pass
478 pass
479 else:
479 else:
480 # otherwise print the formatted directory name followed by
480 # otherwise print the formatted directory name followed by
481 # the formatted filenames
481 # the formatted filenames
482 dirname_output_line = dirname_output_format % dirname
482 dirname_output_line = dirname_output_format % dirname
483 result.append(dirname_output_line)
483 result.append(dirname_output_line)
484 for fname in display_fnames:
484 for fname in display_fnames:
485 fp = fp_format % (dirname,fname)
485 fp = fp_format % (dirname,fname)
486 if fp_cleaner is not None:
486 if fp_cleaner is not None:
487 fp = fp_cleaner(fp)
487 fp = fp_cleaner(fp)
488 try:
488 try:
489 # output can include both a filepath and a filename...
489 # output can include both a filepath and a filename...
490 fname_output_line = fname_output_format % (fp, fname)
490 fname_output_line = fname_output_format % (fp, fname)
491 except TypeError:
491 except TypeError:
492 # ... or just a single filepath
492 # ... or just a single filepath
493 fname_output_line = fname_output_format % fname
493 fname_output_line = fname_output_format % fname
494 result.append(fname_output_line)
494 result.append(fname_output_line)
495 return result
495 return result
496 return f
496 return f
497
497
498 def _get_notebook_display_formatter(self,
498 def _get_notebook_display_formatter(self,
499 spacer="&nbsp;&nbsp;"):
499 spacer="&nbsp;&nbsp;"):
500 """ generate function to use for notebook formatting
500 """ generate function to use for notebook formatting
501 """
501 """
502 dirname_output_format = \
502 dirname_output_format = \
503 self.result_html_prefix + "%s/" + self.result_html_suffix
503 self.result_html_prefix + "%s/" + self.result_html_suffix
504 fname_output_format = \
504 fname_output_format = \
505 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
505 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
506 fp_format = self.url_prefix + '%s/%s'
506 fp_format = self.url_prefix + '%s/%s'
507 if sep == "\\":
507 if sep == "\\":
508 # Working on a platform where the path separator is "\", so
508 # Working on a platform where the path separator is "\", so
509 # must convert these to "/" for generating a URI
509 # must convert these to "/" for generating a URI
510 def fp_cleaner(fp):
510 def fp_cleaner(fp):
511 # Replace all occurrences of backslash ("\") with a forward
511 # Replace all occurrences of backslash ("\") with a forward
512 # slash ("/") - this is necessary on windows when a path is
512 # slash ("/") - this is necessary on windows when a path is
513 # provided as input, but we must link to a URI
513 # provided as input, but we must link to a URI
514 return fp.replace('\\','/')
514 return fp.replace('\\','/')
515 else:
515 else:
516 fp_cleaner = None
516 fp_cleaner = None
517
517
518 return self._get_display_formatter(dirname_output_format,
518 return self._get_display_formatter(dirname_output_format,
519 fname_output_format,
519 fname_output_format,
520 fp_format,
520 fp_format,
521 fp_cleaner)
521 fp_cleaner)
522
522
523 def _get_terminal_display_formatter(self,
523 def _get_terminal_display_formatter(self,
524 spacer=" "):
524 spacer=" "):
525 """ generate function to use for terminal formatting
525 """ generate function to use for terminal formatting
526 """
526 """
527 dirname_output_format = "%s/"
527 dirname_output_format = "%s/"
528 fname_output_format = spacer + "%s"
528 fname_output_format = spacer + "%s"
529 fp_format = '%s/%s'
529 fp_format = '%s/%s'
530
530
531 return self._get_display_formatter(dirname_output_format,
531 return self._get_display_formatter(dirname_output_format,
532 fname_output_format,
532 fname_output_format,
533 fp_format)
533 fp_format)
534
534
535 def _format_path(self):
535 def _format_path(self):
536 result_lines = []
536 result_lines = []
537 if self.recursive:
537 if self.recursive:
538 walked_dir = list(walk(self.path))
538 walked_dir = list(walk(self.path))
539 else:
539 else:
540 walked_dir = [next(walk(self.path))]
540 walked_dir = [next(walk(self.path))]
541 walked_dir.sort()
541 walked_dir.sort()
542 for dirname, subdirs, fnames in walked_dir:
542 for dirname, subdirs, fnames in walked_dir:
543 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
543 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
544 return '\n'.join(result_lines)
544 return '\n'.join(result_lines)
545
545
546 def __repr__(self):
546 def __repr__(self):
547 """return newline-separated absolute paths
547 """return newline-separated absolute paths
548 """
548 """
549 result_lines = []
549 result_lines = []
550 if self.recursive:
550 if self.recursive:
551 walked_dir = list(walk(self.path))
551 walked_dir = list(walk(self.path))
552 else:
552 else:
553 walked_dir = [next(walk(self.path))]
553 walked_dir = [next(walk(self.path))]
554 walked_dir.sort()
554 walked_dir.sort()
555 for dirname, subdirs, fnames in walked_dir:
555 for dirname, subdirs, fnames in walked_dir:
556 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
556 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
557 return '\n'.join(result_lines)
557 return '\n'.join(result_lines)
558
558
559
559
560 class Code(TextDisplayObject):
560 class Code(TextDisplayObject):
561 """Display syntax-highlighted source code.
561 """Display syntax-highlighted source code.
562
562
563 This uses Pygments to highlight the code for HTML and Latex output.
563 This uses Pygments to highlight the code for HTML and Latex output.
564
564
565 Parameters
565 Parameters
566 ----------
566 ----------
567 data : str
567 data : str
568 The code as a string
568 The code as a string
569 url : str
569 url : str
570 A URL to fetch the code from
570 A URL to fetch the code from
571 filename : str
571 filename : str
572 A local filename to load the code from
572 A local filename to load the code from
573 language : str
573 language : str
574 The short name of a Pygments lexer to use for highlighting.
574 The short name of a Pygments lexer to use for highlighting.
575 If not specified, it will guess the lexer based on the filename
575 If not specified, it will guess the lexer based on the filename
576 or the code. Available lexers: http://pygments.org/docs/lexers/
576 or the code. Available lexers: http://pygments.org/docs/lexers/
577 """
577 """
578 def __init__(self, data=None, url=None, filename=None, language=None):
578 def __init__(self, data=None, url=None, filename=None, language=None):
579 self.language = language
579 self.language = language
580 super().__init__(data=data, url=url, filename=filename)
580 super().__init__(data=data, url=url, filename=filename)
581
581
582 def _get_lexer(self):
582 def _get_lexer(self):
583 if self.language:
583 if self.language:
584 from pygments.lexers import get_lexer_by_name
584 from pygments.lexers import get_lexer_by_name
585 return get_lexer_by_name(self.language)
585 return get_lexer_by_name(self.language)
586 elif self.filename:
586 elif self.filename:
587 from pygments.lexers import get_lexer_for_filename
587 from pygments.lexers import get_lexer_for_filename
588 return get_lexer_for_filename(self.filename)
588 return get_lexer_for_filename(self.filename)
589 else:
589 else:
590 from pygments.lexers import guess_lexer
590 from pygments.lexers import guess_lexer
591 return guess_lexer(self.data)
591 return guess_lexer(self.data)
592
592
593 def __repr__(self):
594 return self.data
595
593 def _repr_html_(self):
596 def _repr_html_(self):
594 from pygments import highlight
597 from pygments import highlight
595 from pygments.formatters import HtmlFormatter
598 from pygments.formatters import HtmlFormatter
596 fmt = HtmlFormatter()
599 fmt = HtmlFormatter()
597 style = '<style>{}</style>'.format(fmt.get_style_defs('.output_html'))
600 style = '<style>{}</style>'.format(fmt.get_style_defs('.output_html'))
598 return style + highlight(self.data, self._get_lexer(), fmt)
601 return style + highlight(self.data, self._get_lexer(), fmt)
599
602
600 def _repr_latex_(self):
603 def _repr_latex_(self):
601 from pygments import highlight
604 from pygments import highlight
602 from pygments.formatters import LatexFormatter
605 from pygments.formatters import LatexFormatter
603 return highlight(self.data, self._get_lexer(), LatexFormatter())
606 return highlight(self.data, self._get_lexer(), LatexFormatter())
General Comments 0
You need to be logged in to leave comments. Login now