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