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