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