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