##// END OF EJS Templates
Merge pull request #7090 from dsblank/master...
Min RK -
r19375:1a89b81d merge
parent child Browse files
Show More
@@ -1,533 +1,544 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',
10 __all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument',
11 'FileLink', 'FileLinks']
11 'FileLink', 'FileLinks']
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 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
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#parameter-subheader
254 https://developers.google.com/youtube/player_parameters#parameter-subheader
255 """
255 """
256
256
257 def __init__(self, id, width=400, height=300, **kwargs):
257 def __init__(self, id, width=400, height=300, **kwargs):
258 src = "https://www.youtube.com/embed/{0}".format(id)
258 src = "https://www.youtube.com/embed/{0}".format(id)
259 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
259 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
260
260
261 class VimeoVideo(IFrame):
261 class VimeoVideo(IFrame):
262 """
262 """
263 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.
264 """
264 """
265
265
266 def __init__(self, id, width=400, height=300, **kwargs):
266 def __init__(self, id, width=400, height=300, **kwargs):
267 src="https://player.vimeo.com/video/{0}".format(id)
267 src="https://player.vimeo.com/video/{0}".format(id)
268 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
268 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
269
269
270 class ScribdDocument(IFrame):
270 class ScribdDocument(IFrame):
271 """
271 """
272 Class for embedding a Scribd document in an IPython session
272 Class for embedding a Scribd document in an IPython session
273
273
274 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
275 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
276
276
277 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
278
278
279 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")
280 """
280 """
281
281
282 def __init__(self, id, width=400, height=300, **kwargs):
282 def __init__(self, id, width=400, height=300, **kwargs):
283 src="https://www.scribd.com/embeds/{0}/content".format(id)
283 src="https://www.scribd.com/embeds/{0}/content".format(id)
284 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
284 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
285
285
286 class FileLink(object):
286 class FileLink(object):
287 """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
288
288
289 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
290
290
291 you would do::
291 you would do::
292
292
293 local_file = FileLink("my/data.txt")
293 local_file = FileLink("my/data.txt")
294 display(local_file)
294 display(local_file)
295
295
296 or in the HTML notebook, just::
296 or in the HTML notebook, just::
297
297
298 FileLink("my/data.txt")
298 FileLink("my/data.txt")
299 """
299 """
300
300
301 html_link_str = "<a href='%s' target='_blank'>%s</a>"
301 html_link_str = "<a href='%s' target='_blank'>%s</a>"
302
302
303 def __init__(self,
303 def __init__(self,
304 path,
304 path,
305 url_prefix='',
305 url_prefix='',
306 result_html_prefix='',
306 result_html_prefix='',
307 result_html_suffix='<br>'):
307 result_html_suffix='<br>'):
308 """
308 """
309 Parameters
309 Parameters
310 ----------
310 ----------
311 path : str
311 path : str
312 path to the file or directory that should be formatted
312 path to the file or directory that should be formatted
313 directory_prefix : str
313 directory_prefix : str
314 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:
315 'files']
315 'files']
316 result_html_prefix : str
316 result_html_prefix : str
317 text to append to beginning to link [default: none]
317 text to append to beginning to link [default: none]
318 result_html_suffix : str
318 result_html_suffix : str
319 text to append at the end of link [default: '<br>']
319 text to append at the end of link [default: '<br>']
320 """
320 """
321 if isdir(path):
321 if isdir(path):
322 raise ValueError("Cannot display a directory using FileLink. "
322 raise ValueError("Cannot display a directory using FileLink. "
323 "Use FileLinks to display '%s'." % path)
323 "Use FileLinks to display '%s'." % path)
324 self.path = path
324 self.path = path
325 self.url_prefix = url_prefix
325 self.url_prefix = url_prefix
326 self.result_html_prefix = result_html_prefix
326 self.result_html_prefix = result_html_prefix
327 self.result_html_suffix = result_html_suffix
327 self.result_html_suffix = result_html_suffix
328
328
329 def _format_path(self):
329 def _format_path(self):
330 fp = ''.join([self.url_prefix,self.path])
330 fp = ''.join([self.url_prefix,self.path])
331 return ''.join([self.result_html_prefix,
331 return ''.join([self.result_html_prefix,
332 self.html_link_str % (fp, self.path),
332 self.html_link_str % (fp, self.path),
333 self.result_html_suffix])
333 self.result_html_suffix])
334
334
335 def _repr_html_(self):
335 def _repr_html_(self):
336 """return html link to file
336 """return html link to file
337 """
337 """
338 if not exists(self.path):
338 if not exists(self.path):
339 return ("Path (<tt>%s</tt>) doesn't exist. "
339 return ("Path (<tt>%s</tt>) doesn't exist. "
340 "It may still be in the process of "
340 "It may still be in the process of "
341 "being generated, or you may have the "
341 "being generated, or you may have the "
342 "incorrect path." % self.path)
342 "incorrect path." % self.path)
343
343
344 return self._format_path()
344 return self._format_path()
345
345
346 def __repr__(self):
346 def __repr__(self):
347 """return absolute path to file
347 """return absolute path to file
348 """
348 """
349 return abspath(self.path)
349 return abspath(self.path)
350
350
351 class FileLinks(FileLink):
351 class FileLinks(FileLink):
352 """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
353
353
354 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
355 under ``my/data``, you would do::
355 under ``my/data``, you would do::
356
356
357 local_files = FileLinks("my/data")
357 local_files = FileLinks("my/data")
358 display(local_files)
358 display(local_files)
359
359
360 or in the HTML notebook, just::
360 or in the HTML notebook, just::
361
361
362 FileLinks("my/data")
362 FileLinks("my/data")
363 """
363 """
364 def __init__(self,
364 def __init__(self,
365 path,
365 path,
366 url_prefix='',
366 url_prefix='',
367 included_suffixes=None,
367 included_suffixes=None,
368 result_html_prefix='',
368 result_html_prefix='',
369 result_html_suffix='<br>',
369 result_html_suffix='<br>',
370 notebook_display_formatter=None,
370 notebook_display_formatter=None,
371 terminal_display_formatter=None):
371 terminal_display_formatter=None,
372 recursive=True):
372 """
373 """
373 See :class:`FileLink` for the ``path``, ``url_prefix``,
374 See :class:`FileLink` for the ``path``, ``url_prefix``,
374 ``result_html_prefix`` and ``result_html_suffix`` parameters.
375 ``result_html_prefix`` and ``result_html_suffix`` parameters.
375
376
376 included_suffixes : list
377 included_suffixes : list
377 Filename suffixes to include when formatting output [default: include
378 Filename suffixes to include when formatting output [default: include
378 all files]
379 all files]
379
380
380 notebook_display_formatter : function
381 notebook_display_formatter : function
381 Used to format links for display in the notebook. See discussion of
382 Used to format links for display in the notebook. See discussion of
382 formatter functions below.
383 formatter functions below.
383
384
384 terminal_display_formatter : function
385 terminal_display_formatter : function
385 Used to format links for display in the terminal. See discussion of
386 Used to format links for display in the terminal. See discussion of
386 formatter functions below.
387 formatter functions below.
387
388
388 Formatter functions must be of the form::
389 Formatter functions must be of the form::
389
390
390 f(dirname, fnames, included_suffixes)
391 f(dirname, fnames, included_suffixes)
391
392
392 dirname : str
393 dirname : str
393 The name of a directory
394 The name of a directory
394 fnames : list
395 fnames : list
395 The files in that directory
396 The files in that directory
396 included_suffixes : list
397 included_suffixes : list
397 The file suffixes that should be included in the output (passing None
398 The file suffixes that should be included in the output (passing None
398 meansto include all suffixes in the output in the built-in formatters)
399 meansto include all suffixes in the output in the built-in formatters)
400 recursive : boolean
401 Whether to recurse into subdirectories. Default is True.
399
402
400 The function should return a list of lines that will be printed in the
403 The function should return a list of lines that will be printed in the
401 notebook (if passing notebook_display_formatter) or the terminal (if
404 notebook (if passing notebook_display_formatter) or the terminal (if
402 passing terminal_display_formatter). This function is iterated over for
405 passing terminal_display_formatter). This function is iterated over for
403 each directory in self.path. Default formatters are in place, can be
406 each directory in self.path. Default formatters are in place, can be
404 passed here to support alternative formatting.
407 passed here to support alternative formatting.
405
408
406 """
409 """
407 if isfile(path):
410 if isfile(path):
408 raise ValueError("Cannot display a file using FileLinks. "
411 raise ValueError("Cannot display a file using FileLinks. "
409 "Use FileLink to display '%s'." % path)
412 "Use FileLink to display '%s'." % path)
410 self.included_suffixes = included_suffixes
413 self.included_suffixes = included_suffixes
411 # remove trailing slashs for more consistent output formatting
414 # remove trailing slashs for more consistent output formatting
412 path = path.rstrip('/')
415 path = path.rstrip('/')
413
416
414 self.path = path
417 self.path = path
415 self.url_prefix = url_prefix
418 self.url_prefix = url_prefix
416 self.result_html_prefix = result_html_prefix
419 self.result_html_prefix = result_html_prefix
417 self.result_html_suffix = result_html_suffix
420 self.result_html_suffix = result_html_suffix
418
421
419 self.notebook_display_formatter = \
422 self.notebook_display_formatter = \
420 notebook_display_formatter or self._get_notebook_display_formatter()
423 notebook_display_formatter or self._get_notebook_display_formatter()
421 self.terminal_display_formatter = \
424 self.terminal_display_formatter = \
422 terminal_display_formatter or self._get_terminal_display_formatter()
425 terminal_display_formatter or self._get_terminal_display_formatter()
423
426
427 self.recursive = recursive
428
424 def _get_display_formatter(self,
429 def _get_display_formatter(self,
425 dirname_output_format,
430 dirname_output_format,
426 fname_output_format,
431 fname_output_format,
427 fp_format,
432 fp_format,
428 fp_cleaner=None):
433 fp_cleaner=None):
429 """ generate built-in formatter function
434 """ generate built-in formatter function
430
435
431 this is used to define both the notebook and terminal built-in
436 this is used to define both the notebook and terminal built-in
432 formatters as they only differ by some wrapper text for each entry
437 formatters as they only differ by some wrapper text for each entry
433
438
434 dirname_output_format: string to use for formatting directory
439 dirname_output_format: string to use for formatting directory
435 names, dirname will be substituted for a single "%s" which
440 names, dirname will be substituted for a single "%s" which
436 must appear in this string
441 must appear in this string
437 fname_output_format: string to use for formatting file names,
442 fname_output_format: string to use for formatting file names,
438 if a single "%s" appears in the string, fname will be substituted
443 if a single "%s" appears in the string, fname will be substituted
439 if two "%s" appear in the string, the path to fname will be
444 if two "%s" appear in the string, the path to fname will be
440 substituted for the first and fname will be substituted for the
445 substituted for the first and fname will be substituted for the
441 second
446 second
442 fp_format: string to use for formatting filepaths, must contain
447 fp_format: string to use for formatting filepaths, must contain
443 exactly two "%s" and the dirname will be subsituted for the first
448 exactly two "%s" and the dirname will be subsituted for the first
444 and fname will be substituted for the second
449 and fname will be substituted for the second
445 """
450 """
446 def f(dirname, fnames, included_suffixes=None):
451 def f(dirname, fnames, included_suffixes=None):
447 result = []
452 result = []
448 # begin by figuring out which filenames, if any,
453 # begin by figuring out which filenames, if any,
449 # are going to be displayed
454 # are going to be displayed
450 display_fnames = []
455 display_fnames = []
451 for fname in fnames:
456 for fname in fnames:
452 if (isfile(join(dirname,fname)) and
457 if (isfile(join(dirname,fname)) and
453 (included_suffixes is None or
458 (included_suffixes is None or
454 splitext(fname)[1] in included_suffixes)):
459 splitext(fname)[1] in included_suffixes)):
455 display_fnames.append(fname)
460 display_fnames.append(fname)
456
461
457 if len(display_fnames) == 0:
462 if len(display_fnames) == 0:
458 # if there are no filenames to display, don't print anything
463 # if there are no filenames to display, don't print anything
459 # (not even the directory name)
464 # (not even the directory name)
460 pass
465 pass
461 else:
466 else:
462 # otherwise print the formatted directory name followed by
467 # otherwise print the formatted directory name followed by
463 # the formatted filenames
468 # the formatted filenames
464 dirname_output_line = dirname_output_format % dirname
469 dirname_output_line = dirname_output_format % dirname
465 result.append(dirname_output_line)
470 result.append(dirname_output_line)
466 for fname in display_fnames:
471 for fname in display_fnames:
467 fp = fp_format % (dirname,fname)
472 fp = fp_format % (dirname,fname)
468 if fp_cleaner is not None:
473 if fp_cleaner is not None:
469 fp = fp_cleaner(fp)
474 fp = fp_cleaner(fp)
470 try:
475 try:
471 # output can include both a filepath and a filename...
476 # output can include both a filepath and a filename...
472 fname_output_line = fname_output_format % (fp, fname)
477 fname_output_line = fname_output_format % (fp, fname)
473 except TypeError:
478 except TypeError:
474 # ... or just a single filepath
479 # ... or just a single filepath
475 fname_output_line = fname_output_format % fname
480 fname_output_line = fname_output_format % fname
476 result.append(fname_output_line)
481 result.append(fname_output_line)
477 return result
482 return result
478 return f
483 return f
479
484
480 def _get_notebook_display_formatter(self,
485 def _get_notebook_display_formatter(self,
481 spacer="&nbsp;&nbsp;"):
486 spacer="&nbsp;&nbsp;"):
482 """ generate function to use for notebook formatting
487 """ generate function to use for notebook formatting
483 """
488 """
484 dirname_output_format = \
489 dirname_output_format = \
485 self.result_html_prefix + "%s/" + self.result_html_suffix
490 self.result_html_prefix + "%s/" + self.result_html_suffix
486 fname_output_format = \
491 fname_output_format = \
487 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
492 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
488 fp_format = self.url_prefix + '%s/%s'
493 fp_format = self.url_prefix + '%s/%s'
489 if sep == "\\":
494 if sep == "\\":
490 # Working on a platform where the path separator is "\", so
495 # Working on a platform where the path separator is "\", so
491 # must convert these to "/" for generating a URI
496 # must convert these to "/" for generating a URI
492 def fp_cleaner(fp):
497 def fp_cleaner(fp):
493 # Replace all occurences of backslash ("\") with a forward
498 # Replace all occurences of backslash ("\") with a forward
494 # slash ("/") - this is necessary on windows when a path is
499 # slash ("/") - this is necessary on windows when a path is
495 # provided as input, but we must link to a URI
500 # provided as input, but we must link to a URI
496 return fp.replace('\\','/')
501 return fp.replace('\\','/')
497 else:
502 else:
498 fp_cleaner = None
503 fp_cleaner = None
499
504
500 return self._get_display_formatter(dirname_output_format,
505 return self._get_display_formatter(dirname_output_format,
501 fname_output_format,
506 fname_output_format,
502 fp_format,
507 fp_format,
503 fp_cleaner)
508 fp_cleaner)
504
509
505 def _get_terminal_display_formatter(self,
510 def _get_terminal_display_formatter(self,
506 spacer=" "):
511 spacer=" "):
507 """ generate function to use for terminal formatting
512 """ generate function to use for terminal formatting
508 """
513 """
509 dirname_output_format = "%s/"
514 dirname_output_format = "%s/"
510 fname_output_format = spacer + "%s"
515 fname_output_format = spacer + "%s"
511 fp_format = '%s/%s'
516 fp_format = '%s/%s'
512
517
513 return self._get_display_formatter(dirname_output_format,
518 return self._get_display_formatter(dirname_output_format,
514 fname_output_format,
519 fname_output_format,
515 fp_format)
520 fp_format)
516
521
517 def _format_path(self):
522 def _format_path(self):
518 result_lines = []
523 result_lines = []
524 if self.recursive:
519 walked_dir = list(walk(self.path))
525 walked_dir = list(walk(self.path))
526 else:
527 walked_dir = [next(walk(self.path))]
520 walked_dir.sort()
528 walked_dir.sort()
521 for dirname, subdirs, fnames in walked_dir:
529 for dirname, subdirs, fnames in walked_dir:
522 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
530 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
523 return '\n'.join(result_lines)
531 return '\n'.join(result_lines)
524
532
525 def __repr__(self):
533 def __repr__(self):
526 """return newline-separated absolute paths
534 """return newline-separated absolute paths
527 """
535 """
528 result_lines = []
536 result_lines = []
537 if self.recursive:
529 walked_dir = list(walk(self.path))
538 walked_dir = list(walk(self.path))
539 else:
540 walked_dir = [next(walk(self.path))]
530 walked_dir.sort()
541 walked_dir.sort()
531 for dirname, subdirs, fnames in walked_dir:
542 for dirname, subdirs, fnames in walked_dir:
532 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
543 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
533 return '\n'.join(result_lines)
544 return '\n'.join(result_lines)
@@ -1,162 +1,178 b''
1 """Tests for IPython.lib.display.
1 """Tests for IPython.lib.display.
2
2
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2012, the IPython Development Team.
5 # Copyright (c) 2012, the IPython Development Team.
6 #
6 #
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8 #
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 from __future__ import print_function
15 from __future__ import print_function
16 from tempfile import NamedTemporaryFile, mkdtemp
16 from tempfile import NamedTemporaryFile, mkdtemp
17 from os.path import split, join as pjoin, dirname
17 from os.path import split, join as pjoin, dirname
18
18
19 # Third-party imports
19 # Third-party imports
20 import nose.tools as nt
20 import nose.tools as nt
21
21
22 # Our own imports
22 # Our own imports
23 from IPython.lib import display
23 from IPython.lib import display
24 from IPython.testing.decorators import skipif_not_numpy
24 from IPython.testing.decorators import skipif_not_numpy
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Classes and functions
27 # Classes and functions
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 #--------------------------
30 #--------------------------
31 # FileLink tests
31 # FileLink tests
32 #--------------------------
32 #--------------------------
33
33
34 def test_instantiation_FileLink():
34 def test_instantiation_FileLink():
35 """FileLink: Test class can be instantiated"""
35 """FileLink: Test class can be instantiated"""
36 fl = display.FileLink('example.txt')
36 fl = display.FileLink('example.txt')
37
37
38 def test_warning_on_non_existant_path_FileLink():
38 def test_warning_on_non_existant_path_FileLink():
39 """FileLink: Calling _repr_html_ on non-existant files returns a warning
39 """FileLink: Calling _repr_html_ on non-existant files returns a warning
40 """
40 """
41 fl = display.FileLink('example.txt')
41 fl = display.FileLink('example.txt')
42 nt.assert_true(fl._repr_html_().startswith('Path (<tt>example.txt</tt>)'))
42 nt.assert_true(fl._repr_html_().startswith('Path (<tt>example.txt</tt>)'))
43
43
44 def test_existing_path_FileLink():
44 def test_existing_path_FileLink():
45 """FileLink: Calling _repr_html_ functions as expected on existing filepath
45 """FileLink: Calling _repr_html_ functions as expected on existing filepath
46 """
46 """
47 tf = NamedTemporaryFile()
47 tf = NamedTemporaryFile()
48 fl = display.FileLink(tf.name)
48 fl = display.FileLink(tf.name)
49 actual = fl._repr_html_()
49 actual = fl._repr_html_()
50 expected = "<a href='%s' target='_blank'>%s</a><br>" % (tf.name,tf.name)
50 expected = "<a href='%s' target='_blank'>%s</a><br>" % (tf.name,tf.name)
51 nt.assert_equal(actual,expected)
51 nt.assert_equal(actual,expected)
52
52
53 def test_existing_path_FileLink_repr():
53 def test_existing_path_FileLink_repr():
54 """FileLink: Calling repr() functions as expected on existing filepath
54 """FileLink: Calling repr() functions as expected on existing filepath
55 """
55 """
56 tf = NamedTemporaryFile()
56 tf = NamedTemporaryFile()
57 fl = display.FileLink(tf.name)
57 fl = display.FileLink(tf.name)
58 actual = repr(fl)
58 actual = repr(fl)
59 expected = tf.name
59 expected = tf.name
60 nt.assert_equal(actual,expected)
60 nt.assert_equal(actual,expected)
61
61
62 def test_error_on_directory_to_FileLink():
62 def test_error_on_directory_to_FileLink():
63 """FileLink: Raises error when passed directory
63 """FileLink: Raises error when passed directory
64 """
64 """
65 td = mkdtemp()
65 td = mkdtemp()
66 nt.assert_raises(ValueError,display.FileLink,td)
66 nt.assert_raises(ValueError,display.FileLink,td)
67
67
68 #--------------------------
68 #--------------------------
69 # FileLinks tests
69 # FileLinks tests
70 #--------------------------
70 #--------------------------
71
71
72 def test_instantiation_FileLinks():
72 def test_instantiation_FileLinks():
73 """FileLinks: Test class can be instantiated
73 """FileLinks: Test class can be instantiated
74 """
74 """
75 fls = display.FileLinks('example')
75 fls = display.FileLinks('example')
76
76
77 def test_warning_on_non_existant_path_FileLinks():
77 def test_warning_on_non_existant_path_FileLinks():
78 """FileLinks: Calling _repr_html_ on non-existant files returns a warning
78 """FileLinks: Calling _repr_html_ on non-existant files returns a warning
79 """
79 """
80 fls = display.FileLinks('example')
80 fls = display.FileLinks('example')
81 nt.assert_true(fls._repr_html_().startswith('Path (<tt>example</tt>)'))
81 nt.assert_true(fls._repr_html_().startswith('Path (<tt>example</tt>)'))
82
82
83 def test_existing_path_FileLinks():
83 def test_existing_path_FileLinks():
84 """FileLinks: Calling _repr_html_ functions as expected on existing dir
84 """FileLinks: Calling _repr_html_ functions as expected on existing dir
85 """
85 """
86 td = mkdtemp()
86 td = mkdtemp()
87 tf1 = NamedTemporaryFile(dir=td)
87 tf1 = NamedTemporaryFile(dir=td)
88 tf2 = NamedTemporaryFile(dir=td)
88 tf2 = NamedTemporaryFile(dir=td)
89 fl = display.FileLinks(td)
89 fl = display.FileLinks(td)
90 actual = fl._repr_html_()
90 actual = fl._repr_html_()
91 actual = actual.split('\n')
91 actual = actual.split('\n')
92 actual.sort()
92 actual.sort()
93 # the links should always have forward slashes, even on windows, so replace
93 # the links should always have forward slashes, even on windows, so replace
94 # backslashes with forward slashes here
94 # backslashes with forward slashes here
95 expected = ["%s/<br>" % td,
95 expected = ["%s/<br>" % td,
96 "&nbsp;&nbsp;<a href='%s' target='_blank'>%s</a><br>" %\
96 "&nbsp;&nbsp;<a href='%s' target='_blank'>%s</a><br>" %\
97 (tf2.name.replace("\\","/"),split(tf2.name)[1]),
97 (tf2.name.replace("\\","/"),split(tf2.name)[1]),
98 "&nbsp;&nbsp;<a href='%s' target='_blank'>%s</a><br>" %\
98 "&nbsp;&nbsp;<a href='%s' target='_blank'>%s</a><br>" %\
99 (tf1.name.replace("\\","/"),split(tf1.name)[1])]
99 (tf1.name.replace("\\","/"),split(tf1.name)[1])]
100 expected.sort()
100 expected.sort()
101 # We compare the sorted list of links here as that's more reliable
101 # We compare the sorted list of links here as that's more reliable
102 nt.assert_equal(actual,expected)
102 nt.assert_equal(actual,expected)
103
103
104 def test_existing_path_FileLinks_alt_formatter():
104 def test_existing_path_FileLinks_alt_formatter():
105 """FileLinks: Calling _repr_html_ functions as expected w/ an alt formatter
105 """FileLinks: Calling _repr_html_ functions as expected w/ an alt formatter
106 """
106 """
107 td = mkdtemp()
107 td = mkdtemp()
108 tf1 = NamedTemporaryFile(dir=td)
108 tf1 = NamedTemporaryFile(dir=td)
109 tf2 = NamedTemporaryFile(dir=td)
109 tf2 = NamedTemporaryFile(dir=td)
110 def fake_formatter(dirname,fnames,included_suffixes):
110 def fake_formatter(dirname,fnames,included_suffixes):
111 return ["hello","world"]
111 return ["hello","world"]
112 fl = display.FileLinks(td,notebook_display_formatter=fake_formatter)
112 fl = display.FileLinks(td,notebook_display_formatter=fake_formatter)
113 actual = fl._repr_html_()
113 actual = fl._repr_html_()
114 actual = actual.split('\n')
114 actual = actual.split('\n')
115 actual.sort()
115 actual.sort()
116 expected = ["hello","world"]
116 expected = ["hello","world"]
117 expected.sort()
117 expected.sort()
118 # We compare the sorted list of links here as that's more reliable
118 # We compare the sorted list of links here as that's more reliable
119 nt.assert_equal(actual,expected)
119 nt.assert_equal(actual,expected)
120
120
121 def test_existing_path_FileLinks_repr():
121 def test_existing_path_FileLinks_repr():
122 """FileLinks: Calling repr() functions as expected on existing directory """
122 """FileLinks: Calling repr() functions as expected on existing directory """
123 td = mkdtemp()
123 td = mkdtemp()
124 tf1 = NamedTemporaryFile(dir=td)
124 tf1 = NamedTemporaryFile(dir=td)
125 tf2 = NamedTemporaryFile(dir=td)
125 tf2 = NamedTemporaryFile(dir=td)
126 fl = display.FileLinks(td)
126 fl = display.FileLinks(td)
127 actual = repr(fl)
127 actual = repr(fl)
128 actual = actual.split('\n')
128 actual = actual.split('\n')
129 actual.sort()
129 actual.sort()
130 expected = ['%s/' % td, ' %s' % split(tf1.name)[1],' %s' % split(tf2.name)[1]]
130 expected = ['%s/' % td, ' %s' % split(tf1.name)[1],' %s' % split(tf2.name)[1]]
131 expected.sort()
131 expected.sort()
132 # We compare the sorted list of links here as that's more reliable
132 # We compare the sorted list of links here as that's more reliable
133 nt.assert_equal(actual,expected)
133 nt.assert_equal(actual,expected)
134
134
135 def test_existing_path_FileLinks_repr_alt_formatter():
135 def test_existing_path_FileLinks_repr_alt_formatter():
136 """FileLinks: Calling repr() functions as expected w/ alt formatter
136 """FileLinks: Calling repr() functions as expected w/ alt formatter
137 """
137 """
138 td = mkdtemp()
138 td = mkdtemp()
139 tf1 = NamedTemporaryFile(dir=td)
139 tf1 = NamedTemporaryFile(dir=td)
140 tf2 = NamedTemporaryFile(dir=td)
140 tf2 = NamedTemporaryFile(dir=td)
141 def fake_formatter(dirname,fnames,included_suffixes):
141 def fake_formatter(dirname,fnames,included_suffixes):
142 return ["hello","world"]
142 return ["hello","world"]
143 fl = display.FileLinks(td,terminal_display_formatter=fake_formatter)
143 fl = display.FileLinks(td,terminal_display_formatter=fake_formatter)
144 actual = repr(fl)
144 actual = repr(fl)
145 actual = actual.split('\n')
145 actual = actual.split('\n')
146 actual.sort()
146 actual.sort()
147 expected = ["hello","world"]
147 expected = ["hello","world"]
148 expected.sort()
148 expected.sort()
149 # We compare the sorted list of links here as that's more reliable
149 # We compare the sorted list of links here as that's more reliable
150 nt.assert_equal(actual,expected)
150 nt.assert_equal(actual,expected)
151
151
152 def test_error_on_file_to_FileLinks():
152 def test_error_on_file_to_FileLinks():
153 """FileLinks: Raises error when passed file
153 """FileLinks: Raises error when passed file
154 """
154 """
155 td = mkdtemp()
155 td = mkdtemp()
156 tf1 = NamedTemporaryFile(dir=td)
156 tf1 = NamedTemporaryFile(dir=td)
157 nt.assert_raises(ValueError,display.FileLinks,tf1.name)
157 nt.assert_raises(ValueError,display.FileLinks,tf1.name)
158
158
159 def test_recursive_FileLinks():
160 """FileLinks: Does not recurse when recursive=False
161 """
162 td = mkdtemp()
163 tf = NamedTemporaryFile(dir=td)
164 subtd = mkdtemp(dir=td)
165 subtf = NamedTemporaryFile(dir=subtd)
166 fl = display.FileLinks(td)
167 actual = str(fl)
168 actual = actual.split('\n')
169 nt.assert_equal(len(actual), 4, actual)
170 fl = display.FileLinks(td, recursive=False)
171 actual = str(fl)
172 actual = actual.split('\n')
173 nt.assert_equal(len(actual), 2, actual)
174
159 @skipif_not_numpy
175 @skipif_not_numpy
160 def test_audio_from_file():
176 def test_audio_from_file():
161 path = pjoin(dirname(__file__), 'test.wav')
177 path = pjoin(dirname(__file__), 'test.wav')
162 display.Audio(filename=path)
178 display.Audio(filename=path)
General Comments 0
You need to be logged in to leave comments. Login now