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