1"""Completion for IPython.
2
3This module started as fork of the rlcompleter module in the Python standard
4library. The original enhancements made to rlcompleter have been sent
5upstream and were accepted as of Python 2.3,
6
7This module now support a wide variety of completion mechanism both available
8for normal classic Python code, as well as completer for IPython specific
9Syntax like magics.
10
11Latex and Unicode completion
12============================
13
14IPython and compatible frontends not only can complete your code, but can help
15you to input a wide range of characters. In particular we allow you to insert
16a unicode character using the tab completion mechanism.
17
18Forward latex/unicode completion
19--------------------------------
20
21Forward completion allows you to easily type a unicode character using its latex
22name, or unicode long description. To do so type a backslash follow by the
23relevant name and press tab:
24
25
26Using latex completion:
27
28.. code::
29
30 \\alpha<tab>
31 α
32
33or using unicode completion:
34
35
36.. code::
37
38 \\GREEK SMALL LETTER ALPHA<tab>
39 α
40
41
42Only valid Python identifiers will complete. Combining characters (like arrow or
43dots) are also available, unlike latex they need to be put after the their
44counterpart that is to say, ``F\\\\vec<tab>`` is correct, not ``\\\\vec<tab>F``.
45
46Some browsers are known to display combining characters incorrectly.
47
48Backward latex completion
49-------------------------
50
51It is sometime challenging to know how to type a character, if you are using
52IPython, or any compatible frontend you can prepend backslash to the character
53and press :kbd:`Tab` to expand it to its latex form.
54
55.. code::
56
57 \\α<tab>
58 \\alpha
59
60
61Both forward and backward completions can be deactivated by setting the
62:std:configtrait:`Completer.backslash_combining_completions` option to
63``False``.
64
65
66Experimental
67============
68
69Starting with IPython 6.0, this module can make use of the Jedi library to
70generate completions both using static analysis of the code, and dynamically
71inspecting multiple namespaces. Jedi is an autocompletion and static analysis
72for Python. The APIs attached to this new mechanism is unstable and will
73raise unless use in an :any:`provisionalcompleter` context manager.
74
75You will find that the following are experimental:
76
77 - :any:`provisionalcompleter`
78 - :any:`IPCompleter.completions`
79 - :any:`Completion`
80 - :any:`rectify_completions`
81
82.. note::
83
84 better name for :any:`rectify_completions` ?
85
86We welcome any feedback on these new API, and we also encourage you to try this
87module in debug mode (start IPython with ``--Completer.debug=True``) in order
88to have extra logging information if :mod:`jedi` is crashing, or if current
89IPython completer pending deprecations are returning results not yet handled
90by :mod:`jedi`
91
92Using Jedi for tab completion allow snippets like the following to work without
93having to execute any code:
94
95 >>> myvar = ['hello', 42]
96 ... myvar[1].bi<tab>
97
98Tab completion will be able to infer that ``myvar[1]`` is a real number without
99executing almost any code unlike the deprecated :any:`IPCompleter.greedy`
100option.
101
102Be sure to update :mod:`jedi` to the latest stable version or to try the
103current development version to get better completions.
104
105Matchers
106========
107
108All completions routines are implemented using unified *Matchers* API.
109The matchers API is provisional and subject to change without notice.
110
111The built-in matchers include:
112
113- :any:`IPCompleter.dict_key_matcher`: dictionary key completions,
114- :any:`IPCompleter.magic_matcher`: completions for magics,
115- :any:`IPCompleter.unicode_name_matcher`,
116 :any:`IPCompleter.fwd_unicode_matcher`
117 and :any:`IPCompleter.latex_name_matcher`: see `Forward latex/unicode completion`_,
118- :any:`back_unicode_name_matcher` and :any:`back_latex_name_matcher`: see `Backward latex completion`_,
119- :any:`IPCompleter.file_matcher`: paths to files and directories,
120- :any:`IPCompleter.python_func_kw_matcher` - function keywords,
121- :any:`IPCompleter.python_matcher` - globals and attributes,
122- ``IPCompleter.jedi_matcher`` - static analysis with Jedi,
123- :any:`IPCompleter.custom_completer_matcher` - pluggable completer with a default
124 implementation in :any:`InteractiveShell` which uses IPython hooks system
125 (`complete_command`) with string dispatch (including regular expressions).
126 Differently to other matchers, ``custom_completer_matcher`` will not suppress
127 Jedi results to match behaviour in earlier IPython versions.
128
129Custom matchers can be added by appending to ``IPCompleter.custom_matchers`` list.
130
131Matcher API
132-----------
133
134Simplifying some details, the ``Matcher`` interface can described as
135
136.. code-block::
137
138 MatcherAPIv1 = Callable[[str], list[str]]
139 MatcherAPIv2 = Callable[[CompletionContext], SimpleMatcherResult]
140
141 Matcher = MatcherAPIv1 | MatcherAPIv2
142
143The ``MatcherAPIv1`` reflects the matcher API as available prior to IPython 8.6.0
144and remains supported as a simplest way for generating completions. This is also
145currently the only API supported by the IPython hooks system `complete_command`.
146
147To distinguish between matcher versions ``matcher_api_version`` attribute is used.
148More precisely, the API allows to omit ``matcher_api_version`` for v1 Matchers,
149and requires a literal ``2`` for v2 Matchers.
150
151Once the API stabilises future versions may relax the requirement for specifying
152``matcher_api_version`` by switching to :func:`functools.singledispatch`, therefore
153please do not rely on the presence of ``matcher_api_version`` for any purposes.
154
155Suppression of competing matchers
156---------------------------------
157
158By default results from all matchers are combined, in the order determined by
159their priority. Matchers can request to suppress results from subsequent
160matchers by setting ``suppress`` to ``True`` in the ``MatcherResult``.
161
162When multiple matchers simultaneously request suppression, the results from of
163the matcher with higher priority will be returned.
164
165Sometimes it is desirable to suppress most but not all other matchers;
166this can be achieved by adding a set of identifiers of matchers which
167should not be suppressed to ``MatcherResult`` under ``do_not_suppress`` key.
168
169The suppression behaviour can is user-configurable via
170:std:configtrait:`IPCompleter.suppress_competing_matchers`.
171"""
172
173
174# Copyright (c) IPython Development Team.
175# Distributed under the terms of the Modified BSD License.
176#
177# Some of this code originated from rlcompleter in the Python standard library
178# Copyright (C) 2001 Python Software Foundation, www.python.org
179
180from __future__ import annotations
181import builtins as builtin_mod
182import enum
183import glob
184import importlib.util
185import inspect
186import itertools
187import keyword
188import ast
189import os
190import re
191import string
192import sys
193import tokenize
194import time
195import unicodedata
196import uuid
197import warnings
198from ast import literal_eval
199from collections import defaultdict
200from contextlib import contextmanager
201from dataclasses import dataclass
202from functools import cached_property, lru_cache, partial
203from types import ModuleType, SimpleNamespace
204from typing import (
205 Union,
206 Any,
207 TYPE_CHECKING,
208 TypeVar,
209 Literal,
210)
211from collections.abc import Iterable, Iterator, Sequence, Sized
212
213from IPython.core.guarded_eval import (
214 guarded_eval,
215 EvaluationContext,
216 _validate_policy_overrides,
217)
218from IPython.core.error import TryNext, UsageError
219from IPython.core.inputtransformer2 import (
220 ESC_MAGIC,
221 SystemAssign,
222 make_tokens_by_line,
223)
224from IPython.core.latex_symbols import latex_symbols, reverse_latex_symbol
225from IPython.testing.skipdoctest import skip_doctest
226from IPython.utils import generics
227from IPython.utils.PyColorize import theme_table
228from IPython.utils.decorators import sphinx_options
229from IPython.utils.dir2 import dir2, get_real_method
230from IPython.utils.path import ensure_dir_exists
231from IPython.utils.process import arg_split
232from traitlets import (
233 Bool,
234 Enum,
235 Int,
236 List as ListTrait,
237 Unicode,
238 Dict as DictTrait,
239 DottedObjectName,
240 Union as UnionTrait,
241 observe,
242)
243from traitlets.config.configurable import Configurable
244from traitlets.utils.importstring import import_item
245
246import __main__
247
248from typing import cast, TypedDict, NotRequired, Protocol, TypeAlias, TypeGuard
249
250
251# skip module docstests
252__skip_doctest__ = True
253
254
255# jedi is expensive to import (it pulls in parso, which compiles grammars), so
256# only check for its presence here and import it lazily via `_get_jedi()` the
257# first time a completion actually needs it. This keeps `import IPython` fast.
258if TYPE_CHECKING:
259 import jedi
260
261JEDI_INSTALLED = importlib.util.find_spec("jedi") is not None
262
263
264@lru_cache(maxsize=1)
265def _get_jedi() -> ModuleType:
266 """Import, configure, and return the ``jedi`` module (cached)."""
267 import jedi
268 import jedi.api.classes
269 import jedi.api.helpers
270
271 jedi.settings.case_insensitive_completion = False
272 return jedi
273
274
275# -----------------------------------------------------------------------------
276# Globals
277#-----------------------------------------------------------------------------
278
279# ranges where we have most of the valid unicode names. We could be more finer
280# grained but is it worth it for performance While unicode have character in the
281# range 0, 0x110000, we seem to have name for about 10% of those. (131808 as I
282# write this). With below range we cover them all, with a density of ~67%
283# biggest next gap we consider only adds up about 1% density and there are 600
284# gaps that would need hard coding.
285_UNICODE_RANGES = [(32, 0x3347A), (0xE0001, 0xE01F0)]
286
287# Public API
288__all__ = ["Completer", "IPCompleter"]
289
290if sys.platform == 'win32':
291 PROTECTABLES = ' '
292else:
293 PROTECTABLES = ' ()[]{}?=\\|;:\'#*"^&'
294
295# Protect against returning an enormous number of completions which the frontend
296# may have trouble processing.
297MATCHES_LIMIT = 500
298
299# Completion type reported when no type can be inferred.
300_UNKNOWN_TYPE = "<unknown>"
301
302# sentinel value to signal lack of a match
303not_found = object()
304
305# Regexes compiled once at import time; some of these are used on every
306# completion request, so recompiling them per call would be wasteful.
307_SNAKE_CASE_RE = re.compile(r"[^_]+(_[^_]+)+?\Z")
308_LEADING_DASHES_RE = re.compile(r"^--", re.MULTILINE)
309_IDENTIFIER_END_RE = re.compile(r"\w+$")
310
311class ProvisionalCompleterWarning(FutureWarning):
312 """
313 Exception raise by an experimental feature in this module.
314
315 Wrap code in :any:`provisionalcompleter` context manager if you
316 are certain you want to use an unstable feature.
317 """
318 pass
319
320warnings.filterwarnings('error', category=ProvisionalCompleterWarning)
321
322
323@skip_doctest
324@contextmanager
325def provisionalcompleter(action='ignore'):
326 """
327 This context manager has to be used in any place where unstable completer
328 behavior and API may be called.
329
330 >>> with provisionalcompleter():
331 ... completer.do_experimental_things() # works
332
333 >>> completer.do_experimental_things() # raises.
334
335 .. note::
336
337 Unstable
338
339 By using this context manager you agree that the API in use may change
340 without warning, and that you won't complain if they do so.
341
342 You also understand that, if the API is not to your liking, you should report
343 a bug to explain your use case upstream.
344
345 We'll be happy to get your feedback, feature requests, and improvements on
346 any of the unstable APIs!
347 """
348 with warnings.catch_warnings():
349 warnings.filterwarnings(action, category=ProvisionalCompleterWarning)
350 yield
351
352
353def has_open_quotes(s: str) -> str | bool:
354 """Return whether a string has open quotes.
355
356 This simply counts whether the number of quote characters of either type in
357 the string is odd.
358
359 Returns
360 -------
361 If there is an open quote, the quote character is returned. Else, return
362 False.
363 """
364 # We check " first, then ', so complex cases with nested quotes will get
365 # the " to take precedence.
366 if s.count('"') % 2:
367 return '"'
368 elif s.count("'") % 2:
369 return "'"
370 else:
371 return False
372
373
374def protect_filename(s: str, protectables: str = PROTECTABLES) -> str:
375 """Escape a string to protect certain characters."""
376 if set(s) & set(protectables):
377 if sys.platform == "win32":
378 return '"' + s + '"'
379 else:
380 return "".join(("\\" + c if c in protectables else c) for c in s)
381 else:
382 return s
383
384
385def expand_user(path: str) -> tuple[str, bool, str]:
386 """Expand ``~``-style usernames in strings.
387
388 This is similar to :func:`os.path.expanduser`, but it computes and returns
389 extra information that will be useful if the input was being used in
390 computing completions, and you wish to return the completions with the
391 original '~' instead of its expanded value.
392
393 Parameters
394 ----------
395 path : str
396 String to be expanded. If no ~ is present, the output is the same as the
397 input.
398
399 Returns
400 -------
401 newpath : str
402 Result of ~ expansion in the input path.
403 tilde_expand : bool
404 Whether any expansion was performed or not.
405 tilde_val : str
406 The value that ~ was replaced with.
407 """
408 # Default values
409 tilde_expand = False
410 tilde_val = ''
411 newpath = path
412
413 if path.startswith('~'):
414 tilde_expand = True
415 rest = len(path)-1
416 newpath = os.path.expanduser(path)
417 if rest:
418 tilde_val = newpath[:-rest]
419 else:
420 tilde_val = newpath
421
422 return newpath, tilde_expand, tilde_val
423
424
425def compress_user(path:str, tilde_expand:bool, tilde_val:str) -> str:
426 """Does the opposite of expand_user, with its outputs.
427 """
428 if tilde_expand:
429 return path.replace(tilde_val, '~')
430 else:
431 return path
432
433
434def completions_sorting_key(word):
435 """key for sorting completions
436
437 This does several things:
438
439 - Demote any completions starting with underscores to the end
440 - Insert any %magic and %%cellmagic completions in the alphabetical order
441 by their name
442 """
443 prio1, prio2 = 0, 0
444
445 if word.startswith('__'):
446 prio1 = 2
447 elif word.startswith('_'):
448 prio1 = 1
449
450 if word.endswith('='):
451 prio1 = -1
452
453 if word.startswith('%%'):
454 # If there's another % in there, this is something else, so leave it alone
455 if "%" not in word[2:]:
456 word = word[2:]
457 prio2 = 2
458 elif word.startswith('%'):
459 if "%" not in word[1:]:
460 word = word[1:]
461 prio2 = 1
462
463 return prio1, word, prio2
464
465
466class _FakeJediCompletion:
467 """
468 This is a workaround to communicate to the UI that Jedi has crashed and to
469 report a bug. Will be used only id :any:`IPCompleter.debug` is set to true.
470
471 Added in IPython 6.0 so should likely be removed for 7.0
472
473 """
474
475 def __init__(self, name):
476
477 self.name = name
478 self.complete = name
479 self.type = 'crashed'
480 self.name_with_symbols = name
481 self.signature = ""
482 self._origin = "fake"
483 self.text = "crashed"
484
485 def __repr__(self):
486 return '<Fake completion object jedi has crashed>'
487
488
489_JediCompletionLike = Union["jedi.api.Completion", _FakeJediCompletion]
490
491
492class Completion:
493 """
494 Completion object used and returned by IPython completers.
495
496 .. warning::
497
498 Unstable
499
500 This function is unstable, API may change without warning.
501 It will also raise unless use in proper context manager.
502
503 This act as a middle ground :any:`Completion` object between the
504 :class:`jedi.api.classes.Completion` object and the Prompt Toolkit completion
505 object. While Jedi need a lot of information about evaluator and how the
506 code should be ran/inspected, PromptToolkit (and other frontend) mostly
507 need user facing information.
508
509 - Which range should be replaced replaced by what.
510 - Some metadata (like completion type), or meta information to displayed to
511 the use user.
512
513 For debugging purpose we can also store the origin of the completion (``jedi``,
514 ``IPython.python_matches``, ``IPython.magics_matches``...).
515 """
516
517 __slots__ = ["_origin", "end", "signature", "start", "text", "type"]
518
519 def __init__(
520 self,
521 start: int,
522 end: int,
523 text: str,
524 *,
525 type: str | None = None,
526 _origin="",
527 signature="",
528 ) -> None:
529 warnings.warn(
530 "``Completion`` is a provisional API (as of IPython 6.0). "
531 "It may change without warnings. "
532 "Use in corresponding context manager.",
533 category=ProvisionalCompleterWarning,
534 stacklevel=2,
535 )
536
537 self.start = start
538 self.end = end
539 self.text = text
540 self.type = type
541 self.signature = signature
542 self._origin = _origin
543
544 def __repr__(self):
545 return '<Completion start=%s end=%s text=%r type=%r, signature=%r,>' % \
546 (self.start, self.end, self.text, self.type or '?', self.signature or '?')
547
548 def __eq__(self, other) -> bool:
549 """
550 Equality and hash do not hash the type (as some completer may not be
551 able to infer the type), but are use to (partially) de-duplicate
552 completion.
553
554 Completely de-duplicating completion is a bit tricker that just
555 comparing as it depends on surrounding text, which Completions are not
556 aware of.
557 """
558 return self.start == other.start and \
559 self.end == other.end and \
560 self.text == other.text
561
562 def __hash__(self):
563 return hash((self.start, self.end, self.text))
564
565
566class SimpleCompletion:
567 """Completion item to be included in the dictionary returned by new-style Matcher (API v2).
568
569 .. warning::
570
571 Provisional
572
573 This class is used to describe the currently supported attributes of
574 simple completion items, and any additional implementation details
575 should not be relied on. Additional attributes may be included in
576 future versions, and meaning of text disambiguated from the current
577 dual meaning of "text to insert" and "text to used as a label".
578 """
579
580 __slots__ = ["text", "type"]
581
582 def __init__(self, text: str, *, type: str | None = None):
583 self.text = text
584 self.type = type
585
586 def __repr__(self):
587 return f"<SimpleCompletion text={self.text!r} type={self.type!r}>"
588
589
590class _MatcherResultBase(TypedDict):
591 """Definition of dictionary to be returned by new-style Matcher (API v2)."""
592
593 #: Suffix of the provided ``CompletionContext.token``, if not given defaults to full token.
594 matched_fragment: NotRequired[str]
595
596 #: Whether to suppress results from all other matchers (True), some
597 #: matchers (set of identifiers) or none (False); default is False.
598 suppress: NotRequired[bool | set[str]]
599
600 #: Identifiers of matchers which should NOT be suppressed when this matcher
601 #: requests to suppress all other matchers; defaults to an empty set.
602 do_not_suppress: NotRequired[set[str]]
603
604 #: Are completions already ordered and should be left as-is? default is False.
605 ordered: NotRequired[bool]
606
607
608@sphinx_options(show_inherited_members=True, exclude_inherited_from=["dict"])
609class SimpleMatcherResult(_MatcherResultBase, TypedDict):
610 """Result of new-style completion matcher."""
611
612 # note: TypedDict is added again to the inheritance chain
613 # in order to get __orig_bases__ for documentation
614
615 #: List of candidate completions
616 completions: Sequence[SimpleCompletion] | Iterator[SimpleCompletion]
617
618
619class _JediMatcherResult(_MatcherResultBase):
620 """Matching result returned by Jedi (will be processed differently)"""
621
622 #: list of candidate completions
623 completions: Iterator[_JediCompletionLike]
624
625
626AnyMatcherCompletion = _JediCompletionLike | SimpleCompletion
627AnyCompletion = TypeVar("AnyCompletion", AnyMatcherCompletion, Completion)
628
629
630@dataclass
631class CompletionContext:
632 """Completion context provided as an argument to matchers in the Matcher API v2."""
633
634 # rationale: many legacy matchers relied on completer state (`self.text_until_cursor`)
635 # which was not explicitly visible as an argument of the matcher, making any refactor
636 # prone to errors; by explicitly passing `cursor_position` we can decouple the matchers
637 # from the completer, and make substituting them in sub-classes easier.
638
639 #: Relevant fragment of code directly preceding the cursor.
640 #: The extraction of token is implemented via splitter heuristic
641 #: (following readline behaviour for legacy reasons), which is user configurable
642 #: (by switching the greedy mode).
643 token: str
644
645 #: The full available content of the editor or buffer
646 full_text: str
647
648 #: Cursor position in the line (the same for ``full_text`` and ``text``).
649 cursor_position: int
650
651 #: Cursor line in ``full_text``.
652 cursor_line: int
653
654 #: The maximum number of completions that will be used downstream.
655 #: Matchers can use this information to abort early.
656 #: The built-in Jedi matcher is currently excepted from this limit.
657 # If not given, return all possible completions.
658 limit: int | None
659
660 @cached_property
661 def text_until_cursor(self) -> str:
662 return self.line_with_cursor[: self.cursor_position]
663
664 @cached_property
665 def line_with_cursor(self) -> str:
666 return self.full_text.split("\n")[self.cursor_line]
667
668
669#: Matcher results for API v2.
670MatcherResult = SimpleMatcherResult | _JediMatcherResult
671
672
673class _MatcherAPIv1Base(Protocol):
674 def __call__(self, text: str) -> list[str]:
675 """Call signature."""
676 ...
677
678 #: Used to construct the default matcher identifier
679 __qualname__: str
680
681
682class _MatcherAPIv1Total(_MatcherAPIv1Base, Protocol):
683 #: API version
684 matcher_api_version: Literal[1] | None
685
686 def __call__(self, text: str) -> list[str]:
687 """Call signature."""
688 ...
689
690
691#: Protocol describing Matcher API v1.
692MatcherAPIv1: TypeAlias = _MatcherAPIv1Base | _MatcherAPIv1Total
693
694
695class MatcherAPIv2(Protocol):
696 """Protocol describing Matcher API v2."""
697
698 #: API version
699 matcher_api_version: Literal[2] = 2
700
701 def __call__(self, context: CompletionContext) -> MatcherResult:
702 """Call signature."""
703 ...
704
705 #: Used to construct the default matcher identifier
706 __qualname__: str
707
708
709Matcher: TypeAlias = MatcherAPIv1 | MatcherAPIv2
710
711
712def _is_matcher_v1(matcher: Matcher) -> TypeGuard[MatcherAPIv1]:
713 api_version = _get_matcher_api_version(matcher)
714 return api_version == 1
715
716
717def _is_matcher_v2(matcher: Matcher) -> TypeGuard[MatcherAPIv2]:
718 api_version = _get_matcher_api_version(matcher)
719 return api_version == 2
720
721
722def _is_sizable(value: Any) -> TypeGuard[Sized]:
723 """Determines whether objects is sizable"""
724 return hasattr(value, "__len__")
725
726
727def _is_iterator(value: Any) -> TypeGuard[Iterator]:
728 """Determines whether objects is sizable"""
729 return hasattr(value, "__next__")
730
731
732def has_any_completions(result: MatcherResult) -> bool:
733 """Check if any result includes any completions."""
734 completions = result["completions"]
735 if _is_sizable(completions):
736 return len(completions) != 0
737 if _is_iterator(completions):
738 try:
739 old_iterator = completions
740 first = next(old_iterator)
741 result["completions"] = cast(
742 Iterator[SimpleCompletion],
743 itertools.chain([first], old_iterator),
744 )
745 return True
746 except StopIteration:
747 return False
748 raise ValueError(
749 "Completions returned by matcher need to be an Iterator or a Sizable"
750 )
751
752
753def completion_matcher(
754 *,
755 priority: float | None = None,
756 identifier: str | None = None,
757 api_version: int = 1,
758) -> Callable[[Matcher], Matcher]:
759 """Adds attributes describing the matcher.
760
761 Parameters
762 ----------
763 priority : Optional[float]
764 The priority of the matcher, determines the order of execution of matchers.
765 Higher priority means that the matcher will be executed first. Defaults to 0.
766 identifier : Optional[str]
767 identifier of the matcher allowing users to modify the behaviour via traitlets,
768 and also used to for debugging (will be passed as ``origin`` with the completions).
769
770 Defaults to matcher function's ``__qualname__`` (for example,
771 ``IPCompleter.file_matcher`` for the built-in matched defined
772 as a ``file_matcher`` method of the ``IPCompleter`` class).
773 api_version: Optional[int]
774 version of the Matcher API used by this matcher.
775 Currently supported values are 1 and 2.
776 Defaults to 1.
777 """
778
779 def wrapper(func: Matcher):
780 func.matcher_priority = priority or 0 # type: ignore
781 func.matcher_identifier = identifier or func.__qualname__ # type: ignore
782 func.matcher_api_version = api_version # type: ignore
783 if TYPE_CHECKING:
784 if api_version == 1:
785 func = cast(MatcherAPIv1, func)
786 elif api_version == 2:
787 func = cast(MatcherAPIv2, func)
788 return func
789
790 return wrapper
791
792
793def _get_matcher_priority(matcher: Matcher):
794 return getattr(matcher, "matcher_priority", 0)
795
796
797def _get_matcher_id(matcher: Matcher):
798 return getattr(matcher, "matcher_identifier", matcher.__qualname__)
799
800
801def _get_matcher_api_version(matcher):
802 return getattr(matcher, "matcher_api_version", 1)
803
804
805context_matcher = partial(completion_matcher, api_version=2)
806
807
808_IC = Iterable[Completion]
809
810
811def _deduplicate_completions(text: str, completions: _IC)-> _IC:
812 """
813 Deduplicate a set of completions.
814
815 .. warning::
816
817 Unstable
818
819 This function is unstable, API may change without warning.
820
821 Parameters
822 ----------
823 text : str
824 text that should be completed.
825 completions : Iterator[Completion]
826 iterator over the completions to deduplicate
827
828 Yields
829 ------
830 `Completions` objects
831 Completions coming from multiple sources, may be different but end up having
832 the same effect when applied to ``text``. If this is the case, this will
833 consider completions as equal and only emit the first encountered.
834 Not folded in `completions()` yet for debugging purpose, and to detect when
835 the IPython completer does return things that Jedi does not, but should be
836 at some point.
837 """
838 completions = list(completions)
839 if not completions:
840 return
841
842 new_start = min(c.start for c in completions)
843 new_end = max(c.end for c in completions)
844
845 seen = set()
846 for c in completions:
847 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
848 if new_text not in seen:
849 yield c
850 seen.add(new_text)
851
852
853def rectify_completions(text: str, completions: _IC, *, _debug: bool = False) -> _IC:
854 """
855 Rectify a set of completions to all have the same ``start`` and ``end``
856
857 .. warning::
858
859 Unstable
860
861 This function is unstable, API may change without warning.
862 It will also raise unless use in proper context manager.
863
864 Parameters
865 ----------
866 text : str
867 text that should be completed.
868 completions : Iterator[Completion]
869 iterator over the completions to rectify
870 _debug : bool
871 Log failed completion
872
873 Notes
874 -----
875 :class:`jedi.api.classes.Completion` s returned by Jedi may not have the same start and end, though
876 the Jupyter Protocol requires them to behave like so. This will readjust
877 the completion to have the same ``start`` and ``end`` by padding both
878 extremities with surrounding text.
879
880 During stabilisation should support a ``_debug`` option to log which
881 completion are return by the IPython completer and not found in Jedi in
882 order to make upstream bug report.
883 """
884 warnings.warn("`rectify_completions` is a provisional API (as of IPython 6.0). "
885 "It may change without warnings. "
886 "Use in corresponding context manager.",
887 category=ProvisionalCompleterWarning, stacklevel=2)
888
889 completions = list(completions)
890 if not completions:
891 return
892 starts = (c.start for c in completions)
893 ends = (c.end for c in completions)
894
895 new_start = min(starts)
896 new_end = max(ends)
897
898 seen_jedi = set()
899 seen_python_matches = set()
900 for c in completions:
901 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
902 if c._origin == 'jedi':
903 seen_jedi.add(new_text)
904 elif c._origin == "IPCompleter.python_matcher":
905 seen_python_matches.add(new_text)
906 yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin, signature=c.signature)
907 diff = seen_python_matches.difference(seen_jedi)
908 if diff and _debug:
909 print('IPython.python matches have extras:', diff)
910
911
912if sys.platform == 'win32':
913 DELIMS = ' \t\n`!@#$^&*()=+[{]}|;\'",<>?'
914else:
915 DELIMS = ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?'
916
917GREEDY_DELIMS = ' =\r\n'
918
919
920class CompletionSplitter:
921 """An object to split an input line in a manner similar to readline.
922
923 By having our own implementation, we can expose readline-like completion in
924 a uniform manner to all frontends. This object only needs to be given the
925 line of text to be split and the cursor position on said line, and it
926 returns the 'word' to be completed on at the cursor after splitting the
927 entire line.
928
929 What characters are used as splitting delimiters can be controlled by
930 setting the ``delims`` attribute (this is a property that internally
931 automatically builds the necessary regular expression)"""
932
933 # Private interface
934
935 # A string of delimiter characters. The default value makes sense for
936 # IPython's most typical usage patterns.
937 _delims = DELIMS
938
939 # The expression (a normal string) to be compiled into a regular expression
940 # for actual splitting. We store it as an attribute mostly for ease of
941 # debugging, since this type of code can be so tricky to debug.
942 _delim_expr = None
943
944 # The regular expression that does the actual splitting
945 _delim_re = None
946
947 def __init__(self, delims=None):
948 delims = CompletionSplitter._delims if delims is None else delims
949 self.delims = delims
950
951 @property
952 def delims(self):
953 """Return the string of delimiter characters."""
954 return self._delims
955
956 @delims.setter
957 def delims(self, delims):
958 """Set the delimiters for line splitting."""
959 expr = '[' + ''.join('\\'+ c for c in delims) + ']'
960 self._delim_re = re.compile(expr)
961 self._delims = delims
962 self._delim_expr = expr
963
964 def split_line(self, line, cursor_pos=None):
965 """Split a line of text with a cursor at the given position.
966 """
967 cut_line = line if cursor_pos is None else line[:cursor_pos]
968 return self._delim_re.split(cut_line)[-1]
969
970
971class Completer(Configurable):
972
973 greedy = Bool(
974 False,
975 help="""Activate greedy completion.
976
977 .. deprecated:: 8.8
978 Use :std:configtrait:`Completer.evaluation` and :std:configtrait:`Completer.auto_close_dict_keys` instead.
979
980 When enabled in IPython 8.8 or newer, changes configuration as follows:
981
982 - ``Completer.evaluation = 'unsafe'``
983 - ``Completer.auto_close_dict_keys = True``
984
985 Kept (deprecated, not yet removed) because downstream projects'
986 test suites still set it via ``%config``.
987 """,
988 ).tag(config=True)
989
990 evaluation = Enum(
991 ("forbidden", "minimal", "limited", "unsafe", "dangerous"),
992 default_value="limited",
993 help="""Policy for code evaluation under completion.
994
995 Successive options allow to enable more eager evaluation for better
996 completion suggestions, including for nested dictionaries, nested lists,
997 or even results of function calls.
998 Setting ``unsafe`` or higher can lead to evaluation of arbitrary user
999 code on :kbd:`Tab` with potentially unwanted or dangerous side effects.
1000
1001 Allowed values are:
1002
1003 - ``forbidden``: no evaluation of code is permitted,
1004 - ``minimal``: evaluation of literals and access to built-in namespace;
1005 no item/attribute evaluation, no access to locals/globals,
1006 no evaluation of any operations or comparisons.
1007 - ``limited``: access to all namespaces, evaluation of hard-coded methods
1008 (for example: :py:meth:`dict.keys`, :py:meth:`object.__getattr__`,
1009 :py:meth:`object.__getitem__`) on allow-listed objects (for example:
1010 :py:class:`dict`, :py:class:`list`, :py:class:`tuple`, ``pandas.Series``),
1011 - ``unsafe``: evaluation of all methods and function calls but not of
1012 syntax with side-effects like `del x`,
1013 - ``dangerous``: completely arbitrary evaluation; does not support auto-import.
1014
1015 To override specific elements of the policy, you can use ``policy_overrides`` trait.
1016 """,
1017 ).tag(config=True)
1018
1019 use_jedi = Bool(default_value=JEDI_INSTALLED,
1020 help="Experimental: Use Jedi to generate autocompletions. "
1021 "Default to True if jedi is installed.").tag(config=True)
1022
1023 jedi_compute_type_timeout = Int(default_value=400,
1024 help="""Experimental: restrict time (in milliseconds) during which Jedi can compute types.
1025 Set to 0 to stop computing types. Non-zero value lower than 100ms may hurt
1026 performance by preventing jedi to build its cache.
1027 """).tag(config=True)
1028
1029 debug = Bool(default_value=False,
1030 help='Enable debug for the Completer. Mostly print extra '
1031 'information for experimental jedi integration.')\
1032 .tag(config=True)
1033
1034 backslash_combining_completions = Bool(True,
1035 help="Enable unicode completions, e.g. \\alpha<tab> . "
1036 "Includes completion of latex commands, unicode names, and expanding "
1037 "unicode characters back to latex commands.").tag(config=True)
1038
1039 auto_close_dict_keys = Bool(
1040 False,
1041 help="""
1042 Enable auto-closing dictionary keys.
1043
1044 When enabled string keys will be suffixed with a final quote
1045 (matching the opening quote), tuple keys will also receive a
1046 separating comma if needed, and keys which are final will
1047 receive a closing bracket (``]``).
1048 """,
1049 ).tag(config=True)
1050
1051 policy_overrides = DictTrait(
1052 default_value={},
1053 key_trait=Unicode(),
1054 help="""Overrides for policy evaluation.
1055
1056 For example, to enable auto-import on completion specify:
1057
1058 .. code-block::
1059
1060 ipython --Completer.policy_overrides='{"allow_auto_import": True}' --Completer.use_jedi=False
1061
1062 """,
1063 ).tag(config=True)
1064
1065 @observe("evaluation")
1066 def _evaluation_changed(self, _change):
1067 _validate_policy_overrides(
1068 policy_name=self.evaluation, policy_overrides=self.policy_overrides
1069 )
1070
1071 @observe("policy_overrides")
1072 def _policy_overrides_changed(self, _change):
1073 _validate_policy_overrides(
1074 policy_name=self.evaluation, policy_overrides=self.policy_overrides
1075 )
1076
1077 auto_import_method = DottedObjectName(
1078 default_value="importlib.import_module",
1079 allow_none=True,
1080 help="""\
1081 Provisional:
1082 This is a provisional API in IPython 9.3, it may change without warnings.
1083
1084 A fully qualified path to an auto-import method for use by completer.
1085 The function should take a single string and return `ModuleType` and
1086 can raise `ImportError` exception if module is not found.
1087
1088 The default auto-import implementation does not populate the user namespace with the imported module.
1089 """,
1090 ).tag(config=True)
1091
1092 def __init__(self, namespace=None, global_namespace=None, **kwargs):
1093 """Create a new completer for the command line.
1094
1095 Completer(namespace=ns, global_namespace=ns2) -> completer instance.
1096
1097 If unspecified, the default namespace where completions are performed
1098 is __main__ (technically, __main__.__dict__). Namespaces should be
1099 given as dictionaries.
1100
1101 An optional second namespace can be given. This allows the completer
1102 to handle cases where both the local and global scopes need to be
1103 distinguished.
1104 """
1105
1106 # Don't bind to namespace quite yet, but flag whether the user wants a
1107 # specific namespace or to use __main__.__dict__. This will allow us
1108 # to bind to __main__.__dict__ at completion time, not now.
1109 if namespace is None:
1110 self.use_main_ns = True
1111 else:
1112 self.use_main_ns = False
1113 self.namespace = namespace
1114
1115 # The global namespace, if given, can be bound directly
1116 if global_namespace is None:
1117 self.global_namespace = {}
1118 else:
1119 self.global_namespace = global_namespace
1120
1121 self.custom_matchers = []
1122
1123 super().__init__(**kwargs)
1124
1125 def complete(self, text, state):
1126 """Return the next possible completion for 'text'.
1127
1128 This is called successively with state == 0, 1, 2, ... until it
1129 returns None. The completion should begin with 'text'.
1130
1131 """
1132 if self.use_main_ns:
1133 self.namespace = __main__.__dict__
1134
1135 if state == 0:
1136 if "." in text:
1137 self.matches = self.attr_matches(text)
1138 else:
1139 self.matches = self.global_matches(text)
1140 try:
1141 return self.matches[state]
1142 except IndexError:
1143 return None
1144
1145 def global_matches(self, text: str, context: CompletionContext | None = None):
1146 """Compute matches when text is a simple name.
1147
1148 Return a list of all keywords, built-in functions and names currently
1149 defined in self.namespace or self.global_namespace that match.
1150
1151 """
1152 matches = []
1153 match_append = matches.append
1154 n = len(text)
1155
1156 search_lists = [
1157 keyword.kwlist,
1158 builtin_mod.__dict__.keys(),
1159 list(self.namespace.keys()),
1160 list(self.global_namespace.keys()),
1161 ]
1162 if context and context.full_text.count("\n") > 1:
1163 # try to evaluate on full buffer
1164 previous_lines = "\n".join(
1165 context.full_text.split("\n")[: context.cursor_line]
1166 )
1167 if previous_lines:
1168 all_code_lines_before_cursor = (
1169 self._extract_code(previous_lines) + "\n" + text
1170 )
1171 context = EvaluationContext(
1172 globals=self.global_namespace,
1173 locals=self.namespace,
1174 evaluation=self.evaluation,
1175 auto_import=self._auto_import,
1176 policy_overrides=self.policy_overrides,
1177 )
1178 try:
1179 obj = guarded_eval(
1180 all_code_lines_before_cursor,
1181 context,
1182 )
1183 except Exception as e:
1184 if self.debug:
1185 warnings.warn(f"Evaluation exception {e}")
1186
1187 search_lists.append(list(context.transient_locals.keys()))
1188
1189 for lst in search_lists:
1190 for word in lst:
1191 if word[:n] == text and word != "__builtins__":
1192 match_append(word)
1193
1194 for lst in [list(self.namespace.keys()), list(self.global_namespace.keys())]:
1195 shortened = {
1196 "_".join([sub[0] for sub in word.split("_")]): word
1197 for word in lst
1198 if _SNAKE_CASE_RE.match(word)
1199 }
1200 for word in shortened.keys():
1201 if word[:n] == text and word != "__builtins__":
1202 match_append(shortened[word])
1203
1204 return matches
1205
1206 def attr_matches(self, text):
1207 """Compute matches when text contains a dot.
1208
1209 Assuming the text is of the form NAME.NAME....[NAME], and is
1210 evaluatable in self.namespace or self.global_namespace, it will be
1211 evaluated and its attributes (as revealed by dir()) are used as
1212 possible completions. (For class instances, class members are
1213 also considered.)
1214
1215 WARNING: this can still invoke arbitrary C code, if an object
1216 with a __getattr__ hook is evaluated.
1217
1218 """
1219 return self._attr_matches(text)[0]
1220
1221 # we simple attribute matching with normal identifiers.
1222 _ATTR_MATCH_RE = re.compile(r"(.+)\.(\w*)$")
1223
1224 def _strip_code_before_operator(self, code: str) -> str:
1225 o_parens = {"(", "[", "{"}
1226 c_parens = {")", "]", "}"}
1227
1228 # Dry-run tokenize to catch errors
1229 try:
1230 _ = list(tokenize.generate_tokens(iter(code.splitlines()).__next__))
1231 except tokenize.TokenError:
1232 # Try trimming the expression and retrying
1233 trimmed_code = self._trim_expr(code)
1234 try:
1235 _ = list(
1236 tokenize.generate_tokens(iter(trimmed_code.splitlines()).__next__)
1237 )
1238 code = trimmed_code
1239 except tokenize.TokenError:
1240 return code
1241
1242 tokens = _parse_tokens(code)
1243 encountered_operator = False
1244 after_operator = []
1245 nesting_level = 0
1246
1247 for t in tokens:
1248 if t.type == tokenize.OP:
1249 if t.string in o_parens:
1250 nesting_level += 1
1251 elif t.string in c_parens:
1252 nesting_level -= 1
1253 elif t.string != "." and nesting_level == 0:
1254 encountered_operator = True
1255 after_operator = []
1256 continue
1257
1258 if encountered_operator:
1259 after_operator.append(t.string)
1260
1261 if encountered_operator:
1262 return "".join(after_operator)
1263 else:
1264 return code
1265
1266 def _extract_code(self, line: str):
1267 """No-op in Completer, but can be used in subclasses to customise behaviour"""
1268 return line
1269
1270 def _attr_matches(
1271 self,
1272 text: str,
1273 include_prefix: bool = True,
1274 context: CompletionContext | None = None,
1275 ) -> tuple[Sequence[str], str]:
1276 m2 = self._ATTR_MATCH_RE.match(text)
1277 if not m2:
1278 return [], ""
1279 expr, attr = m2.group(1, 2)
1280 try:
1281 expr = self._strip_code_before_operator(expr)
1282 except tokenize.TokenError:
1283 pass
1284
1285 obj = self._evaluate_expr(expr)
1286 if obj is not_found:
1287 if context:
1288 # try to evaluate on full buffer
1289 previous_lines = "\n".join(
1290 context.full_text.split("\n")[: context.cursor_line]
1291 )
1292 if previous_lines:
1293 all_code_lines_before_cursor = (
1294 self._extract_code(previous_lines) + "\n" + expr
1295 )
1296 obj = self._evaluate_expr(all_code_lines_before_cursor)
1297
1298 if obj is not_found:
1299 return [], ""
1300
1301 words = dir2(obj)
1302
1303 try:
1304 words = generics.complete_object(obj, words)
1305 except TryNext:
1306 pass
1307 except AssertionError:
1308 raise
1309 except Exception:
1310 # Silence errors from completion function
1311 pass
1312 # Build match list to return
1313 n = len(attr)
1314
1315 # Note: ideally we would just return words here and the prefix
1316 # reconciliator would know that we intend to append to rather than
1317 # replace the input text; this requires refactoring to return range
1318 # which ought to be replaced (as does jedi).
1319 if include_prefix:
1320 tokens = _parse_tokens(expr)
1321 rev_tokens = reversed(tokens)
1322 skip_over = {tokenize.ENDMARKER, tokenize.NEWLINE}
1323 name_turn = True
1324
1325 parts = []
1326 for token in rev_tokens:
1327 if token.type in skip_over:
1328 continue
1329 if token.type == tokenize.NAME and name_turn:
1330 parts.append(token.string)
1331 name_turn = False
1332 elif (
1333 token.type == tokenize.OP and token.string == "." and not name_turn
1334 ):
1335 parts.append(token.string)
1336 name_turn = True
1337 else:
1338 # short-circuit if not empty nor name token
1339 break
1340
1341 prefix_after_space = "".join(reversed(parts))
1342 else:
1343 prefix_after_space = ""
1344
1345 return (
1346 ["{}.{}".format(prefix_after_space, w) for w in words if w[:n] == attr],
1347 "." + attr,
1348 )
1349
1350 def _trim_expr(self, code: str) -> str:
1351 """
1352 Trim the code until it is a valid expression and not a tuple;
1353
1354 return the trimmed expression for guarded_eval.
1355 """
1356 while code:
1357 code = code[1:]
1358 try:
1359 res = ast.parse(code)
1360 except SyntaxError:
1361 continue
1362
1363 assert res is not None
1364 if len(res.body) != 1:
1365 continue
1366 if not isinstance(res.body[0], ast.Expr):
1367 continue
1368 expr = res.body[0].value
1369 if isinstance(expr, ast.Tuple) and not code[-1] == ")":
1370 # we skip implicit tuple, like when trimming `fun(a,b`<completion>
1371 # as `a,b` would be a tuple, and we actually expect to get only `b`
1372 continue
1373 return code
1374 return ""
1375
1376 def _evaluate_expr(self, expr):
1377 obj = not_found
1378 done = False
1379 while not done and expr:
1380 try:
1381 obj = guarded_eval(
1382 expr,
1383 EvaluationContext(
1384 globals=self.global_namespace,
1385 locals=self.namespace,
1386 evaluation=self.evaluation,
1387 auto_import=self._auto_import,
1388 policy_overrides=self.policy_overrides,
1389 ),
1390 )
1391 done = True
1392 except (SyntaxError, TypeError) as e:
1393 if self.debug:
1394 warnings.warn(f"Trimming because of {e}")
1395 # TypeError can show up with something like `+ d`
1396 # where `d` is a dictionary.
1397
1398 # trim the expression to remove any invalid prefix
1399 # e.g. user starts `(d[`, so we get `expr = '(d'`,
1400 # where parenthesis is not closed.
1401 # TODO: make this faster by reusing parts of the computation?
1402 expr = self._trim_expr(expr)
1403 except Exception as e:
1404 if self.debug:
1405 warnings.warn(f"Evaluation exception {e}")
1406 done = True
1407 if self.debug:
1408 warnings.warn(f"Resolved to {obj}")
1409 return obj
1410
1411 @property
1412 def _auto_import(self):
1413 if self.auto_import_method is None:
1414 return None
1415 if not hasattr(self, "_auto_import_func"):
1416 self._auto_import_func = import_item(self.auto_import_method)
1417 return self._auto_import_func
1418
1419
1420def get__all__entries(obj: Any) -> list[str]:
1421 """returns the strings in the __all__ attribute"""
1422 try:
1423 words = getattr(obj, '__all__')
1424 except Exception:
1425 return []
1426
1427 return [w for w in words if isinstance(w, str)]
1428
1429
1430class _DictKeyState(enum.Flag):
1431 """Represent state of the key match in context of other possible matches.
1432
1433 - given `d1 = {'a': 1}` completion on `d1['<tab>` will yield `{'a': END_OF_ITEM}` as there is no tuple.
1434 - given `d2 = {('a', 'b'): 1}`: `d2['a', '<tab>` will yield `{'b': END_OF_TUPLE}` as there is no tuple members to add beyond `'b'`.
1435 - given `d3 = {('a', 'b'): 1}`: `d3['<tab>` will yield `{'a': IN_TUPLE}` as `'a'` can be added.
1436 - given `d4 = {'a': 1, ('a', 'b'): 2}`: `d4['<tab>` will yield `{'a': END_OF_ITEM & END_OF_TUPLE}`
1437 """
1438
1439 BASELINE = 0
1440 END_OF_ITEM = enum.auto()
1441 END_OF_TUPLE = enum.auto()
1442 IN_TUPLE = enum.auto()
1443
1444
1445def _parse_tokens(c: str) -> list[tokenize.TokenInfo]:
1446 """Parse tokens even if there is an error."""
1447 tokens = []
1448 token_generator = tokenize.generate_tokens(iter(c.splitlines()).__next__)
1449 while True:
1450 try:
1451 tokens.append(next(token_generator))
1452 except tokenize.TokenError:
1453 return tokens
1454 except StopIteration:
1455 return tokens
1456
1457
1458def _match_number_in_dict_key_prefix(prefix: str) -> str | None:
1459 """Match any valid Python numeric literal in a prefix of dictionary keys.
1460
1461 References:
1462 - https://docs.python.org/3/reference/lexical_analysis.html#numeric-literals
1463 - https://docs.python.org/3/library/tokenize.html
1464 """
1465 if prefix[-1].isspace():
1466 # if user typed a space we do not have anything to complete
1467 # even if there was a valid number token before
1468 return None
1469 tokens = _parse_tokens(prefix)
1470 rev_tokens = reversed(tokens)
1471 skip_over = {tokenize.ENDMARKER, tokenize.NEWLINE}
1472 number = None
1473 for token in rev_tokens:
1474 if token.type in skip_over:
1475 continue
1476 if number is None:
1477 if token.type == tokenize.NUMBER:
1478 number = token.string
1479 continue
1480 else:
1481 # we did not match a number
1482 return None
1483 if token.type == tokenize.OP:
1484 if token.string == ",":
1485 break
1486 if token.string in {"+", "-"}:
1487 number = token.string + number
1488 else:
1489 return None
1490 return number
1491
1492
1493_INT_FORMATS = {
1494 "0b": bin,
1495 "0o": oct,
1496 "0x": hex,
1497}
1498
1499
1500def match_dict_keys(
1501 keys: list[str | bytes | tuple[str | bytes, ...]],
1502 prefix: str,
1503 delims: str,
1504 extra_prefix: tuple[str | bytes, ...] | None = None,
1505) -> tuple[str, int, dict[str, _DictKeyState]]:
1506 """Used by dict_key_matches, matching the prefix to a list of keys
1507
1508 Parameters
1509 ----------
1510 keys
1511 list of keys in dictionary currently being completed.
1512 prefix
1513 Part of the text already typed by the user. E.g. `mydict[b'fo`
1514 delims
1515 String of delimiters to consider when finding the current key.
1516 extra_prefix : optional
1517 Part of the text already typed in multi-key index cases. E.g. for
1518 `mydict['foo', "bar", 'b`, this would be `('foo', 'bar')`.
1519
1520 Returns
1521 -------
1522 A tuple of three elements: ``quote``, ``token_start``, ``matched``, with
1523 ``quote`` being the quote that need to be used to close current string.
1524 ``token_start`` the position where the replacement should start occurring,
1525 ``matches`` a dictionary of replacement/completion keys on keys and values
1526 indicating whether the state.
1527 """
1528 prefix_tuple = extra_prefix if extra_prefix else ()
1529
1530 prefix_tuple_size = sum(
1531 [
1532 # for pandas, do not count slices as taking space
1533 not isinstance(k, slice)
1534 for k in prefix_tuple
1535 ]
1536 )
1537 text_serializable_types = (str, bytes, int, float, slice)
1538
1539 def filter_prefix_tuple(key):
1540 # Reject too short keys
1541 if len(key) <= prefix_tuple_size:
1542 return False
1543 # Reject keys which cannot be serialised to text
1544 for k in key:
1545 if not isinstance(k, text_serializable_types):
1546 return False
1547 # Reject keys that do not match the prefix
1548 for k, pt in zip(key, prefix_tuple):
1549 if k != pt and not isinstance(pt, slice):
1550 return False
1551 # All checks passed!
1552 return True
1553
1554 filtered_key_is_final: dict[
1555 str | bytes | int | float, _DictKeyState
1556 ] = defaultdict(lambda: _DictKeyState.BASELINE)
1557
1558 for k in keys:
1559 # If at least one of the matches is not final, mark as undetermined.
1560 # This can happen with `d = {111: 'b', (111, 222): 'a'}` where
1561 # `111` appears final on first match but is not final on the second.
1562
1563 if isinstance(k, tuple):
1564 if filter_prefix_tuple(k):
1565 key_fragment = k[prefix_tuple_size]
1566 filtered_key_is_final[key_fragment] |= (
1567 _DictKeyState.END_OF_TUPLE
1568 if len(k) == prefix_tuple_size + 1
1569 else _DictKeyState.IN_TUPLE
1570 )
1571 elif prefix_tuple_size > 0:
1572 # we are completing a tuple but this key is not a tuple,
1573 # so we should ignore it
1574 pass
1575 else:
1576 if isinstance(k, text_serializable_types):
1577 filtered_key_is_final[k] |= _DictKeyState.END_OF_ITEM
1578
1579 filtered_keys = filtered_key_is_final.keys()
1580
1581 if not prefix:
1582 return "", 0, {repr(k): v for k, v in filtered_key_is_final.items()}
1583
1584 quote_match = re.search("(?:\"|')", prefix)
1585 is_user_prefix_numeric = False
1586
1587 if quote_match:
1588 quote = quote_match.group()
1589 valid_prefix = prefix + quote
1590 try:
1591 prefix_str = literal_eval(valid_prefix)
1592 except Exception:
1593 return "", 0, {}
1594 else:
1595 # If it does not look like a string, let's assume
1596 # we are dealing with a number or variable.
1597 number_match = _match_number_in_dict_key_prefix(prefix)
1598
1599 # We do not want the key matcher to suggest variable names so we yield:
1600 if number_match is None:
1601 # The alternative would be to assume that user forgort the quote
1602 # and if the substring matches, suggest adding it at the start.
1603 return "", 0, {}
1604
1605 prefix_str = number_match
1606 is_user_prefix_numeric = True
1607 quote = ""
1608
1609 pattern = '[^' + ''.join('\\' + c for c in delims) + ']*$'
1610 token_match = re.search(pattern, prefix, re.UNICODE)
1611 assert token_match is not None # silence mypy
1612 token_start = token_match.start()
1613 token_prefix = token_match.group()
1614
1615 matched: dict[str, _DictKeyState] = {}
1616
1617 str_key: str | bytes
1618
1619 for key in filtered_keys:
1620 if isinstance(key, (int, float)):
1621 # User typed a number but this key is not a number.
1622 if not is_user_prefix_numeric:
1623 continue
1624 str_key = str(key)
1625 if isinstance(key, int):
1626 int_base = prefix_str[:2].lower()
1627 # if user typed integer using binary/oct/hex notation:
1628 if int_base in _INT_FORMATS:
1629 int_format = _INT_FORMATS[int_base]
1630 str_key = int_format(key)
1631 else:
1632 # User typed a string but this key is a number.
1633 if is_user_prefix_numeric:
1634 continue
1635 str_key = key
1636 try:
1637 if not str_key.startswith(prefix_str):
1638 continue
1639 except (AttributeError, TypeError, UnicodeError):
1640 # Python 3+ TypeError on b'a'.startswith('a') or vice-versa
1641 continue
1642
1643 # reformat remainder of key to begin with prefix
1644 rem = str_key[len(prefix_str) :]
1645 # force repr wrapped in '
1646 rem_repr = repr(rem + '"') if isinstance(rem, str) else repr(rem + b'"')
1647 rem_repr = rem_repr[1 + rem_repr.index("'"):-2]
1648 if quote == '"':
1649 # The entered prefix is quoted with ",
1650 # but the match is quoted with '.
1651 # A contained " hence needs escaping for comparison:
1652 rem_repr = rem_repr.replace('"', '\\"')
1653
1654 # then reinsert prefix from start of token
1655 match = "{}{}".format(token_prefix, rem_repr)
1656
1657 matched[match] = filtered_key_is_final[key]
1658 return quote, token_start, matched
1659
1660
1661def cursor_to_position(text:str, line:int, column:int)->int:
1662 """
1663 Convert the (line,column) position of the cursor in text to an offset in a
1664 string.
1665
1666 Parameters
1667 ----------
1668 text : str
1669 The text in which to calculate the cursor offset
1670 line : int
1671 Line of the cursor; 0-indexed
1672 column : int
1673 Column of the cursor 0-indexed
1674
1675 Returns
1676 -------
1677 Position of the cursor in ``text``, 0-indexed.
1678
1679 See Also
1680 --------
1681 position_to_cursor : reciprocal of this function
1682
1683 """
1684 lines = text.split('\n')
1685 assert line <= len(lines), f'{str(line)} <= {str(len(lines))}'
1686
1687 return sum(len(line) + 1 for line in lines[:line]) + column
1688
1689
1690def position_to_cursor(text: str, offset: int) -> tuple[int, int]:
1691 """
1692 Convert the position of the cursor in text (0 indexed) to a line
1693 number(0-indexed) and a column number (0-indexed) pair
1694
1695 Position should be a valid position in ``text``.
1696
1697 Parameters
1698 ----------
1699 text : str
1700 The text in which to calculate the cursor offset
1701 offset : int
1702 Position of the cursor in ``text``, 0-indexed.
1703
1704 Returns
1705 -------
1706 (line, column) : (int, int)
1707 Line of the cursor; 0-indexed, column of the cursor 0-indexed
1708
1709 See Also
1710 --------
1711 cursor_to_position : reciprocal of this function
1712
1713 """
1714
1715 assert 0 <= offset <= len(text) , "0 <= {} <= {}".format(offset , len(text))
1716
1717 before = text[:offset]
1718 blines = before.split('\n') # ! splitnes trim trailing \n
1719 line = before.count('\n')
1720 col = len(blines[-1])
1721 return line, col
1722
1723
1724def _safe_isinstance(obj, module, class_name, *attrs):
1725 """Checks if obj is an instance of module.class_name if loaded
1726 """
1727 if module in sys.modules:
1728 m = sys.modules[module]
1729 for attr in [class_name, *attrs]:
1730 m = getattr(m, attr)
1731 return isinstance(obj, m)
1732
1733
1734@context_matcher()
1735def back_unicode_name_matcher(context: CompletionContext):
1736 """Match Unicode characters back to Unicode name
1737
1738 Same as :any:`back_unicode_name_matches`, but adopted to new Matcher API.
1739 """
1740 fragment, matches = back_unicode_name_matches(context.text_until_cursor)
1741 return _convert_matcher_v1_result_to_v2(
1742 matches, type="unicode", fragment=fragment, suppress_if_matches=True
1743 )
1744
1745
1746def back_unicode_name_matches(text: str) -> tuple[str, Sequence[str]]:
1747 """Match Unicode characters back to Unicode name
1748
1749 This does ``☃`` -> ``\\snowman``
1750
1751 Note that snowman is not a valid python3 combining character but will be expanded.
1752 Though it will not recombine back to the snowman character by the completion machinery.
1753
1754 This will not either back-complete standard sequences like \\n, \\b ...
1755
1756 .. deprecated:: 8.6
1757 You can use :meth:`back_unicode_name_matcher` instead.
1758
1759 Returns
1760 =======
1761
1762 Return a tuple with two elements:
1763
1764 - The Unicode character that was matched (preceded with a backslash), or
1765 empty string,
1766 - a sequence (of 1), name for the match Unicode character, preceded by
1767 backslash, or empty if no match.
1768 """
1769 if len(text)<2:
1770 return '', ()
1771 maybe_slash = text[-2]
1772 if maybe_slash != '\\':
1773 return '', ()
1774
1775 char = text[-1]
1776 # no expand on quote for completion in strings.
1777 # nor backcomplete standard ascii keys
1778 if char in string.ascii_letters or char in ('"',"'"):
1779 return '', ()
1780 try :
1781 unic = unicodedata.name(char)
1782 return '\\'+char,('\\'+unic,)
1783 except KeyError:
1784 pass
1785 return '', ()
1786
1787
1788@context_matcher()
1789def back_latex_name_matcher(context: CompletionContext) -> SimpleMatcherResult:
1790 """Match latex characters back to unicode name
1791
1792 This does ``\\ℵ`` -> ``\\aleph``
1793 """
1794
1795 text = context.text_until_cursor
1796 no_match = {
1797 "completions": [],
1798 "suppress": False,
1799 }
1800
1801 if len(text)<2:
1802 return no_match
1803 maybe_slash = text[-2]
1804 if maybe_slash != '\\':
1805 return no_match
1806
1807 char = text[-1]
1808 # no expand on quote for completion in strings.
1809 # nor backcomplete standard ascii keys
1810 if char in string.ascii_letters or char in ('"',"'"):
1811 return no_match
1812 try :
1813 latex = reverse_latex_symbol[char]
1814 # '\\' replace the \ as well
1815 return {
1816 "completions": [SimpleCompletion(text=latex, type="latex")],
1817 "suppress": True,
1818 "matched_fragment": "\\" + char,
1819 }
1820 except KeyError:
1821 pass
1822
1823 return no_match
1824
1825def _formatparamchildren(parameter) -> str:
1826 """
1827 Get parameter name and value from Jedi Private API
1828
1829 Jedi does not expose a simple way to get `param=value` from its API.
1830
1831 Parameters
1832 ----------
1833 parameter
1834 Jedi's function `Param`
1835
1836 Returns
1837 -------
1838 A string like 'a', 'b=1', '*args', '**kwargs'
1839
1840 """
1841 description = parameter.description
1842 if not description.startswith('param '):
1843 raise ValueError('Jedi function parameter description have change format.'
1844 'Expected "param ...", found %r".' % description)
1845 return description[6:]
1846
1847def _make_signature(completion)-> str:
1848 """
1849 Make the signature from a jedi completion
1850
1851 Parameters
1852 ----------
1853 completion : jedi.Completion
1854 object does not complete a function type
1855
1856 Returns
1857 -------
1858 a string consisting of the function signature, with the parenthesis but
1859 without the function name. example:
1860 `(a, *args, b=1, **kwargs)`
1861
1862 """
1863
1864 # it looks like this might work on jedi 0.17
1865 if hasattr(completion, 'get_signatures'):
1866 signatures = completion.get_signatures()
1867 if not signatures:
1868 return '(?)'
1869
1870 c0 = completion.get_signatures()[0]
1871 return '('+c0.to_string().split('(', maxsplit=1)[1]
1872
1873 return '(%s)'% ', '.join([f for f in (_formatparamchildren(p) for signature in completion.get_signatures()
1874 for p in signature.defined_names()) if f])
1875
1876
1877_CompleteResult = dict[str, MatcherResult]
1878
1879
1880DICT_MATCHER_REGEX = re.compile(
1881 r"""(?x)
1882( # match dict-referring - or any get item object - expression
1883 .+
1884)
1885\[ # open bracket
1886\s* # and optional whitespace
1887# Capture any number of serializable objects (e.g. "a", "b", 'c')
1888# and slices
1889((?:(?:
1890 (?: # closed string
1891 [uUbB]? # string prefix (r not handled)
1892 (?:
1893 '(?:[^']|(?<!\\)\\')*'
1894 |
1895 "(?:[^"]|(?<!\\)\\")*"
1896 )
1897 )
1898 |
1899 # capture integers and slices
1900 (?:[-+]?\d+)?(?::(?:[-+]?\d+)?){0,2}
1901 |
1902 # integer in bin/hex/oct notation
1903 0[bBxXoO]_?(?:\w|\d)+
1904 )
1905 \s*,\s*
1906)*)
1907((?:
1908 (?: # unclosed string
1909 [uUbB]? # string prefix (r not handled)
1910 (?:
1911 '(?:[^']|(?<!\\)\\')*
1912 |
1913 "(?:[^"]|(?<!\\)\\")*
1914 )
1915 )
1916 |
1917 # unfinished integer
1918 (?:[-+]?\d+)
1919 |
1920 # integer in bin/hex/oct notation
1921 0[bBxXoO]_?(?:\w|\d)+
1922 )
1923)?
1924$
1925"""
1926)
1927
1928
1929def _convert_matcher_v1_result_to_v2_no_no(
1930 matches: Sequence[str],
1931 type: str,
1932) -> SimpleMatcherResult:
1933 """same as _convert_matcher_v1_result_to_v2 but fragment=None, and suppress_if_matches is False by construction"""
1934 return SimpleMatcherResult(
1935 completions=[SimpleCompletion(text=match, type=type) for match in matches],
1936 suppress=False,
1937 )
1938
1939
1940def _convert_matcher_v1_result_to_v2(
1941 matches: Sequence[str],
1942 type: str,
1943 fragment: str | None = None,
1944 suppress_if_matches: bool = False,
1945) -> SimpleMatcherResult:
1946 """Utility to help with transition"""
1947 result = {
1948 "completions": [SimpleCompletion(text=match, type=type) for match in matches],
1949 "suppress": (True if matches else False) if suppress_if_matches else False,
1950 }
1951 if fragment is not None:
1952 result["matched_fragment"] = fragment
1953 return cast(SimpleMatcherResult, result)
1954
1955
1956class IPCompleter(Completer):
1957 """Extension of the completer class with IPython-specific features"""
1958
1959 @observe("greedy")
1960 def _greedy_changed(self, change):
1961 """update the splitter and readline delims when greedy is changed"""
1962 if change["new"]:
1963 self.evaluation = "unsafe"
1964 self.auto_close_dict_keys = True
1965 self.splitter.delims = GREEDY_DELIMS
1966 else:
1967 self.evaluation = "limited"
1968 self.auto_close_dict_keys = False
1969 self.splitter.delims = DELIMS
1970
1971 dict_keys_only = Bool(
1972 False,
1973 help="""
1974 Whether to show dict key matches only.
1975
1976 (disables all matchers except for `IPCompleter.dict_key_matcher`).
1977 """,
1978 )
1979
1980 suppress_competing_matchers = UnionTrait(
1981 [Bool(allow_none=True), DictTrait(Bool(None, allow_none=True))],
1982 default_value=None,
1983 help="""
1984 Whether to suppress completions from other *Matchers*.
1985
1986 When set to ``None`` (default) the matchers will attempt to auto-detect
1987 whether suppression of other matchers is desirable. For example, at
1988 the beginning of a line followed by `%` we expect a magic completion
1989 to be the only applicable option, and after ``my_dict['`` we usually
1990 expect a completion with an existing dictionary key.
1991
1992 If you want to disable this heuristic and see completions from all matchers,
1993 set ``IPCompleter.suppress_competing_matchers = False``.
1994 To disable the heuristic for specific matchers provide a dictionary mapping:
1995 ``IPCompleter.suppress_competing_matchers = {'IPCompleter.dict_key_matcher': False}``.
1996
1997 Set ``IPCompleter.suppress_competing_matchers = True`` to limit
1998 completions to the set of matchers with the highest priority;
1999 this is equivalent to ``IPCompleter.merge_completions`` and
2000 can be beneficial for performance, but will sometimes omit relevant
2001 candidates from matchers further down the priority list.
2002 """,
2003 ).tag(config=True)
2004
2005 merge_completions = Bool(
2006 True,
2007 help="""Whether to merge completion results into a single list
2008
2009 If False, only the completion results from the first non-empty
2010 completer will be returned.
2011
2012 As of version 8.6.0, setting the value to ``False`` is an alias for:
2013 ``IPCompleter.suppress_competing_matchers = True.``.
2014 """,
2015 ).tag(config=True)
2016
2017 disable_matchers = ListTrait(
2018 Unicode(),
2019 help="""List of matchers to disable.
2020
2021 The list should contain matcher identifiers (see :any:`completion_matcher`).
2022 """,
2023 ).tag(config=True)
2024
2025 omit__names = Enum(
2026 (0, 1, 2),
2027 default_value=2,
2028 help="""Instruct the completer to omit private method names
2029
2030 Specifically, when completing on ``object.<tab>``.
2031
2032 When 2 [default]: all names that start with '_' will be excluded.
2033
2034 When 1: all 'magic' names (``__foo__``) will be excluded.
2035
2036 When 0: nothing will be excluded.
2037 """
2038 ).tag(config=True)
2039 profile_completions = Bool(
2040 default_value=False,
2041 help="If True, emit profiling data for completion subsystem using cProfile."
2042 ).tag(config=True)
2043
2044 profiler_output_dir = Unicode(
2045 default_value=".completion_profiles",
2046 help="Template for path at which to output profile data for completions."
2047 ).tag(config=True)
2048
2049 def __init__(
2050 self, shell=None, namespace=None, global_namespace=None, config=None, **kwargs
2051 ):
2052 """IPCompleter() -> completer
2053
2054 Return a completer object.
2055
2056 Parameters
2057 ----------
2058 shell
2059 a pointer to the ipython shell itself. This is needed
2060 because this completer knows about magic functions, and those can
2061 only be accessed via the ipython instance.
2062 namespace : dict, optional
2063 an optional dict where completions are performed.
2064 global_namespace : dict, optional
2065 secondary optional dict for completions, to
2066 handle cases (such as IPython embedded inside functions) where
2067 both Python scopes are visible.
2068 config : Config
2069 traitlet's config object
2070 **kwargs
2071 passed to super class unmodified.
2072 """
2073
2074 self.magic_escape = ESC_MAGIC
2075 self.splitter = CompletionSplitter()
2076
2077 # _greedy_changed() depends on splitter and readline being defined:
2078 super().__init__(
2079 namespace=namespace,
2080 global_namespace=global_namespace,
2081 config=config,
2082 **kwargs,
2083 )
2084
2085 # List where completion matches will be stored
2086 self.matches = []
2087 self.shell = shell
2088 # Regexp to split filenames with spaces in them
2089 self.space_name_re = re.compile(r'([^\\] )')
2090 # Hold a local ref. to glob.glob for speed
2091 self.glob = glob.glob
2092
2093 # Determine if we are running on 'dumb' terminals, like (X)Emacs
2094 # buffers, to avoid completion problems.
2095 term = os.environ.get('TERM','xterm')
2096 self.dumb_terminal = term in ['dumb','emacs']
2097
2098 # Special handling of backslashes needed in win32 platforms
2099 if sys.platform == "win32":
2100 self.clean_glob = self._clean_glob_win32
2101 else:
2102 self.clean_glob = self._clean_glob
2103
2104 #regexp to parse docstring for function signature
2105 self.docstring_sig_re = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
2106 self.docstring_kwd_re = re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
2107 #use this if positional argument name is also needed
2108 #= re.compile(r'[\s|\[]*(\w+)(?:\s*=?\s*.*)')
2109
2110 self.magic_arg_matchers = [
2111 self.magic_config_matcher,
2112 self.magic_color_matcher,
2113 ]
2114
2115 # This is set externally by InteractiveShell
2116 self.custom_completers = None
2117
2118 # This is a list of names of unicode characters that can be completed
2119 # into their corresponding unicode value. The list is large, so we
2120 # lazily initialize it on first use. Consuming code should access this
2121 # attribute through the `@unicode_names` property.
2122 self._unicode_names = None
2123
2124 self._backslash_combining_matchers = [
2125 self.latex_name_matcher,
2126 self.unicode_name_matcher,
2127 back_latex_name_matcher,
2128 back_unicode_name_matcher,
2129 self.fwd_unicode_matcher,
2130 ]
2131
2132 if not self.backslash_combining_completions:
2133 for matcher in self._backslash_combining_matchers:
2134 self.disable_matchers.append(_get_matcher_id(matcher))
2135
2136 if not self.merge_completions:
2137 self.suppress_competing_matchers = True
2138
2139 @property
2140 def matchers(self) -> list[Matcher]:
2141 """All active matcher routines for completion"""
2142 if self.dict_keys_only:
2143 return [self.dict_key_matcher]
2144
2145 if self.use_jedi:
2146 return [
2147 *self.custom_matchers,
2148 *self._backslash_combining_matchers,
2149 *self.magic_arg_matchers,
2150 self.custom_completer_matcher,
2151 self.magic_matcher,
2152 self._jedi_matcher,
2153 self.dict_key_matcher,
2154 self.file_matcher,
2155 ]
2156 else:
2157 return [
2158 *self.custom_matchers,
2159 *self._backslash_combining_matchers,
2160 *self.magic_arg_matchers,
2161 self.custom_completer_matcher,
2162 self.dict_key_matcher,
2163 self.magic_matcher,
2164 self.python_matcher,
2165 self.file_matcher,
2166 self.python_func_kw_matcher,
2167 ]
2168
2169 def all_completions(self, text: str) -> list[str]:
2170 """
2171 Wrapper around the completion methods for the benefit of emacs.
2172 """
2173 prefix = text.rpartition('.')[0]
2174 with provisionalcompleter():
2175 return ['.'.join([prefix, c.text]) if prefix and self.use_jedi else c.text
2176 for c in self.completions(text, len(text))]
2177
2178 return self.complete(text)[1]
2179
2180 def _clean_glob(self, text:str):
2181 return self.glob("%s*" % text)
2182
2183 def _clean_glob_win32(self, text:str):
2184 return [f.replace("\\","/")
2185 for f in self.glob("%s*" % text)]
2186
2187 @context_matcher()
2188 def file_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
2189 """Match filenames, expanding ~USER type strings.
2190
2191 Most of the seemingly convoluted logic in this completer is an
2192 attempt to handle filenames with spaces in them. And yet it's not
2193 quite perfect, because Python's readline doesn't expose all of the
2194 GNU readline details needed for this to be done correctly.
2195
2196 For a filename with a space in it, the printed completions will be
2197 only the parts after what's already been typed (instead of the
2198 full completions, as is normally done). I don't think with the
2199 current (as of Python 2.3) Python readline it's possible to do
2200 better.
2201 """
2202 # TODO: add a heuristic for suppressing (e.g. if it has OS-specific delimiter,
2203 # starts with `/home/`, `C:\`, etc)
2204
2205 text = context.token
2206 raw_text_until_cursor = context.text_until_cursor
2207 code_until_cursor = self._extract_code(raw_text_until_cursor)
2208 in_cli_context = self._is_completing_in_cli_context(
2209 raw_text_until_cursor
2210 ) or self._is_completing_in_cli_context(code_until_cursor)
2211 if (
2212 not in_cli_context
2213 and not self._is_completing_in_string(code_until_cursor)
2214 and not self._looks_like_path(text)
2215 ):
2216 return {
2217 "completions": [],
2218 "suppress": False,
2219 }
2220
2221 completion_type = self._determine_completion_context(code_until_cursor)
2222 if (
2223 completion_type == self._CompletionContextType.ATTRIBUTE
2224 and not in_cli_context
2225 ):
2226 return {
2227 "completions": [],
2228 "suppress": False,
2229 }
2230
2231 # chars that require escaping with backslash - i.e. chars
2232 # that readline treats incorrectly as delimiters, but we
2233 # don't want to treat as delimiters in filename matching
2234 # when escaped with backslash
2235 if text.startswith('!'):
2236 text = text[1:]
2237 text_prefix = '!'
2238 else:
2239 text_prefix = ''
2240
2241 text_until_cursor = self.text_until_cursor
2242 # track strings with open quotes
2243 open_quotes = has_open_quotes(text_until_cursor)
2244
2245 if '(' in text_until_cursor or '[' in text_until_cursor:
2246 lsplit = text
2247 else:
2248 try:
2249 # arg_split ~ shlex.split, but with unicode bugs fixed by us
2250 lsplit = arg_split(text_until_cursor)[-1]
2251 except ValueError:
2252 # typically an unmatched ", or backslash without escaped char.
2253 if open_quotes:
2254 lsplit = text_until_cursor.split(open_quotes)[-1]
2255 else:
2256 return {
2257 "completions": [],
2258 "suppress": False,
2259 }
2260 except IndexError:
2261 # tab pressed on empty line
2262 lsplit = ""
2263
2264 if not open_quotes and lsplit != protect_filename(lsplit):
2265 # if protectables are found, do matching on the whole escaped name
2266 has_protectables = True
2267 text0,text = text,lsplit
2268 else:
2269 has_protectables = False
2270 text = os.path.expanduser(text)
2271
2272 if text == "":
2273 return {
2274 "completions": [
2275 SimpleCompletion(
2276 text=text_prefix + protect_filename(f), type="path"
2277 )
2278 for f in self.glob("*")
2279 ],
2280 "suppress": False,
2281 }
2282
2283 # Compute the matches from the filesystem
2284 if sys.platform == 'win32':
2285 m0 = self.clean_glob(text)
2286 else:
2287 m0 = self.clean_glob(text.replace('\\', ''))
2288
2289 if has_protectables:
2290 # If we had protectables, we need to revert our changes to the
2291 # beginning of filename so that we don't double-write the part
2292 # of the filename we have so far
2293 len_lsplit = len(lsplit)
2294 matches = [text_prefix + text0 +
2295 protect_filename(f[len_lsplit:]) for f in m0]
2296 else:
2297 if open_quotes:
2298 # if we have a string with an open quote, we don't need to
2299 # protect the names beyond the quote (and we _shouldn't_, as
2300 # it would cause bugs when the filesystem call is made).
2301 matches = m0 if sys.platform == "win32" else\
2302 [protect_filename(f, open_quotes) for f in m0]
2303 else:
2304 matches = [text_prefix +
2305 protect_filename(f) for f in m0]
2306
2307 # Mark directories in input list by appending '/' to their names.
2308 return {
2309 "completions": [
2310 SimpleCompletion(text=x + "/" if os.path.isdir(x) else x, type="path")
2311 for x in matches
2312 ],
2313 "suppress": False,
2314 }
2315
2316 def _extract_code(self, line: str) -> str:
2317 """Extract code from magics if any."""
2318
2319 if not line:
2320 return line
2321 maybe_magic, *rest = line.split(maxsplit=1)
2322 if not rest:
2323 return line
2324 args = rest[0]
2325 known_magics = self.shell.magics_manager.lsmagic()
2326 line_magics = known_magics["line"]
2327 magic_name = maybe_magic.lstrip(self.magic_escape)
2328 if magic_name not in line_magics:
2329 return line
2330
2331 if not maybe_magic.startswith(self.magic_escape):
2332 all_variables = [*self.namespace.keys(), *self.global_namespace.keys()]
2333 if magic_name in all_variables:
2334 # short circuit if we see a line starting with say `time`
2335 # but time is defined as a variable (in addition to being
2336 # a magic). In these cases users need to use explicit `%time`.
2337 return line
2338
2339 magic_method = line_magics[magic_name]
2340
2341 try:
2342 if magic_name == "timeit":
2343 opts, stmt = magic_method.__self__.parse_options(
2344 args,
2345 "n:r:tcp:qov:",
2346 posix=False,
2347 strict=False,
2348 preserve_non_opts=True,
2349 )
2350 return stmt
2351 elif magic_name == "prun":
2352 opts, stmt = magic_method.__self__.parse_options(
2353 args, "D:l:rs:T:q", list_all=True, posix=False
2354 )
2355 return stmt
2356 elif hasattr(magic_method, "parser") and getattr(
2357 magic_method, "has_arguments", False
2358 ):
2359 # e.g. %debug, %time
2360 args, extra = magic_method.parser.parse_argstring(args, partial=True)
2361 return " ".join(extra)
2362 except UsageError:
2363 return line
2364
2365 return line
2366
2367 @context_matcher()
2368 def magic_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
2369 """Match magics."""
2370
2371 # Get all shell magics now rather than statically, so magics loaded at
2372 # runtime show up too.
2373 text = context.token
2374 lsm = self.shell.magics_manager.lsmagic()
2375 line_magics = lsm['line']
2376 cell_magics = lsm['cell']
2377 pre = self.magic_escape
2378 pre2 = pre + pre
2379
2380 explicit_magic = text.startswith(pre)
2381
2382 # Completion logic:
2383 # - user gives %%: only do cell magics
2384 # - user gives %: do both line and cell magics
2385 # - no prefix: do both
2386 # In other words, line magics are skipped if the user gives %% explicitly
2387 #
2388 # We also exclude magics that match any currently visible names:
2389 # https://github.com/ipython/ipython/issues/4877, unless the user has
2390 # typed a %:
2391 # https://github.com/ipython/ipython/issues/10754
2392 bare_text = text.lstrip(pre)
2393 global_matches = self.global_matches(bare_text)
2394 if not explicit_magic:
2395 def matches(magic):
2396 """
2397 Filter magics, in particular remove magics that match
2398 a name present in global namespace.
2399 """
2400 return ( magic.startswith(bare_text) and
2401 magic not in global_matches )
2402 else:
2403 def matches(magic):
2404 return magic.startswith(bare_text)
2405
2406 completions = [pre2 + m for m in cell_magics if matches(m)]
2407 if not text.startswith(pre2):
2408 completions += [pre + m for m in line_magics if matches(m)]
2409
2410 is_magic_prefix = len(text) > 0 and text[0] == "%"
2411
2412 return {
2413 "completions": [
2414 SimpleCompletion(text=comp, type="magic") for comp in completions
2415 ],
2416 "suppress": is_magic_prefix and len(completions) > 0,
2417 }
2418
2419 @context_matcher()
2420 def magic_config_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
2421 """Match class names and attributes for %config magic."""
2422 # NOTE: uses `line_buffer` equivalent for compatibility
2423 matches = self.magic_config_matches(context.line_with_cursor)
2424 return _convert_matcher_v1_result_to_v2_no_no(matches, type="param")
2425
2426 def magic_config_matches(self, text: str) -> list[str]:
2427 """Match class names and attributes for %config magic.
2428
2429 .. deprecated:: 8.6
2430 You can use :meth:`magic_config_matcher` instead.
2431 """
2432 texts = text.strip().split()
2433
2434 if len(texts) > 0 and (texts[0] == 'config' or texts[0] == '%config'):
2435 # get all configuration classes
2436 classes = sorted({ c for c in self.shell.configurables
2437 if c.__class__.class_traits(config=True)
2438 }, key=lambda x: x.__class__.__name__)
2439 classnames = [ c.__class__.__name__ for c in classes ]
2440
2441 # return all classnames if config or %config is given
2442 if len(texts) == 1:
2443 return classnames
2444
2445 # match classname
2446 classname_texts = texts[1].split('.')
2447 classname = classname_texts[0]
2448 classname_matches = [ c for c in classnames
2449 if c.startswith(classname) ]
2450
2451 # return matched classes or the matched class with attributes
2452 if texts[1].find('.') < 0:
2453 return classname_matches
2454 elif len(classname_matches) == 1 and \
2455 classname_matches[0] == classname:
2456 cls = classes[classnames.index(classname)].__class__
2457 help = cls.class_get_help()
2458 # strip leading '--' from cl-args:
2459 help = _LEADING_DASHES_RE.sub("", help)
2460 return [ attr.split('=')[0]
2461 for attr in help.strip().splitlines()
2462 if attr.startswith(texts[1]) ]
2463 return []
2464
2465 @context_matcher()
2466 def magic_color_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
2467 """Match color schemes for %colors magic."""
2468 text = context.line_with_cursor
2469 texts = text.split()
2470 if text.endswith(' '):
2471 # .split() strips off the trailing whitespace. Add '' back
2472 # so that: '%colors ' -> ['%colors', '']
2473 texts.append('')
2474
2475 if len(texts) == 2 and (texts[0] == 'colors' or texts[0] == '%colors'):
2476 prefix = texts[1]
2477 return SimpleMatcherResult(
2478 completions=[
2479 SimpleCompletion(color, type="param")
2480 for color in theme_table.keys()
2481 if color.startswith(prefix)
2482 ],
2483 suppress=False,
2484 )
2485 return SimpleMatcherResult(
2486 completions=[],
2487 suppress=False,
2488 )
2489
2490 @context_matcher(identifier="IPCompleter.jedi_matcher")
2491 def _jedi_matcher(self, context: CompletionContext) -> _JediMatcherResult:
2492 matches = self._jedi_matches(
2493 cursor_column=context.cursor_position,
2494 cursor_line=context.cursor_line,
2495 text=context.full_text,
2496 )
2497 return {
2498 "completions": matches,
2499 # static analysis should not suppress other matcher
2500 # NOTE: file_matcher is automatically suppressed on attribute completions
2501 "suppress": False,
2502 }
2503
2504 def _jedi_matches(
2505 self, cursor_column: int, cursor_line: int, text: str
2506 ) -> Iterator[_JediCompletionLike]:
2507 """
2508 Return a list of :any:`jedi.api.Completion`\\s object from a ``text`` and
2509 cursor position.
2510
2511 Parameters
2512 ----------
2513 cursor_column : int
2514 column position of the cursor in ``text``, 0-indexed.
2515 cursor_line : int
2516 line position of the cursor in ``text``, 0-indexed
2517 text : str
2518 text to complete
2519
2520 Notes
2521 -----
2522 If ``IPCompleter.debug`` is ``True`` may return a :any:`_FakeJediCompletion`
2523 object containing a string with the Jedi debug information attached.
2524
2525 .. deprecated:: 8.6
2526 You can use :meth:`_jedi_matcher` instead.
2527 """
2528 namespaces = [self.namespace]
2529 if self.global_namespace is not None:
2530 namespaces.append(self.global_namespace)
2531
2532 completion_filter = lambda x:x
2533 offset = cursor_to_position(text, cursor_line, cursor_column)
2534 # filter output if we are completing for object members
2535 if offset:
2536 pre = text[offset-1]
2537 if pre == '.':
2538 if self.omit__names == 2:
2539 completion_filter = lambda c:not c.name.startswith('_')
2540 elif self.omit__names == 1:
2541 completion_filter = lambda c:not (c.name.startswith('__') and c.name.endswith('__'))
2542 elif self.omit__names == 0:
2543 completion_filter = lambda x:x
2544 else:
2545 raise ValueError(f"Don't understand self.omit__names == {self.omit__names}")
2546
2547 interpreter = _get_jedi().Interpreter(text[:offset], namespaces)
2548 try_jedi = True
2549
2550 try:
2551 # find the first token in the current tree -- if it is a ' or " then we are in a string
2552 completing_string = False
2553 try:
2554 first_child = next(c for c in interpreter._get_module().tree_node.children if hasattr(c, 'value'))
2555 except StopIteration:
2556 pass
2557 else:
2558 # note the value may be ', ", or it may also be ''' or """, or
2559 # in some cases, """what/you/typed..., but all of these are
2560 # strings.
2561 completing_string = len(first_child.value) > 0 and first_child.value[0] in {"'", '"'}
2562
2563 # if we are in a string jedi is likely not the right candidate for
2564 # now. Skip it.
2565 try_jedi = not completing_string
2566 except Exception as e:
2567 # many of things can go wrong, we are using private API just don't crash.
2568 if self.debug:
2569 print("Error detecting if completing a non-finished string :", e, '|')
2570
2571 if not try_jedi:
2572 return iter([])
2573 try:
2574 return filter(completion_filter, interpreter.complete(column=cursor_column, line=cursor_line + 1))
2575 except Exception as e:
2576 if self.debug:
2577 return iter(
2578 [
2579 _FakeJediCompletion(
2580 'Oops Jedi has crashed, please report a bug with the following:\n"""\n%s\ns"""'
2581 % (e)
2582 )
2583 ]
2584 )
2585 else:
2586 return iter([])
2587
2588 class _CompletionContextType(enum.Enum):
2589 ATTRIBUTE = "attribute" # For attribute completion
2590 GLOBAL = "global" # For global completion
2591
2592 def _determine_completion_context(self, line):
2593 """
2594 Determine whether the cursor is in an attribute or global completion context.
2595 """
2596 # Cursor in string/comment → GLOBAL.
2597 is_string, is_in_expression = self._is_in_string_or_comment(line)
2598 if is_string and not is_in_expression:
2599 return self._CompletionContextType.GLOBAL
2600
2601 # If we're in a template string expression, handle specially
2602 if is_string and is_in_expression:
2603 # Extract the expression part - look for the last { that isn't closed
2604 expr_start = line.rfind("{")
2605 if expr_start >= 0:
2606 # We're looking at the expression inside a template string
2607 expr = line[expr_start + 1 :]
2608 # Recursively determine the context of the expression
2609 return self._determine_completion_context(expr)
2610
2611 # Handle plain number literals - should be global context
2612 # Ex: 3. -42.14 but not 3.1.
2613 if re.search(r"(?<!\w)(?<!\d\.)([-+]?\d+\.(\d+)?)(?!\w)$", line):
2614 return self._CompletionContextType.GLOBAL
2615
2616 # Handle all other attribute matches np.ran, d[0].k, (a,b).count, obj._private
2617 chain_match = re.search(r".*(.+(?<!\s)\.(?:[a-zA-Z_]\w*)?)$", line)
2618 if chain_match:
2619 return self._CompletionContextType.ATTRIBUTE
2620
2621 return self._CompletionContextType.GLOBAL
2622
2623 def _is_completing_in_cli_context(self, text: str) -> bool:
2624 """
2625 Determine if we are completing in a CLI alias, line magic, or bang expression context.
2626 """
2627 stripped = text.lstrip()
2628 if stripped.startswith("!") or stripped.startswith("%"):
2629 return True
2630 if self._is_completing_in_system_assignment(text):
2631 return True
2632 # Check for CLI aliases
2633 try:
2634 tokens = stripped.split(None, 1)
2635 if not tokens:
2636 return False
2637 first_token = tokens[0]
2638
2639 # Must have arguments after the command for this to apply
2640 if len(tokens) < 2:
2641 return False
2642
2643 # Check if first token is a known alias
2644 if not any(
2645 alias[0] == first_token for alias in self.shell.alias_manager.aliases
2646 ):
2647 return False
2648
2649 try:
2650 if first_token in self.shell.user_ns:
2651 # There's a variable defined, so the alias is overshadowed
2652 return False
2653 except (AttributeError, KeyError):
2654 pass
2655
2656 return True
2657 except Exception:
2658 return False
2659
2660 def _is_completing_in_system_assignment(self, text: str) -> bool:
2661 """Return True for IPython ``name = !command`` syntax."""
2662 try:
2663 transform = SystemAssign.find(make_tokens_by_line([text + "\n"]))
2664 except Exception:
2665 return False
2666 return transform is not None and transform.start_col < len(text)
2667
2668 def _is_completing_in_string(self, text: str) -> bool:
2669 """Return True if the cursor is in a string literal, not a comment."""
2670 is_string, is_in_expression = self._is_in_string_or_comment(text)
2671 if not is_string or is_in_expression:
2672 return False
2673 return not any(token.type == tokenize.COMMENT for token in _parse_tokens(text))
2674
2675 def _looks_like_path(self, text: str) -> bool:
2676 if text.startswith(("~", "/", "./", "../", ".\\", "..\\")):
2677 return True
2678 return bool(sys.platform == "win32" and re.match(r"^[a-zA-Z]:[\\/]", text))
2679
2680 def _is_in_string_or_comment(self, text):
2681 """
2682 Determine if the cursor is inside a string or comment.
2683 Returns (is_string, is_in_expression) tuple:
2684 - is_string: True if in any kind of string
2685 - is_in_expression: True if inside an f-string/t-string expression
2686 """
2687 in_single_quote = False
2688 in_double_quote = False
2689 in_triple_single = False
2690 in_triple_double = False
2691 in_template_string = False # Covers both f-strings and t-strings
2692 in_expression = False # For expressions in f/t-strings
2693 expression_depth = 0 # Track nested braces in expressions
2694 i = 0
2695
2696 while i < len(text):
2697 # Check for f-string or t-string start
2698 if (
2699 i + 1 < len(text)
2700 and text[i] in ("f", "t")
2701 and (text[i + 1] == '"' or text[i + 1] == "'")
2702 and not (
2703 in_single_quote
2704 or in_double_quote
2705 or in_triple_single
2706 or in_triple_double
2707 )
2708 ):
2709 in_template_string = True
2710 i += 1 # Skip the 'f' or 't'
2711
2712 # Handle triple quotes
2713 if i + 2 < len(text):
2714 if (
2715 text[i : i + 3] == '"""'
2716 and not in_single_quote
2717 and not in_triple_single
2718 ):
2719 in_triple_double = not in_triple_double
2720 if not in_triple_double:
2721 in_template_string = False
2722 i += 3
2723 continue
2724 if (
2725 text[i : i + 3] == "'''"
2726 and not in_double_quote
2727 and not in_triple_double
2728 ):
2729 in_triple_single = not in_triple_single
2730 if not in_triple_single:
2731 in_template_string = False
2732 i += 3
2733 continue
2734
2735 # Handle escapes
2736 if text[i] == "\\" and i + 1 < len(text):
2737 i += 2
2738 continue
2739
2740 # Handle nested braces within f-strings
2741 if in_template_string:
2742 # Special handling for consecutive opening braces
2743 if i + 1 < len(text) and text[i : i + 2] == "{{":
2744 i += 2
2745 continue
2746
2747 # Detect start of an expression
2748 if text[i] == "{":
2749 # Only increment depth and mark as expression if not already in an expression
2750 # or if we're at a top-level nested brace
2751 if not in_expression or (in_expression and expression_depth == 0):
2752 in_expression = True
2753 expression_depth += 1
2754 i += 1
2755 continue
2756
2757 # Detect end of an expression
2758 if text[i] == "}":
2759 expression_depth -= 1
2760 if expression_depth <= 0:
2761 in_expression = False
2762 expression_depth = 0
2763 i += 1
2764 continue
2765
2766 in_triple_quote = in_triple_single or in_triple_double
2767
2768 # Handle quotes - also reset template string when closing quotes are encountered
2769 if text[i] == '"' and not in_single_quote and not in_triple_quote:
2770 in_double_quote = not in_double_quote
2771 if not in_double_quote and not in_triple_quote:
2772 in_template_string = False
2773 elif text[i] == "'" and not in_double_quote and not in_triple_quote:
2774 in_single_quote = not in_single_quote
2775 if not in_single_quote and not in_triple_quote:
2776 in_template_string = False
2777
2778 # Check for comment
2779 if text[i] == "#" and not (
2780 in_single_quote or in_double_quote or in_triple_quote
2781 ):
2782 return True, False
2783
2784 i += 1
2785
2786 is_string = (
2787 in_single_quote or in_double_quote or in_triple_single or in_triple_double
2788 )
2789
2790 # Return tuple (is_string, is_in_expression)
2791 return (
2792 is_string or (in_template_string and not in_expression),
2793 in_expression and expression_depth > 0,
2794 )
2795
2796 @context_matcher()
2797 def python_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
2798 """Match attributes or global python names"""
2799 text = context.text_until_cursor
2800 text = self._extract_code(text)
2801 in_cli_context = self._is_completing_in_cli_context(text)
2802 if in_cli_context:
2803 completion_type = self._CompletionContextType.GLOBAL
2804 else:
2805 completion_type = self._determine_completion_context(text)
2806 if completion_type == self._CompletionContextType.ATTRIBUTE:
2807 try:
2808 matches, fragment = self._attr_matches(
2809 text, include_prefix=False, context=context
2810 )
2811 if text.endswith(".") and self.omit__names:
2812 if self.omit__names == 1:
2813 # true if txt is _not_ a __ name, false otherwise:
2814 no__name = lambda txt: re.match(r".*\.__.*?__", txt) is None
2815 else:
2816 # true if txt is _not_ a _ name, false otherwise:
2817 no__name = (
2818 lambda txt: re.match(r"\._.*?", txt[txt.rindex(".") :])
2819 is None
2820 )
2821 matches = filter(no__name, matches)
2822 matches = _convert_matcher_v1_result_to_v2(
2823 matches, type="attribute", fragment=fragment
2824 )
2825 return matches
2826 except NameError:
2827 # catches <undefined attributes>.<tab>
2828 return SimpleMatcherResult(completions=[], suppress=False)
2829 else:
2830 try:
2831 matches = self.global_matches(context.token, context=context)
2832 except TypeError:
2833 matches = self.global_matches(context.token)
2834 # TODO: maybe distinguish between functions, modules and just "variables"
2835 return SimpleMatcherResult(
2836 completions=[
2837 SimpleCompletion(text=match, type="variable") for match in matches
2838 ],
2839 suppress=False,
2840 )
2841
2842 def _default_arguments_from_docstring(self, doc):
2843 """Parse the first line of docstring for call signature.
2844
2845 Docstring should be of the form 'min(iterable[, key=func])\n'.
2846 It can also parse cython docstring of the form
2847 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)'.
2848 """
2849 if doc is None:
2850 return []
2851
2852 #care only the firstline
2853 line = doc.lstrip().splitlines()[0]
2854
2855 #p = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
2856 #'min(iterable[, key=func])\n' -> 'iterable[, key=func]'
2857 sig = self.docstring_sig_re.search(line)
2858 if sig is None:
2859 return []
2860 # iterable[, key=func]' -> ['iterable[' ,' key=func]']
2861 sig = sig.groups()[0].split(',')
2862 ret = []
2863 for s in sig:
2864 #re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
2865 ret += self.docstring_kwd_re.findall(s)
2866 return ret
2867
2868 def _default_arguments(self, obj):
2869 """Return the list of default arguments of obj if it is callable,
2870 or empty list otherwise."""
2871 call_obj = obj
2872 ret = []
2873 if inspect.isbuiltin(obj):
2874 pass
2875 elif not (inspect.isfunction(obj) or inspect.ismethod(obj)):
2876 if inspect.isclass(obj):
2877 #for cython embedsignature=True the constructor docstring
2878 #belongs to the object itself not __init__
2879 ret += self._default_arguments_from_docstring(
2880 getattr(obj, '__doc__', ''))
2881 # for classes, check for __init__,__new__
2882 call_obj = (getattr(obj, '__init__', None) or
2883 getattr(obj, '__new__', None))
2884 # for all others, check if they are __call__able
2885 elif hasattr(obj, '__call__'):
2886 call_obj = obj.__call__
2887 ret += self._default_arguments_from_docstring(
2888 getattr(call_obj, '__doc__', ''))
2889
2890 _keeps = (inspect.Parameter.KEYWORD_ONLY,
2891 inspect.Parameter.POSITIONAL_OR_KEYWORD)
2892
2893 try:
2894 sig = inspect.signature(obj)
2895 ret.extend(k for k, v in sig.parameters.items() if
2896 v.kind in _keeps)
2897 except ValueError:
2898 pass
2899
2900 return list(set(ret))
2901
2902 @context_matcher()
2903 def python_func_kw_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
2904 """Match named parameters (kwargs) of the last open function."""
2905 matches = self.python_func_kw_matches(context.token)
2906 return _convert_matcher_v1_result_to_v2_no_no(matches, type="param")
2907
2908 def python_func_kw_matches(self, text: str) -> list[str]:
2909 """Match named parameters (kwargs) of the last open function.
2910
2911 .. deprecated:: 8.6
2912 You can use :meth:`python_func_kw_matcher` instead.
2913 """
2914
2915 if "." in text: # a parameter cannot be dotted
2916 return []
2917 try: regexp = self.__funcParamsRegex
2918 except AttributeError:
2919 regexp = self.__funcParamsRegex = re.compile(r'''
2920 '.*?(?<!\\)' | # single quoted strings or
2921 ".*?(?<!\\)" | # double quoted strings or
2922 \w+ | # identifier
2923 \S # other characters
2924 ''', re.VERBOSE | re.DOTALL)
2925 # 1. find the nearest identifier that comes before an unclosed
2926 # parenthesis before the cursor
2927 # e.g. for "foo (1+bar(x), pa<cursor>,a=1)", the candidate is "foo"
2928 tokens = regexp.findall(self.text_until_cursor)
2929 iterTokens = reversed(tokens)
2930 openPar = 0
2931
2932 for token in iterTokens:
2933 if token == ')':
2934 openPar -= 1
2935 elif token == '(':
2936 openPar += 1
2937 if openPar > 0:
2938 # found the last unclosed parenthesis
2939 break
2940 else:
2941 return []
2942 # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" )
2943 ids = []
2944 isId = _IDENTIFIER_END_RE.match
2945
2946 while True:
2947 try:
2948 ids.append(next(iterTokens))
2949 if not isId(ids[-1]):
2950 ids.pop()
2951 break
2952 if not next(iterTokens) == '.':
2953 break
2954 except StopIteration:
2955 break
2956
2957 # Find all named arguments already assigned to, as to avoid suggesting
2958 # them again
2959 usedNamedArgs = set()
2960 par_level = -1
2961 for token, next_token in itertools.pairwise(tokens):
2962 if token == '(':
2963 par_level += 1
2964 elif token == ')':
2965 par_level -= 1
2966
2967 if par_level != 0:
2968 continue
2969
2970 if next_token != '=':
2971 continue
2972
2973 usedNamedArgs.add(token)
2974
2975 argMatches = []
2976 try:
2977 callableObj = '.'.join(ids[::-1])
2978 namedArgs = self._default_arguments(eval(callableObj,
2979 self.namespace))
2980
2981 # Remove used named arguments from the list, no need to show twice
2982 for namedArg in set(namedArgs) - usedNamedArgs:
2983 if namedArg.startswith(text):
2984 argMatches.append("%s=" %namedArg)
2985 except Exception:
2986 pass
2987
2988 return argMatches
2989
2990 @staticmethod
2991 def _get_keys(obj: Any) -> list[Any]:
2992 # Objects can define their own completions by defining an
2993 # _ipy_key_completions_() method.
2994 method = get_real_method(obj, '_ipython_key_completions_')
2995 if method is not None:
2996 return method()
2997
2998 # Special case some common in-memory dict-like types
2999 if isinstance(obj, dict) or _safe_isinstance(obj, "pandas", "DataFrame"):
3000 try:
3001 return list(obj.keys())
3002 except Exception:
3003 return []
3004 elif _safe_isinstance(obj, "pandas", "core", "indexing", "_LocIndexer"):
3005 try:
3006 return list(obj.obj.keys())
3007 except Exception:
3008 return []
3009 elif _safe_isinstance(obj, 'numpy', 'ndarray') or\
3010 _safe_isinstance(obj, 'numpy', 'void'):
3011 return obj.dtype.names or []
3012 return []
3013
3014 @context_matcher()
3015 def dict_key_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
3016 """Match string keys in a dictionary, after e.g. ``foo[``."""
3017 matches = self.dict_key_matches(context.token)
3018 return _convert_matcher_v1_result_to_v2(
3019 matches, type="dict key", suppress_if_matches=True
3020 )
3021
3022 def dict_key_matches(self, text: str) -> list[str]:
3023 """Match string keys in a dictionary, after e.g. ``foo[``.
3024
3025 .. deprecated:: 8.6
3026 You can use :meth:`dict_key_matcher` instead.
3027 """
3028
3029 # Short-circuit on closed dictionary (regular expression would
3030 # not match anyway, but would take quite a while).
3031 if self.text_until_cursor.strip().endswith("]"):
3032 return []
3033
3034 match = DICT_MATCHER_REGEX.search(self.text_until_cursor)
3035
3036 if match is None:
3037 return []
3038
3039 expr, prior_tuple_keys, key_prefix = match.groups()
3040
3041 obj = self._evaluate_expr(expr)
3042
3043 if obj is not_found:
3044 return []
3045
3046 keys = self._get_keys(obj)
3047 if not keys:
3048 return keys
3049
3050 tuple_prefix = guarded_eval(
3051 prior_tuple_keys,
3052 EvaluationContext(
3053 globals=self.global_namespace,
3054 locals=self.namespace,
3055 evaluation=self.evaluation, # type: ignore
3056 in_subscript=True,
3057 auto_import=self._auto_import,
3058 policy_overrides=self.policy_overrides,
3059 ),
3060 )
3061
3062 closing_quote, token_offset, matches = match_dict_keys(
3063 keys, key_prefix, self.splitter.delims, extra_prefix=tuple_prefix
3064 )
3065 if not matches:
3066 return []
3067
3068 # get the cursor position of
3069 # - the text being completed
3070 # - the start of the key text
3071 # - the start of the completion
3072 text_start = len(self.text_until_cursor) - len(text)
3073 if key_prefix:
3074 key_start = match.start(3)
3075 completion_start = key_start + token_offset
3076 else:
3077 key_start = completion_start = match.end()
3078
3079 # grab the leading prefix, to make sure all completions start with `text`
3080 if text_start > key_start:
3081 leading = ''
3082 else:
3083 leading = text[text_start:completion_start]
3084
3085 # append closing quote and bracket as appropriate
3086 # this is *not* appropriate if the opening quote or bracket is outside
3087 # the text given to this method, e.g. `d["""a\nt
3088 can_close_quote = False
3089 can_close_bracket = False
3090
3091 continuation = self.line_buffer[len(self.text_until_cursor) :].strip()
3092
3093 if continuation.startswith(closing_quote):
3094 # do not close if already closed, e.g. `d['a<tab>'`
3095 continuation = continuation[len(closing_quote) :]
3096 else:
3097 can_close_quote = True
3098
3099 continuation = continuation.strip()
3100
3101 # e.g. `pandas.DataFrame` has different tuple indexer behaviour,
3102 # handling it is out of scope, so let's avoid appending suffixes.
3103 has_known_tuple_handling = isinstance(obj, dict)
3104
3105 can_close_bracket = (
3106 not continuation.startswith("]") and self.auto_close_dict_keys
3107 )
3108 can_close_tuple_item = (
3109 not continuation.startswith(",")
3110 and has_known_tuple_handling
3111 and self.auto_close_dict_keys
3112 )
3113 can_close_quote = can_close_quote and self.auto_close_dict_keys
3114
3115 # fast path if closing quote should be appended but not suffix is allowed
3116 if not can_close_quote and not can_close_bracket and closing_quote:
3117 return [leading + k for k in matches]
3118
3119 results = []
3120
3121 end_of_tuple_or_item = _DictKeyState.END_OF_TUPLE | _DictKeyState.END_OF_ITEM
3122
3123 for k, state_flag in matches.items():
3124 result = leading + k
3125 if can_close_quote and closing_quote:
3126 result += closing_quote
3127
3128 if state_flag == end_of_tuple_or_item:
3129 # We do not know which suffix to add,
3130 # e.g. both tuple item and string
3131 # match this item.
3132 pass
3133
3134 if state_flag in end_of_tuple_or_item and can_close_bracket:
3135 result += "]"
3136 if state_flag == _DictKeyState.IN_TUPLE and can_close_tuple_item:
3137 result += ", "
3138 results.append(result)
3139 return results
3140
3141 @context_matcher()
3142 def unicode_name_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
3143 """Match Latex-like syntax for unicode characters base
3144 on the name of the character.
3145
3146 This does ``\\GREEK SMALL LETTER ETA`` -> ``η``
3147
3148 Works only on valid python 3 identifier, or on combining characters that
3149 will combine to form a valid identifier.
3150 """
3151
3152 text = context.text_until_cursor
3153
3154 slashpos = text.rfind('\\')
3155 if slashpos > -1:
3156 s = text[slashpos+1:]
3157 try :
3158 unic = unicodedata.lookup(s)
3159 # allow combining chars
3160 if ('a'+unic).isidentifier():
3161 return {
3162 "completions": [SimpleCompletion(text=unic, type="unicode")],
3163 "suppress": True,
3164 "matched_fragment": "\\" + s,
3165 }
3166 except KeyError:
3167 pass
3168 return {
3169 "completions": [],
3170 "suppress": False,
3171 }
3172
3173 @context_matcher()
3174 def latex_name_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
3175 """Match Latex syntax for unicode characters.
3176
3177 This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
3178 """
3179 fragment, matches = self.latex_matches(context.text_until_cursor)
3180 return _convert_matcher_v1_result_to_v2(
3181 matches, type="latex", fragment=fragment, suppress_if_matches=True
3182 )
3183
3184 def latex_matches(self, text: str) -> tuple[str, Sequence[str]]:
3185 """Match Latex syntax for unicode characters.
3186
3187 This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
3188
3189 .. deprecated:: 8.6
3190 You can use :meth:`latex_name_matcher` instead.
3191 """
3192 slashpos = text.rfind('\\')
3193 if slashpos > -1:
3194 s = text[slashpos:]
3195 if s in latex_symbols:
3196 # Try to complete a full latex symbol to unicode
3197 # \\alpha -> α
3198 return s, [latex_symbols[s]]
3199 else:
3200 # If a user has partially typed a latex symbol, give them
3201 # a full list of options \al -> [\aleph, \alpha]
3202 matches = [k for k in latex_symbols if k.startswith(s)]
3203 if matches:
3204 return s, matches
3205 return '', ()
3206
3207 @context_matcher()
3208 def custom_completer_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
3209 """Dispatch custom completer.
3210
3211 If a match is found, suppresses all other matchers except for Jedi.
3212 """
3213 matches = self.dispatch_custom_completer(context.token) or []
3214 result = _convert_matcher_v1_result_to_v2(
3215 matches, type=_UNKNOWN_TYPE, suppress_if_matches=True
3216 )
3217 result["ordered"] = True
3218 result["do_not_suppress"] = {_get_matcher_id(self._jedi_matcher)}
3219 return result
3220
3221 def dispatch_custom_completer(self, text: str) -> list[str] | None:
3222 """
3223 .. deprecated:: 8.6
3224 You can use :meth:`custom_completer_matcher` instead.
3225 """
3226 if not self.custom_completers:
3227 return
3228
3229 line = self.line_buffer
3230 if not line.strip():
3231 return None
3232
3233 # Create a little structure to pass all the relevant information about
3234 # the current completion to any custom completer.
3235 event = SimpleNamespace()
3236 event.line = line
3237 event.symbol = text
3238 cmd = line.split(None,1)[0]
3239 event.command = cmd
3240 event.text_until_cursor = self.text_until_cursor
3241
3242 # for foo etc, try also to find completer for %foo
3243 if not cmd.startswith(self.magic_escape):
3244 try_magic = self.custom_completers.s_matches(
3245 self.magic_escape + cmd)
3246 else:
3247 try_magic = []
3248
3249 for c in itertools.chain(self.custom_completers.s_matches(cmd),
3250 try_magic,
3251 self.custom_completers.flat_matches(self.text_until_cursor)):
3252 try:
3253 res = c(event)
3254 if res:
3255 # first, try case sensitive match
3256 withcase = [r for r in res if r.startswith(text)]
3257 if withcase:
3258 return withcase
3259 # if none, then case insensitive ones are ok too
3260 text_low = text.lower()
3261 return [r for r in res if r.lower().startswith(text_low)]
3262 except TryNext:
3263 pass
3264 except KeyboardInterrupt:
3265 """
3266 If custom completer take too long,
3267 let keyboard interrupt abort and return nothing.
3268 """
3269 break
3270
3271 return None
3272
3273 def completions(self, text: str, offset: int)->Iterator[Completion]:
3274 """
3275 Returns an iterator over the possible completions
3276
3277 .. warning::
3278
3279 Unstable
3280
3281 This function is unstable, API may change without warning.
3282 It will also raise unless use in proper context manager.
3283
3284 Parameters
3285 ----------
3286 text : str
3287 Full text of the current input, multi line string.
3288 offset : int
3289 Integer representing the position of the cursor in ``text``. Offset
3290 is 0-based indexed.
3291
3292 Yields
3293 ------
3294 Completion
3295
3296 Notes
3297 -----
3298 The cursor on a text can either be seen as being "in between"
3299 characters or "On" a character depending on the interface visible to
3300 the user. For consistency the cursor being on "in between" characters X
3301 and Y is equivalent to the cursor being "on" character Y, that is to say
3302 the character the cursor is on is considered as being after the cursor.
3303
3304 Combining characters may span more that one position in the
3305 text.
3306
3307 .. note::
3308
3309 If ``IPCompleter.debug`` is :py:data:`True` will yield a ``--jedi/ipython--``
3310 fake Completion token to distinguish completion returned by Jedi
3311 and usual IPython completion.
3312
3313 .. note::
3314
3315 Completions are not completely deduplicated yet. If identical
3316 completions are coming from different sources this function does not
3317 ensure that each completion object will only be present once.
3318 """
3319 warnings.warn("_complete is a provisional API (as of IPython 6.0). "
3320 "It may change without warnings. "
3321 "Use in corresponding context manager.",
3322 category=ProvisionalCompleterWarning, stacklevel=2)
3323
3324 seen = set()
3325 profiler:cProfile.Profile | None
3326 try:
3327 if self.profile_completions:
3328 import cProfile
3329 profiler = cProfile.Profile()
3330 profiler.enable()
3331 else:
3332 profiler = None
3333
3334 for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
3335 if c and (c in seen):
3336 continue
3337 yield c
3338 seen.add(c)
3339 except KeyboardInterrupt:
3340 """if completions take too long and users send keyboard interrupt,
3341 do not crash and return ASAP. """
3342 pass
3343 finally:
3344 if profiler is not None:
3345 profiler.disable()
3346 ensure_dir_exists(self.profiler_output_dir)
3347 output_path = os.path.join(self.profiler_output_dir, str(uuid.uuid4()))
3348 print("Writing profiler output to", output_path)
3349 profiler.dump_stats(output_path)
3350
3351 def _completions(self, full_text: str, offset: int, *, _timeout) -> Iterator[Completion]:
3352 """
3353 Core completion module.Same signature as :any:`completions`, with the
3354 extra `timeout` parameter (in seconds).
3355
3356 Computing jedi's completion ``.type`` can be quite expensive (it is a
3357 lazy property) and can require some warm-up, more warm up than just
3358 computing the ``name`` of a completion. The warm-up can be :
3359
3360 - Long warm-up the first time a module is encountered after
3361 install/update: actually build parse/inference tree.
3362
3363 - first time the module is encountered in a session: load tree from
3364 disk.
3365
3366 We don't want to block completions for tens of seconds so we give the
3367 completer a "budget" of ``_timeout`` seconds per invocation to compute
3368 completions types, the completions that have not yet been computed will
3369 be marked as "unknown" an will have a chance to be computed next round
3370 are things get cached.
3371
3372 Keep in mind that Jedi is not the only thing treating the completion so
3373 keep the timeout short-ish as if we take more than 0.3 second we still
3374 have lots of processing to do.
3375
3376 """
3377 deadline = time.monotonic() + _timeout
3378
3379 before = full_text[:offset]
3380 cursor_line, cursor_column = position_to_cursor(full_text, offset)
3381
3382 jedi_matcher_id = _get_matcher_id(self._jedi_matcher)
3383
3384 def is_non_jedi_result(
3385 result: MatcherResult, identifier: str
3386 ) -> TypeGuard[SimpleMatcherResult]:
3387 return identifier != jedi_matcher_id
3388
3389 results = self._complete(
3390 full_text=full_text, cursor_line=cursor_line, cursor_pos=cursor_column
3391 )
3392
3393 non_jedi_results: dict[str, SimpleMatcherResult] = {
3394 identifier: result
3395 for identifier, result in results.items()
3396 if is_non_jedi_result(result, identifier)
3397 }
3398
3399 jedi_matches = (
3400 cast(_JediMatcherResult, results[jedi_matcher_id])["completions"]
3401 if jedi_matcher_id in results
3402 else ()
3403 )
3404
3405 iter_jm = iter(jedi_matches)
3406 if _timeout:
3407 for jm in iter_jm:
3408 try:
3409 type_ = jm.type
3410 except Exception:
3411 if self.debug:
3412 print("Error in Jedi getting type of ", jm)
3413 type_ = None
3414 delta = len(jm.name_with_symbols) - len(jm.complete)
3415 if type_ == 'function':
3416 signature = _make_signature(jm)
3417 else:
3418 signature = ''
3419 yield Completion(start=offset - delta,
3420 end=offset,
3421 text=jm.name_with_symbols,
3422 type=type_,
3423 signature=signature,
3424 _origin='jedi')
3425
3426 if time.monotonic() > deadline:
3427 break
3428
3429 for jm in iter_jm:
3430 delta = len(jm.name_with_symbols) - len(jm.complete)
3431 yield Completion(
3432 start=offset - delta,
3433 end=offset,
3434 text=jm.name_with_symbols,
3435 type=_UNKNOWN_TYPE, # don't compute type for speed
3436 _origin="jedi",
3437 signature="",
3438 )
3439
3440 # TODO:
3441 # Suppress this, right now just for debug.
3442 if jedi_matches and non_jedi_results and self.debug:
3443 some_start_offset = before.rfind(
3444 next(iter(non_jedi_results.values()))["matched_fragment"]
3445 )
3446 yield Completion(
3447 start=some_start_offset,
3448 end=offset,
3449 text="--jedi/ipython--",
3450 _origin="debug",
3451 type="none",
3452 signature="",
3453 )
3454
3455 ordered: list[Completion] = []
3456 sortable: list[Completion] = []
3457
3458 for origin, result in non_jedi_results.items():
3459 matched_text = result["matched_fragment"]
3460 start_offset = before.rfind(matched_text)
3461 is_ordered = result.get("ordered", False)
3462 container = ordered if is_ordered else sortable
3463
3464 # I'm unsure if this is always true, so let's assert and see if it
3465 # crash
3466 assert before.endswith(matched_text)
3467
3468 for simple_completion in result["completions"]:
3469 completion = Completion(
3470 start=start_offset,
3471 end=offset,
3472 text=simple_completion.text,
3473 _origin=origin,
3474 signature="",
3475 type=simple_completion.type or _UNKNOWN_TYPE,
3476 )
3477 container.append(completion)
3478
3479 yield from list(self._deduplicate(ordered + self._sort(sortable)))[
3480 :MATCHES_LIMIT
3481 ]
3482
3483 def complete(
3484 self, text=None, line_buffer=None, cursor_pos=None
3485 ) -> tuple[str, Sequence[str]]:
3486 """Find completions for the given text and line context.
3487
3488 Note that both the text and the line_buffer are optional, but at least
3489 one of them must be given.
3490
3491 Parameters
3492 ----------
3493 text : string, optional
3494 Text to perform the completion on. If not given, the line buffer
3495 is split using the instance's CompletionSplitter object.
3496 line_buffer : string, optional
3497 If not given, the completer attempts to obtain the current line
3498 buffer via readline. This keyword allows clients which are
3499 requesting for text completions in non-readline contexts to inform
3500 the completer of the entire text.
3501 cursor_pos : int, optional
3502 Index of the cursor in the full line buffer. Should be provided by
3503 remote frontends where kernel has no access to frontend state.
3504
3505 Returns
3506 -------
3507 Tuple of two items:
3508 text : str
3509 Text that was actually used in the completion.
3510 matches : list
3511 A list of completion matches.
3512
3513 Notes
3514 -----
3515 This API is likely to be deprecated and replaced by
3516 :any:`IPCompleter.completions` in the future.
3517
3518 """
3519 warnings.warn('`Completer.complete` is pending deprecation since '
3520 'IPython 6.0 and will be replaced by `Completer.completions`.',
3521 PendingDeprecationWarning)
3522 # potential todo, FOLD the 3rd throw away argument of _complete
3523 # into the first 2 one.
3524 # TODO: Q: does the above refer to jedi completions (i.e. 0-indexed?)
3525 # TODO: should we deprecate now, or does it stay?
3526
3527 results = self._complete(
3528 line_buffer=line_buffer, cursor_pos=cursor_pos, text=text, cursor_line=0
3529 )
3530
3531 jedi_matcher_id = _get_matcher_id(self._jedi_matcher)
3532
3533 return self._arrange_and_extract(
3534 results,
3535 # TODO: can we confirm that excluding Jedi here was a deliberate choice in previous version?
3536 skip_matchers={jedi_matcher_id},
3537 # this API does not support different start/end positions (fragments of token).
3538 abort_if_offset_changes=True,
3539 )
3540
3541 def _arrange_and_extract(
3542 self,
3543 results: dict[str, MatcherResult],
3544 skip_matchers: set[str],
3545 abort_if_offset_changes: bool,
3546 ):
3547 sortable: list[AnyMatcherCompletion] = []
3548 ordered: list[AnyMatcherCompletion] = []
3549 most_recent_fragment = None
3550 for identifier, result in results.items():
3551 if identifier in skip_matchers:
3552 continue
3553 if not result["completions"]:
3554 continue
3555 if not most_recent_fragment:
3556 most_recent_fragment = result["matched_fragment"]
3557 if (
3558 abort_if_offset_changes
3559 and result["matched_fragment"] != most_recent_fragment
3560 ):
3561 break
3562 if result.get("ordered", False):
3563 ordered.extend(result["completions"])
3564 else:
3565 sortable.extend(result["completions"])
3566
3567 if not most_recent_fragment:
3568 most_recent_fragment = "" # to satisfy typechecker (and just in case)
3569
3570 return most_recent_fragment, [
3571 m.text for m in self._deduplicate(ordered + self._sort(sortable))
3572 ]
3573
3574 def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None,
3575 full_text=None) -> _CompleteResult:
3576 """
3577 Like complete but can also returns raw jedi completions as well as the
3578 origin of the completion text. This could (and should) be made much
3579 cleaner but that will be simpler once we drop the old (and stateful)
3580 :any:`complete` API.
3581
3582 With current provisional API, cursor_pos act both (depending on the
3583 caller) as the offset in the ``text`` or ``line_buffer``, or as the
3584 ``column`` when passing multiline strings this could/should be renamed
3585 but would add extra noise.
3586
3587 Parameters
3588 ----------
3589 cursor_line
3590 Index of the line the cursor is on. 0 indexed.
3591 cursor_pos
3592 Position of the cursor in the current line/line_buffer/text. 0
3593 indexed.
3594 line_buffer : optional, str
3595 The current line the cursor is in, this is mostly due to legacy
3596 reason that readline could only give a us the single current line.
3597 Prefer `full_text`.
3598 text : str
3599 The current "token" the cursor is in, mostly also for historical
3600 reasons. as the completer would trigger only after the current line
3601 was parsed.
3602 full_text : str
3603 Full text of the current cell.
3604
3605 Returns
3606 -------
3607 An ordered dictionary where keys are identifiers of completion
3608 matchers and values are ``MatcherResult``s.
3609 """
3610
3611 # if the cursor position isn't given, the only sane assumption we can
3612 # make is that it's at the end of the line (the common case)
3613 if cursor_pos is None:
3614 cursor_pos = len(line_buffer) if text is None else len(text)
3615
3616 if self.use_main_ns:
3617 self.namespace = __main__.__dict__
3618
3619 # if text is either None or an empty string, rely on the line buffer
3620 if (not line_buffer) and full_text:
3621 line_buffer = full_text.split('\n')[cursor_line]
3622 if not text: # issue #11508: check line_buffer before calling split_line
3623 text = (
3624 self.splitter.split_line(line_buffer, cursor_pos) if line_buffer else ""
3625 )
3626
3627 # If no line buffer is given, assume the input text is all there was
3628 if line_buffer is None:
3629 line_buffer = text
3630
3631 # deprecated - do not use `line_buffer` in new code.
3632 self.line_buffer = line_buffer
3633 self.text_until_cursor = self.line_buffer[:cursor_pos]
3634
3635 if not full_text:
3636 full_text = line_buffer
3637
3638 context = CompletionContext(
3639 full_text=full_text,
3640 cursor_position=cursor_pos,
3641 cursor_line=cursor_line,
3642 token=self._extract_code(text),
3643 limit=MATCHES_LIMIT,
3644 )
3645
3646 # Start with a clean slate of completions
3647 results: dict[str, MatcherResult] = {}
3648
3649 jedi_matcher_id = _get_matcher_id(self._jedi_matcher)
3650
3651 suppressed_matchers: set[str] = set()
3652
3653 matchers = {
3654 _get_matcher_id(matcher): matcher
3655 for matcher in sorted(
3656 self.matchers, key=_get_matcher_priority, reverse=True
3657 )
3658 }
3659
3660 for matcher_id, matcher in matchers.items():
3661 matcher_id = _get_matcher_id(matcher)
3662
3663 if matcher_id in self.disable_matchers:
3664 continue
3665
3666 if matcher_id in results:
3667 warnings.warn(f"Duplicate matcher ID: {matcher_id}.")
3668
3669 if matcher_id in suppressed_matchers:
3670 continue
3671
3672 result: MatcherResult
3673 try:
3674 if _is_matcher_v1(matcher):
3675 result = _convert_matcher_v1_result_to_v2_no_no(
3676 matcher(text), type=_UNKNOWN_TYPE
3677 )
3678 elif _is_matcher_v2(matcher):
3679 result = matcher(context)
3680 else:
3681 api_version = _get_matcher_api_version(matcher)
3682 raise ValueError(f"Unsupported API version {api_version}")
3683 except BaseException:
3684 # Show the ugly traceback if the matcher causes an
3685 # exception, but do NOT crash the kernel!
3686 sys.excepthook(*sys.exc_info())
3687 continue
3688
3689 # set default value for matched fragment if suffix was not selected.
3690 result["matched_fragment"] = result.get("matched_fragment", context.token)
3691
3692 if not suppressed_matchers:
3693 suppression_recommended: bool | set[str] = result.get(
3694 "suppress", False
3695 )
3696
3697 suppression_config = (
3698 self.suppress_competing_matchers.get(matcher_id, None)
3699 if isinstance(self.suppress_competing_matchers, dict)
3700 else self.suppress_competing_matchers
3701 )
3702 should_suppress = (
3703 (suppression_config is True)
3704 or (suppression_recommended and (suppression_config is not False))
3705 ) and has_any_completions(result)
3706
3707 if should_suppress:
3708 suppression_exceptions: set[str] = result.get(
3709 "do_not_suppress", set()
3710 )
3711 if isinstance(suppression_recommended, Iterable):
3712 to_suppress = set(suppression_recommended)
3713 else:
3714 to_suppress = set(matchers)
3715 suppressed_matchers = to_suppress - suppression_exceptions
3716
3717 new_results = {}
3718 for previous_matcher_id, previous_result in results.items():
3719 if previous_matcher_id not in suppressed_matchers:
3720 new_results[previous_matcher_id] = previous_result
3721 results = new_results
3722
3723 results[matcher_id] = result
3724
3725 _, matches = self._arrange_and_extract(
3726 results,
3727 # TODO Jedi completions non included in legacy stateful API; was this deliberate or omission?
3728 # if it was omission, we can remove the filtering step, otherwise remove this comment.
3729 skip_matchers={jedi_matcher_id},
3730 abort_if_offset_changes=False,
3731 )
3732
3733 # populate legacy stateful API
3734 self.matches = matches
3735
3736 return results
3737
3738 @staticmethod
3739 def _deduplicate(
3740 matches: Sequence[AnyCompletion],
3741 ) -> Iterable[AnyCompletion]:
3742 filtered_matches: dict[str, AnyCompletion] = {}
3743 for match in matches:
3744 text = match.text
3745 if (
3746 text not in filtered_matches
3747 or filtered_matches[text].type == _UNKNOWN_TYPE
3748 ):
3749 filtered_matches[text] = match
3750
3751 return filtered_matches.values()
3752
3753 @staticmethod
3754 def _sort(matches: Sequence[AnyCompletion]):
3755 return sorted(matches, key=lambda x: completions_sorting_key(x.text))
3756
3757 @context_matcher()
3758 def fwd_unicode_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
3759 """Same as :any:`fwd_unicode_match`, but adopted to new Matcher API."""
3760 # TODO: use `context.limit` to terminate early once we matched the maximum
3761 # number that will be used downstream; can be added as an optional to
3762 # `fwd_unicode_match(text: str, limit: int = None)` or we could re-implement here.
3763 fragment, matches = self.fwd_unicode_match(context.text_until_cursor)
3764 return _convert_matcher_v1_result_to_v2(
3765 matches, type="unicode", fragment=fragment, suppress_if_matches=True
3766 )
3767
3768 def fwd_unicode_match(self, text: str) -> tuple[str, Sequence[str]]:
3769 """
3770 Forward match a string starting with a backslash with a list of
3771 potential Unicode completions.
3772
3773 Will compute list of Unicode character names on first call and cache it.
3774
3775 .. deprecated:: 8.6
3776 You can use :meth:`fwd_unicode_matcher` instead.
3777
3778 Returns
3779 -------
3780 At tuple with:
3781 - matched text (empty if no matches)
3782 - list of potential completions, empty tuple otherwise)
3783 """
3784 # TODO: self.unicode_names is here a list we traverse each time with ~100k elements.
3785 # We could do a faster match using a Trie.
3786
3787 # Using pygtrie the following seem to work:
3788
3789 # s = PrefixSet()
3790
3791 # for c in range(0,0x10FFFF + 1):
3792 # try:
3793 # s.add(unicodedata.name(chr(c)))
3794 # except ValueError:
3795 # pass
3796 # [''.join(k) for k in s.iter(prefix)]
3797
3798 # But need to be timed and adds an extra dependency.
3799
3800 slashpos = text.rfind('\\')
3801 # if text starts with slash
3802 if slashpos > -1:
3803 # PERF: It's important that we don't access self._unicode_names
3804 # until we're inside this if-block. _unicode_names is lazily
3805 # initialized, and it takes a user-noticeable amount of time to
3806 # initialize it, so we don't want to initialize it unless we're
3807 # actually going to use it.
3808 s = text[slashpos + 1 :]
3809 sup = s.upper()
3810 candidates = [x for x in self.unicode_names if x.startswith(sup)]
3811 if candidates:
3812 return s, candidates
3813 candidates = [x for x in self.unicode_names if sup in x]
3814 if candidates:
3815 return s, candidates
3816 splitsup = sup.split(" ")
3817 candidates = [
3818 x for x in self.unicode_names if all(u in x for u in splitsup)
3819 ]
3820 if candidates:
3821 return s, candidates
3822
3823 return "", ()
3824
3825 # if text does not start with slash
3826 else:
3827 return '', ()
3828
3829 @property
3830 def unicode_names(self) -> list[str]:
3831 """List of names of unicode code points that can be completed.
3832
3833 The list is lazily initialized on first access.
3834 """
3835 if self._unicode_names is None:
3836 names = []
3837 for c in range(0,0x10FFFF + 1):
3838 try:
3839 names.append(unicodedata.name(chr(c)))
3840 except ValueError:
3841 pass
3842 self._unicode_names = _unicode_name_compute(_UNICODE_RANGES)
3843
3844 return self._unicode_names
3845
3846
3847def _unicode_name_compute(ranges: list[tuple[int, int]]) -> list[str]:
3848 names = []
3849 for start,stop in ranges:
3850 for c in range(start, stop) :
3851 try:
3852 names.append(unicodedata.name(chr(c)))
3853 except ValueError:
3854 pass
3855 return names