##// END OF EJS Templates
fix doc
Matthias Bussonnier -
Show More
@@ -1,2229 +1,2239 b''
1 1 """Completion for IPython.
2 2
3 3 This module started as fork of the rlcompleter module in the Python standard
4 4 library. The original enhancements made to rlcompleter have been sent
5 5 upstream and were accepted as of Python 2.3,
6 6
7 7 This module now support a wide variety of completion mechanism both available
8 8 for normal classic Python code, as well as completer for IPython specific
9 9 Syntax like magics.
10 10
11 11 Latex and Unicode completion
12 12 ============================
13 13
14 14 IPython and compatible frontends not only can complete your code, but can help
15 15 you to input a wide range of characters. In particular we allow you to insert
16 16 a unicode character using the tab completion mechanism.
17 17
18 18 Forward latex/unicode completion
19 19 --------------------------------
20 20
21 21 Forward completion allows you to easily type a unicode character using its latex
22 22 name, or unicode long description. To do so type a backslash follow by the
23 23 relevant name and press tab:
24 24
25 25
26 26 Using latex completion:
27 27
28 28 .. code::
29 29
30 30 \\alpha<tab>
31 31 Ξ±
32 32
33 33 or using unicode completion:
34 34
35 35
36 36 .. code::
37 37
38 38 \\GREEK SMALL LETTER ALPHA<tab>
39 39 Ξ±
40 40
41 41
42 42 Only valid Python identifiers will complete. Combining characters (like arrow or
43 43 dots) are also available, unlike latex they need to be put after the their
44 44 counterpart that is to say, `F\\\\vec<tab>` is correct, not `\\\\vec<tab>F`.
45 45
46 46 Some browsers are known to display combining characters incorrectly.
47 47
48 48 Backward latex completion
49 49 -------------------------
50 50
51 51 It is sometime challenging to know how to type a character, if you are using
52 52 IPython, or any compatible frontend you can prepend backslash to the character
53 53 and press `<tab>` to expand it to its latex form.
54 54
55 55 .. code::
56 56
57 57 \\Ξ±<tab>
58 58 \\alpha
59 59
60 60
61 61 Both forward and backward completions can be deactivated by setting the
62 62 ``Completer.backslash_combining_completions`` option to ``False``.
63 63
64 64
65 65 Experimental
66 66 ============
67 67
68 68 Starting with IPython 6.0, this module can make use of the Jedi library to
69 69 generate completions both using static analysis of the code, and dynamically
70 70 inspecting multiple namespaces. Jedi is an autocompletion and static analysis
71 71 for Python. The APIs attached to this new mechanism is unstable and will
72 72 raise unless use in an :any:`provisionalcompleter` context manager.
73 73
74 74 You will find that the following are experimental:
75 75
76 76 - :any:`provisionalcompleter`
77 77 - :any:`IPCompleter.completions`
78 78 - :any:`Completion`
79 79 - :any:`rectify_completions`
80 80
81 81 .. note::
82 82
83 83 better name for :any:`rectify_completions` ?
84 84
85 85 We welcome any feedback on these new API, and we also encourage you to try this
86 86 module in debug mode (start IPython with ``--Completer.debug=True``) in order
87 87 to have extra logging information if :any:`jedi` is crashing, or if current
88 88 IPython completer pending deprecations are returning results not yet handled
89 89 by :any:`jedi`
90 90
91 91 Using Jedi for tab completion allow snippets like the following to work without
92 92 having to execute any code:
93 93
94 94 >>> myvar = ['hello', 42]
95 95 ... myvar[1].bi<tab>
96 96
97 97 Tab completion will be able to infer that ``myvar[1]`` is a real number without
98 98 executing any code unlike the previously available ``IPCompleter.greedy``
99 99 option.
100 100
101 101 Be sure to update :any:`jedi` to the latest stable version or to try the
102 102 current development version to get better completions.
103 103 """
104 104
105 105
106 106 # Copyright (c) IPython Development Team.
107 107 # Distributed under the terms of the Modified BSD License.
108 108 #
109 109 # Some of this code originated from rlcompleter in the Python standard library
110 110 # Copyright (C) 2001 Python Software Foundation, www.python.org
111 111
112 112
113 113 import builtins as builtin_mod
114 114 import glob
115 115 import inspect
116 116 import itertools
117 117 import keyword
118 118 import os
119 119 import re
120 120 import string
121 121 import sys
122 122 import time
123 123 import unicodedata
124 124 import uuid
125 125 import warnings
126 126 from contextlib import contextmanager
127 127 from importlib import import_module
128 128 from types import SimpleNamespace
129 129 from typing import Iterable, Iterator, List, Tuple, Union, Any, Sequence, Dict, NamedTuple, Pattern, Optional
130 130
131 131 from IPython.core.error import TryNext
132 132 from IPython.core.inputtransformer2 import ESC_MAGIC
133 133 from IPython.core.latex_symbols import latex_symbols, reverse_latex_symbol
134 134 from IPython.core.oinspect import InspectColors
135 135 from IPython.utils import generics
136 136 from IPython.utils.dir2 import dir2, get_real_method
137 137 from IPython.utils.path import ensure_dir_exists
138 138 from IPython.utils.process import arg_split
139 139 from traitlets import Bool, Enum, Int, List as ListTrait, Unicode, default, observe
140 140 from traitlets.config.configurable import Configurable
141 141
142 142 import __main__
143 143
144 144 # skip module docstests
145 145 skip_doctest = True
146 146
147 147 try:
148 148 import jedi
149 149 jedi.settings.case_insensitive_completion = False
150 150 import jedi.api.helpers
151 151 import jedi.api.classes
152 152 JEDI_INSTALLED = True
153 153 except ImportError:
154 154 JEDI_INSTALLED = False
155 155 #-----------------------------------------------------------------------------
156 156 # Globals
157 157 #-----------------------------------------------------------------------------
158 158
159 159 # ranges where we have most of the valid unicode names. We could be more finer
160 160 # grained but is it worth it for performace While unicode have character in the
161 161 # rage 0, 0x110000, we seem to have name for about 10% of those. (131808 as I
162 162 # write this). With below range we cover them all, with a density of ~67%
163 163 # biggest next gap we consider only adds up about 1% density and there are 600
164 164 # gaps that would need hard coding.
165 165 _UNICODE_RANGES = [(32, 0x3134b), (0xe0001, 0xe01f0)]
166 166
167 167 # Public API
168 168 __all__ = ['Completer','IPCompleter']
169 169
170 170 if sys.platform == 'win32':
171 171 PROTECTABLES = ' '
172 172 else:
173 173 PROTECTABLES = ' ()[]{}?=\\|;:\'#*"^&'
174 174
175 175 # Protect against returning an enormous number of completions which the frontend
176 176 # may have trouble processing.
177 177 MATCHES_LIMIT = 500
178 178
179 179 _deprecation_readline_sentinel = object()
180 180
181 181
182 182 class ProvisionalCompleterWarning(FutureWarning):
183 183 """
184 184 Exception raise by an experimental feature in this module.
185 185
186 186 Wrap code in :any:`provisionalcompleter` context manager if you
187 187 are certain you want to use an unstable feature.
188 188 """
189 189 pass
190 190
191 191 warnings.filterwarnings('error', category=ProvisionalCompleterWarning)
192 192
193 193 @contextmanager
194 194 def provisionalcompleter(action='ignore'):
195 195 """
196 196 This context manager has to be used in any place where unstable completer
197 197 behavior and API may be called.
198 198
199 199 >>> with provisionalcompleter():
200 200 ... completer.do_experimental_things() # works
201 201
202 202 >>> completer.do_experimental_things() # raises.
203 203
204 .. note:: Unstable
204 .. note::
205
206 Unstable
205 207
206 208 By using this context manager you agree that the API in use may change
207 209 without warning, and that you won't complain if they do so.
208 210
209 211 You also understand that, if the API is not to your liking, you should report
210 212 a bug to explain your use case upstream.
211 213
212 214 We'll be happy to get your feedback, feature requests, and improvements on
213 215 any of the unstable APIs!
214 216 """
215 217 with warnings.catch_warnings():
216 218 warnings.filterwarnings(action, category=ProvisionalCompleterWarning)
217 219 yield
218 220
219 221
220 222 def has_open_quotes(s):
221 223 """Return whether a string has open quotes.
222 224
223 225 This simply counts whether the number of quote characters of either type in
224 226 the string is odd.
225 227
226 228 Returns
227 229 -------
228 230 If there is an open quote, the quote character is returned. Else, return
229 231 False.
230 232 """
231 233 # We check " first, then ', so complex cases with nested quotes will get
232 234 # the " to take precedence.
233 235 if s.count('"') % 2:
234 236 return '"'
235 237 elif s.count("'") % 2:
236 238 return "'"
237 239 else:
238 240 return False
239 241
240 242
241 243 def protect_filename(s, protectables=PROTECTABLES):
242 244 """Escape a string to protect certain characters."""
243 245 if set(s) & set(protectables):
244 246 if sys.platform == "win32":
245 247 return '"' + s + '"'
246 248 else:
247 249 return "".join(("\\" + c if c in protectables else c) for c in s)
248 250 else:
249 251 return s
250 252
251 253
252 254 def expand_user(path:str) -> Tuple[str, bool, str]:
253 255 """Expand ``~``-style usernames in strings.
254 256
255 257 This is similar to :func:`os.path.expanduser`, but it computes and returns
256 258 extra information that will be useful if the input was being used in
257 259 computing completions, and you wish to return the completions with the
258 260 original '~' instead of its expanded value.
259 261
260 262 Parameters
261 263 ----------
262 264 path : str
263 265 String to be expanded. If no ~ is present, the output is the same as the
264 266 input.
265 267
266 268 Returns
267 269 -------
268 270 newpath : str
269 271 Result of ~ expansion in the input path.
270 272 tilde_expand : bool
271 273 Whether any expansion was performed or not.
272 274 tilde_val : str
273 275 The value that ~ was replaced with.
274 276 """
275 277 # Default values
276 278 tilde_expand = False
277 279 tilde_val = ''
278 280 newpath = path
279 281
280 282 if path.startswith('~'):
281 283 tilde_expand = True
282 284 rest = len(path)-1
283 285 newpath = os.path.expanduser(path)
284 286 if rest:
285 287 tilde_val = newpath[:-rest]
286 288 else:
287 289 tilde_val = newpath
288 290
289 291 return newpath, tilde_expand, tilde_val
290 292
291 293
292 294 def compress_user(path:str, tilde_expand:bool, tilde_val:str) -> str:
293 295 """Does the opposite of expand_user, with its outputs.
294 296 """
295 297 if tilde_expand:
296 298 return path.replace(tilde_val, '~')
297 299 else:
298 300 return path
299 301
300 302
301 303 def completions_sorting_key(word):
302 304 """key for sorting completions
303 305
304 306 This does several things:
305 307
306 308 - Demote any completions starting with underscores to the end
307 309 - Insert any %magic and %%cellmagic completions in the alphabetical order
308 310 by their name
309 311 """
310 312 prio1, prio2 = 0, 0
311 313
312 314 if word.startswith('__'):
313 315 prio1 = 2
314 316 elif word.startswith('_'):
315 317 prio1 = 1
316 318
317 319 if word.endswith('='):
318 320 prio1 = -1
319 321
320 322 if word.startswith('%%'):
321 323 # If there's another % in there, this is something else, so leave it alone
322 324 if not "%" in word[2:]:
323 325 word = word[2:]
324 326 prio2 = 2
325 327 elif word.startswith('%'):
326 328 if not "%" in word[1:]:
327 329 word = word[1:]
328 330 prio2 = 1
329 331
330 332 return prio1, word, prio2
331 333
332 334
333 335 class _FakeJediCompletion:
334 336 """
335 337 This is a workaround to communicate to the UI that Jedi has crashed and to
336 338 report a bug. Will be used only id :any:`IPCompleter.debug` is set to true.
337 339
338 340 Added in IPython 6.0 so should likely be removed for 7.0
339 341
340 342 """
341 343
342 344 def __init__(self, name):
343 345
344 346 self.name = name
345 347 self.complete = name
346 348 self.type = 'crashed'
347 349 self.name_with_symbols = name
348 350 self.signature = ''
349 351 self._origin = 'fake'
350 352
351 353 def __repr__(self):
352 354 return '<Fake completion object jedi has crashed>'
353 355
354 356
355 357 class Completion:
356 358 """
357 359 Completion object used and return by IPython completers.
358 360
359 .. warning:: Unstable
361 .. warning::
362
363 Unstable
360 364
361 365 This function is unstable, API may change without warning.
362 366 It will also raise unless use in proper context manager.
363 367
364 368 This act as a middle ground :any:`Completion` object between the
365 369 :any:`jedi.api.classes.Completion` object and the Prompt Toolkit completion
366 370 object. While Jedi need a lot of information about evaluator and how the
367 371 code should be ran/inspected, PromptToolkit (and other frontend) mostly
368 372 need user facing information.
369 373
370 374 - Which range should be replaced replaced by what.
371 375 - Some metadata (like completion type), or meta information to displayed to
372 376 the use user.
373 377
374 378 For debugging purpose we can also store the origin of the completion (``jedi``,
375 379 ``IPython.python_matches``, ``IPython.magics_matches``...).
376 380 """
377 381
378 382 __slots__ = ['start', 'end', 'text', 'type', 'signature', '_origin']
379 383
380 384 def __init__(self, start: int, end: int, text: str, *, type: str=None, _origin='', signature='') -> None:
381 385 warnings.warn("``Completion`` is a provisional API (as of IPython 6.0). "
382 386 "It may change without warnings. "
383 387 "Use in corresponding context manager.",
384 388 category=ProvisionalCompleterWarning, stacklevel=2)
385 389
386 390 self.start = start
387 391 self.end = end
388 392 self.text = text
389 393 self.type = type
390 394 self.signature = signature
391 395 self._origin = _origin
392 396
393 397 def __repr__(self):
394 398 return '<Completion start=%s end=%s text=%r type=%r, signature=%r,>' % \
395 399 (self.start, self.end, self.text, self.type or '?', self.signature or '?')
396 400
397 401 def __eq__(self, other)->Bool:
398 402 """
399 403 Equality and hash do not hash the type (as some completer may not be
400 404 able to infer the type), but are use to (partially) de-duplicate
401 405 completion.
402 406
403 407 Completely de-duplicating completion is a bit tricker that just
404 408 comparing as it depends on surrounding text, which Completions are not
405 409 aware of.
406 410 """
407 411 return self.start == other.start and \
408 412 self.end == other.end and \
409 413 self.text == other.text
410 414
411 415 def __hash__(self):
412 416 return hash((self.start, self.end, self.text))
413 417
414 418
415 419 _IC = Iterable[Completion]
416 420
417 421
418 422 def _deduplicate_completions(text: str, completions: _IC)-> _IC:
419 423 """
420 424 Deduplicate a set of completions.
421 425
422 .. warning:: Unstable
426 .. warning::
427
428 Unstable
423 429
424 430 This function is unstable, API may change without warning.
425 431
426 432 Parameters
427 433 ----------
428 434 text: str
429 435 text that should be completed.
430 436 completions: Iterator[Completion]
431 437 iterator over the completions to deduplicate
432 438
433 439 Yields
434 440 ------
435 441 `Completions` objects
436 442 Completions coming from multiple sources, may be different but end up having
437 443 the same effect when applied to ``text``. If this is the case, this will
438 444 consider completions as equal and only emit the first encountered.
439 445 Not folded in `completions()` yet for debugging purpose, and to detect when
440 446 the IPython completer does return things that Jedi does not, but should be
441 447 at some point.
442 448 """
443 449 completions = list(completions)
444 450 if not completions:
445 451 return
446 452
447 453 new_start = min(c.start for c in completions)
448 454 new_end = max(c.end for c in completions)
449 455
450 456 seen = set()
451 457 for c in completions:
452 458 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
453 459 if new_text not in seen:
454 460 yield c
455 461 seen.add(new_text)
456 462
457 463
458 464 def rectify_completions(text: str, completions: _IC, *, _debug=False)->_IC:
459 465 """
460 466 Rectify a set of completions to all have the same ``start`` and ``end``
461 467
462 .. warning:: Unstable
468 .. warning::
469
470 Unstable
463 471
464 472 This function is unstable, API may change without warning.
465 473 It will also raise unless use in proper context manager.
466 474
467 475 Parameters
468 476 ----------
469 477 text: str
470 478 text that should be completed.
471 479 completions: Iterator[Completion]
472 480 iterator over the completions to rectify
473 481
474 482 Notes
475 483 -----
476 484 :any:`jedi.api.classes.Completion` s returned by Jedi may not have the same start and end, though
477 485 the Jupyter Protocol requires them to behave like so. This will readjust
478 486 the completion to have the same ``start`` and ``end`` by padding both
479 487 extremities with surrounding text.
480 488
481 489 During stabilisation should support a ``_debug`` option to log which
482 490 completion are return by the IPython completer and not found in Jedi in
483 491 order to make upstream bug report.
484 492 """
485 493 warnings.warn("`rectify_completions` is a provisional API (as of IPython 6.0). "
486 494 "It may change without warnings. "
487 495 "Use in corresponding context manager.",
488 496 category=ProvisionalCompleterWarning, stacklevel=2)
489 497
490 498 completions = list(completions)
491 499 if not completions:
492 500 return
493 501 starts = (c.start for c in completions)
494 502 ends = (c.end for c in completions)
495 503
496 504 new_start = min(starts)
497 505 new_end = max(ends)
498 506
499 507 seen_jedi = set()
500 508 seen_python_matches = set()
501 509 for c in completions:
502 510 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
503 511 if c._origin == 'jedi':
504 512 seen_jedi.add(new_text)
505 513 elif c._origin == 'IPCompleter.python_matches':
506 514 seen_python_matches.add(new_text)
507 515 yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin, signature=c.signature)
508 516 diff = seen_python_matches.difference(seen_jedi)
509 517 if diff and _debug:
510 518 print('IPython.python matches have extras:', diff)
511 519
512 520
513 521 if sys.platform == 'win32':
514 522 DELIMS = ' \t\n`!@#$^&*()=+[{]}|;\'",<>?'
515 523 else:
516 524 DELIMS = ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?'
517 525
518 526 GREEDY_DELIMS = ' =\r\n'
519 527
520 528
521 529 class CompletionSplitter(object):
522 530 """An object to split an input line in a manner similar to readline.
523 531
524 532 By having our own implementation, we can expose readline-like completion in
525 533 a uniform manner to all frontends. This object only needs to be given the
526 534 line of text to be split and the cursor position on said line, and it
527 535 returns the 'word' to be completed on at the cursor after splitting the
528 536 entire line.
529 537
530 538 What characters are used as splitting delimiters can be controlled by
531 539 setting the ``delims`` attribute (this is a property that internally
532 540 automatically builds the necessary regular expression)"""
533 541
534 542 # Private interface
535 543
536 544 # A string of delimiter characters. The default value makes sense for
537 545 # IPython's most typical usage patterns.
538 546 _delims = DELIMS
539 547
540 548 # The expression (a normal string) to be compiled into a regular expression
541 549 # for actual splitting. We store it as an attribute mostly for ease of
542 550 # debugging, since this type of code can be so tricky to debug.
543 551 _delim_expr = None
544 552
545 553 # The regular expression that does the actual splitting
546 554 _delim_re = None
547 555
548 556 def __init__(self, delims=None):
549 557 delims = CompletionSplitter._delims if delims is None else delims
550 558 self.delims = delims
551 559
552 560 @property
553 561 def delims(self):
554 562 """Return the string of delimiter characters."""
555 563 return self._delims
556 564
557 565 @delims.setter
558 566 def delims(self, delims):
559 567 """Set the delimiters for line splitting."""
560 568 expr = '[' + ''.join('\\'+ c for c in delims) + ']'
561 569 self._delim_re = re.compile(expr)
562 570 self._delims = delims
563 571 self._delim_expr = expr
564 572
565 573 def split_line(self, line, cursor_pos=None):
566 574 """Split a line of text with a cursor at the given position.
567 575 """
568 576 l = line if cursor_pos is None else line[:cursor_pos]
569 577 return self._delim_re.split(l)[-1]
570 578
571 579
572 580
573 581 class Completer(Configurable):
574 582
575 583 greedy = Bool(False,
576 584 help="""Activate greedy completion
577 585 PENDING DEPRECTION. this is now mostly taken care of with Jedi.
578 586
579 587 This will enable completion on elements of lists, results of function calls, etc.,
580 588 but can be unsafe because the code is actually evaluated on TAB.
581 589 """
582 590 ).tag(config=True)
583 591
584 592 use_jedi = Bool(default_value=JEDI_INSTALLED,
585 593 help="Experimental: Use Jedi to generate autocompletions. "
586 594 "Default to True if jedi is installed.").tag(config=True)
587 595
588 596 jedi_compute_type_timeout = Int(default_value=400,
589 597 help="""Experimental: restrict time (in milliseconds) during which Jedi can compute types.
590 598 Set to 0 to stop computing types. Non-zero value lower than 100ms may hurt
591 599 performance by preventing jedi to build its cache.
592 600 """).tag(config=True)
593 601
594 602 debug = Bool(default_value=False,
595 603 help='Enable debug for the Completer. Mostly print extra '
596 604 'information for experimental jedi integration.')\
597 605 .tag(config=True)
598 606
599 607 backslash_combining_completions = Bool(True,
600 608 help="Enable unicode completions, e.g. \\alpha<tab> . "
601 609 "Includes completion of latex commands, unicode names, and expanding "
602 610 "unicode characters back to latex commands.").tag(config=True)
603 611
604 612
605 613
606 614 def __init__(self, namespace=None, global_namespace=None, **kwargs):
607 615 """Create a new completer for the command line.
608 616
609 617 Completer(namespace=ns, global_namespace=ns2) -> completer instance.
610 618
611 619 If unspecified, the default namespace where completions are performed
612 620 is __main__ (technically, __main__.__dict__). Namespaces should be
613 621 given as dictionaries.
614 622
615 623 An optional second namespace can be given. This allows the completer
616 624 to handle cases where both the local and global scopes need to be
617 625 distinguished.
618 626 """
619 627
620 628 # Don't bind to namespace quite yet, but flag whether the user wants a
621 629 # specific namespace or to use __main__.__dict__. This will allow us
622 630 # to bind to __main__.__dict__ at completion time, not now.
623 631 if namespace is None:
624 632 self.use_main_ns = True
625 633 else:
626 634 self.use_main_ns = False
627 635 self.namespace = namespace
628 636
629 637 # The global namespace, if given, can be bound directly
630 638 if global_namespace is None:
631 639 self.global_namespace = {}
632 640 else:
633 641 self.global_namespace = global_namespace
634 642
635 643 self.custom_matchers = []
636 644
637 645 super(Completer, self).__init__(**kwargs)
638 646
639 647 def complete(self, text, state):
640 648 """Return the next possible completion for 'text'.
641 649
642 650 This is called successively with state == 0, 1, 2, ... until it
643 651 returns None. The completion should begin with 'text'.
644 652
645 653 """
646 654 if self.use_main_ns:
647 655 self.namespace = __main__.__dict__
648 656
649 657 if state == 0:
650 658 if "." in text:
651 659 self.matches = self.attr_matches(text)
652 660 else:
653 661 self.matches = self.global_matches(text)
654 662 try:
655 663 return self.matches[state]
656 664 except IndexError:
657 665 return None
658 666
659 667 def global_matches(self, text):
660 668 """Compute matches when text is a simple name.
661 669
662 670 Return a list of all keywords, built-in functions and names currently
663 671 defined in self.namespace or self.global_namespace that match.
664 672
665 673 """
666 674 matches = []
667 675 match_append = matches.append
668 676 n = len(text)
669 677 for lst in [keyword.kwlist,
670 678 builtin_mod.__dict__.keys(),
671 679 self.namespace.keys(),
672 680 self.global_namespace.keys()]:
673 681 for word in lst:
674 682 if word[:n] == text and word != "__builtins__":
675 683 match_append(word)
676 684
677 685 snake_case_re = re.compile(r"[^_]+(_[^_]+)+?\Z")
678 686 for lst in [self.namespace.keys(),
679 687 self.global_namespace.keys()]:
680 688 shortened = {"_".join([sub[0] for sub in word.split('_')]) : word
681 689 for word in lst if snake_case_re.match(word)}
682 690 for word in shortened.keys():
683 691 if word[:n] == text and word != "__builtins__":
684 692 match_append(shortened[word])
685 693 return matches
686 694
687 695 def attr_matches(self, text):
688 696 """Compute matches when text contains a dot.
689 697
690 698 Assuming the text is of the form NAME.NAME....[NAME], and is
691 699 evaluatable in self.namespace or self.global_namespace, it will be
692 700 evaluated and its attributes (as revealed by dir()) are used as
693 701 possible completions. (For class instances, class members are
694 702 also considered.)
695 703
696 704 WARNING: this can still invoke arbitrary C code, if an object
697 705 with a __getattr__ hook is evaluated.
698 706
699 707 """
700 708
701 709 # Another option, seems to work great. Catches things like ''.<tab>
702 710 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)
703 711
704 712 if m:
705 713 expr, attr = m.group(1, 3)
706 714 elif self.greedy:
707 715 m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer)
708 716 if not m2:
709 717 return []
710 718 expr, attr = m2.group(1,2)
711 719 else:
712 720 return []
713 721
714 722 try:
715 723 obj = eval(expr, self.namespace)
716 724 except:
717 725 try:
718 726 obj = eval(expr, self.global_namespace)
719 727 except:
720 728 return []
721 729
722 730 if self.limit_to__all__ and hasattr(obj, '__all__'):
723 731 words = get__all__entries(obj)
724 732 else:
725 733 words = dir2(obj)
726 734
727 735 try:
728 736 words = generics.complete_object(obj, words)
729 737 except TryNext:
730 738 pass
731 739 except AssertionError:
732 740 raise
733 741 except Exception:
734 742 # Silence errors from completion function
735 743 #raise # dbg
736 744 pass
737 745 # Build match list to return
738 746 n = len(attr)
739 747 return [u"%s.%s" % (expr, w) for w in words if w[:n] == attr ]
740 748
741 749
742 750 def get__all__entries(obj):
743 751 """returns the strings in the __all__ attribute"""
744 752 try:
745 753 words = getattr(obj, '__all__')
746 754 except:
747 755 return []
748 756
749 757 return [w for w in words if isinstance(w, str)]
750 758
751 759
752 760 def match_dict_keys(keys: List[Union[str, bytes, Tuple[Union[str, bytes]]]], prefix: str, delims: str,
753 761 extra_prefix: Optional[Tuple[str, bytes]]=None) -> Tuple[str, int, List[str]]:
754 762 """Used by dict_key_matches, matching the prefix to a list of keys
755 763
756 764 Parameters
757 765 ----------
758 766 keys:
759 767 list of keys in dictionary currently being completed.
760 768 prefix:
761 769 Part of the text already typed by the user. E.g. `mydict[b'fo`
762 770 delims:
763 771 String of delimiters to consider when finding the current key.
764 772 extra_prefix: optional
765 773 Part of the text already typed in multi-key index cases. E.g. for
766 774 `mydict['foo', "bar", 'b`, this would be `('foo', 'bar')`.
767 775
768 776 Returns
769 777 -------
770 778 A tuple of three elements: ``quote``, ``token_start``, ``matched``, with
771 779 ``quote`` being the quote that need to be used to close current string.
772 780 ``token_start`` the position where the replacement should start occurring,
773 781 ``matches`` a list of replacement/completion
774 782
775 783 """
776 784 prefix_tuple = extra_prefix if extra_prefix else ()
777 785 Nprefix = len(prefix_tuple)
778 786 def filter_prefix_tuple(key):
779 787 # Reject too short keys
780 788 if len(key) <= Nprefix:
781 789 return False
782 790 # Reject keys with non str/bytes in it
783 791 for k in key:
784 792 if not isinstance(k, (str, bytes)):
785 793 return False
786 794 # Reject keys that do not match the prefix
787 795 for k, pt in zip(key, prefix_tuple):
788 796 if k != pt:
789 797 return False
790 798 # All checks passed!
791 799 return True
792 800
793 801 filtered_keys:List[Union[str,bytes]] = []
794 802 def _add_to_filtered_keys(key):
795 803 if isinstance(key, (str, bytes)):
796 804 filtered_keys.append(key)
797 805
798 806 for k in keys:
799 807 if isinstance(k, tuple):
800 808 if filter_prefix_tuple(k):
801 809 _add_to_filtered_keys(k[Nprefix])
802 810 else:
803 811 _add_to_filtered_keys(k)
804 812
805 813 if not prefix:
806 814 return '', 0, [repr(k) for k in filtered_keys]
807 815 quote_match = re.search('["\']', prefix)
808 816 assert quote_match is not None # silence mypy
809 817 quote = quote_match.group()
810 818 try:
811 819 prefix_str = eval(prefix + quote, {})
812 820 except Exception:
813 821 return '', 0, []
814 822
815 823 pattern = '[^' + ''.join('\\' + c for c in delims) + ']*$'
816 824 token_match = re.search(pattern, prefix, re.UNICODE)
817 825 assert token_match is not None # silence mypy
818 826 token_start = token_match.start()
819 827 token_prefix = token_match.group()
820 828
821 829 matched:List[str] = []
822 830 for key in filtered_keys:
823 831 try:
824 832 if not key.startswith(prefix_str):
825 833 continue
826 834 except (AttributeError, TypeError, UnicodeError):
827 835 # Python 3+ TypeError on b'a'.startswith('a') or vice-versa
828 836 continue
829 837
830 838 # reformat remainder of key to begin with prefix
831 839 rem = key[len(prefix_str):]
832 840 # force repr wrapped in '
833 841 rem_repr = repr(rem + '"') if isinstance(rem, str) else repr(rem + b'"')
834 842 rem_repr = rem_repr[1 + rem_repr.index("'"):-2]
835 843 if quote == '"':
836 844 # The entered prefix is quoted with ",
837 845 # but the match is quoted with '.
838 846 # A contained " hence needs escaping for comparison:
839 847 rem_repr = rem_repr.replace('"', '\\"')
840 848
841 849 # then reinsert prefix from start of token
842 850 matched.append('%s%s' % (token_prefix, rem_repr))
843 851 return quote, token_start, matched
844 852
845 853
846 854 def cursor_to_position(text:str, line:int, column:int)->int:
847 855 """
848 856 Convert the (line,column) position of the cursor in text to an offset in a
849 857 string.
850 858
851 859 Parameters
852 860 ----------
853 861 text : str
854 862 The text in which to calculate the cursor offset
855 863 line : int
856 864 Line of the cursor; 0-indexed
857 865 column : int
858 866 Column of the cursor 0-indexed
859 867
860 868 Returns
861 869 -------
862 870 Position of the cursor in ``text``, 0-indexed.
863 871
864 872 See Also
865 873 --------
866 874 position_to_cursor : reciprocal of this function
867 875
868 876 """
869 877 lines = text.split('\n')
870 878 assert line <= len(lines), '{} <= {}'.format(str(line), str(len(lines)))
871 879
872 880 return sum(len(l) + 1 for l in lines[:line]) + column
873 881
874 882 def position_to_cursor(text:str, offset:int)->Tuple[int, int]:
875 883 """
876 884 Convert the position of the cursor in text (0 indexed) to a line
877 885 number(0-indexed) and a column number (0-indexed) pair
878 886
879 887 Position should be a valid position in ``text``.
880 888
881 889 Parameters
882 890 ----------
883 891 text : str
884 892 The text in which to calculate the cursor offset
885 893 offset : int
886 894 Position of the cursor in ``text``, 0-indexed.
887 895
888 896 Returns
889 897 -------
890 898 (line, column) : (int, int)
891 899 Line of the cursor; 0-indexed, column of the cursor 0-indexed
892 900
893 901 See Also
894 902 --------
895 903 cursor_to_position : reciprocal of this function
896 904
897 905 """
898 906
899 907 assert 0 <= offset <= len(text) , "0 <= %s <= %s" % (offset , len(text))
900 908
901 909 before = text[:offset]
902 910 blines = before.split('\n') # ! splitnes trim trailing \n
903 911 line = before.count('\n')
904 912 col = len(blines[-1])
905 913 return line, col
906 914
907 915
908 916 def _safe_isinstance(obj, module, class_name):
909 917 """Checks if obj is an instance of module.class_name if loaded
910 918 """
911 919 return (module in sys.modules and
912 920 isinstance(obj, getattr(import_module(module), class_name)))
913 921
914 922 def back_unicode_name_matches(text:str) -> Tuple[str, Sequence[str]]:
915 923 """Match Unicode characters back to Unicode name
916 924
917 925 This does ``β˜ƒ`` -> ``\\snowman``
918 926
919 927 Note that snowman is not a valid python3 combining character but will be expanded.
920 928 Though it will not recombine back to the snowman character by the completion machinery.
921 929
922 930 This will not either back-complete standard sequences like \\n, \\b ...
923 931
924 932 Returns
925 933 =======
926 934
927 935 Return a tuple with two elements:
928 936
929 937 - The Unicode character that was matched (preceded with a backslash), or
930 938 empty string,
931 939 - a sequence (of 1), name for the match Unicode character, preceded by
932 940 backslash, or empty if no match.
933 941
934 942 """
935 943 if len(text)<2:
936 944 return '', ()
937 945 maybe_slash = text[-2]
938 946 if maybe_slash != '\\':
939 947 return '', ()
940 948
941 949 char = text[-1]
942 950 # no expand on quote for completion in strings.
943 951 # nor backcomplete standard ascii keys
944 952 if char in string.ascii_letters or char in ('"',"'"):
945 953 return '', ()
946 954 try :
947 955 unic = unicodedata.name(char)
948 956 return '\\'+char,('\\'+unic,)
949 957 except KeyError:
950 958 pass
951 959 return '', ()
952 960
953 961 def back_latex_name_matches(text:str) -> Tuple[str, Sequence[str]] :
954 962 """Match latex characters back to unicode name
955 963
956 964 This does ``\\β„΅`` -> ``\\aleph``
957 965
958 966 """
959 967 if len(text)<2:
960 968 return '', ()
961 969 maybe_slash = text[-2]
962 970 if maybe_slash != '\\':
963 971 return '', ()
964 972
965 973
966 974 char = text[-1]
967 975 # no expand on quote for completion in strings.
968 976 # nor backcomplete standard ascii keys
969 977 if char in string.ascii_letters or char in ('"',"'"):
970 978 return '', ()
971 979 try :
972 980 latex = reverse_latex_symbol[char]
973 981 # '\\' replace the \ as well
974 982 return '\\'+char,[latex]
975 983 except KeyError:
976 984 pass
977 985 return '', ()
978 986
979 987
980 988 def _formatparamchildren(parameter) -> str:
981 989 """
982 990 Get parameter name and value from Jedi Private API
983 991
984 992 Jedi does not expose a simple way to get `param=value` from its API.
985 993
986 994 Parameters
987 995 ----------
988 996 parameter:
989 997 Jedi's function `Param`
990 998
991 999 Returns
992 1000 -------
993 1001 A string like 'a', 'b=1', '*args', '**kwargs'
994 1002
995 1003 """
996 1004 description = parameter.description
997 1005 if not description.startswith('param '):
998 1006 raise ValueError('Jedi function parameter description have change format.'
999 1007 'Expected "param ...", found %r".' % description)
1000 1008 return description[6:]
1001 1009
1002 1010 def _make_signature(completion)-> str:
1003 1011 """
1004 1012 Make the signature from a jedi completion
1005 1013
1006 1014 Parameters
1007 1015 ----------
1008 1016 completion: jedi.Completion
1009 1017 object does not complete a function type
1010 1018
1011 1019 Returns
1012 1020 -------
1013 1021 a string consisting of the function signature, with the parenthesis but
1014 1022 without the function name. example:
1015 1023 `(a, *args, b=1, **kwargs)`
1016 1024
1017 1025 """
1018 1026
1019 1027 # it looks like this might work on jedi 0.17
1020 1028 if hasattr(completion, 'get_signatures'):
1021 1029 signatures = completion.get_signatures()
1022 1030 if not signatures:
1023 1031 return '(?)'
1024 1032
1025 1033 c0 = completion.get_signatures()[0]
1026 1034 return '('+c0.to_string().split('(', maxsplit=1)[1]
1027 1035
1028 1036 return '(%s)'% ', '.join([f for f in (_formatparamchildren(p) for signature in completion.get_signatures()
1029 1037 for p in signature.defined_names()) if f])
1030 1038
1031 1039
1032 1040 class _CompleteResult(NamedTuple):
1033 1041 matched_text : str
1034 1042 matches: Sequence[str]
1035 1043 matches_origin: Sequence[str]
1036 1044 jedi_matches: Any
1037 1045
1038 1046
1039 1047 class IPCompleter(Completer):
1040 1048 """Extension of the completer class with IPython-specific features"""
1041 1049
1042 1050 __dict_key_regexps: Optional[Dict[bool,Pattern]] = None
1043 1051
1044 1052 @observe('greedy')
1045 1053 def _greedy_changed(self, change):
1046 1054 """update the splitter and readline delims when greedy is changed"""
1047 1055 if change['new']:
1048 1056 self.splitter.delims = GREEDY_DELIMS
1049 1057 else:
1050 1058 self.splitter.delims = DELIMS
1051 1059
1052 1060 dict_keys_only = Bool(False,
1053 1061 help="""Whether to show dict key matches only""")
1054 1062
1055 1063 merge_completions = Bool(True,
1056 1064 help="""Whether to merge completion results into a single list
1057 1065
1058 1066 If False, only the completion results from the first non-empty
1059 1067 completer will be returned.
1060 1068 """
1061 1069 ).tag(config=True)
1062 1070 omit__names = Enum((0,1,2), default_value=2,
1063 1071 help="""Instruct the completer to omit private method names
1064 1072
1065 1073 Specifically, when completing on ``object.<tab>``.
1066 1074
1067 1075 When 2 [default]: all names that start with '_' will be excluded.
1068 1076
1069 1077 When 1: all 'magic' names (``__foo__``) will be excluded.
1070 1078
1071 1079 When 0: nothing will be excluded.
1072 1080 """
1073 1081 ).tag(config=True)
1074 1082 limit_to__all__ = Bool(False,
1075 1083 help="""
1076 1084 DEPRECATED as of version 5.0.
1077 1085
1078 1086 Instruct the completer to use __all__ for the completion
1079 1087
1080 1088 Specifically, when completing on ``object.<tab>``.
1081 1089
1082 1090 When True: only those names in obj.__all__ will be included.
1083 1091
1084 1092 When False [default]: the __all__ attribute is ignored
1085 1093 """,
1086 1094 ).tag(config=True)
1087 1095
1088 1096 profile_completions = Bool(
1089 1097 default_value=False,
1090 1098 help="If True, emit profiling data for completion subsystem using cProfile."
1091 1099 ).tag(config=True)
1092 1100
1093 1101 profiler_output_dir = Unicode(
1094 1102 default_value=".completion_profiles",
1095 1103 help="Template for path at which to output profile data for completions."
1096 1104 ).tag(config=True)
1097 1105
1098 1106 @observe('limit_to__all__')
1099 1107 def _limit_to_all_changed(self, change):
1100 1108 warnings.warn('`IPython.core.IPCompleter.limit_to__all__` configuration '
1101 1109 'value has been deprecated since IPython 5.0, will be made to have '
1102 1110 'no effects and then removed in future version of IPython.',
1103 1111 UserWarning)
1104 1112
1105 1113 def __init__(self, shell=None, namespace=None, global_namespace=None,
1106 1114 use_readline=_deprecation_readline_sentinel, config=None, **kwargs):
1107 1115 """IPCompleter() -> completer
1108 1116
1109 1117 Return a completer object.
1110 1118
1111 1119 Parameters
1112 1120 ----------
1113 1121 shell
1114 1122 a pointer to the ipython shell itself. This is needed
1115 1123 because this completer knows about magic functions, and those can
1116 1124 only be accessed via the ipython instance.
1117 1125 namespace : dict, optional
1118 1126 an optional dict where completions are performed.
1119 1127 global_namespace : dict, optional
1120 1128 secondary optional dict for completions, to
1121 1129 handle cases (such as IPython embedded inside functions) where
1122 1130 both Python scopes are visible.
1123 1131 use_readline : bool, optional
1124 1132 DEPRECATED, ignored since IPython 6.0, will have no effects
1125 1133 """
1126 1134
1127 1135 self.magic_escape = ESC_MAGIC
1128 1136 self.splitter = CompletionSplitter()
1129 1137
1130 1138 if use_readline is not _deprecation_readline_sentinel:
1131 1139 warnings.warn('The `use_readline` parameter is deprecated and ignored since IPython 6.0.',
1132 1140 DeprecationWarning, stacklevel=2)
1133 1141
1134 1142 # _greedy_changed() depends on splitter and readline being defined:
1135 1143 Completer.__init__(self, namespace=namespace, global_namespace=global_namespace,
1136 1144 config=config, **kwargs)
1137 1145
1138 1146 # List where completion matches will be stored
1139 1147 self.matches = []
1140 1148 self.shell = shell
1141 1149 # Regexp to split filenames with spaces in them
1142 1150 self.space_name_re = re.compile(r'([^\\] )')
1143 1151 # Hold a local ref. to glob.glob for speed
1144 1152 self.glob = glob.glob
1145 1153
1146 1154 # Determine if we are running on 'dumb' terminals, like (X)Emacs
1147 1155 # buffers, to avoid completion problems.
1148 1156 term = os.environ.get('TERM','xterm')
1149 1157 self.dumb_terminal = term in ['dumb','emacs']
1150 1158
1151 1159 # Special handling of backslashes needed in win32 platforms
1152 1160 if sys.platform == "win32":
1153 1161 self.clean_glob = self._clean_glob_win32
1154 1162 else:
1155 1163 self.clean_glob = self._clean_glob
1156 1164
1157 1165 #regexp to parse docstring for function signature
1158 1166 self.docstring_sig_re = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
1159 1167 self.docstring_kwd_re = re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
1160 1168 #use this if positional argument name is also needed
1161 1169 #= re.compile(r'[\s|\[]*(\w+)(?:\s*=?\s*.*)')
1162 1170
1163 1171 self.magic_arg_matchers = [
1164 1172 self.magic_config_matches,
1165 1173 self.magic_color_matches,
1166 1174 ]
1167 1175
1168 1176 # This is set externally by InteractiveShell
1169 1177 self.custom_completers = None
1170 1178
1171 1179 # This is a list of names of unicode characters that can be completed
1172 1180 # into their corresponding unicode value. The list is large, so we
1173 1181 # laziliy initialize it on first use. Consuming code should access this
1174 1182 # attribute through the `@unicode_names` property.
1175 1183 self._unicode_names = None
1176 1184
1177 1185 @property
1178 1186 def matchers(self) -> List[Any]:
1179 1187 """All active matcher routines for completion"""
1180 1188 if self.dict_keys_only:
1181 1189 return [self.dict_key_matches]
1182 1190
1183 1191 if self.use_jedi:
1184 1192 return [
1185 1193 *self.custom_matchers,
1186 1194 self.file_matches,
1187 1195 self.magic_matches,
1188 1196 self.dict_key_matches,
1189 1197 ]
1190 1198 else:
1191 1199 return [
1192 1200 *self.custom_matchers,
1193 1201 self.python_matches,
1194 1202 self.file_matches,
1195 1203 self.magic_matches,
1196 1204 self.python_func_kw_matches,
1197 1205 self.dict_key_matches,
1198 1206 ]
1199 1207
1200 1208 def all_completions(self, text:str) -> List[str]:
1201 1209 """
1202 1210 Wrapper around the completion methods for the benefit of emacs.
1203 1211 """
1204 1212 prefix = text.rpartition('.')[0]
1205 1213 with provisionalcompleter():
1206 1214 return ['.'.join([prefix, c.text]) if prefix and self.use_jedi else c.text
1207 1215 for c in self.completions(text, len(text))]
1208 1216
1209 1217 return self.complete(text)[1]
1210 1218
1211 1219 def _clean_glob(self, text:str):
1212 1220 return self.glob("%s*" % text)
1213 1221
1214 1222 def _clean_glob_win32(self, text:str):
1215 1223 return [f.replace("\\","/")
1216 1224 for f in self.glob("%s*" % text)]
1217 1225
1218 1226 def file_matches(self, text:str)->List[str]:
1219 1227 """Match filenames, expanding ~USER type strings.
1220 1228
1221 1229 Most of the seemingly convoluted logic in this completer is an
1222 1230 attempt to handle filenames with spaces in them. And yet it's not
1223 1231 quite perfect, because Python's readline doesn't expose all of the
1224 1232 GNU readline details needed for this to be done correctly.
1225 1233
1226 1234 For a filename with a space in it, the printed completions will be
1227 1235 only the parts after what's already been typed (instead of the
1228 1236 full completions, as is normally done). I don't think with the
1229 1237 current (as of Python 2.3) Python readline it's possible to do
1230 1238 better."""
1231 1239
1232 1240 # chars that require escaping with backslash - i.e. chars
1233 1241 # that readline treats incorrectly as delimiters, but we
1234 1242 # don't want to treat as delimiters in filename matching
1235 1243 # when escaped with backslash
1236 1244 if text.startswith('!'):
1237 1245 text = text[1:]
1238 1246 text_prefix = u'!'
1239 1247 else:
1240 1248 text_prefix = u''
1241 1249
1242 1250 text_until_cursor = self.text_until_cursor
1243 1251 # track strings with open quotes
1244 1252 open_quotes = has_open_quotes(text_until_cursor)
1245 1253
1246 1254 if '(' in text_until_cursor or '[' in text_until_cursor:
1247 1255 lsplit = text
1248 1256 else:
1249 1257 try:
1250 1258 # arg_split ~ shlex.split, but with unicode bugs fixed by us
1251 1259 lsplit = arg_split(text_until_cursor)[-1]
1252 1260 except ValueError:
1253 1261 # typically an unmatched ", or backslash without escaped char.
1254 1262 if open_quotes:
1255 1263 lsplit = text_until_cursor.split(open_quotes)[-1]
1256 1264 else:
1257 1265 return []
1258 1266 except IndexError:
1259 1267 # tab pressed on empty line
1260 1268 lsplit = ""
1261 1269
1262 1270 if not open_quotes and lsplit != protect_filename(lsplit):
1263 1271 # if protectables are found, do matching on the whole escaped name
1264 1272 has_protectables = True
1265 1273 text0,text = text,lsplit
1266 1274 else:
1267 1275 has_protectables = False
1268 1276 text = os.path.expanduser(text)
1269 1277
1270 1278 if text == "":
1271 1279 return [text_prefix + protect_filename(f) for f in self.glob("*")]
1272 1280
1273 1281 # Compute the matches from the filesystem
1274 1282 if sys.platform == 'win32':
1275 1283 m0 = self.clean_glob(text)
1276 1284 else:
1277 1285 m0 = self.clean_glob(text.replace('\\', ''))
1278 1286
1279 1287 if has_protectables:
1280 1288 # If we had protectables, we need to revert our changes to the
1281 1289 # beginning of filename so that we don't double-write the part
1282 1290 # of the filename we have so far
1283 1291 len_lsplit = len(lsplit)
1284 1292 matches = [text_prefix + text0 +
1285 1293 protect_filename(f[len_lsplit:]) for f in m0]
1286 1294 else:
1287 1295 if open_quotes:
1288 1296 # if we have a string with an open quote, we don't need to
1289 1297 # protect the names beyond the quote (and we _shouldn't_, as
1290 1298 # it would cause bugs when the filesystem call is made).
1291 1299 matches = m0 if sys.platform == "win32" else\
1292 1300 [protect_filename(f, open_quotes) for f in m0]
1293 1301 else:
1294 1302 matches = [text_prefix +
1295 1303 protect_filename(f) for f in m0]
1296 1304
1297 1305 # Mark directories in input list by appending '/' to their names.
1298 1306 return [x+'/' if os.path.isdir(x) else x for x in matches]
1299 1307
1300 1308 def magic_matches(self, text:str):
1301 1309 """Match magics"""
1302 1310 # Get all shell magics now rather than statically, so magics loaded at
1303 1311 # runtime show up too.
1304 1312 lsm = self.shell.magics_manager.lsmagic()
1305 1313 line_magics = lsm['line']
1306 1314 cell_magics = lsm['cell']
1307 1315 pre = self.magic_escape
1308 1316 pre2 = pre+pre
1309 1317
1310 1318 explicit_magic = text.startswith(pre)
1311 1319
1312 1320 # Completion logic:
1313 1321 # - user gives %%: only do cell magics
1314 1322 # - user gives %: do both line and cell magics
1315 1323 # - no prefix: do both
1316 1324 # In other words, line magics are skipped if the user gives %% explicitly
1317 1325 #
1318 1326 # We also exclude magics that match any currently visible names:
1319 1327 # https://github.com/ipython/ipython/issues/4877, unless the user has
1320 1328 # typed a %:
1321 1329 # https://github.com/ipython/ipython/issues/10754
1322 1330 bare_text = text.lstrip(pre)
1323 1331 global_matches = self.global_matches(bare_text)
1324 1332 if not explicit_magic:
1325 1333 def matches(magic):
1326 1334 """
1327 1335 Filter magics, in particular remove magics that match
1328 1336 a name present in global namespace.
1329 1337 """
1330 1338 return ( magic.startswith(bare_text) and
1331 1339 magic not in global_matches )
1332 1340 else:
1333 1341 def matches(magic):
1334 1342 return magic.startswith(bare_text)
1335 1343
1336 1344 comp = [ pre2+m for m in cell_magics if matches(m)]
1337 1345 if not text.startswith(pre2):
1338 1346 comp += [ pre+m for m in line_magics if matches(m)]
1339 1347
1340 1348 return comp
1341 1349
1342 1350 def magic_config_matches(self, text:str) -> List[str]:
1343 1351 """ Match class names and attributes for %config magic """
1344 1352 texts = text.strip().split()
1345 1353
1346 1354 if len(texts) > 0 and (texts[0] == 'config' or texts[0] == '%config'):
1347 1355 # get all configuration classes
1348 1356 classes = sorted(set([ c for c in self.shell.configurables
1349 1357 if c.__class__.class_traits(config=True)
1350 1358 ]), key=lambda x: x.__class__.__name__)
1351 1359 classnames = [ c.__class__.__name__ for c in classes ]
1352 1360
1353 1361 # return all classnames if config or %config is given
1354 1362 if len(texts) == 1:
1355 1363 return classnames
1356 1364
1357 1365 # match classname
1358 1366 classname_texts = texts[1].split('.')
1359 1367 classname = classname_texts[0]
1360 1368 classname_matches = [ c for c in classnames
1361 1369 if c.startswith(classname) ]
1362 1370
1363 1371 # return matched classes or the matched class with attributes
1364 1372 if texts[1].find('.') < 0:
1365 1373 return classname_matches
1366 1374 elif len(classname_matches) == 1 and \
1367 1375 classname_matches[0] == classname:
1368 1376 cls = classes[classnames.index(classname)].__class__
1369 1377 help = cls.class_get_help()
1370 1378 # strip leading '--' from cl-args:
1371 1379 help = re.sub(re.compile(r'^--', re.MULTILINE), '', help)
1372 1380 return [ attr.split('=')[0]
1373 1381 for attr in help.strip().splitlines()
1374 1382 if attr.startswith(texts[1]) ]
1375 1383 return []
1376 1384
1377 1385 def magic_color_matches(self, text:str) -> List[str] :
1378 1386 """ Match color schemes for %colors magic"""
1379 1387 texts = text.split()
1380 1388 if text.endswith(' '):
1381 1389 # .split() strips off the trailing whitespace. Add '' back
1382 1390 # so that: '%colors ' -> ['%colors', '']
1383 1391 texts.append('')
1384 1392
1385 1393 if len(texts) == 2 and (texts[0] == 'colors' or texts[0] == '%colors'):
1386 1394 prefix = texts[1]
1387 1395 return [ color for color in InspectColors.keys()
1388 1396 if color.startswith(prefix) ]
1389 1397 return []
1390 1398
1391 1399 def _jedi_matches(self, cursor_column:int, cursor_line:int, text:str) -> Iterable[Any]:
1392 1400 """
1393 1401 Return a list of :any:`jedi.api.Completions` object from a ``text`` and
1394 1402 cursor position.
1395 1403
1396 1404 Parameters
1397 1405 ----------
1398 1406 cursor_column : int
1399 1407 column position of the cursor in ``text``, 0-indexed.
1400 1408 cursor_line : int
1401 1409 line position of the cursor in ``text``, 0-indexed
1402 1410 text : str
1403 1411 text to complete
1404 1412
1405 1413 Notes
1406 1414 -----
1407 1415 If ``IPCompleter.debug`` is ``True`` may return a :any:`_FakeJediCompletion`
1408 1416 object containing a string with the Jedi debug information attached.
1409 1417 """
1410 1418 namespaces = [self.namespace]
1411 1419 if self.global_namespace is not None:
1412 1420 namespaces.append(self.global_namespace)
1413 1421
1414 1422 completion_filter = lambda x:x
1415 1423 offset = cursor_to_position(text, cursor_line, cursor_column)
1416 1424 # filter output if we are completing for object members
1417 1425 if offset:
1418 1426 pre = text[offset-1]
1419 1427 if pre == '.':
1420 1428 if self.omit__names == 2:
1421 1429 completion_filter = lambda c:not c.name.startswith('_')
1422 1430 elif self.omit__names == 1:
1423 1431 completion_filter = lambda c:not (c.name.startswith('__') and c.name.endswith('__'))
1424 1432 elif self.omit__names == 0:
1425 1433 completion_filter = lambda x:x
1426 1434 else:
1427 1435 raise ValueError("Don't understand self.omit__names == {}".format(self.omit__names))
1428 1436
1429 1437 interpreter = jedi.Interpreter(text[:offset], namespaces)
1430 1438 try_jedi = True
1431 1439
1432 1440 try:
1433 1441 # find the first token in the current tree -- if it is a ' or " then we are in a string
1434 1442 completing_string = False
1435 1443 try:
1436 1444 first_child = next(c for c in interpreter._get_module().tree_node.children if hasattr(c, 'value'))
1437 1445 except StopIteration:
1438 1446 pass
1439 1447 else:
1440 1448 # note the value may be ', ", or it may also be ''' or """, or
1441 1449 # in some cases, """what/you/typed..., but all of these are
1442 1450 # strings.
1443 1451 completing_string = len(first_child.value) > 0 and first_child.value[0] in {"'", '"'}
1444 1452
1445 1453 # if we are in a string jedi is likely not the right candidate for
1446 1454 # now. Skip it.
1447 1455 try_jedi = not completing_string
1448 1456 except Exception as e:
1449 1457 # many of things can go wrong, we are using private API just don't crash.
1450 1458 if self.debug:
1451 1459 print("Error detecting if completing a non-finished string :", e, '|')
1452 1460
1453 1461 if not try_jedi:
1454 1462 return []
1455 1463 try:
1456 1464 return filter(completion_filter, interpreter.complete(column=cursor_column, line=cursor_line + 1))
1457 1465 except Exception as e:
1458 1466 if self.debug:
1459 1467 return [_FakeJediCompletion('Oops Jedi has crashed, please report a bug with the following:\n"""\n%s\ns"""' % (e))]
1460 1468 else:
1461 1469 return []
1462 1470
1463 1471 def python_matches(self, text:str)->List[str]:
1464 1472 """Match attributes or global python names"""
1465 1473 if "." in text:
1466 1474 try:
1467 1475 matches = self.attr_matches(text)
1468 1476 if text.endswith('.') and self.omit__names:
1469 1477 if self.omit__names == 1:
1470 1478 # true if txt is _not_ a __ name, false otherwise:
1471 1479 no__name = (lambda txt:
1472 1480 re.match(r'.*\.__.*?__',txt) is None)
1473 1481 else:
1474 1482 # true if txt is _not_ a _ name, false otherwise:
1475 1483 no__name = (lambda txt:
1476 1484 re.match(r'\._.*?',txt[txt.rindex('.'):]) is None)
1477 1485 matches = filter(no__name, matches)
1478 1486 except NameError:
1479 1487 # catches <undefined attributes>.<tab>
1480 1488 matches = []
1481 1489 else:
1482 1490 matches = self.global_matches(text)
1483 1491 return matches
1484 1492
1485 1493 def _default_arguments_from_docstring(self, doc):
1486 1494 """Parse the first line of docstring for call signature.
1487 1495
1488 1496 Docstring should be of the form 'min(iterable[, key=func])\n'.
1489 1497 It can also parse cython docstring of the form
1490 1498 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)'.
1491 1499 """
1492 1500 if doc is None:
1493 1501 return []
1494 1502
1495 1503 #care only the firstline
1496 1504 line = doc.lstrip().splitlines()[0]
1497 1505
1498 1506 #p = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
1499 1507 #'min(iterable[, key=func])\n' -> 'iterable[, key=func]'
1500 1508 sig = self.docstring_sig_re.search(line)
1501 1509 if sig is None:
1502 1510 return []
1503 1511 # iterable[, key=func]' -> ['iterable[' ,' key=func]']
1504 1512 sig = sig.groups()[0].split(',')
1505 1513 ret = []
1506 1514 for s in sig:
1507 1515 #re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
1508 1516 ret += self.docstring_kwd_re.findall(s)
1509 1517 return ret
1510 1518
1511 1519 def _default_arguments(self, obj):
1512 1520 """Return the list of default arguments of obj if it is callable,
1513 1521 or empty list otherwise."""
1514 1522 call_obj = obj
1515 1523 ret = []
1516 1524 if inspect.isbuiltin(obj):
1517 1525 pass
1518 1526 elif not (inspect.isfunction(obj) or inspect.ismethod(obj)):
1519 1527 if inspect.isclass(obj):
1520 1528 #for cython embedsignature=True the constructor docstring
1521 1529 #belongs to the object itself not __init__
1522 1530 ret += self._default_arguments_from_docstring(
1523 1531 getattr(obj, '__doc__', ''))
1524 1532 # for classes, check for __init__,__new__
1525 1533 call_obj = (getattr(obj, '__init__', None) or
1526 1534 getattr(obj, '__new__', None))
1527 1535 # for all others, check if they are __call__able
1528 1536 elif hasattr(obj, '__call__'):
1529 1537 call_obj = obj.__call__
1530 1538 ret += self._default_arguments_from_docstring(
1531 1539 getattr(call_obj, '__doc__', ''))
1532 1540
1533 1541 _keeps = (inspect.Parameter.KEYWORD_ONLY,
1534 1542 inspect.Parameter.POSITIONAL_OR_KEYWORD)
1535 1543
1536 1544 try:
1537 1545 sig = inspect.signature(call_obj)
1538 1546 ret.extend(k for k, v in sig.parameters.items() if
1539 1547 v.kind in _keeps)
1540 1548 except ValueError:
1541 1549 pass
1542 1550
1543 1551 return list(set(ret))
1544 1552
1545 1553 def python_func_kw_matches(self, text):
1546 1554 """Match named parameters (kwargs) of the last open function"""
1547 1555
1548 1556 if "." in text: # a parameter cannot be dotted
1549 1557 return []
1550 1558 try: regexp = self.__funcParamsRegex
1551 1559 except AttributeError:
1552 1560 regexp = self.__funcParamsRegex = re.compile(r'''
1553 1561 '.*?(?<!\\)' | # single quoted strings or
1554 1562 ".*?(?<!\\)" | # double quoted strings or
1555 1563 \w+ | # identifier
1556 1564 \S # other characters
1557 1565 ''', re.VERBOSE | re.DOTALL)
1558 1566 # 1. find the nearest identifier that comes before an unclosed
1559 1567 # parenthesis before the cursor
1560 1568 # e.g. for "foo (1+bar(x), pa<cursor>,a=1)", the candidate is "foo"
1561 1569 tokens = regexp.findall(self.text_until_cursor)
1562 1570 iterTokens = reversed(tokens); openPar = 0
1563 1571
1564 1572 for token in iterTokens:
1565 1573 if token == ')':
1566 1574 openPar -= 1
1567 1575 elif token == '(':
1568 1576 openPar += 1
1569 1577 if openPar > 0:
1570 1578 # found the last unclosed parenthesis
1571 1579 break
1572 1580 else:
1573 1581 return []
1574 1582 # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" )
1575 1583 ids = []
1576 1584 isId = re.compile(r'\w+$').match
1577 1585
1578 1586 while True:
1579 1587 try:
1580 1588 ids.append(next(iterTokens))
1581 1589 if not isId(ids[-1]):
1582 1590 ids.pop(); break
1583 1591 if not next(iterTokens) == '.':
1584 1592 break
1585 1593 except StopIteration:
1586 1594 break
1587 1595
1588 1596 # Find all named arguments already assigned to, as to avoid suggesting
1589 1597 # them again
1590 1598 usedNamedArgs = set()
1591 1599 par_level = -1
1592 1600 for token, next_token in zip(tokens, tokens[1:]):
1593 1601 if token == '(':
1594 1602 par_level += 1
1595 1603 elif token == ')':
1596 1604 par_level -= 1
1597 1605
1598 1606 if par_level != 0:
1599 1607 continue
1600 1608
1601 1609 if next_token != '=':
1602 1610 continue
1603 1611
1604 1612 usedNamedArgs.add(token)
1605 1613
1606 1614 argMatches = []
1607 1615 try:
1608 1616 callableObj = '.'.join(ids[::-1])
1609 1617 namedArgs = self._default_arguments(eval(callableObj,
1610 1618 self.namespace))
1611 1619
1612 1620 # Remove used named arguments from the list, no need to show twice
1613 1621 for namedArg in set(namedArgs) - usedNamedArgs:
1614 1622 if namedArg.startswith(text):
1615 1623 argMatches.append("%s=" %namedArg)
1616 1624 except:
1617 1625 pass
1618 1626
1619 1627 return argMatches
1620 1628
1621 1629 @staticmethod
1622 1630 def _get_keys(obj: Any) -> List[Any]:
1623 1631 # Objects can define their own completions by defining an
1624 1632 # _ipy_key_completions_() method.
1625 1633 method = get_real_method(obj, '_ipython_key_completions_')
1626 1634 if method is not None:
1627 1635 return method()
1628 1636
1629 1637 # Special case some common in-memory dict-like types
1630 1638 if isinstance(obj, dict) or\
1631 1639 _safe_isinstance(obj, 'pandas', 'DataFrame'):
1632 1640 try:
1633 1641 return list(obj.keys())
1634 1642 except Exception:
1635 1643 return []
1636 1644 elif _safe_isinstance(obj, 'numpy', 'ndarray') or\
1637 1645 _safe_isinstance(obj, 'numpy', 'void'):
1638 1646 return obj.dtype.names or []
1639 1647 return []
1640 1648
1641 1649 def dict_key_matches(self, text:str) -> List[str]:
1642 1650 "Match string keys in a dictionary, after e.g. 'foo[' "
1643 1651
1644 1652
1645 1653 if self.__dict_key_regexps is not None:
1646 1654 regexps = self.__dict_key_regexps
1647 1655 else:
1648 1656 dict_key_re_fmt = r'''(?x)
1649 1657 ( # match dict-referring expression wrt greedy setting
1650 1658 %s
1651 1659 )
1652 1660 \[ # open bracket
1653 1661 \s* # and optional whitespace
1654 1662 # Capture any number of str-like objects (e.g. "a", "b", 'c')
1655 1663 ((?:[uUbB]? # string prefix (r not handled)
1656 1664 (?:
1657 1665 '(?:[^']|(?<!\\)\\')*'
1658 1666 |
1659 1667 "(?:[^"]|(?<!\\)\\")*"
1660 1668 )
1661 1669 \s*,\s*
1662 1670 )*)
1663 1671 ([uUbB]? # string prefix (r not handled)
1664 1672 (?: # unclosed string
1665 1673 '(?:[^']|(?<!\\)\\')*
1666 1674 |
1667 1675 "(?:[^"]|(?<!\\)\\")*
1668 1676 )
1669 1677 )?
1670 1678 $
1671 1679 '''
1672 1680 regexps = self.__dict_key_regexps = {
1673 1681 False: re.compile(dict_key_re_fmt % r'''
1674 1682 # identifiers separated by .
1675 1683 (?!\d)\w+
1676 1684 (?:\.(?!\d)\w+)*
1677 1685 '''),
1678 1686 True: re.compile(dict_key_re_fmt % '''
1679 1687 .+
1680 1688 ''')
1681 1689 }
1682 1690
1683 1691 match = regexps[self.greedy].search(self.text_until_cursor)
1684 1692
1685 1693 if match is None:
1686 1694 return []
1687 1695
1688 1696 expr, prefix0, prefix = match.groups()
1689 1697 try:
1690 1698 obj = eval(expr, self.namespace)
1691 1699 except Exception:
1692 1700 try:
1693 1701 obj = eval(expr, self.global_namespace)
1694 1702 except Exception:
1695 1703 return []
1696 1704
1697 1705 keys = self._get_keys(obj)
1698 1706 if not keys:
1699 1707 return keys
1700 1708
1701 1709 extra_prefix = eval(prefix0) if prefix0 != '' else None
1702 1710
1703 1711 closing_quote, token_offset, matches = match_dict_keys(keys, prefix, self.splitter.delims, extra_prefix=extra_prefix)
1704 1712 if not matches:
1705 1713 return matches
1706 1714
1707 1715 # get the cursor position of
1708 1716 # - the text being completed
1709 1717 # - the start of the key text
1710 1718 # - the start of the completion
1711 1719 text_start = len(self.text_until_cursor) - len(text)
1712 1720 if prefix:
1713 1721 key_start = match.start(3)
1714 1722 completion_start = key_start + token_offset
1715 1723 else:
1716 1724 key_start = completion_start = match.end()
1717 1725
1718 1726 # grab the leading prefix, to make sure all completions start with `text`
1719 1727 if text_start > key_start:
1720 1728 leading = ''
1721 1729 else:
1722 1730 leading = text[text_start:completion_start]
1723 1731
1724 1732 # the index of the `[` character
1725 1733 bracket_idx = match.end(1)
1726 1734
1727 1735 # append closing quote and bracket as appropriate
1728 1736 # this is *not* appropriate if the opening quote or bracket is outside
1729 1737 # the text given to this method
1730 1738 suf = ''
1731 1739 continuation = self.line_buffer[len(self.text_until_cursor):]
1732 1740 if key_start > text_start and closing_quote:
1733 1741 # quotes were opened inside text, maybe close them
1734 1742 if continuation.startswith(closing_quote):
1735 1743 continuation = continuation[len(closing_quote):]
1736 1744 else:
1737 1745 suf += closing_quote
1738 1746 if bracket_idx > text_start:
1739 1747 # brackets were opened inside text, maybe close them
1740 1748 if not continuation.startswith(']'):
1741 1749 suf += ']'
1742 1750
1743 1751 return [leading + k + suf for k in matches]
1744 1752
1745 1753 @staticmethod
1746 1754 def unicode_name_matches(text:str) -> Tuple[str, List[str]] :
1747 1755 """Match Latex-like syntax for unicode characters base
1748 1756 on the name of the character.
1749 1757
1750 1758 This does ``\\GREEK SMALL LETTER ETA`` -> ``Ξ·``
1751 1759
1752 1760 Works only on valid python 3 identifier, or on combining characters that
1753 1761 will combine to form a valid identifier.
1754 1762 """
1755 1763 slashpos = text.rfind('\\')
1756 1764 if slashpos > -1:
1757 1765 s = text[slashpos+1:]
1758 1766 try :
1759 1767 unic = unicodedata.lookup(s)
1760 1768 # allow combining chars
1761 1769 if ('a'+unic).isidentifier():
1762 1770 return '\\'+s,[unic]
1763 1771 except KeyError:
1764 1772 pass
1765 1773 return '', []
1766 1774
1767 1775
1768 1776 def latex_matches(self, text:str) -> Tuple[str, Sequence[str]]:
1769 1777 """Match Latex syntax for unicode characters.
1770 1778
1771 1779 This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``Ξ±``
1772 1780 """
1773 1781 slashpos = text.rfind('\\')
1774 1782 if slashpos > -1:
1775 1783 s = text[slashpos:]
1776 1784 if s in latex_symbols:
1777 1785 # Try to complete a full latex symbol to unicode
1778 1786 # \\alpha -> Ξ±
1779 1787 return s, [latex_symbols[s]]
1780 1788 else:
1781 1789 # If a user has partially typed a latex symbol, give them
1782 1790 # a full list of options \al -> [\aleph, \alpha]
1783 1791 matches = [k for k in latex_symbols if k.startswith(s)]
1784 1792 if matches:
1785 1793 return s, matches
1786 1794 return '', ()
1787 1795
1788 1796 def dispatch_custom_completer(self, text):
1789 1797 if not self.custom_completers:
1790 1798 return
1791 1799
1792 1800 line = self.line_buffer
1793 1801 if not line.strip():
1794 1802 return None
1795 1803
1796 1804 # Create a little structure to pass all the relevant information about
1797 1805 # the current completion to any custom completer.
1798 1806 event = SimpleNamespace()
1799 1807 event.line = line
1800 1808 event.symbol = text
1801 1809 cmd = line.split(None,1)[0]
1802 1810 event.command = cmd
1803 1811 event.text_until_cursor = self.text_until_cursor
1804 1812
1805 1813 # for foo etc, try also to find completer for %foo
1806 1814 if not cmd.startswith(self.magic_escape):
1807 1815 try_magic = self.custom_completers.s_matches(
1808 1816 self.magic_escape + cmd)
1809 1817 else:
1810 1818 try_magic = []
1811 1819
1812 1820 for c in itertools.chain(self.custom_completers.s_matches(cmd),
1813 1821 try_magic,
1814 1822 self.custom_completers.flat_matches(self.text_until_cursor)):
1815 1823 try:
1816 1824 res = c(event)
1817 1825 if res:
1818 1826 # first, try case sensitive match
1819 1827 withcase = [r for r in res if r.startswith(text)]
1820 1828 if withcase:
1821 1829 return withcase
1822 1830 # if none, then case insensitive ones are ok too
1823 1831 text_low = text.lower()
1824 1832 return [r for r in res if r.lower().startswith(text_low)]
1825 1833 except TryNext:
1826 1834 pass
1827 1835 except KeyboardInterrupt:
1828 1836 """
1829 1837 If custom completer take too long,
1830 1838 let keyboard interrupt abort and return nothing.
1831 1839 """
1832 1840 break
1833 1841
1834 1842 return None
1835 1843
1836 1844 def completions(self, text: str, offset: int)->Iterator[Completion]:
1837 1845 """
1838 1846 Returns an iterator over the possible completions
1839 1847
1840 .. warning:: Unstable
1848 .. warning::
1849
1850 Unstable
1841 1851
1842 1852 This function is unstable, API may change without warning.
1843 1853 It will also raise unless use in proper context manager.
1844 1854
1845 1855 Parameters
1846 1856 ----------
1847 1857 text:str
1848 1858 Full text of the current input, multi line string.
1849 1859 offset:int
1850 1860 Integer representing the position of the cursor in ``text``. Offset
1851 1861 is 0-based indexed.
1852 1862
1853 1863 Yields
1854 1864 ------
1855 1865 Completion
1856 1866
1857 1867 Notes
1858 1868 -----
1859 1869 The cursor on a text can either be seen as being "in between"
1860 1870 characters or "On" a character depending on the interface visible to
1861 1871 the user. For consistency the cursor being on "in between" characters X
1862 1872 and Y is equivalent to the cursor being "on" character Y, that is to say
1863 1873 the character the cursor is on is considered as being after the cursor.
1864 1874
1865 1875 Combining characters may span more that one position in the
1866 1876 text.
1867 1877
1868 1878 .. note::
1869 1879
1870 1880 If ``IPCompleter.debug`` is :any:`True` will yield a ``--jedi/ipython--``
1871 1881 fake Completion token to distinguish completion returned by Jedi
1872 1882 and usual IPython completion.
1873 1883
1874 1884 .. note::
1875 1885
1876 1886 Completions are not completely deduplicated yet. If identical
1877 1887 completions are coming from different sources this function does not
1878 1888 ensure that each completion object will only be present once.
1879 1889 """
1880 1890 warnings.warn("_complete is a provisional API (as of IPython 6.0). "
1881 1891 "It may change without warnings. "
1882 1892 "Use in corresponding context manager.",
1883 1893 category=ProvisionalCompleterWarning, stacklevel=2)
1884 1894
1885 1895 seen = set()
1886 1896 profiler:Optional[cProfile.Profile]
1887 1897 try:
1888 1898 if self.profile_completions:
1889 1899 import cProfile
1890 1900 profiler = cProfile.Profile()
1891 1901 profiler.enable()
1892 1902 else:
1893 1903 profiler = None
1894 1904
1895 1905 for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
1896 1906 if c and (c in seen):
1897 1907 continue
1898 1908 yield c
1899 1909 seen.add(c)
1900 1910 except KeyboardInterrupt:
1901 1911 """if completions take too long and users send keyboard interrupt,
1902 1912 do not crash and return ASAP. """
1903 1913 pass
1904 1914 finally:
1905 1915 if profiler is not None:
1906 1916 profiler.disable()
1907 1917 ensure_dir_exists(self.profiler_output_dir)
1908 1918 output_path = os.path.join(self.profiler_output_dir, str(uuid.uuid4()))
1909 1919 print("Writing profiler output to", output_path)
1910 1920 profiler.dump_stats(output_path)
1911 1921
1912 1922 def _completions(self, full_text: str, offset: int, *, _timeout) -> Iterator[Completion]:
1913 1923 """
1914 1924 Core completion module.Same signature as :any:`completions`, with the
1915 1925 extra `timeout` parameter (in seconds).
1916 1926
1917 1927 Computing jedi's completion ``.type`` can be quite expensive (it is a
1918 1928 lazy property) and can require some warm-up, more warm up than just
1919 1929 computing the ``name`` of a completion. The warm-up can be :
1920 1930
1921 1931 - Long warm-up the first time a module is encountered after
1922 1932 install/update: actually build parse/inference tree.
1923 1933
1924 1934 - first time the module is encountered in a session: load tree from
1925 1935 disk.
1926 1936
1927 1937 We don't want to block completions for tens of seconds so we give the
1928 1938 completer a "budget" of ``_timeout`` seconds per invocation to compute
1929 1939 completions types, the completions that have not yet been computed will
1930 1940 be marked as "unknown" an will have a chance to be computed next round
1931 1941 are things get cached.
1932 1942
1933 1943 Keep in mind that Jedi is not the only thing treating the completion so
1934 1944 keep the timeout short-ish as if we take more than 0.3 second we still
1935 1945 have lots of processing to do.
1936 1946
1937 1947 """
1938 1948 deadline = time.monotonic() + _timeout
1939 1949
1940 1950
1941 1951 before = full_text[:offset]
1942 1952 cursor_line, cursor_column = position_to_cursor(full_text, offset)
1943 1953
1944 1954 matched_text, matches, matches_origin, jedi_matches = self._complete(
1945 1955 full_text=full_text, cursor_line=cursor_line, cursor_pos=cursor_column)
1946 1956
1947 1957 iter_jm = iter(jedi_matches)
1948 1958 if _timeout:
1949 1959 for jm in iter_jm:
1950 1960 try:
1951 1961 type_ = jm.type
1952 1962 except Exception:
1953 1963 if self.debug:
1954 1964 print("Error in Jedi getting type of ", jm)
1955 1965 type_ = None
1956 1966 delta = len(jm.name_with_symbols) - len(jm.complete)
1957 1967 if type_ == 'function':
1958 1968 signature = _make_signature(jm)
1959 1969 else:
1960 1970 signature = ''
1961 1971 yield Completion(start=offset - delta,
1962 1972 end=offset,
1963 1973 text=jm.name_with_symbols,
1964 1974 type=type_,
1965 1975 signature=signature,
1966 1976 _origin='jedi')
1967 1977
1968 1978 if time.monotonic() > deadline:
1969 1979 break
1970 1980
1971 1981 for jm in iter_jm:
1972 1982 delta = len(jm.name_with_symbols) - len(jm.complete)
1973 1983 yield Completion(start=offset - delta,
1974 1984 end=offset,
1975 1985 text=jm.name_with_symbols,
1976 1986 type='<unknown>', # don't compute type for speed
1977 1987 _origin='jedi',
1978 1988 signature='')
1979 1989
1980 1990
1981 1991 start_offset = before.rfind(matched_text)
1982 1992
1983 1993 # TODO:
1984 1994 # Suppress this, right now just for debug.
1985 1995 if jedi_matches and matches and self.debug:
1986 1996 yield Completion(start=start_offset, end=offset, text='--jedi/ipython--',
1987 1997 _origin='debug', type='none', signature='')
1988 1998
1989 1999 # I'm unsure if this is always true, so let's assert and see if it
1990 2000 # crash
1991 2001 assert before.endswith(matched_text)
1992 2002 for m, t in zip(matches, matches_origin):
1993 2003 yield Completion(start=start_offset, end=offset, text=m, _origin=t, signature='', type='<unknown>')
1994 2004
1995 2005
1996 2006 def complete(self, text=None, line_buffer=None, cursor_pos=None) -> Tuple[str, Sequence[str]]:
1997 2007 """Find completions for the given text and line context.
1998 2008
1999 2009 Note that both the text and the line_buffer are optional, but at least
2000 2010 one of them must be given.
2001 2011
2002 2012 Parameters
2003 2013 ----------
2004 2014 text : string, optional
2005 2015 Text to perform the completion on. If not given, the line buffer
2006 2016 is split using the instance's CompletionSplitter object.
2007 2017 line_buffer : string, optional
2008 2018 If not given, the completer attempts to obtain the current line
2009 2019 buffer via readline. This keyword allows clients which are
2010 2020 requesting for text completions in non-readline contexts to inform
2011 2021 the completer of the entire text.
2012 2022 cursor_pos : int, optional
2013 2023 Index of the cursor in the full line buffer. Should be provided by
2014 2024 remote frontends where kernel has no access to frontend state.
2015 2025
2016 2026 Returns
2017 2027 -------
2018 2028 Tuple of two items:
2019 2029 text : str
2020 2030 Text that was actually used in the completion.
2021 2031 matches : list
2022 2032 A list of completion matches.
2023 2033
2024 2034 Notes
2025 2035 -----
2026 2036 This API is likely to be deprecated and replaced by
2027 2037 :any:`IPCompleter.completions` in the future.
2028 2038
2029 2039 """
2030 2040 warnings.warn('`Completer.complete` is pending deprecation since '
2031 2041 'IPython 6.0 and will be replaced by `Completer.completions`.',
2032 2042 PendingDeprecationWarning)
2033 2043 # potential todo, FOLD the 3rd throw away argument of _complete
2034 2044 # into the first 2 one.
2035 2045 return self._complete(line_buffer=line_buffer, cursor_pos=cursor_pos, text=text, cursor_line=0)[:2]
2036 2046
2037 2047 def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None,
2038 2048 full_text=None) -> _CompleteResult:
2039 2049 """
2040 2050 Like complete but can also returns raw jedi completions as well as the
2041 2051 origin of the completion text. This could (and should) be made much
2042 2052 cleaner but that will be simpler once we drop the old (and stateful)
2043 2053 :any:`complete` API.
2044 2054
2045 2055 With current provisional API, cursor_pos act both (depending on the
2046 2056 caller) as the offset in the ``text`` or ``line_buffer``, or as the
2047 2057 ``column`` when passing multiline strings this could/should be renamed
2048 2058 but would add extra noise.
2049 2059
2050 2060 Returns
2051 2061 -------
2052 2062 A tuple of N elements which are (likely):
2053 2063 matched_text: ? the text that the complete matched
2054 2064 matches: list of completions ?
2055 2065 matches_origin: ? list same lenght as matches, and where each completion came from
2056 2066 jedi_matches: list of Jedi matches, have it's own structure.
2057 2067 """
2058 2068
2059 2069
2060 2070 # if the cursor position isn't given, the only sane assumption we can
2061 2071 # make is that it's at the end of the line (the common case)
2062 2072 if cursor_pos is None:
2063 2073 cursor_pos = len(line_buffer) if text is None else len(text)
2064 2074
2065 2075 if self.use_main_ns:
2066 2076 self.namespace = __main__.__dict__
2067 2077
2068 2078 # if text is either None or an empty string, rely on the line buffer
2069 2079 if (not line_buffer) and full_text:
2070 2080 line_buffer = full_text.split('\n')[cursor_line]
2071 2081 if not text: # issue #11508: check line_buffer before calling split_line
2072 2082 text = self.splitter.split_line(line_buffer, cursor_pos) if line_buffer else ''
2073 2083
2074 2084 if self.backslash_combining_completions:
2075 2085 # allow deactivation of these on windows.
2076 2086 base_text = text if not line_buffer else line_buffer[:cursor_pos]
2077 2087
2078 2088 for meth in (self.latex_matches,
2079 2089 self.unicode_name_matches,
2080 2090 back_latex_name_matches,
2081 2091 back_unicode_name_matches,
2082 2092 self.fwd_unicode_match):
2083 2093 name_text, name_matches = meth(base_text)
2084 2094 if name_text:
2085 2095 return _CompleteResult(name_text, name_matches[:MATCHES_LIMIT], \
2086 2096 [meth.__qualname__]*min(len(name_matches), MATCHES_LIMIT), ())
2087 2097
2088 2098
2089 2099 # If no line buffer is given, assume the input text is all there was
2090 2100 if line_buffer is None:
2091 2101 line_buffer = text
2092 2102
2093 2103 self.line_buffer = line_buffer
2094 2104 self.text_until_cursor = self.line_buffer[:cursor_pos]
2095 2105
2096 2106 # Do magic arg matches
2097 2107 for matcher in self.magic_arg_matchers:
2098 2108 matches = list(matcher(line_buffer))[:MATCHES_LIMIT]
2099 2109 if matches:
2100 2110 origins = [matcher.__qualname__] * len(matches)
2101 2111 return _CompleteResult(text, matches, origins, ())
2102 2112
2103 2113 # Start with a clean slate of completions
2104 2114 matches = []
2105 2115
2106 2116 # FIXME: we should extend our api to return a dict with completions for
2107 2117 # different types of objects. The rlcomplete() method could then
2108 2118 # simply collapse the dict into a list for readline, but we'd have
2109 2119 # richer completion semantics in other environments.
2110 2120 completions:Iterable[Any] = []
2111 2121 if self.use_jedi:
2112 2122 if not full_text:
2113 2123 full_text = line_buffer
2114 2124 completions = self._jedi_matches(
2115 2125 cursor_pos, cursor_line, full_text)
2116 2126
2117 2127 if self.merge_completions:
2118 2128 matches = []
2119 2129 for matcher in self.matchers:
2120 2130 try:
2121 2131 matches.extend([(m, matcher.__qualname__)
2122 2132 for m in matcher(text)])
2123 2133 except:
2124 2134 # Show the ugly traceback if the matcher causes an
2125 2135 # exception, but do NOT crash the kernel!
2126 2136 sys.excepthook(*sys.exc_info())
2127 2137 else:
2128 2138 for matcher in self.matchers:
2129 2139 matches = [(m, matcher.__qualname__)
2130 2140 for m in matcher(text)]
2131 2141 if matches:
2132 2142 break
2133 2143
2134 2144 seen = set()
2135 2145 filtered_matches = set()
2136 2146 for m in matches:
2137 2147 t, c = m
2138 2148 if t not in seen:
2139 2149 filtered_matches.add(m)
2140 2150 seen.add(t)
2141 2151
2142 2152 _filtered_matches = sorted(filtered_matches, key=lambda x: completions_sorting_key(x[0]))
2143 2153
2144 2154 custom_res = [(m, 'custom') for m in self.dispatch_custom_completer(text) or []]
2145 2155
2146 2156 _filtered_matches = custom_res or _filtered_matches
2147 2157
2148 2158 _filtered_matches = _filtered_matches[:MATCHES_LIMIT]
2149 2159 _matches = [m[0] for m in _filtered_matches]
2150 2160 origins = [m[1] for m in _filtered_matches]
2151 2161
2152 2162 self.matches = _matches
2153 2163
2154 2164 return _CompleteResult(text, _matches, origins, completions)
2155 2165
2156 2166 def fwd_unicode_match(self, text:str) -> Tuple[str, Sequence[str]]:
2157 2167 """
2158 2168 Forward match a string starting with a backslash with a list of
2159 2169 potential Unicode completions.
2160 2170
2161 2171 Will compute list list of Unicode character names on first call and cache it.
2162 2172
2163 2173 Returns
2164 2174 -------
2165 2175 At tuple with:
2166 2176 - matched text (empty if no matches)
2167 2177 - list of potential completions, empty tuple otherwise)
2168 2178 """
2169 2179 # TODO: self.unicode_names is here a list we traverse each time with ~100k elements.
2170 2180 # We could do a faster match using a Trie.
2171 2181
2172 2182 # Using pygtrie the follwing seem to work:
2173 2183
2174 2184 # s = PrefixSet()
2175 2185
2176 2186 # for c in range(0,0x10FFFF + 1):
2177 2187 # try:
2178 2188 # s.add(unicodedata.name(chr(c)))
2179 2189 # except ValueError:
2180 2190 # pass
2181 2191 # [''.join(k) for k in s.iter(prefix)]
2182 2192
2183 2193 # But need to be timed and adds an extra dependency.
2184 2194
2185 2195 slashpos = text.rfind('\\')
2186 2196 # if text starts with slash
2187 2197 if slashpos > -1:
2188 2198 # PERF: It's important that we don't access self._unicode_names
2189 2199 # until we're inside this if-block. _unicode_names is lazily
2190 2200 # initialized, and it takes a user-noticeable amount of time to
2191 2201 # initialize it, so we don't want to initialize it unless we're
2192 2202 # actually going to use it.
2193 2203 s = text[slashpos+1:]
2194 2204 candidates = [x for x in self.unicode_names if x.startswith(s)]
2195 2205 if candidates:
2196 2206 return s, candidates
2197 2207 else:
2198 2208 return '', ()
2199 2209
2200 2210 # if text does not start with slash
2201 2211 else:
2202 2212 return '', ()
2203 2213
2204 2214 @property
2205 2215 def unicode_names(self) -> List[str]:
2206 2216 """List of names of unicode code points that can be completed.
2207 2217
2208 2218 The list is lazily initialized on first access.
2209 2219 """
2210 2220 if self._unicode_names is None:
2211 2221 names = []
2212 2222 for c in range(0,0x10FFFF + 1):
2213 2223 try:
2214 2224 names.append(unicodedata.name(chr(c)))
2215 2225 except ValueError:
2216 2226 pass
2217 2227 self._unicode_names = _unicode_name_compute(_UNICODE_RANGES)
2218 2228
2219 2229 return self._unicode_names
2220 2230
2221 2231 def _unicode_name_compute(ranges:List[Tuple[int,int]]) -> List[str]:
2222 2232 names = []
2223 2233 for start,stop in ranges:
2224 2234 for c in range(start, stop) :
2225 2235 try:
2226 2236 names.append(unicodedata.name(chr(c)))
2227 2237 except ValueError:
2228 2238 pass
2229 2239 return names
@@ -1,1211 +1,1211 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Top-level display functions for displaying object in different formats."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7
8 8 from binascii import b2a_base64, hexlify
9 9 import json
10 10 import mimetypes
11 11 import os
12 12 import struct
13 13 import warnings
14 14 from copy import deepcopy
15 15 from os.path import splitext
16 16 from pathlib import Path, PurePath
17 17
18 18 from IPython.utils.py3compat import cast_unicode
19 19 from IPython.testing.skipdoctest import skip_doctest
20 20 from . import display_functions
21 21
22 22
23 23 __all__ = ['display_pretty', 'display_html', 'display_markdown',
24 24 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
25 25 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
26 26 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON',
27 27 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats',
28 28 'set_matplotlib_close',
29 29 'Video']
30 30
31 31 _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"]
32 32
33 33 __all__ = __all__ + _deprecated_names
34 34
35 35
36 36 # ----- warn to import from IPython.display -----
37 37
38 38 from warnings import warn
39 39
40 40
41 41 def __getattr__(name):
42 42 if name in _deprecated_names:
43 43 warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2)
44 44 return getattr(display_functions, name)
45 45
46 46 if name in globals().keys():
47 47 return globals()[name]
48 48 else:
49 49 raise AttributeError(f"module {__name__} has no attribute {name}")
50 50
51 51
52 52 #-----------------------------------------------------------------------------
53 53 # utility functions
54 54 #-----------------------------------------------------------------------------
55 55
56 56 def _safe_exists(path):
57 57 """Check path, but don't let exceptions raise"""
58 58 try:
59 59 return os.path.exists(path)
60 60 except Exception:
61 61 return False
62 62
63 63
64 64 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
65 65 """internal implementation of all display_foo methods
66 66
67 67 Parameters
68 68 ----------
69 69 mimetype : str
70 70 The mimetype to be published (e.g. 'image/png')
71 71 *objs : object
72 72 The Python objects to display, or if raw=True raw text data to
73 73 display.
74 74 raw : bool
75 75 Are the data objects raw data or Python objects that need to be
76 76 formatted before display? [default: False]
77 77 metadata : dict (optional)
78 78 Metadata to be associated with the specific mimetype output.
79 79 """
80 80 if metadata:
81 81 metadata = {mimetype: metadata}
82 82 if raw:
83 83 # turn list of pngdata into list of { 'image/png': pngdata }
84 84 objs = [ {mimetype: obj} for obj in objs ]
85 85 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
86 86
87 87 #-----------------------------------------------------------------------------
88 88 # Main functions
89 89 #-----------------------------------------------------------------------------
90 90
91 91
92 92 def display_pretty(*objs, **kwargs):
93 93 """Display the pretty (default) representation of an object.
94 94
95 95 Parameters
96 96 ----------
97 97 *objs : object
98 98 The Python objects to display, or if raw=True raw text data to
99 99 display.
100 100 raw : bool
101 101 Are the data objects raw data or Python objects that need to be
102 102 formatted before display? [default: False]
103 103 metadata : dict (optional)
104 104 Metadata to be associated with the specific mimetype output.
105 105 """
106 106 _display_mimetype('text/plain', objs, **kwargs)
107 107
108 108
109 109 def display_html(*objs, **kwargs):
110 110 """Display the HTML representation of an object.
111 111
112 112 Note: If raw=False and the object does not have a HTML
113 113 representation, no HTML will be shown.
114 114
115 115 Parameters
116 116 ----------
117 117 *objs : object
118 118 The Python objects to display, or if raw=True raw HTML data to
119 119 display.
120 120 raw : bool
121 121 Are the data objects raw data or Python objects that need to be
122 122 formatted before display? [default: False]
123 123 metadata : dict (optional)
124 124 Metadata to be associated with the specific mimetype output.
125 125 """
126 126 _display_mimetype('text/html', objs, **kwargs)
127 127
128 128
129 129 def display_markdown(*objs, **kwargs):
130 130 """Displays the Markdown representation of an object.
131 131
132 132 Parameters
133 133 ----------
134 134 *objs : object
135 135 The Python objects to display, or if raw=True raw markdown data to
136 136 display.
137 137 raw : bool
138 138 Are the data objects raw data or Python objects that need to be
139 139 formatted before display? [default: False]
140 140 metadata : dict (optional)
141 141 Metadata to be associated with the specific mimetype output.
142 142 """
143 143
144 144 _display_mimetype('text/markdown', objs, **kwargs)
145 145
146 146
147 147 def display_svg(*objs, **kwargs):
148 148 """Display the SVG representation of an object.
149 149
150 150 Parameters
151 151 ----------
152 152 *objs : object
153 153 The Python objects to display, or if raw=True raw svg data to
154 154 display.
155 155 raw : bool
156 156 Are the data objects raw data or Python objects that need to be
157 157 formatted before display? [default: False]
158 158 metadata : dict (optional)
159 159 Metadata to be associated with the specific mimetype output.
160 160 """
161 161 _display_mimetype('image/svg+xml', objs, **kwargs)
162 162
163 163
164 164 def display_png(*objs, **kwargs):
165 165 """Display the PNG representation of an object.
166 166
167 167 Parameters
168 168 ----------
169 169 *objs : object
170 170 The Python objects to display, or if raw=True raw png data to
171 171 display.
172 172 raw : bool
173 173 Are the data objects raw data or Python objects that need to be
174 174 formatted before display? [default: False]
175 175 metadata : dict (optional)
176 176 Metadata to be associated with the specific mimetype output.
177 177 """
178 178 _display_mimetype('image/png', objs, **kwargs)
179 179
180 180
181 181 def display_jpeg(*objs, **kwargs):
182 182 """Display the JPEG representation of an object.
183 183
184 184 Parameters
185 185 ----------
186 186 *objs : object
187 187 The Python objects to display, or if raw=True raw JPEG data to
188 188 display.
189 189 raw : bool
190 190 Are the data objects raw data or Python objects that need to be
191 191 formatted before display? [default: False]
192 192 metadata : dict (optional)
193 193 Metadata to be associated with the specific mimetype output.
194 194 """
195 195 _display_mimetype('image/jpeg', objs, **kwargs)
196 196
197 197
198 198 def display_latex(*objs, **kwargs):
199 199 """Display the LaTeX representation of an object.
200 200
201 201 Parameters
202 202 ----------
203 203 *objs : object
204 204 The Python objects to display, or if raw=True raw latex data to
205 205 display.
206 206 raw : bool
207 207 Are the data objects raw data or Python objects that need to be
208 208 formatted before display? [default: False]
209 209 metadata : dict (optional)
210 210 Metadata to be associated with the specific mimetype output.
211 211 """
212 212 _display_mimetype('text/latex', objs, **kwargs)
213 213
214 214
215 215 def display_json(*objs, **kwargs):
216 216 """Display the JSON representation of an object.
217 217
218 218 Note that not many frontends support displaying JSON.
219 219
220 220 Parameters
221 221 ----------
222 222 *objs : object
223 223 The Python objects to display, or if raw=True raw json data to
224 224 display.
225 225 raw : bool
226 226 Are the data objects raw data or Python objects that need to be
227 227 formatted before display? [default: False]
228 228 metadata : dict (optional)
229 229 Metadata to be associated with the specific mimetype output.
230 230 """
231 231 _display_mimetype('application/json', objs, **kwargs)
232 232
233 233
234 234 def display_javascript(*objs, **kwargs):
235 235 """Display the Javascript representation of an object.
236 236
237 237 Parameters
238 238 ----------
239 239 *objs : object
240 240 The Python objects to display, or if raw=True raw javascript data to
241 241 display.
242 242 raw : bool
243 243 Are the data objects raw data or Python objects that need to be
244 244 formatted before display? [default: False]
245 245 metadata : dict (optional)
246 246 Metadata to be associated with the specific mimetype output.
247 247 """
248 248 _display_mimetype('application/javascript', objs, **kwargs)
249 249
250 250
251 251 def display_pdf(*objs, **kwargs):
252 252 """Display the PDF representation of an object.
253 253
254 254 Parameters
255 255 ----------
256 256 *objs : object
257 257 The Python objects to display, or if raw=True raw javascript data to
258 258 display.
259 259 raw : bool
260 260 Are the data objects raw data or Python objects that need to be
261 261 formatted before display? [default: False]
262 262 metadata : dict (optional)
263 263 Metadata to be associated with the specific mimetype output.
264 264 """
265 265 _display_mimetype('application/pdf', objs, **kwargs)
266 266
267 267
268 268 #-----------------------------------------------------------------------------
269 269 # Smart classes
270 270 #-----------------------------------------------------------------------------
271 271
272 272
273 273 class DisplayObject(object):
274 274 """An object that wraps data to be displayed."""
275 275
276 276 _read_flags = 'r'
277 277 _show_mem_addr = False
278 278 metadata = None
279 279
280 280 def __init__(self, data=None, url=None, filename=None, metadata=None):
281 281 """Create a display object given raw data.
282 282
283 283 When this object is returned by an expression or passed to the
284 284 display function, it will result in the data being displayed
285 285 in the frontend. The MIME type of the data should match the
286 286 subclasses used, so the Png subclass should be used for 'image/png'
287 287 data. If the data is a URL, the data will first be downloaded
288 288 and then displayed. If
289 289
290 290 Parameters
291 291 ----------
292 292 data : unicode, str or bytes
293 293 The raw data or a URL or file to load the data from
294 294 url : unicode
295 295 A URL to download the data from.
296 296 filename : unicode
297 297 Path to a local file to load the data from.
298 298 metadata : dict
299 299 Dict of metadata associated to be the object when displayed
300 300 """
301 301 if isinstance(data, (Path, PurePath)):
302 302 data = str(data)
303 303
304 304 if data is not None and isinstance(data, str):
305 305 if data.startswith('http') and url is None:
306 306 url = data
307 307 filename = None
308 308 data = None
309 309 elif _safe_exists(data) and filename is None:
310 310 url = None
311 311 filename = data
312 312 data = None
313 313
314 314 self.url = url
315 315 self.filename = filename
316 316 # because of @data.setter methods in
317 317 # subclasses ensure url and filename are set
318 318 # before assigning to self.data
319 319 self.data = data
320 320
321 321 if metadata is not None:
322 322 self.metadata = metadata
323 323 elif self.metadata is None:
324 324 self.metadata = {}
325 325
326 326 self.reload()
327 327 self._check_data()
328 328
329 329 def __repr__(self):
330 330 if not self._show_mem_addr:
331 331 cls = self.__class__
332 332 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
333 333 else:
334 334 r = super(DisplayObject, self).__repr__()
335 335 return r
336 336
337 337 def _check_data(self):
338 338 """Override in subclasses if there's something to check."""
339 339 pass
340 340
341 341 def _data_and_metadata(self):
342 342 """shortcut for returning metadata with shape information, if defined"""
343 343 if self.metadata:
344 344 return self.data, deepcopy(self.metadata)
345 345 else:
346 346 return self.data
347 347
348 348 def reload(self):
349 349 """Reload the raw data from file or URL."""
350 350 if self.filename is not None:
351 351 with open(self.filename, self._read_flags) as f:
352 352 self.data = f.read()
353 353 elif self.url is not None:
354 354 # Deferred import
355 355 from urllib.request import urlopen
356 356 response = urlopen(self.url)
357 357 data = response.read()
358 358 # extract encoding from header, if there is one:
359 359 encoding = None
360 360 if 'content-type' in response.headers:
361 361 for sub in response.headers['content-type'].split(';'):
362 362 sub = sub.strip()
363 363 if sub.startswith('charset'):
364 364 encoding = sub.split('=')[-1].strip()
365 365 break
366 366 if 'content-encoding' in response.headers:
367 367 # TODO: do deflate?
368 368 if 'gzip' in response.headers['content-encoding']:
369 369 import gzip
370 370 from io import BytesIO
371 371 with gzip.open(BytesIO(data), 'rt', encoding=encoding) as fp:
372 372 encoding = None
373 373 data = fp.read()
374 374
375 375 # decode data, if an encoding was specified
376 376 # We only touch self.data once since
377 377 # subclasses such as SVG have @data.setter methods
378 378 # that transform self.data into ... well svg.
379 379 if encoding:
380 380 self.data = data.decode(encoding, 'replace')
381 381 else:
382 382 self.data = data
383 383
384 384
385 385 class TextDisplayObject(DisplayObject):
386 386 """Validate that display data is text"""
387 387 def _check_data(self):
388 388 if self.data is not None and not isinstance(self.data, str):
389 389 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
390 390
391 391 class Pretty(TextDisplayObject):
392 392
393 393 def _repr_pretty_(self, pp, cycle):
394 394 return pp.text(self.data)
395 395
396 396
397 397 class HTML(TextDisplayObject):
398 398
399 399 def __init__(self, data=None, url=None, filename=None, metadata=None):
400 400 def warn():
401 401 if not data:
402 402 return False
403 403
404 404 #
405 405 # Avoid calling lower() on the entire data, because it could be a
406 406 # long string and we're only interested in its beginning and end.
407 407 #
408 408 prefix = data[:10].lower()
409 409 suffix = data[-10:].lower()
410 410 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
411 411
412 412 if warn():
413 413 warnings.warn("Consider using IPython.display.IFrame instead")
414 414 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
415 415
416 416 def _repr_html_(self):
417 417 return self._data_and_metadata()
418 418
419 419 def __html__(self):
420 420 """
421 421 This method exists to inform other HTML-using modules (e.g. Markupsafe,
422 422 htmltag, etc) that this object is HTML and does not need things like
423 423 special characters (<>&) escaped.
424 424 """
425 425 return self._repr_html_()
426 426
427 427
428 428 class Markdown(TextDisplayObject):
429 429
430 430 def _repr_markdown_(self):
431 431 return self._data_and_metadata()
432 432
433 433
434 434 class Math(TextDisplayObject):
435 435
436 436 def _repr_latex_(self):
437 437 s = r"$\displaystyle %s$" % self.data.strip('$')
438 438 if self.metadata:
439 439 return s, deepcopy(self.metadata)
440 440 else:
441 441 return s
442 442
443 443
444 444 class Latex(TextDisplayObject):
445 445
446 446 def _repr_latex_(self):
447 447 return self._data_and_metadata()
448 448
449 449
450 450 class SVG(DisplayObject):
451 451 """Embed an SVG into the display.
452 452
453 453 Note if you just want to view a svg image via a URL use `:class:Image` with
454 454 a url=URL keyword argument.
455 455 """
456 456
457 457 _read_flags = 'rb'
458 458 # wrap data in a property, which extracts the <svg> tag, discarding
459 459 # document headers
460 460 _data = None
461 461
462 462 @property
463 463 def data(self):
464 464 return self._data
465 465
466 466 @data.setter
467 467 def data(self, svg):
468 468 if svg is None:
469 469 self._data = None
470 470 return
471 471 # parse into dom object
472 472 from xml.dom import minidom
473 473 x = minidom.parseString(svg)
474 474 # get svg tag (should be 1)
475 475 found_svg = x.getElementsByTagName('svg')
476 476 if found_svg:
477 477 svg = found_svg[0].toxml()
478 478 else:
479 479 # fallback on the input, trust the user
480 480 # but this is probably an error.
481 481 pass
482 482 svg = cast_unicode(svg)
483 483 self._data = svg
484 484
485 485 def _repr_svg_(self):
486 486 return self._data_and_metadata()
487 487
488 488 class ProgressBar(DisplayObject):
489 489 """Progressbar supports displaying a progressbar like element
490 490 """
491 491 def __init__(self, total):
492 492 """Creates a new progressbar
493 493
494 494 Parameters
495 495 ----------
496 496 total : int
497 497 maximum size of the progressbar
498 498 """
499 499 self.total = total
500 500 self._progress = 0
501 501 self.html_width = '60ex'
502 502 self.text_width = 60
503 503 self._display_id = hexlify(os.urandom(8)).decode('ascii')
504 504
505 505 def __repr__(self):
506 506 fraction = self.progress / self.total
507 507 filled = '=' * int(fraction * self.text_width)
508 508 rest = ' ' * (self.text_width - len(filled))
509 509 return '[{}{}] {}/{}'.format(
510 510 filled, rest,
511 511 self.progress, self.total,
512 512 )
513 513
514 514 def _repr_html_(self):
515 515 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
516 516 self.html_width, self.total, self.progress)
517 517
518 518 def display(self):
519 519 display(self, display_id=self._display_id)
520 520
521 521 def update(self):
522 522 display(self, display_id=self._display_id, update=True)
523 523
524 524 @property
525 525 def progress(self):
526 526 return self._progress
527 527
528 528 @progress.setter
529 529 def progress(self, value):
530 530 self._progress = value
531 531 self.update()
532 532
533 533 def __iter__(self):
534 534 self.display()
535 535 self._progress = -1 # First iteration is 0
536 536 return self
537 537
538 538 def __next__(self):
539 539 """Returns current value and increments display by one."""
540 540 self.progress += 1
541 541 if self.progress < self.total:
542 542 return self.progress
543 543 else:
544 544 raise StopIteration()
545 545
546 546 class JSON(DisplayObject):
547 547 """JSON expects a JSON-able dict or list
548 548
549 549 not an already-serialized JSON string.
550 550
551 551 Scalar types (None, number, string) are not allowed, only dict or list containers.
552 552 """
553 553 # wrap data in a property, which warns about passing already-serialized JSON
554 554 _data = None
555 555 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
556 556 """Create a JSON display object given raw data.
557 557
558 558 Parameters
559 559 ----------
560 560 data : dict or list
561 561 JSON data to display. Not an already-serialized JSON string.
562 562 Scalar types (None, number, string) are not allowed, only dict
563 563 or list containers.
564 564 url : unicode
565 565 A URL to download the data from.
566 566 filename : unicode
567 567 Path to a local file to load the data from.
568 568 expanded : boolean
569 569 Metadata to control whether a JSON display component is expanded.
570 570 metadata : dict
571 571 Specify extra metadata to attach to the json display object.
572 572 root : str
573 573 The name of the root element of the JSON tree
574 574 """
575 575 self.metadata = {
576 576 'expanded': expanded,
577 577 'root': root,
578 578 }
579 579 if metadata:
580 580 self.metadata.update(metadata)
581 581 if kwargs:
582 582 self.metadata.update(kwargs)
583 583 super(JSON, self).__init__(data=data, url=url, filename=filename)
584 584
585 585 def _check_data(self):
586 586 if self.data is not None and not isinstance(self.data, (dict, list)):
587 587 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
588 588
589 589 @property
590 590 def data(self):
591 591 return self._data
592 592
593 593 @data.setter
594 594 def data(self, data):
595 595 if isinstance(data, (Path, PurePath)):
596 596 data = str(data)
597 597
598 598 if isinstance(data, str):
599 599 if self.filename is None and self.url is None:
600 600 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
601 601 data = json.loads(data)
602 602 self._data = data
603 603
604 604 def _data_and_metadata(self):
605 605 return self.data, self.metadata
606 606
607 607 def _repr_json_(self):
608 608 return self._data_and_metadata()
609 609
610 610 _css_t = """var link = document.createElement("link");
611 611 link.ref = "stylesheet";
612 612 link.type = "text/css";
613 613 link.href = "%s";
614 614 document.head.appendChild(link);
615 615 """
616 616
617 617 _lib_t1 = """new Promise(function(resolve, reject) {
618 618 var script = document.createElement("script");
619 619 script.onload = resolve;
620 620 script.onerror = reject;
621 621 script.src = "%s";
622 622 document.head.appendChild(script);
623 623 }).then(() => {
624 624 """
625 625
626 626 _lib_t2 = """
627 627 });"""
628 628
629 629 class GeoJSON(JSON):
630 630 """GeoJSON expects JSON-able dict
631 631
632 632 not an already-serialized JSON string.
633 633
634 634 Scalar types (None, number, string) are not allowed, only dict containers.
635 635 """
636 636
637 637 def __init__(self, *args, **kwargs):
638 638 """Create a GeoJSON display object given raw data.
639 639
640 640 Parameters
641 641 ----------
642 642 data : dict or list
643 643 VegaLite data. Not an already-serialized JSON string.
644 644 Scalar types (None, number, string) are not allowed, only dict
645 645 or list containers.
646 646 url_template : string
647 647 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
648 648 layer_options : dict
649 649 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
650 650 url : unicode
651 651 A URL to download the data from.
652 652 filename : unicode
653 653 Path to a local file to load the data from.
654 654 metadata : dict
655 655 Specify extra metadata to attach to the json display object.
656 656
657 657 Examples
658 658 --------
659 659 The following will display an interactive map of Mars with a point of
660 660 interest on frontend that do support GeoJSON display.
661 661
662 662 >>> from IPython.display import GeoJSON
663 663
664 664 >>> GeoJSON(data={
665 665 ... "type": "Feature",
666 666 ... "geometry": {
667 667 ... "type": "Point",
668 668 ... "coordinates": [-81.327, 296.038]
669 669 ... }
670 670 ... },
671 671 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
672 672 ... layer_options={
673 673 ... "basemap_id": "celestia_mars-shaded-16k_global",
674 674 ... "attribution" : "Celestia/praesepe",
675 675 ... "minZoom" : 0,
676 676 ... "maxZoom" : 18,
677 677 ... })
678 678 <IPython.core.display.GeoJSON object>
679 679
680 680 In the terminal IPython, you will only see the text representation of
681 681 the GeoJSON object.
682 682
683 683 """
684 684
685 685 super(GeoJSON, self).__init__(*args, **kwargs)
686 686
687 687
688 688 def _ipython_display_(self):
689 689 bundle = {
690 690 'application/geo+json': self.data,
691 691 'text/plain': '<IPython.display.GeoJSON object>'
692 692 }
693 693 metadata = {
694 694 'application/geo+json': self.metadata
695 695 }
696 696 display(bundle, metadata=metadata, raw=True)
697 697
698 698 class Javascript(TextDisplayObject):
699 699
700 700 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
701 701 """Create a Javascript display object given raw data.
702 702
703 703 When this object is returned by an expression or passed to the
704 704 display function, it will result in the data being displayed
705 705 in the frontend. If the data is a URL, the data will first be
706 706 downloaded and then displayed.
707 707
708 708 In the Notebook, the containing element will be available as `element`,
709 709 and jQuery will be available. Content appended to `element` will be
710 710 visible in the output area.
711 711
712 712 Parameters
713 713 ----------
714 714 data : unicode, str or bytes
715 715 The Javascript source code or a URL to download it from.
716 716 url : unicode
717 717 A URL to download the data from.
718 718 filename : unicode
719 719 Path to a local file to load the data from.
720 720 lib : list or str
721 721 A sequence of Javascript library URLs to load asynchronously before
722 722 running the source code. The full URLs of the libraries should
723 723 be given. A single Javascript library URL can also be given as a
724 724 string.
725 725 css : list or str
726 726 A sequence of css files to load before running the source code.
727 727 The full URLs of the css files should be given. A single css URL
728 728 can also be given as a string.
729 729 """
730 730 if isinstance(lib, str):
731 731 lib = [lib]
732 732 elif lib is None:
733 733 lib = []
734 734 if isinstance(css, str):
735 735 css = [css]
736 736 elif css is None:
737 737 css = []
738 738 if not isinstance(lib, (list,tuple)):
739 739 raise TypeError('expected sequence, got: %r' % lib)
740 740 if not isinstance(css, (list,tuple)):
741 741 raise TypeError('expected sequence, got: %r' % css)
742 742 self.lib = lib
743 743 self.css = css
744 744 super(Javascript, self).__init__(data=data, url=url, filename=filename)
745 745
746 746 def _repr_javascript_(self):
747 747 r = ''
748 748 for c in self.css:
749 749 r += _css_t % c
750 750 for l in self.lib:
751 751 r += _lib_t1 % l
752 752 r += self.data
753 753 r += _lib_t2*len(self.lib)
754 754 return r
755 755
756 756 # constants for identifying png/jpeg data
757 757 _PNG = b'\x89PNG\r\n\x1a\n'
758 758 _JPEG = b'\xff\xd8'
759 759
760 760 def _pngxy(data):
761 761 """read the (width, height) from a PNG header"""
762 762 ihdr = data.index(b'IHDR')
763 763 # next 8 bytes are width/height
764 764 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
765 765
766 766 def _jpegxy(data):
767 767 """read the (width, height) from a JPEG header"""
768 768 # adapted from http://www.64lines.com/jpeg-width-height
769 769
770 770 idx = 4
771 771 while True:
772 772 block_size = struct.unpack('>H', data[idx:idx+2])[0]
773 773 idx = idx + block_size
774 774 if data[idx:idx+2] == b'\xFF\xC0':
775 775 # found Start of Frame
776 776 iSOF = idx
777 777 break
778 778 else:
779 779 # read another block
780 780 idx += 2
781 781
782 782 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
783 783 return w, h
784 784
785 785 def _gifxy(data):
786 786 """read the (width, height) from a GIF header"""
787 787 return struct.unpack('<HH', data[6:10])
788 788
789 789
790 790 class Image(DisplayObject):
791 791
792 792 _read_flags = 'rb'
793 793 _FMT_JPEG = u'jpeg'
794 794 _FMT_PNG = u'png'
795 795 _FMT_GIF = u'gif'
796 796 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
797 797 _MIMETYPES = {
798 798 _FMT_PNG: 'image/png',
799 799 _FMT_JPEG: 'image/jpeg',
800 800 _FMT_GIF: 'image/gif',
801 801 }
802 802
803 803 def __init__(self, data=None, url=None, filename=None, format=None,
804 804 embed=None, width=None, height=None, retina=False,
805 805 unconfined=False, metadata=None):
806 806 """Create a PNG/JPEG/GIF image object given raw data.
807 807
808 808 When this object is returned by an input cell or passed to the
809 809 display function, it will result in the image being displayed
810 810 in the frontend.
811 811
812 812 Parameters
813 813 ----------
814 814 data : unicode, str or bytes
815 815 The raw image data or a URL or filename to load the data from.
816 816 This always results in embedded image data.
817 817 url : unicode
818 818 A URL to download the data from. If you specify `url=`,
819 819 the image data will not be embedded unless you also specify `embed=True`.
820 820 filename : unicode
821 821 Path to a local file to load the data from.
822 822 Images from a file are always embedded.
823 823 format : unicode
824 824 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
825 825 for format will be inferred from the filename extension.
826 826 embed : bool
827 827 Should the image data be embedded using a data URI (True) or be
828 828 loaded using an <img> tag. Set this to True if you want the image
829 829 to be viewable later with no internet connection in the notebook.
830 830
831 831 Default is `True`, unless the keyword argument `url` is set, then
832 832 default value is `False`.
833 833
834 834 Note that QtConsole is not able to display images if `embed` is set to `False`
835 835 width : int
836 836 Width in pixels to which to constrain the image in html
837 837 height : int
838 838 Height in pixels to which to constrain the image in html
839 839 retina : bool
840 840 Automatically set the width and height to half of the measured
841 841 width and height.
842 842 This only works for embedded images because it reads the width/height
843 843 from image data.
844 844 For non-embedded images, you can just set the desired display width
845 845 and height directly.
846 846 unconfined : bool
847 847 Set unconfined=True to disable max-width confinement of the image.
848 848 metadata : dict
849 849 Specify extra metadata to attach to the image.
850 850
851 851 Examples
852 852 --------
853 853 embedded image data, works in qtconsole and notebook
854 854 when passed positionally, the first arg can be any of raw image data,
855 855 a URL, or a filename from which to load image data.
856 856 The result is always embedding image data for inline images.
857 857
858 858 >>> Image('http://www.google.fr/images/srpr/logo3w.png')
859 859 <IPython.core.display.Image object>
860 860
861 861 >>> Image('/path/to/image.jpg')
862 862 <IPython.core.display.Image object>
863 863
864 864 >>> Image(b'RAW_PNG_DATA...')
865 865 <IPython.core.display.Image object>
866 866
867 867 Specifying Image(url=...) does not embed the image data,
868 868 it only generates ``<img>`` tag with a link to the source.
869 869 This will not work in the qtconsole or offline.
870 870
871 871 >>> Image(url='http://www.google.fr/images/srpr/logo3w.png')
872 872 <IPython.core.display.Image object>
873 873
874 874 """
875 875 if isinstance(data, (Path, PurePath)):
876 876 data = str(data)
877 877
878 878 if filename is not None:
879 879 ext = self._find_ext(filename)
880 880 elif url is not None:
881 881 ext = self._find_ext(url)
882 882 elif data is None:
883 883 raise ValueError("No image data found. Expecting filename, url, or data.")
884 884 elif isinstance(data, str) and (
885 885 data.startswith('http') or _safe_exists(data)
886 886 ):
887 887 ext = self._find_ext(data)
888 888 else:
889 889 ext = None
890 890
891 891 if format is None:
892 892 if ext is not None:
893 893 if ext == u'jpg' or ext == u'jpeg':
894 894 format = self._FMT_JPEG
895 895 elif ext == u'png':
896 896 format = self._FMT_PNG
897 897 elif ext == u'gif':
898 898 format = self._FMT_GIF
899 899 else:
900 900 format = ext.lower()
901 901 elif isinstance(data, bytes):
902 902 # infer image type from image data header,
903 903 # only if format has not been specified.
904 904 if data[:2] == _JPEG:
905 905 format = self._FMT_JPEG
906 906
907 907 # failed to detect format, default png
908 908 if format is None:
909 909 format = self._FMT_PNG
910 910
911 911 if format.lower() == 'jpg':
912 912 # jpg->jpeg
913 913 format = self._FMT_JPEG
914 914
915 915 self.format = format.lower()
916 916 self.embed = embed if embed is not None else (url is None)
917 917
918 918 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
919 919 raise ValueError("Cannot embed the '%s' image format" % (self.format))
920 920 if self.embed:
921 921 self._mimetype = self._MIMETYPES.get(self.format)
922 922
923 923 self.width = width
924 924 self.height = height
925 925 self.retina = retina
926 926 self.unconfined = unconfined
927 927 super(Image, self).__init__(data=data, url=url, filename=filename,
928 928 metadata=metadata)
929 929
930 930 if self.width is None and self.metadata.get('width', {}):
931 931 self.width = metadata['width']
932 932
933 933 if self.height is None and self.metadata.get('height', {}):
934 934 self.height = metadata['height']
935 935
936 936 if retina:
937 937 self._retina_shape()
938 938
939 939
940 940 def _retina_shape(self):
941 941 """load pixel-doubled width and height from image data"""
942 942 if not self.embed:
943 943 return
944 944 if self.format == self._FMT_PNG:
945 945 w, h = _pngxy(self.data)
946 946 elif self.format == self._FMT_JPEG:
947 947 w, h = _jpegxy(self.data)
948 948 elif self.format == self._FMT_GIF:
949 949 w, h = _gifxy(self.data)
950 950 else:
951 951 # retina only supports png
952 952 return
953 953 self.width = w // 2
954 954 self.height = h // 2
955 955
956 956 def reload(self):
957 957 """Reload the raw data from file or URL."""
958 958 if self.embed:
959 959 super(Image,self).reload()
960 960 if self.retina:
961 961 self._retina_shape()
962 962
963 963 def _repr_html_(self):
964 964 if not self.embed:
965 965 width = height = klass = ''
966 966 if self.width:
967 967 width = ' width="%d"' % self.width
968 968 if self.height:
969 969 height = ' height="%d"' % self.height
970 970 if self.unconfined:
971 971 klass = ' class="unconfined"'
972 972 return u'<img src="{url}"{width}{height}{klass}/>'.format(
973 973 url=self.url,
974 974 width=width,
975 975 height=height,
976 976 klass=klass,
977 977 )
978 978
979 979 def _repr_mimebundle_(self, include=None, exclude=None):
980 980 """Return the image as a mimebundle
981 981
982 982 Any new mimetype support should be implemented here.
983 983 """
984 984 if self.embed:
985 985 mimetype = self._mimetype
986 986 data, metadata = self._data_and_metadata(always_both=True)
987 987 if metadata:
988 988 metadata = {mimetype: metadata}
989 989 return {mimetype: data}, metadata
990 990 else:
991 991 return {'text/html': self._repr_html_()}
992 992
993 993 def _data_and_metadata(self, always_both=False):
994 994 """shortcut for returning metadata with shape information, if defined"""
995 995 try:
996 996 b64_data = b2a_base64(self.data).decode('ascii')
997 997 except TypeError as e:
998 998 raise FileNotFoundError(
999 999 "No such file or directory: '%s'" % (self.data)) from e
1000 1000 md = {}
1001 1001 if self.metadata:
1002 1002 md.update(self.metadata)
1003 1003 if self.width:
1004 1004 md['width'] = self.width
1005 1005 if self.height:
1006 1006 md['height'] = self.height
1007 1007 if self.unconfined:
1008 1008 md['unconfined'] = self.unconfined
1009 1009 if md or always_both:
1010 1010 return b64_data, md
1011 1011 else:
1012 1012 return b64_data
1013 1013
1014 1014 def _repr_png_(self):
1015 1015 if self.embed and self.format == self._FMT_PNG:
1016 1016 return self._data_and_metadata()
1017 1017
1018 1018 def _repr_jpeg_(self):
1019 1019 if self.embed and self.format == self._FMT_JPEG:
1020 1020 return self._data_and_metadata()
1021 1021
1022 1022 def _find_ext(self, s):
1023 1023 base, ext = splitext(s)
1024 1024
1025 1025 if not ext:
1026 1026 return base
1027 1027
1028 1028 # `splitext` includes leading period, so we skip it
1029 1029 return ext[1:].lower()
1030 1030
1031 1031
1032 1032 class Video(DisplayObject):
1033 1033
1034 1034 def __init__(self, data=None, url=None, filename=None, embed=False,
1035 1035 mimetype=None, width=None, height=None, html_attributes="controls"):
1036 1036 """Create a video object given raw data or an URL.
1037 1037
1038 1038 When this object is returned by an input cell or passed to the
1039 1039 display function, it will result in the video being displayed
1040 1040 in the frontend.
1041 1041
1042 1042 Parameters
1043 1043 ----------
1044 1044 data : unicode, str or bytes
1045 1045 The raw video data or a URL or filename to load the data from.
1046 Raw data will require passing `embed=True`.
1046 Raw data will require passing ``embed=True``.
1047 1047 url : unicode
1048 A URL for the video. If you specify `url=`,
1048 A URL for the video. If you specify ``url=``,
1049 1049 the image data will not be embedded.
1050 1050 filename : unicode
1051 1051 Path to a local file containing the video.
1052 Will be interpreted as a local URL unless `embed=True`.
1052 Will be interpreted as a local URL unless ``embed=True``.
1053 1053 embed : bool
1054 1054 Should the video be embedded using a data URI (True) or be
1055 1055 loaded using a <video> tag (False).
1056 1056
1057 1057 Since videos are large, embedding them should be avoided, if possible.
1058 You must confirm embedding as your intention by passing `embed=True`.
1058 You must confirm embedding as your intention by passing ``embed=True``.
1059 1059
1060 1060 Local files can be displayed with URLs without embedding the content, via::
1061 1061
1062 1062 Video('./video.mp4')
1063 1063 mimetype : unicode
1064 1064 Specify the mimetype for embedded videos.
1065 1065 Default will be guessed from file extension, if available.
1066 1066 width : int
1067 1067 Width in pixels to which to constrain the video in HTML.
1068 1068 If not supplied, defaults to the width of the video.
1069 1069 height : int
1070 1070 Height in pixels to which to constrain the video in html.
1071 1071 If not supplied, defaults to the height of the video.
1072 1072 html_attributes : str
1073 Attributes for the HTML `<video>` block.
1074 Default: `"controls"` to get video controls.
1075 Other examples: `"controls muted"` for muted video with controls,
1076 `"loop autoplay"` for looping autoplaying video without controls.
1073 Attributes for the HTML ``<video>`` block.
1074 Default: ``"controls"`` to get video controls.
1075 Other examples: ``"controls muted"`` for muted video with controls,
1076 ``"loop autoplay"`` for looping autoplaying video without controls.
1077 1077
1078 1078 Examples
1079 1079 --------
1080 1080 ::
1081 1081
1082 1082 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1083 1083 Video('path/to/video.mp4')
1084 1084 Video('path/to/video.mp4', embed=True)
1085 1085 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1086 1086 Video(b'raw-videodata', embed=True)
1087 1087 """
1088 1088 if isinstance(data, (Path, PurePath)):
1089 1089 data = str(data)
1090 1090
1091 1091 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1092 1092 url = data
1093 1093 data = None
1094 1094 elif data is not None and os.path.exists(data):
1095 1095 filename = data
1096 1096 data = None
1097 1097
1098 1098 if data and not embed:
1099 1099 msg = ''.join([
1100 1100 "To embed videos, you must pass embed=True ",
1101 1101 "(this may make your notebook files huge)\n",
1102 1102 "Consider passing Video(url='...')",
1103 1103 ])
1104 1104 raise ValueError(msg)
1105 1105
1106 1106 self.mimetype = mimetype
1107 1107 self.embed = embed
1108 1108 self.width = width
1109 1109 self.height = height
1110 1110 self.html_attributes = html_attributes
1111 1111 super(Video, self).__init__(data=data, url=url, filename=filename)
1112 1112
1113 1113 def _repr_html_(self):
1114 1114 width = height = ''
1115 1115 if self.width:
1116 1116 width = ' width="%d"' % self.width
1117 1117 if self.height:
1118 1118 height = ' height="%d"' % self.height
1119 1119
1120 1120 # External URLs and potentially local files are not embedded into the
1121 1121 # notebook output.
1122 1122 if not self.embed:
1123 1123 url = self.url if self.url is not None else self.filename
1124 1124 output = """<video src="{0}" {1} {2} {3}>
1125 1125 Your browser does not support the <code>video</code> element.
1126 1126 </video>""".format(url, self.html_attributes, width, height)
1127 1127 return output
1128 1128
1129 1129 # Embedded videos are base64-encoded.
1130 1130 mimetype = self.mimetype
1131 1131 if self.filename is not None:
1132 1132 if not mimetype:
1133 1133 mimetype, _ = mimetypes.guess_type(self.filename)
1134 1134
1135 1135 with open(self.filename, 'rb') as f:
1136 1136 video = f.read()
1137 1137 else:
1138 1138 video = self.data
1139 1139 if isinstance(video, str):
1140 1140 # unicode input is already b64-encoded
1141 1141 b64_video = video
1142 1142 else:
1143 1143 b64_video = b2a_base64(video).decode('ascii').rstrip()
1144 1144
1145 1145 output = """<video {0} {1} {2}>
1146 1146 <source src="data:{3};base64,{4}" type="{3}">
1147 1147 Your browser does not support the video tag.
1148 1148 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1149 1149 return output
1150 1150
1151 1151 def reload(self):
1152 1152 # TODO
1153 1153 pass
1154 1154
1155 1155
1156 1156 @skip_doctest
1157 1157 def set_matplotlib_formats(*formats, **kwargs):
1158 1158 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1159 1159
1160 1160 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1161 1161
1162 1162 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1163 1163
1164 1164 To set this in your config files use the following::
1165 1165
1166 1166 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1167 1167 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1168 1168
1169 1169 Parameters
1170 1170 ----------
1171 1171 *formats : strs
1172 1172 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1173 1173 **kwargs
1174 1174 Keyword args will be relayed to ``figure.canvas.print_figure``.
1175 1175 """
1176 1176 from IPython.core.interactiveshell import InteractiveShell
1177 1177 from IPython.core.pylabtools import select_figure_formats
1178 1178 # build kwargs, starting with InlineBackend config
1179 1179 kw = {}
1180 1180 from ipykernel.pylab.config import InlineBackend
1181 1181 cfg = InlineBackend.instance()
1182 1182 kw.update(cfg.print_figure_kwargs)
1183 1183 kw.update(**kwargs)
1184 1184 shell = InteractiveShell.instance()
1185 1185 select_figure_formats(shell, formats, **kw)
1186 1186
1187 1187 @skip_doctest
1188 1188 def set_matplotlib_close(close=True):
1189 1189 """Set whether the inline backend closes all figures automatically or not.
1190 1190
1191 1191 By default, the inline backend used in the IPython Notebook will close all
1192 1192 matplotlib figures automatically after each cell is run. This means that
1193 1193 plots in different cells won't interfere. Sometimes, you may want to make
1194 1194 a plot in one cell and then refine it in later cells. This can be accomplished
1195 1195 by::
1196 1196
1197 1197 In [1]: set_matplotlib_close(False)
1198 1198
1199 1199 To set this in your config files use the following::
1200 1200
1201 1201 c.InlineBackend.close_figures = False
1202 1202
1203 1203 Parameters
1204 1204 ----------
1205 1205 close : bool
1206 1206 Should all matplotlib figures be automatically closed after each cell is
1207 1207 run?
1208 1208 """
1209 1209 from ipykernel.pylab.config import InlineBackend
1210 1210 cfg = InlineBackend.instance()
1211 1211 cfg.close_figures = close
General Comments 0
You need to be logged in to leave comments. Login now