##// END OF EJS Templates
Merge branch 'main' into shaperilio/autoreload-verbosity
Emilio Graff -
r27880:51eb040f merge
parent child Browse files
Show More
@@ -0,0 +1,3 b''
1 import os
2
3 GENERATING_DOCUMENTATION = os.environ.get("IN_SPHINX_RUN", None) == "True"
@@ -28,7 +28,7 b' jobs:'
28 - name: Install dependencies
28 - name: Install dependencies
29 run: |
29 run: |
30 python -m pip install --upgrade pip
30 python -m pip install --upgrade pip
31 pip install darker black==21.12b0
31 pip install darker==1.5.1 black==22.10.0
32 - name: Lint with darker
32 - name: Lint with darker
33 run: |
33 run: |
34 darker -r 60625f241f298b5039cb2debc365db38aa7bb522 --check --diff . || (
34 darker -r 60625f241f298b5039cb2debc365db38aa7bb522 --check --diff . || (
@@ -24,7 +24,6 b' __pycache__'
24 .cache
24 .cache
25 .coverage
25 .coverage
26 *.swp
26 *.swp
27 .vscode
28 .pytest_cache
27 .pytest_cache
29 .python-version
28 .python-version
30 venv*/
29 venv*/
@@ -73,25 +73,6 b' class CachingCompiler(codeop.Compile):'
73 def __init__(self):
73 def __init__(self):
74 codeop.Compile.__init__(self)
74 codeop.Compile.__init__(self)
75
75
76 # This is ugly, but it must be done this way to allow multiple
77 # simultaneous ipython instances to coexist. Since Python itself
78 # directly accesses the data structures in the linecache module, and
79 # the cache therein is global, we must work with that data structure.
80 # We must hold a reference to the original checkcache routine and call
81 # that in our own check_cache() below, but the special IPython cache
82 # must also be shared by all IPython instances. If we were to hold
83 # separate caches (one in each CachingCompiler instance), any call made
84 # by Python itself to linecache.checkcache() would obliterate the
85 # cached data from the other IPython instances.
86 if not hasattr(linecache, '_ipython_cache'):
87 linecache._ipython_cache = {}
88 if not hasattr(linecache, '_checkcache_ori'):
89 linecache._checkcache_ori = linecache.checkcache
90 # Now, we must monkeypatch the linecache directly so that parts of the
91 # stdlib that call it outside our control go through our codepath
92 # (otherwise we'd lose our tracebacks).
93 linecache.checkcache = check_linecache_ipython
94
95 # Caching a dictionary { filename: execution_count } for nicely
76 # Caching a dictionary { filename: execution_count } for nicely
96 # rendered tracebacks. The filename corresponds to the filename
77 # rendered tracebacks. The filename corresponds to the filename
97 # argument used for the builtins.compile function.
78 # argument used for the builtins.compile function.
@@ -161,14 +142,24 b' class CachingCompiler(codeop.Compile):'
161 # Save the execution count
142 # Save the execution count
162 self._filename_map[name] = number
143 self._filename_map[name] = number
163
144
145 # Since Python 2.5, setting mtime to `None` means the lines will
146 # never be removed by `linecache.checkcache`. This means all the
147 # monkeypatching has *never* been necessary, since this code was
148 # only added in 2010, at which point IPython had already stopped
149 # supporting Python 2.4.
150 #
151 # Note that `linecache.clearcache` and `linecache.updatecache` may
152 # still remove our code from the cache, but those show explicit
153 # intent, and we should not try to interfere. Normally the former
154 # is never called except when out of memory, and the latter is only
155 # called for lines *not* in the cache.
164 entry = (
156 entry = (
165 len(transformed_code),
157 len(transformed_code),
166 time.time(),
158 None,
167 [line + "\n" for line in transformed_code.splitlines()],
159 [line + "\n" for line in transformed_code.splitlines()],
168 name,
160 name,
169 )
161 )
170 linecache.cache[name] = entry
162 linecache.cache[name] = entry
171 linecache._ipython_cache[name] = entry
172 return name
163 return name
173
164
174 @contextmanager
165 @contextmanager
@@ -187,10 +178,22 b' class CachingCompiler(codeop.Compile):'
187
178
188
179
189 def check_linecache_ipython(*args):
180 def check_linecache_ipython(*args):
190 """Call linecache.checkcache() safely protecting our cached values.
181 """Deprecated since IPython 8.6. Call linecache.checkcache() directly.
182
183 It was already not necessary to call this function directly. If no
184 CachingCompiler had been created, this function would fail badly. If
185 an instance had been created, this function would've been monkeypatched
186 into place.
187
188 As of IPython 8.6, the monkeypatching has gone away entirely. But there
189 were still internal callers of this function, so maybe external callers
190 also existed?
191 """
191 """
192 # First call the original checkcache as intended
192 import warnings
193 linecache._checkcache_ori(*args)
193
194 # Then, update back the cache with our data, so that tracebacks related
194 warnings.warn(
195 # to our compiled codes can be produced.
195 "Deprecated Since IPython 8.6, Just call linecache.checkcache() directly.",
196 linecache.cache.update(linecache._ipython_cache)
196 DeprecationWarning,
197 stacklevel=2,
198 )
199 linecache.checkcache()
This diff has been collapsed as it changes many lines, (973 lines changed) Show them Hide them
@@ -100,6 +100,73 b' option.'
100
100
101 Be sure to update :any:`jedi` to the latest stable version or to try the
101 Be sure to update :any:`jedi` to the latest stable version or to try the
102 current development version to get better completions.
102 current development version to get better completions.
103
104 Matchers
105 ========
106
107 All completions routines are implemented using unified *Matchers* API.
108 The matchers API is provisional and subject to change without notice.
109
110 The built-in matchers include:
111
112 - :any:`IPCompleter.dict_key_matcher`: dictionary key completions,
113 - :any:`IPCompleter.magic_matcher`: completions for magics,
114 - :any:`IPCompleter.unicode_name_matcher`,
115 :any:`IPCompleter.fwd_unicode_matcher`
116 and :any:`IPCompleter.latex_name_matcher`: see `Forward latex/unicode completion`_,
117 - :any:`back_unicode_name_matcher` and :any:`back_latex_name_matcher`: see `Backward latex completion`_,
118 - :any:`IPCompleter.file_matcher`: paths to files and directories,
119 - :any:`IPCompleter.python_func_kw_matcher` - function keywords,
120 - :any:`IPCompleter.python_matches` - globals and attributes (v1 API),
121 - ``IPCompleter.jedi_matcher`` - static analysis with Jedi,
122 - :any:`IPCompleter.custom_completer_matcher` - pluggable completer with a default
123 implementation in :any:`InteractiveShell` which uses IPython hooks system
124 (`complete_command`) with string dispatch (including regular expressions).
125 Differently to other matchers, ``custom_completer_matcher`` will not suppress
126 Jedi results to match behaviour in earlier IPython versions.
127
128 Custom matchers can be added by appending to ``IPCompleter.custom_matchers`` list.
129
130 Matcher API
131 -----------
132
133 Simplifying some details, the ``Matcher`` interface can described as
134
135 .. code-block::
136
137 MatcherAPIv1 = Callable[[str], list[str]]
138 MatcherAPIv2 = Callable[[CompletionContext], SimpleMatcherResult]
139
140 Matcher = MatcherAPIv1 | MatcherAPIv2
141
142 The ``MatcherAPIv1`` reflects the matcher API as available prior to IPython 8.6.0
143 and remains supported as a simplest way for generating completions. This is also
144 currently the only API supported by the IPython hooks system `complete_command`.
145
146 To distinguish between matcher versions ``matcher_api_version`` attribute is used.
147 More precisely, the API allows to omit ``matcher_api_version`` for v1 Matchers,
148 and requires a literal ``2`` for v2 Matchers.
149
150 Once the API stabilises future versions may relax the requirement for specifying
151 ``matcher_api_version`` by switching to :any:`functools.singledispatch`, therefore
152 please do not rely on the presence of ``matcher_api_version`` for any purposes.
153
154 Suppression of competing matchers
155 ---------------------------------
156
157 By default results from all matchers are combined, in the order determined by
158 their priority. Matchers can request to suppress results from subsequent
159 matchers by setting ``suppress`` to ``True`` in the ``MatcherResult``.
160
161 When multiple matchers simultaneously request surpression, the results from of
162 the matcher with higher priority will be returned.
163
164 Sometimes it is desirable to suppress most but not all other matchers;
165 this can be achieved by adding a list of identifiers of matchers which
166 should not be suppressed to ``MatcherResult`` under ``do_not_suppress`` key.
167
168 The suppression behaviour can is user-configurable via
169 :any:`IPCompleter.suppress_competing_matchers`.
103 """
170 """
104
171
105
172
@@ -109,7 +176,7 b' current development version to get better completions.'
109 # Some of this code originated from rlcompleter in the Python standard library
176 # Some of this code originated from rlcompleter in the Python standard library
110 # Copyright (C) 2001 Python Software Foundation, www.python.org
177 # Copyright (C) 2001 Python Software Foundation, www.python.org
111
178
112
179 from __future__ import annotations
113 import builtins as builtin_mod
180 import builtins as builtin_mod
114 import glob
181 import glob
115 import inspect
182 import inspect
@@ -124,9 +191,26 b' import unicodedata'
124 import uuid
191 import uuid
125 import warnings
192 import warnings
126 from contextlib import contextmanager
193 from contextlib import contextmanager
194 from dataclasses import dataclass
195 from functools import cached_property, partial
127 from importlib import import_module
196 from importlib import import_module
128 from types import SimpleNamespace
197 from types import SimpleNamespace
129 from typing import Iterable, Iterator, List, Tuple, Union, Any, Sequence, Dict, NamedTuple, Pattern, Optional
198 from typing import (
199 Iterable,
200 Iterator,
201 List,
202 Tuple,
203 Union,
204 Any,
205 Sequence,
206 Dict,
207 NamedTuple,
208 Pattern,
209 Optional,
210 TYPE_CHECKING,
211 Set,
212 Literal,
213 )
130
214
131 from IPython.core.error import TryNext
215 from IPython.core.error import TryNext
132 from IPython.core.inputtransformer2 import ESC_MAGIC
216 from IPython.core.inputtransformer2 import ESC_MAGIC
@@ -134,10 +218,22 b' from IPython.core.latex_symbols import latex_symbols, reverse_latex_symbol'
134 from IPython.core.oinspect import InspectColors
218 from IPython.core.oinspect import InspectColors
135 from IPython.testing.skipdoctest import skip_doctest
219 from IPython.testing.skipdoctest import skip_doctest
136 from IPython.utils import generics
220 from IPython.utils import generics
221 from IPython.utils.decorators import sphinx_options
137 from IPython.utils.dir2 import dir2, get_real_method
222 from IPython.utils.dir2 import dir2, get_real_method
223 from IPython.utils.docs import GENERATING_DOCUMENTATION
138 from IPython.utils.path import ensure_dir_exists
224 from IPython.utils.path import ensure_dir_exists
139 from IPython.utils.process import arg_split
225 from IPython.utils.process import arg_split
140 from traitlets import Bool, Enum, Int, List as ListTrait, Unicode, default, observe
226 from traitlets import (
227 Bool,
228 Enum,
229 Int,
230 List as ListTrait,
231 Unicode,
232 Dict as DictTrait,
233 Union as UnionTrait,
234 default,
235 observe,
236 )
141 from traitlets.config.configurable import Configurable
237 from traitlets.config.configurable import Configurable
142
238
143 import __main__
239 import __main__
@@ -145,6 +241,7 b' import __main__'
145 # skip module docstests
241 # skip module docstests
146 __skip_doctest__ = True
242 __skip_doctest__ = True
147
243
244
148 try:
245 try:
149 import jedi
246 import jedi
150 jedi.settings.case_insensitive_completion = False
247 jedi.settings.case_insensitive_completion = False
@@ -153,7 +250,26 b' try:'
153 JEDI_INSTALLED = True
250 JEDI_INSTALLED = True
154 except ImportError:
251 except ImportError:
155 JEDI_INSTALLED = False
252 JEDI_INSTALLED = False
156 #-----------------------------------------------------------------------------
253
254
255 if TYPE_CHECKING or GENERATING_DOCUMENTATION:
256 from typing import cast
257 from typing_extensions import TypedDict, NotRequired, Protocol, TypeAlias
258 else:
259
260 def cast(obj, type_):
261 """Workaround for `TypeError: MatcherAPIv2() takes no arguments`"""
262 return obj
263
264 # do not require on runtime
265 NotRequired = Tuple # requires Python >=3.11
266 TypedDict = Dict # by extension of `NotRequired` requires 3.11 too
267 Protocol = object # requires Python >=3.8
268 TypeAlias = Any # requires Python >=3.10
269 if GENERATING_DOCUMENTATION:
270 from typing import TypedDict
271
272 # -----------------------------------------------------------------------------
157 # Globals
273 # Globals
158 #-----------------------------------------------------------------------------
274 #-----------------------------------------------------------------------------
159
275
@@ -166,7 +282,7 b' except ImportError:'
166 _UNICODE_RANGES = [(32, 0x3134b), (0xe0001, 0xe01f0)]
282 _UNICODE_RANGES = [(32, 0x3134b), (0xe0001, 0xe01f0)]
167
283
168 # Public API
284 # Public API
169 __all__ = ['Completer','IPCompleter']
285 __all__ = ["Completer", "IPCompleter"]
170
286
171 if sys.platform == 'win32':
287 if sys.platform == 'win32':
172 PROTECTABLES = ' '
288 PROTECTABLES = ' '
@@ -177,6 +293,8 b' else:'
177 # may have trouble processing.
293 # may have trouble processing.
178 MATCHES_LIMIT = 500
294 MATCHES_LIMIT = 500
179
295
296 # Completion type reported when no type can be inferred.
297 _UNKNOWN_TYPE = "<unknown>"
180
298
181 class ProvisionalCompleterWarning(FutureWarning):
299 class ProvisionalCompleterWarning(FutureWarning):
182 """
300 """
@@ -355,9 +473,12 b' class _FakeJediCompletion:'
355 return '<Fake completion object jedi has crashed>'
473 return '<Fake completion object jedi has crashed>'
356
474
357
475
476 _JediCompletionLike = Union[jedi.api.Completion, _FakeJediCompletion]
477
478
358 class Completion:
479 class Completion:
359 """
480 """
360 Completion object used and return by IPython completers.
481 Completion object used and returned by IPython completers.
361
482
362 .. warning::
483 .. warning::
363
484
@@ -417,6 +538,188 b' class Completion:'
417 return hash((self.start, self.end, self.text))
538 return hash((self.start, self.end, self.text))
418
539
419
540
541 class SimpleCompletion:
542 """Completion item to be included in the dictionary returned by new-style Matcher (API v2).
543
544 .. warning::
545
546 Provisional
547
548 This class is used to describe the currently supported attributes of
549 simple completion items, and any additional implementation details
550 should not be relied on. Additional attributes may be included in
551 future versions, and meaning of text disambiguated from the current
552 dual meaning of "text to insert" and "text to used as a label".
553 """
554
555 __slots__ = ["text", "type"]
556
557 def __init__(self, text: str, *, type: str = None):
558 self.text = text
559 self.type = type
560
561 def __repr__(self):
562 return f"<SimpleCompletion text={self.text!r} type={self.type!r}>"
563
564
565 class _MatcherResultBase(TypedDict):
566 """Definition of dictionary to be returned by new-style Matcher (API v2)."""
567
568 #: Suffix of the provided ``CompletionContext.token``, if not given defaults to full token.
569 matched_fragment: NotRequired[str]
570
571 #: Whether to suppress results from all other matchers (True), some
572 #: matchers (set of identifiers) or none (False); default is False.
573 suppress: NotRequired[Union[bool, Set[str]]]
574
575 #: Identifiers of matchers which should NOT be suppressed when this matcher
576 #: requests to suppress all other matchers; defaults to an empty set.
577 do_not_suppress: NotRequired[Set[str]]
578
579 #: Are completions already ordered and should be left as-is? default is False.
580 ordered: NotRequired[bool]
581
582
583 @sphinx_options(show_inherited_members=True, exclude_inherited_from=["dict"])
584 class SimpleMatcherResult(_MatcherResultBase, TypedDict):
585 """Result of new-style completion matcher."""
586
587 # note: TypedDict is added again to the inheritance chain
588 # in order to get __orig_bases__ for documentation
589
590 #: List of candidate completions
591 completions: Sequence[SimpleCompletion]
592
593
594 class _JediMatcherResult(_MatcherResultBase):
595 """Matching result returned by Jedi (will be processed differently)"""
596
597 #: list of candidate completions
598 completions: Iterable[_JediCompletionLike]
599
600
601 @dataclass
602 class CompletionContext:
603 """Completion context provided as an argument to matchers in the Matcher API v2."""
604
605 # rationale: many legacy matchers relied on completer state (`self.text_until_cursor`)
606 # which was not explicitly visible as an argument of the matcher, making any refactor
607 # prone to errors; by explicitly passing `cursor_position` we can decouple the matchers
608 # from the completer, and make substituting them in sub-classes easier.
609
610 #: Relevant fragment of code directly preceding the cursor.
611 #: The extraction of token is implemented via splitter heuristic
612 #: (following readline behaviour for legacy reasons), which is user configurable
613 #: (by switching the greedy mode).
614 token: str
615
616 #: The full available content of the editor or buffer
617 full_text: str
618
619 #: Cursor position in the line (the same for ``full_text`` and ``text``).
620 cursor_position: int
621
622 #: Cursor line in ``full_text``.
623 cursor_line: int
624
625 #: The maximum number of completions that will be used downstream.
626 #: Matchers can use this information to abort early.
627 #: The built-in Jedi matcher is currently excepted from this limit.
628 # If not given, return all possible completions.
629 limit: Optional[int]
630
631 @cached_property
632 def text_until_cursor(self) -> str:
633 return self.line_with_cursor[: self.cursor_position]
634
635 @cached_property
636 def line_with_cursor(self) -> str:
637 return self.full_text.split("\n")[self.cursor_line]
638
639
640 #: Matcher results for API v2.
641 MatcherResult = Union[SimpleMatcherResult, _JediMatcherResult]
642
643
644 class _MatcherAPIv1Base(Protocol):
645 def __call__(self, text: str) -> list[str]:
646 """Call signature."""
647
648
649 class _MatcherAPIv1Total(_MatcherAPIv1Base, Protocol):
650 #: API version
651 matcher_api_version: Optional[Literal[1]]
652
653 def __call__(self, text: str) -> list[str]:
654 """Call signature."""
655
656
657 #: Protocol describing Matcher API v1.
658 MatcherAPIv1: TypeAlias = Union[_MatcherAPIv1Base, _MatcherAPIv1Total]
659
660
661 class MatcherAPIv2(Protocol):
662 """Protocol describing Matcher API v2."""
663
664 #: API version
665 matcher_api_version: Literal[2] = 2
666
667 def __call__(self, context: CompletionContext) -> MatcherResult:
668 """Call signature."""
669
670
671 Matcher: TypeAlias = Union[MatcherAPIv1, MatcherAPIv2]
672
673
674 def completion_matcher(
675 *, priority: float = None, identifier: str = None, api_version: int = 1
676 ):
677 """Adds attributes describing the matcher.
678
679 Parameters
680 ----------
681 priority : Optional[float]
682 The priority of the matcher, determines the order of execution of matchers.
683 Higher priority means that the matcher will be executed first. Defaults to 0.
684 identifier : Optional[str]
685 identifier of the matcher allowing users to modify the behaviour via traitlets,
686 and also used to for debugging (will be passed as ``origin`` with the completions).
687 Defaults to matcher function ``__qualname__``.
688 api_version: Optional[int]
689 version of the Matcher API used by this matcher.
690 Currently supported values are 1 and 2.
691 Defaults to 1.
692 """
693
694 def wrapper(func: Matcher):
695 func.matcher_priority = priority or 0
696 func.matcher_identifier = identifier or func.__qualname__
697 func.matcher_api_version = api_version
698 if TYPE_CHECKING:
699 if api_version == 1:
700 func = cast(func, MatcherAPIv1)
701 elif api_version == 2:
702 func = cast(func, MatcherAPIv2)
703 return func
704
705 return wrapper
706
707
708 def _get_matcher_priority(matcher: Matcher):
709 return getattr(matcher, "matcher_priority", 0)
710
711
712 def _get_matcher_id(matcher: Matcher):
713 return getattr(matcher, "matcher_identifier", matcher.__qualname__)
714
715
716 def _get_matcher_api_version(matcher):
717 return getattr(matcher, "matcher_api_version", 1)
718
719
720 context_matcher = partial(completion_matcher, api_version=2)
721
722
420 _IC = Iterable[Completion]
723 _IC = Iterable[Completion]
421
724
422
725
@@ -924,7 +1227,20 b' def _safe_isinstance(obj, module, class_name):'
924 return (module in sys.modules and
1227 return (module in sys.modules and
925 isinstance(obj, getattr(import_module(module), class_name)))
1228 isinstance(obj, getattr(import_module(module), class_name)))
926
1229
927 def back_unicode_name_matches(text:str) -> Tuple[str, Sequence[str]]:
1230
1231 @context_matcher()
1232 def back_unicode_name_matcher(context: CompletionContext):
1233 """Match Unicode characters back to Unicode name
1234
1235 Same as :any:`back_unicode_name_matches`, but adopted to new Matcher API.
1236 """
1237 fragment, matches = back_unicode_name_matches(context.text_until_cursor)
1238 return _convert_matcher_v1_result_to_v2(
1239 matches, type="unicode", fragment=fragment, suppress_if_matches=True
1240 )
1241
1242
1243 def back_unicode_name_matches(text: str) -> Tuple[str, Sequence[str]]:
928 """Match Unicode characters back to Unicode name
1244 """Match Unicode characters back to Unicode name
929
1245
930 This does ``☃`` -> ``\\snowman``
1246 This does ``☃`` -> ``\\snowman``
@@ -934,6 +1250,9 b' def back_unicode_name_matches(text:str) -> Tuple[str, Sequence[str]]:'
934
1250
935 This will not either back-complete standard sequences like \\n, \\b ...
1251 This will not either back-complete standard sequences like \\n, \\b ...
936
1252
1253 .. deprecated:: 8.6
1254 You can use :meth:`back_unicode_name_matcher` instead.
1255
937 Returns
1256 Returns
938 =======
1257 =======
939
1258
@@ -943,7 +1262,6 b' def back_unicode_name_matches(text:str) -> Tuple[str, Sequence[str]]:'
943 empty string,
1262 empty string,
944 - a sequence (of 1), name for the match Unicode character, preceded by
1263 - a sequence (of 1), name for the match Unicode character, preceded by
945 backslash, or empty if no match.
1264 backslash, or empty if no match.
946
947 """
1265 """
948 if len(text)<2:
1266 if len(text)<2:
949 return '', ()
1267 return '', ()
@@ -963,11 +1281,26 b' def back_unicode_name_matches(text:str) -> Tuple[str, Sequence[str]]:'
963 pass
1281 pass
964 return '', ()
1282 return '', ()
965
1283
966 def back_latex_name_matches(text:str) -> Tuple[str, Sequence[str]] :
1284
1285 @context_matcher()
1286 def back_latex_name_matcher(context: CompletionContext):
1287 """Match latex characters back to unicode name
1288
1289 Same as :any:`back_latex_name_matches`, but adopted to new Matcher API.
1290 """
1291 fragment, matches = back_latex_name_matches(context.text_until_cursor)
1292 return _convert_matcher_v1_result_to_v2(
1293 matches, type="latex", fragment=fragment, suppress_if_matches=True
1294 )
1295
1296
1297 def back_latex_name_matches(text: str) -> Tuple[str, Sequence[str]]:
967 """Match latex characters back to unicode name
1298 """Match latex characters back to unicode name
968
1299
969 This does ``\\ℵ`` -> ``\\aleph``
1300 This does ``\\ℵ`` -> ``\\aleph``
970
1301
1302 .. deprecated:: 8.6
1303 You can use :meth:`back_latex_name_matcher` instead.
971 """
1304 """
972 if len(text)<2:
1305 if len(text)<2:
973 return '', ()
1306 return '', ()
@@ -1042,11 +1375,23 b' def _make_signature(completion)-> str:'
1042 for p in signature.defined_names()) if f])
1375 for p in signature.defined_names()) if f])
1043
1376
1044
1377
1045 class _CompleteResult(NamedTuple):
1378 _CompleteResult = Dict[str, MatcherResult]
1046 matched_text : str
1379
1047 matches: Sequence[str]
1380
1048 matches_origin: Sequence[str]
1381 def _convert_matcher_v1_result_to_v2(
1049 jedi_matches: Any
1382 matches: Sequence[str],
1383 type: str,
1384 fragment: str = None,
1385 suppress_if_matches: bool = False,
1386 ) -> SimpleMatcherResult:
1387 """Utility to help with transition"""
1388 result = {
1389 "completions": [SimpleCompletion(text=match, type=type) for match in matches],
1390 "suppress": (True if matches else False) if suppress_if_matches else False,
1391 }
1392 if fragment is not None:
1393 result["matched_fragment"] = fragment
1394 return result
1050
1395
1051
1396
1052 class IPCompleter(Completer):
1397 class IPCompleter(Completer):
@@ -1062,17 +1407,59 b' class IPCompleter(Completer):'
1062 else:
1407 else:
1063 self.splitter.delims = DELIMS
1408 self.splitter.delims = DELIMS
1064
1409
1065 dict_keys_only = Bool(False,
1410 dict_keys_only = Bool(
1066 help="""Whether to show dict key matches only""")
1411 False,
1412 help="""
1413 Whether to show dict key matches only.
1414
1415 (disables all matchers except for `IPCompleter.dict_key_matcher`).
1416 """,
1417 )
1418
1419 suppress_competing_matchers = UnionTrait(
1420 [Bool(allow_none=True), DictTrait(Bool(None, allow_none=True))],
1421 default_value=None,
1422 help="""
1423 Whether to suppress completions from other *Matchers*.
1424
1425 When set to ``None`` (default) the matchers will attempt to auto-detect
1426 whether suppression of other matchers is desirable. For example, at
1427 the beginning of a line followed by `%` we expect a magic completion
1428 to be the only applicable option, and after ``my_dict['`` we usually
1429 expect a completion with an existing dictionary key.
1430
1431 If you want to disable this heuristic and see completions from all matchers,
1432 set ``IPCompleter.suppress_competing_matchers = False``.
1433 To disable the heuristic for specific matchers provide a dictionary mapping:
1434 ``IPCompleter.suppress_competing_matchers = {'IPCompleter.dict_key_matcher': False}``.
1435
1436 Set ``IPCompleter.suppress_competing_matchers = True`` to limit
1437 completions to the set of matchers with the highest priority;
1438 this is equivalent to ``IPCompleter.merge_completions`` and
1439 can be beneficial for performance, but will sometimes omit relevant
1440 candidates from matchers further down the priority list.
1441 """,
1442 ).tag(config=True)
1067
1443
1068 merge_completions = Bool(True,
1444 merge_completions = Bool(
1445 True,
1069 help="""Whether to merge completion results into a single list
1446 help="""Whether to merge completion results into a single list
1070
1447
1071 If False, only the completion results from the first non-empty
1448 If False, only the completion results from the first non-empty
1072 completer will be returned.
1449 completer will be returned.
1073 """
1450
1451 As of version 8.6.0, setting the value to ``False`` is an alias for:
1452 ``IPCompleter.suppress_competing_matchers = True.``.
1453 """,
1454 ).tag(config=True)
1455
1456 disable_matchers = ListTrait(
1457 Unicode(), help="""List of matchers to disable."""
1074 ).tag(config=True)
1458 ).tag(config=True)
1075 omit__names = Enum((0,1,2), default_value=2,
1459
1460 omit__names = Enum(
1461 (0, 1, 2),
1462 default_value=2,
1076 help="""Instruct the completer to omit private method names
1463 help="""Instruct the completer to omit private method names
1077
1464
1078 Specifically, when completing on ``object.<tab>``.
1465 Specifically, when completing on ``object.<tab>``.
@@ -1148,7 +1535,7 b' class IPCompleter(Completer):'
1148 namespace=namespace,
1535 namespace=namespace,
1149 global_namespace=global_namespace,
1536 global_namespace=global_namespace,
1150 config=config,
1537 config=config,
1151 **kwargs
1538 **kwargs,
1152 )
1539 )
1153
1540
1154 # List where completion matches will be stored
1541 # List where completion matches will be stored
@@ -1177,8 +1564,8 b' class IPCompleter(Completer):'
1177 #= re.compile(r'[\s|\[]*(\w+)(?:\s*=?\s*.*)')
1564 #= re.compile(r'[\s|\[]*(\w+)(?:\s*=?\s*.*)')
1178
1565
1179 self.magic_arg_matchers = [
1566 self.magic_arg_matchers = [
1180 self.magic_config_matches,
1567 self.magic_config_matcher,
1181 self.magic_color_matches,
1568 self.magic_color_matcher,
1182 ]
1569 ]
1183
1570
1184 # This is set externally by InteractiveShell
1571 # This is set externally by InteractiveShell
@@ -1190,27 +1577,50 b' class IPCompleter(Completer):'
1190 # attribute through the `@unicode_names` property.
1577 # attribute through the `@unicode_names` property.
1191 self._unicode_names = None
1578 self._unicode_names = None
1192
1579
1580 self._backslash_combining_matchers = [
1581 self.latex_name_matcher,
1582 self.unicode_name_matcher,
1583 back_latex_name_matcher,
1584 back_unicode_name_matcher,
1585 self.fwd_unicode_matcher,
1586 ]
1587
1588 if not self.backslash_combining_completions:
1589 for matcher in self._backslash_combining_matchers:
1590 self.disable_matchers.append(matcher.matcher_identifier)
1591
1592 if not self.merge_completions:
1593 self.suppress_competing_matchers = True
1594
1193 @property
1595 @property
1194 def matchers(self) -> List[Any]:
1596 def matchers(self) -> List[Matcher]:
1195 """All active matcher routines for completion"""
1597 """All active matcher routines for completion"""
1196 if self.dict_keys_only:
1598 if self.dict_keys_only:
1197 return [self.dict_key_matches]
1599 return [self.dict_key_matcher]
1198
1600
1199 if self.use_jedi:
1601 if self.use_jedi:
1200 return [
1602 return [
1201 *self.custom_matchers,
1603 *self.custom_matchers,
1202 self.dict_key_matches,
1604 *self._backslash_combining_matchers,
1203 self.file_matches,
1605 *self.magic_arg_matchers,
1204 self.magic_matches,
1606 self.custom_completer_matcher,
1607 self.magic_matcher,
1608 self._jedi_matcher,
1609 self.dict_key_matcher,
1610 self.file_matcher,
1205 ]
1611 ]
1206 else:
1612 else:
1207 return [
1613 return [
1208 *self.custom_matchers,
1614 *self.custom_matchers,
1209 self.dict_key_matches,
1615 *self._backslash_combining_matchers,
1616 *self.magic_arg_matchers,
1617 self.custom_completer_matcher,
1618 self.dict_key_matcher,
1619 # TODO: convert python_matches to v2 API
1620 self.magic_matcher,
1210 self.python_matches,
1621 self.python_matches,
1211 self.file_matches,
1622 self.file_matcher,
1212 self.magic_matches,
1623 self.python_func_kw_matcher,
1213 self.python_func_kw_matches,
1214 ]
1624 ]
1215
1625
1216 def all_completions(self, text:str) -> List[str]:
1626 def all_completions(self, text:str) -> List[str]:
@@ -1231,7 +1641,15 b' class IPCompleter(Completer):'
1231 return [f.replace("\\","/")
1641 return [f.replace("\\","/")
1232 for f in self.glob("%s*" % text)]
1642 for f in self.glob("%s*" % text)]
1233
1643
1234 def file_matches(self, text:str)->List[str]:
1644 @context_matcher()
1645 def file_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
1646 """Same as :any:`file_matches`, but adopted to new Matcher API."""
1647 matches = self.file_matches(context.token)
1648 # TODO: add a heuristic for suppressing (e.g. if it has OS-specific delimiter,
1649 # starts with `/home/`, `C:\`, etc)
1650 return _convert_matcher_v1_result_to_v2(matches, type="path")
1651
1652 def file_matches(self, text: str) -> List[str]:
1235 """Match filenames, expanding ~USER type strings.
1653 """Match filenames, expanding ~USER type strings.
1236
1654
1237 Most of the seemingly convoluted logic in this completer is an
1655 Most of the seemingly convoluted logic in this completer is an
@@ -1243,7 +1661,11 b' class IPCompleter(Completer):'
1243 only the parts after what's already been typed (instead of the
1661 only the parts after what's already been typed (instead of the
1244 full completions, as is normally done). I don't think with the
1662 full completions, as is normally done). I don't think with the
1245 current (as of Python 2.3) Python readline it's possible to do
1663 current (as of Python 2.3) Python readline it's possible to do
1246 better."""
1664 better.
1665
1666 .. deprecated:: 8.6
1667 You can use :meth:`file_matcher` instead.
1668 """
1247
1669
1248 # chars that require escaping with backslash - i.e. chars
1670 # chars that require escaping with backslash - i.e. chars
1249 # that readline treats incorrectly as delimiters, but we
1671 # that readline treats incorrectly as delimiters, but we
@@ -1313,8 +1735,22 b' class IPCompleter(Completer):'
1313 # Mark directories in input list by appending '/' to their names.
1735 # Mark directories in input list by appending '/' to their names.
1314 return [x+'/' if os.path.isdir(x) else x for x in matches]
1736 return [x+'/' if os.path.isdir(x) else x for x in matches]
1315
1737
1316 def magic_matches(self, text:str):
1738 @context_matcher()
1317 """Match magics"""
1739 def magic_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
1740 """Match magics."""
1741 text = context.token
1742 matches = self.magic_matches(text)
1743 result = _convert_matcher_v1_result_to_v2(matches, type="magic")
1744 is_magic_prefix = len(text) > 0 and text[0] == "%"
1745 result["suppress"] = is_magic_prefix and bool(result["completions"])
1746 return result
1747
1748 def magic_matches(self, text: str):
1749 """Match magics.
1750
1751 .. deprecated:: 8.6
1752 You can use :meth:`magic_matcher` instead.
1753 """
1318 # Get all shell magics now rather than statically, so magics loaded at
1754 # Get all shell magics now rather than statically, so magics loaded at
1319 # runtime show up too.
1755 # runtime show up too.
1320 lsm = self.shell.magics_manager.lsmagic()
1756 lsm = self.shell.magics_manager.lsmagic()
@@ -1355,8 +1791,19 b' class IPCompleter(Completer):'
1355
1791
1356 return comp
1792 return comp
1357
1793
1358 def magic_config_matches(self, text:str) -> List[str]:
1794 @context_matcher()
1359 """ Match class names and attributes for %config magic """
1795 def magic_config_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
1796 """Match class names and attributes for %config magic."""
1797 # NOTE: uses `line_buffer` equivalent for compatibility
1798 matches = self.magic_config_matches(context.line_with_cursor)
1799 return _convert_matcher_v1_result_to_v2(matches, type="param")
1800
1801 def magic_config_matches(self, text: str) -> List[str]:
1802 """Match class names and attributes for %config magic.
1803
1804 .. deprecated:: 8.6
1805 You can use :meth:`magic_config_matcher` instead.
1806 """
1360 texts = text.strip().split()
1807 texts = text.strip().split()
1361
1808
1362 if len(texts) > 0 and (texts[0] == 'config' or texts[0] == '%config'):
1809 if len(texts) > 0 and (texts[0] == 'config' or texts[0] == '%config'):
@@ -1390,8 +1837,19 b' class IPCompleter(Completer):'
1390 if attr.startswith(texts[1]) ]
1837 if attr.startswith(texts[1]) ]
1391 return []
1838 return []
1392
1839
1393 def magic_color_matches(self, text:str) -> List[str] :
1840 @context_matcher()
1394 """ Match color schemes for %colors magic"""
1841 def magic_color_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
1842 """Match color schemes for %colors magic."""
1843 # NOTE: uses `line_buffer` equivalent for compatibility
1844 matches = self.magic_color_matches(context.line_with_cursor)
1845 return _convert_matcher_v1_result_to_v2(matches, type="param")
1846
1847 def magic_color_matches(self, text: str) -> List[str]:
1848 """Match color schemes for %colors magic.
1849
1850 .. deprecated:: 8.6
1851 You can use :meth:`magic_color_matcher` instead.
1852 """
1395 texts = text.split()
1853 texts = text.split()
1396 if text.endswith(' '):
1854 if text.endswith(' '):
1397 # .split() strips off the trailing whitespace. Add '' back
1855 # .split() strips off the trailing whitespace. Add '' back
@@ -1404,9 +1862,24 b' class IPCompleter(Completer):'
1404 if color.startswith(prefix) ]
1862 if color.startswith(prefix) ]
1405 return []
1863 return []
1406
1864
1407 def _jedi_matches(self, cursor_column:int, cursor_line:int, text:str) -> Iterable[Any]:
1865 @context_matcher(identifier="IPCompleter.jedi_matcher")
1866 def _jedi_matcher(self, context: CompletionContext) -> _JediMatcherResult:
1867 matches = self._jedi_matches(
1868 cursor_column=context.cursor_position,
1869 cursor_line=context.cursor_line,
1870 text=context.full_text,
1871 )
1872 return {
1873 "completions": matches,
1874 # static analysis should not suppress other matchers
1875 "suppress": False,
1876 }
1877
1878 def _jedi_matches(
1879 self, cursor_column: int, cursor_line: int, text: str
1880 ) -> Iterable[_JediCompletionLike]:
1408 """
1881 """
1409 Return a list of :any:`jedi.api.Completions` object from a ``text`` and
1882 Return a list of :any:`jedi.api.Completion`s object from a ``text`` and
1410 cursor position.
1883 cursor position.
1411
1884
1412 Parameters
1885 Parameters
@@ -1422,6 +1895,9 b' class IPCompleter(Completer):'
1422 -----
1895 -----
1423 If ``IPCompleter.debug`` is ``True`` may return a :any:`_FakeJediCompletion`
1896 If ``IPCompleter.debug`` is ``True`` may return a :any:`_FakeJediCompletion`
1424 object containing a string with the Jedi debug information attached.
1897 object containing a string with the Jedi debug information attached.
1898
1899 .. deprecated:: 8.6
1900 You can use :meth:`_jedi_matcher` instead.
1425 """
1901 """
1426 namespaces = [self.namespace]
1902 namespaces = [self.namespace]
1427 if self.global_namespace is not None:
1903 if self.global_namespace is not None:
@@ -1558,8 +2034,18 b' class IPCompleter(Completer):'
1558
2034
1559 return list(set(ret))
2035 return list(set(ret))
1560
2036
2037 @context_matcher()
2038 def python_func_kw_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
2039 """Match named parameters (kwargs) of the last open function."""
2040 matches = self.python_func_kw_matches(context.token)
2041 return _convert_matcher_v1_result_to_v2(matches, type="param")
2042
1561 def python_func_kw_matches(self, text):
2043 def python_func_kw_matches(self, text):
1562 """Match named parameters (kwargs) of the last open function"""
2044 """Match named parameters (kwargs) of the last open function.
2045
2046 .. deprecated:: 8.6
2047 You can use :meth:`python_func_kw_matcher` instead.
2048 """
1563
2049
1564 if "." in text: # a parameter cannot be dotted
2050 if "." in text: # a parameter cannot be dotted
1565 return []
2051 return []
@@ -1654,9 +2140,20 b' class IPCompleter(Completer):'
1654 return obj.dtype.names or []
2140 return obj.dtype.names or []
1655 return []
2141 return []
1656
2142
1657 def dict_key_matches(self, text:str) -> List[str]:
2143 @context_matcher()
1658 "Match string keys in a dictionary, after e.g. 'foo[' "
2144 def dict_key_matcher(self, context: CompletionContext) -> SimpleMatcherResult:
2145 """Match string keys in a dictionary, after e.g. ``foo[``."""
2146 matches = self.dict_key_matches(context.token)
2147 return _convert_matcher_v1_result_to_v2(
2148 matches, type="dict key", suppress_if_matches=True
2149 )
2150
2151 def dict_key_matches(self, text: str) -> List[str]:
2152 """Match string keys in a dictionary, after e.g. ``foo[``.
1659
2153
2154 .. deprecated:: 8.6
2155 You can use :meth:`dict_key_matcher` instead.
2156 """
1660
2157
1661 if self.__dict_key_regexps is not None:
2158 if self.__dict_key_regexps is not None:
1662 regexps = self.__dict_key_regexps
2159 regexps = self.__dict_key_regexps
@@ -1758,8 +2255,16 b' class IPCompleter(Completer):'
1758
2255
1759 return [leading + k + suf for k in matches]
2256 return [leading + k + suf for k in matches]
1760
2257
2258 @context_matcher()
2259 def unicode_name_matcher(self, context: CompletionContext):
2260 """Same as :any:`unicode_name_matches`, but adopted to new Matcher API."""
2261 fragment, matches = self.unicode_name_matches(context.text_until_cursor)
2262 return _convert_matcher_v1_result_to_v2(
2263 matches, type="unicode", fragment=fragment, suppress_if_matches=True
2264 )
2265
1761 @staticmethod
2266 @staticmethod
1762 def unicode_name_matches(text:str) -> Tuple[str, List[str]] :
2267 def unicode_name_matches(text: str) -> Tuple[str, List[str]]:
1763 """Match Latex-like syntax for unicode characters base
2268 """Match Latex-like syntax for unicode characters base
1764 on the name of the character.
2269 on the name of the character.
1765
2270
@@ -1780,11 +2285,24 b' class IPCompleter(Completer):'
1780 pass
2285 pass
1781 return '', []
2286 return '', []
1782
2287
2288 @context_matcher()
2289 def latex_name_matcher(self, context: CompletionContext):
2290 """Match Latex syntax for unicode characters.
1783
2291
1784 def latex_matches(self, text:str) -> Tuple[str, Sequence[str]]:
2292 This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
2293 """
2294 fragment, matches = self.latex_matches(context.text_until_cursor)
2295 return _convert_matcher_v1_result_to_v2(
2296 matches, type="latex", fragment=fragment, suppress_if_matches=True
2297 )
2298
2299 def latex_matches(self, text: str) -> Tuple[str, Sequence[str]]:
1785 """Match Latex syntax for unicode characters.
2300 """Match Latex syntax for unicode characters.
1786
2301
1787 This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
2302 This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
2303
2304 .. deprecated:: 8.6
2305 You can use :meth:`latex_name_matcher` instead.
1788 """
2306 """
1789 slashpos = text.rfind('\\')
2307 slashpos = text.rfind('\\')
1790 if slashpos > -1:
2308 if slashpos > -1:
@@ -1801,7 +2319,25 b' class IPCompleter(Completer):'
1801 return s, matches
2319 return s, matches
1802 return '', ()
2320 return '', ()
1803
2321
2322 @context_matcher()
2323 def custom_completer_matcher(self, context):
2324 """Dispatch custom completer.
2325
2326 If a match is found, suppresses all other matchers except for Jedi.
2327 """
2328 matches = self.dispatch_custom_completer(context.token) or []
2329 result = _convert_matcher_v1_result_to_v2(
2330 matches, type=_UNKNOWN_TYPE, suppress_if_matches=True
2331 )
2332 result["ordered"] = True
2333 result["do_not_suppress"] = {_get_matcher_id(self._jedi_matcher)}
2334 return result
2335
1804 def dispatch_custom_completer(self, text):
2336 def dispatch_custom_completer(self, text):
2337 """
2338 .. deprecated:: 8.6
2339 You can use :meth:`custom_completer_matcher` instead.
2340 """
1805 if not self.custom_completers:
2341 if not self.custom_completers:
1806 return
2342 return
1807
2343
@@ -1955,12 +2491,25 b' class IPCompleter(Completer):'
1955 """
2491 """
1956 deadline = time.monotonic() + _timeout
2492 deadline = time.monotonic() + _timeout
1957
2493
1958
1959 before = full_text[:offset]
2494 before = full_text[:offset]
1960 cursor_line, cursor_column = position_to_cursor(full_text, offset)
2495 cursor_line, cursor_column = position_to_cursor(full_text, offset)
1961
2496
1962 matched_text, matches, matches_origin, jedi_matches = self._complete(
2497 jedi_matcher_id = _get_matcher_id(self._jedi_matcher)
1963 full_text=full_text, cursor_line=cursor_line, cursor_pos=cursor_column)
2498
2499 results = self._complete(
2500 full_text=full_text, cursor_line=cursor_line, cursor_pos=cursor_column
2501 )
2502 non_jedi_results: Dict[str, SimpleMatcherResult] = {
2503 identifier: result
2504 for identifier, result in results.items()
2505 if identifier != jedi_matcher_id
2506 }
2507
2508 jedi_matches = (
2509 cast(results[jedi_matcher_id], _JediMatcherResult)["completions"]
2510 if jedi_matcher_id in results
2511 else ()
2512 )
1964
2513
1965 iter_jm = iter(jedi_matches)
2514 iter_jm = iter(jedi_matches)
1966 if _timeout:
2515 if _timeout:
@@ -1988,28 +2537,57 b' class IPCompleter(Completer):'
1988
2537
1989 for jm in iter_jm:
2538 for jm in iter_jm:
1990 delta = len(jm.name_with_symbols) - len(jm.complete)
2539 delta = len(jm.name_with_symbols) - len(jm.complete)
1991 yield Completion(start=offset - delta,
2540 yield Completion(
1992 end=offset,
2541 start=offset - delta,
1993 text=jm.name_with_symbols,
2542 end=offset,
1994 type='<unknown>', # don't compute type for speed
2543 text=jm.name_with_symbols,
1995 _origin='jedi',
2544 type=_UNKNOWN_TYPE, # don't compute type for speed
1996 signature='')
2545 _origin="jedi",
1997
2546 signature="",
1998
2547 )
1999 start_offset = before.rfind(matched_text)
2000
2548
2001 # TODO:
2549 # TODO:
2002 # Suppress this, right now just for debug.
2550 # Suppress this, right now just for debug.
2003 if jedi_matches and matches and self.debug:
2551 if jedi_matches and non_jedi_results and self.debug:
2004 yield Completion(start=start_offset, end=offset, text='--jedi/ipython--',
2552 some_start_offset = before.rfind(
2005 _origin='debug', type='none', signature='')
2553 next(iter(non_jedi_results.values()))["matched_fragment"]
2554 )
2555 yield Completion(
2556 start=some_start_offset,
2557 end=offset,
2558 text="--jedi/ipython--",
2559 _origin="debug",
2560 type="none",
2561 signature="",
2562 )
2006
2563
2007 # I'm unsure if this is always true, so let's assert and see if it
2564 ordered = []
2008 # crash
2565 sortable = []
2009 assert before.endswith(matched_text)
2566
2010 for m, t in zip(matches, matches_origin):
2567 for origin, result in non_jedi_results.items():
2011 yield Completion(start=start_offset, end=offset, text=m, _origin=t, signature='', type='<unknown>')
2568 matched_text = result["matched_fragment"]
2569 start_offset = before.rfind(matched_text)
2570 is_ordered = result.get("ordered", False)
2571 container = ordered if is_ordered else sortable
2572
2573 # I'm unsure if this is always true, so let's assert and see if it
2574 # crash
2575 assert before.endswith(matched_text)
2576
2577 for simple_completion in result["completions"]:
2578 completion = Completion(
2579 start=start_offset,
2580 end=offset,
2581 text=simple_completion.text,
2582 _origin=origin,
2583 signature="",
2584 type=simple_completion.type or _UNKNOWN_TYPE,
2585 )
2586 container.append(completion)
2012
2587
2588 yield from list(self._deduplicate(ordered + self._sort(sortable)))[
2589 :MATCHES_LIMIT
2590 ]
2013
2591
2014 def complete(self, text=None, line_buffer=None, cursor_pos=None) -> Tuple[str, Sequence[str]]:
2592 def complete(self, text=None, line_buffer=None, cursor_pos=None) -> Tuple[str, Sequence[str]]:
2015 """Find completions for the given text and line context.
2593 """Find completions for the given text and line context.
@@ -2050,7 +2628,56 b' class IPCompleter(Completer):'
2050 PendingDeprecationWarning)
2628 PendingDeprecationWarning)
2051 # potential todo, FOLD the 3rd throw away argument of _complete
2629 # potential todo, FOLD the 3rd throw away argument of _complete
2052 # into the first 2 one.
2630 # into the first 2 one.
2053 return self._complete(line_buffer=line_buffer, cursor_pos=cursor_pos, text=text, cursor_line=0)[:2]
2631 # TODO: Q: does the above refer to jedi completions (i.e. 0-indexed?)
2632 # TODO: should we deprecate now, or does it stay?
2633
2634 results = self._complete(
2635 line_buffer=line_buffer, cursor_pos=cursor_pos, text=text, cursor_line=0
2636 )
2637
2638 jedi_matcher_id = _get_matcher_id(self._jedi_matcher)
2639
2640 return self._arrange_and_extract(
2641 results,
2642 # TODO: can we confirm that excluding Jedi here was a deliberate choice in previous version?
2643 skip_matchers={jedi_matcher_id},
2644 # this API does not support different start/end positions (fragments of token).
2645 abort_if_offset_changes=True,
2646 )
2647
2648 def _arrange_and_extract(
2649 self,
2650 results: Dict[str, MatcherResult],
2651 skip_matchers: Set[str],
2652 abort_if_offset_changes: bool,
2653 ):
2654
2655 sortable = []
2656 ordered = []
2657 most_recent_fragment = None
2658 for identifier, result in results.items():
2659 if identifier in skip_matchers:
2660 continue
2661 if not result["completions"]:
2662 continue
2663 if not most_recent_fragment:
2664 most_recent_fragment = result["matched_fragment"]
2665 if (
2666 abort_if_offset_changes
2667 and result["matched_fragment"] != most_recent_fragment
2668 ):
2669 break
2670 if result.get("ordered", False):
2671 ordered.extend(result["completions"])
2672 else:
2673 sortable.extend(result["completions"])
2674
2675 if not most_recent_fragment:
2676 most_recent_fragment = "" # to satisfy typechecker (and just in case)
2677
2678 return most_recent_fragment, [
2679 m.text for m in self._deduplicate(ordered + self._sort(sortable))
2680 ]
2054
2681
2055 def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None,
2682 def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None,
2056 full_text=None) -> _CompleteResult:
2683 full_text=None) -> _CompleteResult:
@@ -2085,14 +2712,10 b' class IPCompleter(Completer):'
2085
2712
2086 Returns
2713 Returns
2087 -------
2714 -------
2088 A tuple of N elements which are (likely):
2715 An ordered dictionary where keys are identifiers of completion
2089 matched_text: ? the text that the complete matched
2716 matchers and values are ``MatcherResult``s.
2090 matches: list of completions ?
2091 matches_origin: ? list same length as matches, and where each completion came from
2092 jedi_matches: list of Jedi matches, have it's own structure.
2093 """
2717 """
2094
2718
2095
2096 # if the cursor position isn't given, the only sane assumption we can
2719 # if the cursor position isn't given, the only sane assumption we can
2097 # make is that it's at the end of the line (the common case)
2720 # make is that it's at the end of the line (the common case)
2098 if cursor_pos is None:
2721 if cursor_pos is None:
@@ -2104,98 +2727,156 b' class IPCompleter(Completer):'
2104 # if text is either None or an empty string, rely on the line buffer
2727 # if text is either None or an empty string, rely on the line buffer
2105 if (not line_buffer) and full_text:
2728 if (not line_buffer) and full_text:
2106 line_buffer = full_text.split('\n')[cursor_line]
2729 line_buffer = full_text.split('\n')[cursor_line]
2107 if not text: # issue #11508: check line_buffer before calling split_line
2730 if not text: # issue #11508: check line_buffer before calling split_line
2108 text = self.splitter.split_line(line_buffer, cursor_pos) if line_buffer else ''
2731 text = (
2109
2732 self.splitter.split_line(line_buffer, cursor_pos) if line_buffer else ""
2110 if self.backslash_combining_completions:
2733 )
2111 # allow deactivation of these on windows.
2112 base_text = text if not line_buffer else line_buffer[:cursor_pos]
2113
2114 for meth in (self.latex_matches,
2115 self.unicode_name_matches,
2116 back_latex_name_matches,
2117 back_unicode_name_matches,
2118 self.fwd_unicode_match):
2119 name_text, name_matches = meth(base_text)
2120 if name_text:
2121 return _CompleteResult(name_text, name_matches[:MATCHES_LIMIT], \
2122 [meth.__qualname__]*min(len(name_matches), MATCHES_LIMIT), ())
2123
2124
2734
2125 # If no line buffer is given, assume the input text is all there was
2735 # If no line buffer is given, assume the input text is all there was
2126 if line_buffer is None:
2736 if line_buffer is None:
2127 line_buffer = text
2737 line_buffer = text
2128
2738
2739 # deprecated - do not use `line_buffer` in new code.
2129 self.line_buffer = line_buffer
2740 self.line_buffer = line_buffer
2130 self.text_until_cursor = self.line_buffer[:cursor_pos]
2741 self.text_until_cursor = self.line_buffer[:cursor_pos]
2131
2742
2132 # Do magic arg matches
2743 if not full_text:
2133 for matcher in self.magic_arg_matchers:
2744 full_text = line_buffer
2134 matches = list(matcher(line_buffer))[:MATCHES_LIMIT]
2745
2135 if matches:
2746 context = CompletionContext(
2136 origins = [matcher.__qualname__] * len(matches)
2747 full_text=full_text,
2137 return _CompleteResult(text, matches, origins, ())
2748 cursor_position=cursor_pos,
2749 cursor_line=cursor_line,
2750 token=text,
2751 limit=MATCHES_LIMIT,
2752 )
2138
2753
2139 # Start with a clean slate of completions
2754 # Start with a clean slate of completions
2140 matches = []
2755 results = {}
2141
2756
2142 # FIXME: we should extend our api to return a dict with completions for
2757 jedi_matcher_id = _get_matcher_id(self._jedi_matcher)
2143 # different types of objects. The rlcomplete() method could then
2144 # simply collapse the dict into a list for readline, but we'd have
2145 # richer completion semantics in other environments.
2146 is_magic_prefix = len(text) > 0 and text[0] == "%"
2147 completions: Iterable[Any] = []
2148 if self.use_jedi and not is_magic_prefix:
2149 if not full_text:
2150 full_text = line_buffer
2151 completions = self._jedi_matches(
2152 cursor_pos, cursor_line, full_text)
2153
2154 if self.merge_completions:
2155 matches = []
2156 for matcher in self.matchers:
2157 try:
2158 matches.extend([(m, matcher.__qualname__)
2159 for m in matcher(text)])
2160 except:
2161 # Show the ugly traceback if the matcher causes an
2162 # exception, but do NOT crash the kernel!
2163 sys.excepthook(*sys.exc_info())
2164 else:
2165 for matcher in self.matchers:
2166 matches = [(m, matcher.__qualname__)
2167 for m in matcher(text)]
2168 if matches:
2169 break
2170
2171 seen = set()
2172 filtered_matches = set()
2173 for m in matches:
2174 t, c = m
2175 if t not in seen:
2176 filtered_matches.add(m)
2177 seen.add(t)
2178
2758
2179 _filtered_matches = sorted(filtered_matches, key=lambda x: completions_sorting_key(x[0]))
2759 suppressed_matchers = set()
2180
2760
2181 custom_res = [(m, 'custom') for m in self.dispatch_custom_completer(text) or []]
2761 matchers = {
2182
2762 _get_matcher_id(matcher): matcher
2183 _filtered_matches = custom_res or _filtered_matches
2763 for matcher in sorted(
2184
2764 self.matchers, key=_get_matcher_priority, reverse=True
2185 _filtered_matches = _filtered_matches[:MATCHES_LIMIT]
2765 )
2186 _matches = [m[0] for m in _filtered_matches]
2766 }
2187 origins = [m[1] for m in _filtered_matches]
2188
2767
2189 self.matches = _matches
2768 for matcher_id, matcher in matchers.items():
2769 api_version = _get_matcher_api_version(matcher)
2770 matcher_id = _get_matcher_id(matcher)
2190
2771
2191 return _CompleteResult(text, _matches, origins, completions)
2772 if matcher_id in self.disable_matchers:
2192
2773 continue
2193 def fwd_unicode_match(self, text:str) -> Tuple[str, Sequence[str]]:
2774
2775 if matcher_id in results:
2776 warnings.warn(f"Duplicate matcher ID: {matcher_id}.")
2777
2778 if matcher_id in suppressed_matchers:
2779 continue
2780
2781 try:
2782 if api_version == 1:
2783 result = _convert_matcher_v1_result_to_v2(
2784 matcher(text), type=_UNKNOWN_TYPE
2785 )
2786 elif api_version == 2:
2787 result = cast(matcher, MatcherAPIv2)(context)
2788 else:
2789 raise ValueError(f"Unsupported API version {api_version}")
2790 except:
2791 # Show the ugly traceback if the matcher causes an
2792 # exception, but do NOT crash the kernel!
2793 sys.excepthook(*sys.exc_info())
2794 continue
2795
2796 # set default value for matched fragment if suffix was not selected.
2797 result["matched_fragment"] = result.get("matched_fragment", context.token)
2798
2799 if not suppressed_matchers:
2800 suppression_recommended = result.get("suppress", False)
2801
2802 suppression_config = (
2803 self.suppress_competing_matchers.get(matcher_id, None)
2804 if isinstance(self.suppress_competing_matchers, dict)
2805 else self.suppress_competing_matchers
2806 )
2807 should_suppress = (
2808 (suppression_config is True)
2809 or (suppression_recommended and (suppression_config is not False))
2810 ) and len(result["completions"])
2811
2812 if should_suppress:
2813 suppression_exceptions = result.get("do_not_suppress", set())
2814 try:
2815 to_suppress = set(suppression_recommended)
2816 except TypeError:
2817 to_suppress = set(matchers)
2818 suppressed_matchers = to_suppress - suppression_exceptions
2819
2820 new_results = {}
2821 for previous_matcher_id, previous_result in results.items():
2822 if previous_matcher_id not in suppressed_matchers:
2823 new_results[previous_matcher_id] = previous_result
2824 results = new_results
2825
2826 results[matcher_id] = result
2827
2828 _, matches = self._arrange_and_extract(
2829 results,
2830 # TODO Jedi completions non included in legacy stateful API; was this deliberate or omission?
2831 # if it was omission, we can remove the filtering step, otherwise remove this comment.
2832 skip_matchers={jedi_matcher_id},
2833 abort_if_offset_changes=False,
2834 )
2835
2836 # populate legacy stateful API
2837 self.matches = matches
2838
2839 return results
2840
2841 @staticmethod
2842 def _deduplicate(
2843 matches: Sequence[SimpleCompletion],
2844 ) -> Iterable[SimpleCompletion]:
2845 filtered_matches = {}
2846 for match in matches:
2847 text = match.text
2848 if (
2849 text not in filtered_matches
2850 or filtered_matches[text].type == _UNKNOWN_TYPE
2851 ):
2852 filtered_matches[text] = match
2853
2854 return filtered_matches.values()
2855
2856 @staticmethod
2857 def _sort(matches: Sequence[SimpleCompletion]):
2858 return sorted(matches, key=lambda x: completions_sorting_key(x.text))
2859
2860 @context_matcher()
2861 def fwd_unicode_matcher(self, context: CompletionContext):
2862 """Same as :any:`fwd_unicode_match`, but adopted to new Matcher API."""
2863 # TODO: use `context.limit` to terminate early once we matched the maximum
2864 # number that will be used downstream; can be added as an optional to
2865 # `fwd_unicode_match(text: str, limit: int = None)` or we could re-implement here.
2866 fragment, matches = self.fwd_unicode_match(context.text_until_cursor)
2867 return _convert_matcher_v1_result_to_v2(
2868 matches, type="unicode", fragment=fragment, suppress_if_matches=True
2869 )
2870
2871 def fwd_unicode_match(self, text: str) -> Tuple[str, Sequence[str]]:
2194 """
2872 """
2195 Forward match a string starting with a backslash with a list of
2873 Forward match a string starting with a backslash with a list of
2196 potential Unicode completions.
2874 potential Unicode completions.
2197
2875
2198 Will compute list list of Unicode character names on first call and cache it.
2876 Will compute list of Unicode character names on first call and cache it.
2877
2878 .. deprecated:: 8.6
2879 You can use :meth:`fwd_unicode_matcher` instead.
2199
2880
2200 Returns
2881 Returns
2201 -------
2882 -------
@@ -389,7 +389,19 b' class DisplayObject(object):'
389
389
390
390
391 class TextDisplayObject(DisplayObject):
391 class TextDisplayObject(DisplayObject):
392 """Validate that display data is text"""
392 """Create a text display object given raw data.
393
394 Parameters
395 ----------
396 data : str or unicode
397 The raw data or a URL or file to load the data from.
398 url : unicode
399 A URL to download the data from.
400 filename : unicode
401 Path to a local file to load the data from.
402 metadata : dict
403 Dict of metadata associated to be the object when displayed
404 """
393 def _check_data(self):
405 def _check_data(self):
394 if self.data is not None and not isinstance(self.data, str):
406 if self.data is not None and not isinstance(self.data, str):
395 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
407 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
@@ -613,8 +625,9 b' class JSON(DisplayObject):'
613 def _repr_json_(self):
625 def _repr_json_(self):
614 return self._data_and_metadata()
626 return self._data_and_metadata()
615
627
628
616 _css_t = """var link = document.createElement("link");
629 _css_t = """var link = document.createElement("link");
617 link.ref = "stylesheet";
630 link.rel = "stylesheet";
618 link.type = "text/css";
631 link.type = "text/css";
619 link.href = "%s";
632 link.href = "%s";
620 document.head.appendChild(link);
633 document.head.appendChild(link);
@@ -88,13 +88,7 b' class ExtensionManager(Configurable):'
88
88
89 with self.shell.builtin_trap:
89 with self.shell.builtin_trap:
90 if module_str not in sys.modules:
90 if module_str not in sys.modules:
91 with prepended_to_syspath(self.ipython_extension_dir):
91 mod = import_module(module_str)
92 mod = import_module(module_str)
93 if mod.__file__.startswith(self.ipython_extension_dir):
94 print(("Loading extensions from {dir} is deprecated. "
95 "We recommend managing extensions like any "
96 "other Python packages, in site-packages.").format(
97 dir=compress_user(self.ipython_extension_dir)))
98 mod = sys.modules[module_str]
92 mod = sys.modules[module_str]
99 if self._call_load_ipython_extension(mod):
93 if self._call_load_ipython_extension(mod):
100 self.loaded.add(module_str)
94 self.loaded.add(module_str)
@@ -155,13 +149,3 b' class ExtensionManager(Configurable):'
155 if hasattr(mod, 'unload_ipython_extension'):
149 if hasattr(mod, 'unload_ipython_extension'):
156 mod.unload_ipython_extension(self.shell)
150 mod.unload_ipython_extension(self.shell)
157 return True
151 return True
158
159 @undoc
160 def install_extension(self, url, filename=None):
161 """
162 Deprecated.
163 """
164 # Ensure the extension directory exists
165 raise DeprecationWarning(
166 '`install_extension` and the `install_ext` magic have been deprecated since IPython 4.0'
167 'Use pip or other package managers to manage ipython extensions.')
@@ -429,13 +429,17 b' class EscapedCommand(TokenTransformBase):'
429
429
430 return lines_before + [new_line] + lines_after
430 return lines_before + [new_line] + lines_after
431
431
432 _help_end_re = re.compile(r"""(%{0,2}
432
433 (?!\d)[\w*]+ # Variable name
433 _help_end_re = re.compile(
434 (\.(?!\d)[\w*]+)* # .etc.etc
434 r"""(%{0,2}
435 )
435 (?!\d)[\w*]+ # Variable name
436 (\?\??)$ # ? or ??
436 (\.(?!\d)[\w*]+|\[-?[0-9]+\])* # .etc.etc or [0], we only support literal integers.
437 """,
437 )
438 re.VERBOSE)
438 (\?\??)$ # ? or ??
439 """,
440 re.VERBOSE,
441 )
442
439
443
440 class HelpEnd(TokenTransformBase):
444 class HelpEnd(TokenTransformBase):
441 """Transformer for help syntax: obj? and obj??"""
445 """Transformer for help syntax: obj? and obj??"""
@@ -464,10 +468,11 b' class HelpEnd(TokenTransformBase):'
464 def transform(self, lines):
468 def transform(self, lines):
465 """Transform a help command found by the ``find()`` classmethod.
469 """Transform a help command found by the ``find()`` classmethod.
466 """
470 """
467 piece = ''.join(lines[self.start_line:self.q_line+1])
471
468 indent, content = piece[:self.start_col], piece[self.start_col:]
472 piece = "".join(lines[self.start_line : self.q_line + 1])
469 lines_before = lines[:self.start_line]
473 indent, content = piece[: self.start_col], piece[self.start_col :]
470 lines_after = lines[self.q_line + 1:]
474 lines_before = lines[: self.start_line]
475 lines_after = lines[self.q_line + 1 :]
471
476
472 m = _help_end_re.search(content)
477 m = _help_end_re.search(content)
473 if not m:
478 if not m:
@@ -543,8 +548,13 b' def has_sunken_brackets(tokens: List[tokenize.TokenInfo]):'
543
548
544 def show_linewise_tokens(s: str):
549 def show_linewise_tokens(s: str):
545 """For investigation and debugging"""
550 """For investigation and debugging"""
546 if not s.endswith('\n'):
551 warnings.warn(
547 s += '\n'
552 "show_linewise_tokens is deprecated since IPython 8.6",
553 DeprecationWarning,
554 stacklevel=2,
555 )
556 if not s.endswith("\n"):
557 s += "\n"
548 lines = s.splitlines(keepends=True)
558 lines = s.splitlines(keepends=True)
549 for line in make_tokens_by_line(lines):
559 for line in make_tokens_by_line(lines):
550 print("Line -------")
560 print("Line -------")
@@ -61,7 +61,7 b' from IPython.core import magic, oinspect, page, prefilter, ultratb'
61 from IPython.core.alias import Alias, AliasManager
61 from IPython.core.alias import Alias, AliasManager
62 from IPython.core.autocall import ExitAutocall
62 from IPython.core.autocall import ExitAutocall
63 from IPython.core.builtin_trap import BuiltinTrap
63 from IPython.core.builtin_trap import BuiltinTrap
64 from IPython.core.compilerop import CachingCompiler, check_linecache_ipython
64 from IPython.core.compilerop import CachingCompiler
65 from IPython.core.debugger import InterruptiblePdb
65 from IPython.core.debugger import InterruptiblePdb
66 from IPython.core.display_trap import DisplayTrap
66 from IPython.core.display_trap import DisplayTrap
67 from IPython.core.displayhook import DisplayHook
67 from IPython.core.displayhook import DisplayHook
@@ -147,6 +147,19 b" dedent_re = re.compile(r'^\\s+raise|^\\s+return|^\\s+pass')"
147 # Utilities
147 # Utilities
148 #-----------------------------------------------------------------------------
148 #-----------------------------------------------------------------------------
149
149
150
151 def is_integer_string(s: str):
152 """
153 Variant of "str.isnumeric()" that allow negative values and other ints.
154 """
155 try:
156 int(s)
157 return True
158 except ValueError:
159 return False
160 raise ValueError("Unexpected error")
161
162
150 @undoc
163 @undoc
151 def softspace(file, newvalue):
164 def softspace(file, newvalue):
152 """Copied from code.py, to remove the dependency"""
165 """Copied from code.py, to remove the dependency"""
@@ -213,14 +226,17 b' class ExecutionInfo(object):'
213 raw_cell = (
226 raw_cell = (
214 (self.raw_cell[:50] + "..") if len(self.raw_cell) > 50 else self.raw_cell
227 (self.raw_cell[:50] + "..") if len(self.raw_cell) > 50 else self.raw_cell
215 )
228 )
216 return '<%s object at %x, raw_cell="%s" store_history=%s silent=%s shell_futures=%s cell_id=%s>' % (
229 return (
217 name,
230 '<%s object at %x, raw_cell="%s" store_history=%s silent=%s shell_futures=%s cell_id=%s>'
218 id(self),
231 % (
219 raw_cell,
232 name,
220 self.store_history,
233 id(self),
221 self.silent,
234 raw_cell,
222 self.shell_futures,
235 self.store_history,
223 self.cell_id,
236 self.silent,
237 self.shell_futures,
238 self.cell_id,
239 )
224 )
240 )
225
241
226
242
@@ -254,6 +270,16 b' class ExecutionResult(object):'
254 return '<%s object at %x, execution_count=%s error_before_exec=%s error_in_exec=%s info=%s result=%s>' %\
270 return '<%s object at %x, execution_count=%s error_before_exec=%s error_in_exec=%s info=%s result=%s>' %\
255 (name, id(self), self.execution_count, self.error_before_exec, self.error_in_exec, repr(self.info), repr(self.result))
271 (name, id(self), self.execution_count, self.error_before_exec, self.error_in_exec, repr(self.info), repr(self.result))
256
272
273 @functools.wraps(io_open)
274 def _modified_open(file, *args, **kwargs):
275 if file in {0, 1, 2}:
276 raise ValueError(
277 f"IPython won't let you open fd={file} by default "
278 "as it is likely to crash IPython. If you know what you are doing, "
279 "you can use builtins' open."
280 )
281
282 return io_open(file, *args, **kwargs)
257
283
258 class InteractiveShell(SingletonConfigurable):
284 class InteractiveShell(SingletonConfigurable):
259 """An enhanced, interactive shell for Python."""
285 """An enhanced, interactive shell for Python."""
@@ -1307,6 +1333,7 b' class InteractiveShell(SingletonConfigurable):'
1307
1333
1308 ns['exit'] = self.exiter
1334 ns['exit'] = self.exiter
1309 ns['quit'] = self.exiter
1335 ns['quit'] = self.exiter
1336 ns["open"] = _modified_open
1310
1337
1311 # Sync what we've added so far to user_ns_hidden so these aren't seen
1338 # Sync what we've added so far to user_ns_hidden so these aren't seen
1312 # by %who
1339 # by %who
@@ -1537,10 +1564,33 b' class InteractiveShell(SingletonConfigurable):'
1537 Has special code to detect magic functions.
1564 Has special code to detect magic functions.
1538 """
1565 """
1539 oname = oname.strip()
1566 oname = oname.strip()
1540 if not oname.startswith(ESC_MAGIC) and \
1567 raw_parts = oname.split(".")
1541 not oname.startswith(ESC_MAGIC2) and \
1568 parts = []
1542 not all(a.isidentifier() for a in oname.split(".")):
1569 parts_ok = True
1543 return {'found': False}
1570 for p in raw_parts:
1571 if p.endswith("]"):
1572 var, *indices = p.split("[")
1573 if not var.isidentifier():
1574 parts_ok = False
1575 break
1576 parts.append(var)
1577 for ind in indices:
1578 if ind[-1] != "]" and not is_integer_string(ind[:-1]):
1579 parts_ok = False
1580 break
1581 parts.append(ind[:-1])
1582 continue
1583
1584 if not p.isidentifier():
1585 parts_ok = False
1586 parts.append(p)
1587
1588 if (
1589 not oname.startswith(ESC_MAGIC)
1590 and not oname.startswith(ESC_MAGIC2)
1591 and not parts_ok
1592 ):
1593 return {"found": False}
1544
1594
1545 if namespaces is None:
1595 if namespaces is None:
1546 # Namespaces to search in:
1596 # Namespaces to search in:
@@ -1562,7 +1612,7 b' class InteractiveShell(SingletonConfigurable):'
1562 # Look for the given name by splitting it in parts. If the head is
1612 # Look for the given name by splitting it in parts. If the head is
1563 # found, then we look for all the remaining parts as members, and only
1613 # found, then we look for all the remaining parts as members, and only
1564 # declare success if we can find them all.
1614 # declare success if we can find them all.
1565 oname_parts = oname.split('.')
1615 oname_parts = parts
1566 oname_head, oname_rest = oname_parts[0],oname_parts[1:]
1616 oname_head, oname_rest = oname_parts[0],oname_parts[1:]
1567 for nsname,ns in namespaces:
1617 for nsname,ns in namespaces:
1568 try:
1618 try:
@@ -1579,7 +1629,10 b' class InteractiveShell(SingletonConfigurable):'
1579 if idx == len(oname_rest) - 1:
1629 if idx == len(oname_rest) - 1:
1580 obj = self._getattr_property(obj, part)
1630 obj = self._getattr_property(obj, part)
1581 else:
1631 else:
1582 obj = getattr(obj, part)
1632 if is_integer_string(part):
1633 obj = obj[int(part)]
1634 else:
1635 obj = getattr(obj, part)
1583 except:
1636 except:
1584 # Blanket except b/c some badly implemented objects
1637 # Blanket except b/c some badly implemented objects
1585 # allow __getattr__ to raise exceptions other than
1638 # allow __getattr__ to raise exceptions other than
@@ -1643,7 +1696,10 b' class InteractiveShell(SingletonConfigurable):'
1643 #
1696 #
1644 # The universal alternative is to traverse the mro manually
1697 # The universal alternative is to traverse the mro manually
1645 # searching for attrname in class dicts.
1698 # searching for attrname in class dicts.
1646 attr = getattr(type(obj), attrname)
1699 if is_integer_string(attrname):
1700 return obj[int(attrname)]
1701 else:
1702 attr = getattr(type(obj), attrname)
1647 except AttributeError:
1703 except AttributeError:
1648 pass
1704 pass
1649 else:
1705 else:
@@ -1765,7 +1821,6 b' class InteractiveShell(SingletonConfigurable):'
1765 self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain',
1821 self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain',
1766 color_scheme='NoColor',
1822 color_scheme='NoColor',
1767 tb_offset = 1,
1823 tb_offset = 1,
1768 check_cache=check_linecache_ipython,
1769 debugger_cls=self.debugger_cls, parent=self)
1824 debugger_cls=self.debugger_cls, parent=self)
1770
1825
1771 # The instance will store a pointer to the system-wide exception hook,
1826 # The instance will store a pointer to the system-wide exception hook,
@@ -297,7 +297,10 b' Currently the magic system has the following functions:""",'
297 oname = args and args or '_'
297 oname = args and args or '_'
298 info = self.shell._ofind(oname)
298 info = self.shell._ofind(oname)
299 if info['found']:
299 if info['found']:
300 txt = (raw and str or pformat)( info['obj'] )
300 if raw:
301 txt = str(info["obj"])
302 else:
303 txt = pformat(info["obj"])
301 page.page(txt)
304 page.page(txt)
302 else:
305 else:
303 print('Object `%s` not found' % oname)
306 print('Object `%s` not found' % oname)
@@ -80,6 +80,9 b' class ConfigMagics(Magics):'
80 Enable debug for the Completer. Mostly print extra information for
80 Enable debug for the Completer. Mostly print extra information for
81 experimental jedi integration.
81 experimental jedi integration.
82 Current: False
82 Current: False
83 IPCompleter.disable_matchers=<list-item-1>...
84 List of matchers to disable.
85 Current: []
83 IPCompleter.greedy=<Bool>
86 IPCompleter.greedy=<Bool>
84 Activate greedy completion
87 Activate greedy completion
85 PENDING DEPRECATION. this is now mostly taken care of with Jedi.
88 PENDING DEPRECATION. this is now mostly taken care of with Jedi.
@@ -102,6 +105,8 b' class ConfigMagics(Magics):'
102 Whether to merge completion results into a single list
105 Whether to merge completion results into a single list
103 If False, only the completion results from the first non-empty
106 If False, only the completion results from the first non-empty
104 completer will be returned.
107 completer will be returned.
108 As of version 8.6.0, setting the value to ``False`` is an alias for:
109 ``IPCompleter.suppress_competing_matchers = True.``.
105 Current: True
110 Current: True
106 IPCompleter.omit__names=<Enum>
111 IPCompleter.omit__names=<Enum>
107 Instruct the completer to omit private method names
112 Instruct the completer to omit private method names
@@ -117,6 +122,24 b' class ConfigMagics(Magics):'
117 IPCompleter.profiler_output_dir=<Unicode>
122 IPCompleter.profiler_output_dir=<Unicode>
118 Template for path at which to output profile data for completions.
123 Template for path at which to output profile data for completions.
119 Current: '.completion_profiles'
124 Current: '.completion_profiles'
125 IPCompleter.suppress_competing_matchers=<Union>
126 Whether to suppress completions from other *Matchers*.
127 When set to ``None`` (default) the matchers will attempt to auto-detect
128 whether suppression of other matchers is desirable. For example, at the
129 beginning of a line followed by `%` we expect a magic completion to be the
130 only applicable option, and after ``my_dict['`` we usually expect a
131 completion with an existing dictionary key.
132 If you want to disable this heuristic and see completions from all matchers,
133 set ``IPCompleter.suppress_competing_matchers = False``. To disable the
134 heuristic for specific matchers provide a dictionary mapping:
135 ``IPCompleter.suppress_competing_matchers = {'IPCompleter.dict_key_matcher':
136 False}``.
137 Set ``IPCompleter.suppress_competing_matchers = True`` to limit completions
138 to the set of matchers with the highest priority; this is equivalent to
139 ``IPCompleter.merge_completions`` and can be beneficial for performance, but
140 will sometimes omit relevant candidates from matchers further down the
141 priority list.
142 Current: None
120 IPCompleter.use_jedi=<Bool>
143 IPCompleter.use_jedi=<Bool>
121 Experimental: Use Jedi to generate autocompletions. Default to True if jedi
144 Experimental: Use Jedi to generate autocompletions. Default to True if jedi
122 is installed.
145 is installed.
@@ -492,7 +492,7 b' class NamespaceMagics(Magics):'
492 --aggressive
492 --aggressive
493 Try to aggressively remove modules from sys.modules ; this
493 Try to aggressively remove modules from sys.modules ; this
494 may allow you to reimport Python modules that have been updated and
494 may allow you to reimport Python modules that have been updated and
495 pick up changes, but can have unattended consequences.
495 pick up changes, but can have unintended consequences.
496
496
497 in
497 in
498 reset input history
498 reset input history
@@ -26,6 +26,7 b' backends = {'
26 "qt": "Qt5Agg",
26 "qt": "Qt5Agg",
27 "osx": "MacOSX",
27 "osx": "MacOSX",
28 "nbagg": "nbAgg",
28 "nbagg": "nbAgg",
29 "webagg": "WebAgg",
29 "notebook": "nbAgg",
30 "notebook": "nbAgg",
30 "agg": "agg",
31 "agg": "agg",
31 "svg": "svg",
32 "svg": "svg",
@@ -16,7 +16,7 b''
16 # release. 'dev' as a _version_extra string means this is a development
16 # release. 'dev' as a _version_extra string means this is a development
17 # version
17 # version
18 _version_major = 8
18 _version_major = 8
19 _version_minor = 6
19 _version_minor = 7
20 _version_patch = 0
20 _version_patch = 0
21 _version_extra = ".dev"
21 _version_extra = ".dev"
22 # _version_extra = "rc1"
22 # _version_extra = "rc1"
@@ -36,7 +36,7 b' version_info = (_version_major, _version_minor, _version_patch, _version_extra)'
36 kernel_protocol_version_info = (5, 0)
36 kernel_protocol_version_info = (5, 0)
37 kernel_protocol_version = "%i.%i" % kernel_protocol_version_info
37 kernel_protocol_version = "%i.%i" % kernel_protocol_version_info
38
38
39 license = 'BSD'
39 license = "BSD-3-Clause"
40
40
41 authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'),
41 authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'),
42 'Janko' : ('Janko Hauser','jhauser@zscout.de'),
42 'Janko' : ('Janko Hauser','jhauser@zscout.de'),
@@ -1,4 +1,4 b''
1 # coding: iso-8859-5
1 # coding: iso-8859-5
2 # (Unlikely to be the default encoding for most testers.)
2 # (Unlikely to be the default encoding for most testers.)
3 # ������������������� <- Cyrillic characters
3 # ������������������� <- Cyrillic characters
4 u = '����'
4 u = "����"
@@ -24,6 +24,9 b' from IPython.core.completer import ('
24 provisionalcompleter,
24 provisionalcompleter,
25 match_dict_keys,
25 match_dict_keys,
26 _deduplicate_completions,
26 _deduplicate_completions,
27 completion_matcher,
28 SimpleCompletion,
29 CompletionContext,
27 )
30 )
28
31
29 # -----------------------------------------------------------------------------
32 # -----------------------------------------------------------------------------
@@ -109,6 +112,16 b' def greedy_completion():'
109 ip.Completer.greedy = greedy_original
112 ip.Completer.greedy = greedy_original
110
113
111
114
115 @contextmanager
116 def custom_matchers(matchers):
117 ip = get_ipython()
118 try:
119 ip.Completer.custom_matchers.extend(matchers)
120 yield
121 finally:
122 ip.Completer.custom_matchers.clear()
123
124
112 def test_protect_filename():
125 def test_protect_filename():
113 if sys.platform == "win32":
126 if sys.platform == "win32":
114 pairs = [
127 pairs = [
@@ -298,7 +311,7 b' class TestCompleter(unittest.TestCase):'
298 ip = get_ipython()
311 ip = get_ipython()
299
312
300 name, matches = ip.complete("\\â…¤")
313 name, matches = ip.complete("\\â…¤")
301 self.assertEqual(matches, ("\\ROMAN NUMERAL FIVE",))
314 self.assertEqual(matches, ["\\ROMAN NUMERAL FIVE"])
302
315
303 def test_forward_unicode_completion(self):
316 def test_forward_unicode_completion(self):
304 ip = get_ipython()
317 ip = get_ipython()
@@ -379,6 +392,12 b' class TestCompleter(unittest.TestCase):'
379
392
380 def test_quoted_file_completions(self):
393 def test_quoted_file_completions(self):
381 ip = get_ipython()
394 ip = get_ipython()
395
396 def _(text):
397 return ip.Completer._complete(
398 cursor_line=0, cursor_pos=len(text), full_text=text
399 )["IPCompleter.file_matcher"]["completions"]
400
382 with TemporaryWorkingDirectory():
401 with TemporaryWorkingDirectory():
383 name = "foo'bar"
402 name = "foo'bar"
384 open(name, "w", encoding="utf-8").close()
403 open(name, "w", encoding="utf-8").close()
@@ -387,25 +406,16 b' class TestCompleter(unittest.TestCase):'
387 escaped = name if sys.platform == "win32" else "foo\\'bar"
406 escaped = name if sys.platform == "win32" else "foo\\'bar"
388
407
389 # Single quote matches embedded single quote
408 # Single quote matches embedded single quote
390 text = "open('foo"
409 c = _("open('foo")[0]
391 c = ip.Completer._complete(
410 self.assertEqual(c.text, escaped)
392 cursor_line=0, cursor_pos=len(text), full_text=text
393 )[1]
394 self.assertEqual(c, [escaped])
395
411
396 # Double quote requires no escape
412 # Double quote requires no escape
397 text = 'open("foo'
413 c = _('open("foo')[0]
398 c = ip.Completer._complete(
414 self.assertEqual(c.text, name)
399 cursor_line=0, cursor_pos=len(text), full_text=text
400 )[1]
401 self.assertEqual(c, [name])
402
415
403 # No quote requires an escape
416 # No quote requires an escape
404 text = "%ls foo"
417 c = _("%ls foo")[0]
405 c = ip.Completer._complete(
418 self.assertEqual(c.text, escaped)
406 cursor_line=0, cursor_pos=len(text), full_text=text
407 )[1]
408 self.assertEqual(c, [escaped])
409
419
410 def test_all_completions_dups(self):
420 def test_all_completions_dups(self):
411 """
421 """
@@ -475,6 +485,17 b' class TestCompleter(unittest.TestCase):'
475 "encoding" in c.signature
485 "encoding" in c.signature
476 ), "Signature of function was not found by completer"
486 ), "Signature of function was not found by completer"
477
487
488 def test_completions_have_type(self):
489 """
490 Lets make sure matchers provide completion type.
491 """
492 ip = get_ipython()
493 with provisionalcompleter():
494 ip.Completer.use_jedi = False
495 completions = ip.Completer.completions("%tim", 3)
496 c = next(completions) # should be `%time` or similar
497 assert c.type == "magic", "Type of magic was not assigned by completer"
498
478 @pytest.mark.xfail(reason="Known failure on jedi<=0.18.0")
499 @pytest.mark.xfail(reason="Known failure on jedi<=0.18.0")
479 def test_deduplicate_completions(self):
500 def test_deduplicate_completions(self):
480 """
501 """
@@ -1273,3 +1294,153 b' class TestCompleter(unittest.TestCase):'
1273 completions = completer.completions(text, len(text))
1294 completions = completer.completions(text, len(text))
1274 for c in completions:
1295 for c in completions:
1275 self.assertEqual(c.text[0], "%")
1296 self.assertEqual(c.text[0], "%")
1297
1298 def test_fwd_unicode_restricts(self):
1299 ip = get_ipython()
1300 completer = ip.Completer
1301 text = "\\ROMAN NUMERAL FIVE"
1302
1303 with provisionalcompleter():
1304 completer.use_jedi = True
1305 completions = [
1306 completion.text for completion in completer.completions(text, len(text))
1307 ]
1308 self.assertEqual(completions, ["\u2164"])
1309
1310 def test_dict_key_restrict_to_dicts(self):
1311 """Test that dict key suppresses non-dict completion items"""
1312 ip = get_ipython()
1313 c = ip.Completer
1314 d = {"abc": None}
1315 ip.user_ns["d"] = d
1316
1317 text = 'd["a'
1318
1319 def _():
1320 with provisionalcompleter():
1321 c.use_jedi = True
1322 return [
1323 completion.text for completion in c.completions(text, len(text))
1324 ]
1325
1326 completions = _()
1327 self.assertEqual(completions, ["abc"])
1328
1329 # check that it can be disabled in granular manner:
1330 cfg = Config()
1331 cfg.IPCompleter.suppress_competing_matchers = {
1332 "IPCompleter.dict_key_matcher": False
1333 }
1334 c.update_config(cfg)
1335
1336 completions = _()
1337 self.assertIn("abc", completions)
1338 self.assertGreater(len(completions), 1)
1339
1340 def test_matcher_suppression(self):
1341 @completion_matcher(identifier="a_matcher")
1342 def a_matcher(text):
1343 return ["completion_a"]
1344
1345 @completion_matcher(identifier="b_matcher", api_version=2)
1346 def b_matcher(context: CompletionContext):
1347 text = context.token
1348 result = {"completions": [SimpleCompletion("completion_b")]}
1349
1350 if text == "suppress c":
1351 result["suppress"] = {"c_matcher"}
1352
1353 if text.startswith("suppress all"):
1354 result["suppress"] = True
1355 if text == "suppress all but c":
1356 result["do_not_suppress"] = {"c_matcher"}
1357 if text == "suppress all but a":
1358 result["do_not_suppress"] = {"a_matcher"}
1359
1360 return result
1361
1362 @completion_matcher(identifier="c_matcher")
1363 def c_matcher(text):
1364 return ["completion_c"]
1365
1366 with custom_matchers([a_matcher, b_matcher, c_matcher]):
1367 ip = get_ipython()
1368 c = ip.Completer
1369
1370 def _(text, expected):
1371 c.use_jedi = False
1372 s, matches = c.complete(text)
1373 self.assertEqual(expected, matches)
1374
1375 _("do not suppress", ["completion_a", "completion_b", "completion_c"])
1376 _("suppress all", ["completion_b"])
1377 _("suppress all but a", ["completion_a", "completion_b"])
1378 _("suppress all but c", ["completion_b", "completion_c"])
1379
1380 def configure(suppression_config):
1381 cfg = Config()
1382 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1383 c.update_config(cfg)
1384
1385 # test that configuration takes priority over the run-time decisions
1386
1387 configure(False)
1388 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1389
1390 configure({"b_matcher": False})
1391 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1392
1393 configure({"a_matcher": False})
1394 _("suppress all", ["completion_b"])
1395
1396 configure({"b_matcher": True})
1397 _("do not suppress", ["completion_b"])
1398
1399 def test_matcher_disabling(self):
1400 @completion_matcher(identifier="a_matcher")
1401 def a_matcher(text):
1402 return ["completion_a"]
1403
1404 @completion_matcher(identifier="b_matcher")
1405 def b_matcher(text):
1406 return ["completion_b"]
1407
1408 def _(expected):
1409 s, matches = c.complete("completion_")
1410 self.assertEqual(expected, matches)
1411
1412 with custom_matchers([a_matcher, b_matcher]):
1413 ip = get_ipython()
1414 c = ip.Completer
1415
1416 _(["completion_a", "completion_b"])
1417
1418 cfg = Config()
1419 cfg.IPCompleter.disable_matchers = ["b_matcher"]
1420 c.update_config(cfg)
1421
1422 _(["completion_a"])
1423
1424 cfg.IPCompleter.disable_matchers = []
1425 c.update_config(cfg)
1426
1427 def test_matcher_priority(self):
1428 @completion_matcher(identifier="a_matcher", priority=0, api_version=2)
1429 def a_matcher(text):
1430 return {"completions": [SimpleCompletion("completion_a")], "suppress": True}
1431
1432 @completion_matcher(identifier="b_matcher", priority=2, api_version=2)
1433 def b_matcher(text):
1434 return {"completions": [SimpleCompletion("completion_b")], "suppress": True}
1435
1436 def _(expected):
1437 s, matches = c.complete("completion_")
1438 self.assertEqual(expected, matches)
1439
1440 with custom_matchers([a_matcher, b_matcher]):
1441 ip = get_ipython()
1442 c = ip.Completer
1443
1444 _(["completion_b"])
1445 a_matcher.matcher_priority = 3
1446 _(["completion_a"])
@@ -103,6 +103,18 b' class InteractiveShellTestCase(unittest.TestCase):'
103 res = ip.run_cell("raise = 3")
103 res = ip.run_cell("raise = 3")
104 self.assertIsInstance(res.error_before_exec, SyntaxError)
104 self.assertIsInstance(res.error_before_exec, SyntaxError)
105
105
106 def test_open_standard_input_stream(self):
107 res = ip.run_cell("open(0)")
108 self.assertIsInstance(res.error_in_exec, ValueError)
109
110 def test_open_standard_output_stream(self):
111 res = ip.run_cell("open(1)")
112 self.assertIsInstance(res.error_in_exec, ValueError)
113
114 def test_open_standard_error_stream(self):
115 res = ip.run_cell("open(2)")
116 self.assertIsInstance(res.error_in_exec, ValueError)
117
106 def test_In_variable(self):
118 def test_In_variable(self):
107 "Verify that In variable grows with user input (GH-284)"
119 "Verify that In variable grows with user input (GH-284)"
108 oldlen = len(ip.user_ns['In'])
120 oldlen = len(ip.user_ns['In'])
@@ -1,15 +1,11 b''
1 """Tests for the key interactiveshell module, where the main ipython class is defined.
1 """Tests for the key interactiveshell module, where the main ipython class is defined.
2 """
2 """
3 #-----------------------------------------------------------------------------
4 # Module imports
5 #-----------------------------------------------------------------------------
6
3
4 import stack_data
5 import sys
7
6
8 # our own packages
7 SV_VERSION = tuple([int(x) for x in stack_data.__version__.split(".")[0:2]])
9
8
10 #-----------------------------------------------------------------------------
11 # Test functions
12 #-----------------------------------------------------------------------------
13
9
14 def test_reset():
10 def test_reset():
15 """reset must clear most namespaces."""
11 """reset must clear most namespaces."""
@@ -170,46 +166,93 b' def doctest_tb_sysexit():'
170 """
166 """
171
167
172
168
173 def doctest_tb_sysexit_verbose():
169 if sys.version_info >= (3, 9):
174 """
170 if SV_VERSION < (0, 6):
175 In [18]: %run simpleerr.py exit
171
176 An exception has occurred, use %tb to see the full traceback.
172 def doctest_tb_sysexit_verbose_stack_data_05():
177 SystemExit: (1, 'Mode = exit')
173 """
178
174 In [18]: %run simpleerr.py exit
179 In [19]: %run simpleerr.py exit 2
175 An exception has occurred, use %tb to see the full traceback.
180 An exception has occurred, use %tb to see the full traceback.
176 SystemExit: (1, 'Mode = exit')
181 SystemExit: (2, 'Mode = exit')
177
182
178 In [19]: %run simpleerr.py exit 2
183 In [23]: %xmode verbose
179 An exception has occurred, use %tb to see the full traceback.
184 Exception reporting mode: Verbose
180 SystemExit: (2, 'Mode = exit')
185
181
186 In [24]: %tb
182 In [23]: %xmode verbose
187 ---------------------------------------------------------------------------
183 Exception reporting mode: Verbose
188 SystemExit Traceback (most recent call last)
184
189 <BLANKLINE>
185 In [24]: %tb
190 ...
186 ---------------------------------------------------------------------------
191 30 except IndexError:
187 SystemExit Traceback (most recent call last)
192 31 mode = 'div'
188 <BLANKLINE>
193 ---> 33 bar(mode)
189 ...
194 mode = 'exit'
190 30 except IndexError:
195 <BLANKLINE>
191 31 mode = 'div'
196 ... in bar(mode='exit')
192 ---> 33 bar(mode)
197 ... except:
193 mode = 'exit'
198 ... stat = 1
194 <BLANKLINE>
199 ---> ... sysexit(stat, mode)
195 ... in bar(mode='exit')
200 mode = 'exit'
196 ... except:
201 stat = 2
197 ... stat = 1
202 ... else:
198 ---> ... sysexit(stat, mode)
203 ... raise ValueError('Unknown mode')
199 mode = 'exit'
204 <BLANKLINE>
200 stat = 2
205 ... in sysexit(stat=2, mode='exit')
201 ... else:
206 10 def sysexit(stat, mode):
202 ... raise ValueError('Unknown mode')
207 ---> 11 raise SystemExit(stat, f"Mode = {mode}")
203 <BLANKLINE>
208 stat = 2
204 ... in sysexit(stat=2, mode='exit')
209 <BLANKLINE>
205 10 def sysexit(stat, mode):
210 SystemExit: (2, 'Mode = exit')
206 ---> 11 raise SystemExit(stat, f"Mode = {mode}")
211 """
207 stat = 2
212
208 <BLANKLINE>
209 SystemExit: (2, 'Mode = exit')
210 """
211
212 else:
213 # currently the only difference is
214 # + mode = 'exit'
215
216 def doctest_tb_sysexit_verbose_stack_data_06():
217 """
218 In [18]: %run simpleerr.py exit
219 An exception has occurred, use %tb to see the full traceback.
220 SystemExit: (1, 'Mode = exit')
221
222 In [19]: %run simpleerr.py exit 2
223 An exception has occurred, use %tb to see the full traceback.
224 SystemExit: (2, 'Mode = exit')
225
226 In [23]: %xmode verbose
227 Exception reporting mode: Verbose
228
229 In [24]: %tb
230 ---------------------------------------------------------------------------
231 SystemExit Traceback (most recent call last)
232 <BLANKLINE>
233 ...
234 30 except IndexError:
235 31 mode = 'div'
236 ---> 33 bar(mode)
237 mode = 'exit'
238 <BLANKLINE>
239 ... in bar(mode='exit')
240 ... except:
241 ... stat = 1
242 ---> ... sysexit(stat, mode)
243 mode = 'exit'
244 stat = 2
245 ... else:
246 ... raise ValueError('Unknown mode')
247 <BLANKLINE>
248 ... in sysexit(stat=2, mode='exit')
249 10 def sysexit(stat, mode):
250 ---> 11 raise SystemExit(stat, f"Mode = {mode}")
251 stat = 2
252 mode = 'exit'
253 <BLANKLINE>
254 SystemExit: (2, 'Mode = exit')
255 """
213
256
214 def test_run_cell():
257 def test_run_cell():
215 import textwrap
258 import textwrap
@@ -84,7 +84,7 b' def test_extract_symbols_raises_exception_with_non_python_code():'
84 def test_magic_not_found():
84 def test_magic_not_found():
85 # magic not found raises UsageError
85 # magic not found raises UsageError
86 with pytest.raises(UsageError):
86 with pytest.raises(UsageError):
87 _ip.magic('doesntexist')
87 _ip.run_line_magic("doesntexist", "")
88
88
89 # ensure result isn't success when a magic isn't found
89 # ensure result isn't success when a magic isn't found
90 result = _ip.run_cell('%doesntexist')
90 result = _ip.run_cell('%doesntexist')
@@ -116,13 +116,14 b' def test_config():'
116 magic.
116 magic.
117 """
117 """
118 ## should not raise.
118 ## should not raise.
119 _ip.magic('config')
119 _ip.run_line_magic("config", "")
120
120
121
121 def test_config_available_configs():
122 def test_config_available_configs():
122 """ test that config magic prints available configs in unique and
123 """ test that config magic prints available configs in unique and
123 sorted order. """
124 sorted order. """
124 with capture_output() as captured:
125 with capture_output() as captured:
125 _ip.magic('config')
126 _ip.run_line_magic("config", "")
126
127
127 stdout = captured.stdout
128 stdout = captured.stdout
128 config_classes = stdout.strip().split('\n')[1:]
129 config_classes = stdout.strip().split('\n')[1:]
@@ -131,7 +132,7 b' def test_config_available_configs():'
131 def test_config_print_class():
132 def test_config_print_class():
132 """ test that config with a classname prints the class's options. """
133 """ test that config with a classname prints the class's options. """
133 with capture_output() as captured:
134 with capture_output() as captured:
134 _ip.magic('config TerminalInteractiveShell')
135 _ip.run_line_magic("config", "TerminalInteractiveShell")
135
136
136 stdout = captured.stdout
137 stdout = captured.stdout
137 assert re.match(
138 assert re.match(
@@ -144,7 +145,7 b' def test_rehashx():'
144 _ip.alias_manager.clear_aliases()
145 _ip.alias_manager.clear_aliases()
145 del _ip.db['syscmdlist']
146 del _ip.db['syscmdlist']
146
147
147 _ip.magic('rehashx')
148 _ip.run_line_magic("rehashx", "")
148 # Practically ALL ipython development systems will have more than 10 aliases
149 # Practically ALL ipython development systems will have more than 10 aliases
149
150
150 assert len(_ip.alias_manager.aliases) > 10
151 assert len(_ip.alias_manager.aliases) > 10
@@ -277,11 +278,11 b' def test_macro():'
277 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
278 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
278 for i, cmd in enumerate(cmds, start=1):
279 for i, cmd in enumerate(cmds, start=1):
279 ip.history_manager.store_inputs(i, cmd)
280 ip.history_manager.store_inputs(i, cmd)
280 ip.magic("macro test 1-3")
281 ip.run_line_magic("macro", "test 1-3")
281 assert ip.user_ns["test"].value == "\n".join(cmds) + "\n"
282 assert ip.user_ns["test"].value == "\n".join(cmds) + "\n"
282
283
283 # List macros
284 # List macros
284 assert "test" in ip.magic("macro")
285 assert "test" in ip.run_line_magic("macro", "")
285
286
286
287
287 def test_macro_run():
288 def test_macro_run():
@@ -302,7 +303,7 b' def test_magic_magic():'
302 """Test %magic"""
303 """Test %magic"""
303 ip = get_ipython()
304 ip = get_ipython()
304 with capture_output() as captured:
305 with capture_output() as captured:
305 ip.magic("magic")
306 ip.run_line_magic("magic", "")
306
307
307 stdout = captured.stdout
308 stdout = captured.stdout
308 assert "%magic" in stdout
309 assert "%magic" in stdout
@@ -316,7 +317,7 b' def test_numpy_reset_array_undec():'
316 _ip.ex("import numpy as np")
317 _ip.ex("import numpy as np")
317 _ip.ex("a = np.empty(2)")
318 _ip.ex("a = np.empty(2)")
318 assert "a" in _ip.user_ns
319 assert "a" in _ip.user_ns
319 _ip.magic("reset -f array")
320 _ip.run_line_magic("reset", "-f array")
320 assert "a" not in _ip.user_ns
321 assert "a" not in _ip.user_ns
321
322
322
323
@@ -326,7 +327,7 b' def test_reset_out():'
326 # test '%reset -f out', make an Out prompt
327 # test '%reset -f out', make an Out prompt
327 _ip.run_cell("parrot", store_history=True)
328 _ip.run_cell("parrot", store_history=True)
328 assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")]
329 assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")]
329 _ip.magic("reset -f out")
330 _ip.run_line_magic("reset", "-f out")
330 assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")]
331 assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")]
331 assert len(_ip.user_ns["Out"]) == 0
332 assert len(_ip.user_ns["Out"]) == 0
332
333
@@ -336,7 +337,7 b' def test_reset_in():'
336 # test '%reset -f in'
337 # test '%reset -f in'
337 _ip.run_cell("parrot", store_history=True)
338 _ip.run_cell("parrot", store_history=True)
338 assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
339 assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
339 _ip.magic("%reset -f in")
340 _ip.run_line_magic("reset", "-f in")
340 assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
341 assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
341 assert len(set(_ip.user_ns["In"])) == 1
342 assert len(set(_ip.user_ns["In"])) == 1
342
343
@@ -344,10 +345,10 b' def test_reset_in():'
344 def test_reset_dhist():
345 def test_reset_dhist():
345 "Test '%reset dhist' magic"
346 "Test '%reset dhist' magic"
346 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
347 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
347 _ip.magic("cd " + os.path.dirname(pytest.__file__))
348 _ip.run_line_magic("cd", os.path.dirname(pytest.__file__))
348 _ip.magic("cd -")
349 _ip.run_line_magic("cd", "-")
349 assert len(_ip.user_ns["_dh"]) > 0
350 assert len(_ip.user_ns["_dh"]) > 0
350 _ip.magic("reset -f dhist")
351 _ip.run_line_magic("reset", "-f dhist")
351 assert len(_ip.user_ns["_dh"]) == 0
352 assert len(_ip.user_ns["_dh"]) == 0
352 _ip.run_cell("_dh = [d for d in tmp]") # restore
353 _ip.run_cell("_dh = [d for d in tmp]") # restore
353
354
@@ -472,8 +473,8 b' def test_time_local_ns():'
472
473
473 def test_doctest_mode():
474 def test_doctest_mode():
474 "Toggle doctest_mode twice, it should be a no-op and run without error"
475 "Toggle doctest_mode twice, it should be a no-op and run without error"
475 _ip.magic('doctest_mode')
476 _ip.run_line_magic("doctest_mode", "")
476 _ip.magic('doctest_mode')
477 _ip.run_line_magic("doctest_mode", "")
477
478
478
479
479 def test_parse_options():
480 def test_parse_options():
@@ -498,7 +499,9 b' def test_parse_options_preserve_non_option_string():'
498 def test_run_magic_preserve_code_block():
499 def test_run_magic_preserve_code_block():
499 """Test to assert preservation of non-option part of magic-block, while running magic."""
500 """Test to assert preservation of non-option part of magic-block, while running magic."""
500 _ip.user_ns["spaces"] = []
501 _ip.user_ns["spaces"] = []
501 _ip.magic("timeit -n1 -r1 spaces.append([s.count(' ') for s in ['document']])")
502 _ip.run_line_magic(
503 "timeit", "-n1 -r1 spaces.append([s.count(' ') for s in ['document']])"
504 )
502 assert _ip.user_ns["spaces"] == [[0]]
505 assert _ip.user_ns["spaces"] == [[0]]
503
506
504
507
@@ -509,13 +512,13 b' def test_dirops():'
509 startdir = os.getcwd()
512 startdir = os.getcwd()
510 ipdir = os.path.realpath(_ip.ipython_dir)
513 ipdir = os.path.realpath(_ip.ipython_dir)
511 try:
514 try:
512 _ip.magic('cd "%s"' % ipdir)
515 _ip.run_line_magic("cd", '"%s"' % ipdir)
513 assert curpath() == ipdir
516 assert curpath() == ipdir
514 _ip.magic('cd -')
517 _ip.run_line_magic("cd", "-")
515 assert curpath() == startdir
518 assert curpath() == startdir
516 _ip.magic('pushd "%s"' % ipdir)
519 _ip.run_line_magic("pushd", '"%s"' % ipdir)
517 assert curpath() == ipdir
520 assert curpath() == ipdir
518 _ip.magic('popd')
521 _ip.run_line_magic("popd", "")
519 assert curpath() == startdir
522 assert curpath() == startdir
520 finally:
523 finally:
521 os.chdir(startdir)
524 os.chdir(startdir)
@@ -542,7 +545,7 b' def test_xmode():'
542 # Calling xmode three times should be a no-op
545 # Calling xmode three times should be a no-op
543 xmode = _ip.InteractiveTB.mode
546 xmode = _ip.InteractiveTB.mode
544 for i in range(4):
547 for i in range(4):
545 _ip.magic("xmode")
548 _ip.run_line_magic("xmode", "")
546 assert _ip.InteractiveTB.mode == xmode
549 assert _ip.InteractiveTB.mode == xmode
547
550
548 def test_reset_hard():
551 def test_reset_hard():
@@ -557,7 +560,7 b' def test_reset_hard():'
557 _ip.run_cell("a")
560 _ip.run_cell("a")
558
561
559 assert monitor == []
562 assert monitor == []
560 _ip.magic("reset -f")
563 _ip.run_line_magic("reset", "-f")
561 assert monitor == [1]
564 assert monitor == [1]
562
565
563 class TestXdel(tt.TempFileMixin):
566 class TestXdel(tt.TempFileMixin):
@@ -570,14 +573,14 b' class TestXdel(tt.TempFileMixin):'
570 "a = A()\n")
573 "a = A()\n")
571 self.mktmp(src)
574 self.mktmp(src)
572 # %run creates some hidden references...
575 # %run creates some hidden references...
573 _ip.magic("run %s" % self.fname)
576 _ip.run_line_magic("run", "%s" % self.fname)
574 # ... as does the displayhook.
577 # ... as does the displayhook.
575 _ip.run_cell("a")
578 _ip.run_cell("a")
576
579
577 monitor = _ip.user_ns["A"].monitor
580 monitor = _ip.user_ns["A"].monitor
578 assert monitor == []
581 assert monitor == []
579
582
580 _ip.magic("xdel a")
583 _ip.run_line_magic("xdel", "a")
581
584
582 # Check that a's __del__ method has been called.
585 # Check that a's __del__ method has been called.
583 gc.collect(0)
586 gc.collect(0)
@@ -614,7 +617,7 b' def test_whos():'
614 def __repr__(self):
617 def __repr__(self):
615 raise Exception()
618 raise Exception()
616 _ip.user_ns['a'] = A()
619 _ip.user_ns['a'] = A()
617 _ip.magic("whos")
620 _ip.run_line_magic("whos", "")
618
621
619 def doctest_precision():
622 def doctest_precision():
620 """doctest for %precision
623 """doctest for %precision
@@ -655,12 +658,12 b' def test_psearch():'
655 def test_timeit_shlex():
658 def test_timeit_shlex():
656 """test shlex issues with timeit (#1109)"""
659 """test shlex issues with timeit (#1109)"""
657 _ip.ex("def f(*a,**kw): pass")
660 _ip.ex("def f(*a,**kw): pass")
658 _ip.magic('timeit -n1 "this is a bug".count(" ")')
661 _ip.run_line_magic("timeit", '-n1 "this is a bug".count(" ")')
659 _ip.magic('timeit -r1 -n1 f(" ", 1)')
662 _ip.run_line_magic("timeit", '-r1 -n1 f(" ", 1)')
660 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
663 _ip.run_line_magic("timeit", '-r1 -n1 f(" ", 1, " ", 2, " ")')
661 _ip.magic('timeit -r1 -n1 ("a " + "b")')
664 _ip.run_line_magic("timeit", '-r1 -n1 ("a " + "b")')
662 _ip.magic('timeit -r1 -n1 f("a " + "b")')
665 _ip.run_line_magic("timeit", '-r1 -n1 f("a " + "b")')
663 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
666 _ip.run_line_magic("timeit", '-r1 -n1 f("a " + "b ")')
664
667
665
668
666 def test_timeit_special_syntax():
669 def test_timeit_special_syntax():
@@ -738,9 +741,9 b' def test_extension():'
738 try:
741 try:
739 _ip.user_ns.pop('arq', None)
742 _ip.user_ns.pop('arq', None)
740 invalidate_caches() # Clear import caches
743 invalidate_caches() # Clear import caches
741 _ip.magic("load_ext daft_extension")
744 _ip.run_line_magic("load_ext", "daft_extension")
742 assert _ip.user_ns["arq"] == 185
745 assert _ip.user_ns["arq"] == 185
743 _ip.magic("unload_ext daft_extension")
746 _ip.run_line_magic("unload_ext", "daft_extension")
744 assert 'arq' not in _ip.user_ns
747 assert 'arq' not in _ip.user_ns
745 finally:
748 finally:
746 sys.path.remove(daft_path)
749 sys.path.remove(daft_path)
@@ -755,17 +758,17 b' def test_notebook_export_json():'
755 _ip.history_manager.store_inputs(i, cmd)
758 _ip.history_manager.store_inputs(i, cmd)
756 with TemporaryDirectory() as td:
759 with TemporaryDirectory() as td:
757 outfile = os.path.join(td, "nb.ipynb")
760 outfile = os.path.join(td, "nb.ipynb")
758 _ip.magic("notebook %s" % outfile)
761 _ip.run_line_magic("notebook", "%s" % outfile)
759
762
760
763
761 class TestEnv(TestCase):
764 class TestEnv(TestCase):
762
765
763 def test_env(self):
766 def test_env(self):
764 env = _ip.magic("env")
767 env = _ip.run_line_magic("env", "")
765 self.assertTrue(isinstance(env, dict))
768 self.assertTrue(isinstance(env, dict))
766
769
767 def test_env_secret(self):
770 def test_env_secret(self):
768 env = _ip.magic("env")
771 env = _ip.run_line_magic("env", "")
769 hidden = "<hidden>"
772 hidden = "<hidden>"
770 with mock.patch.dict(
773 with mock.patch.dict(
771 os.environ,
774 os.environ,
@@ -776,35 +779,35 b' class TestEnv(TestCase):'
776 "VAR": "abc"
779 "VAR": "abc"
777 }
780 }
778 ):
781 ):
779 env = _ip.magic("env")
782 env = _ip.run_line_magic("env", "")
780 assert env["API_KEY"] == hidden
783 assert env["API_KEY"] == hidden
781 assert env["SECRET_THING"] == hidden
784 assert env["SECRET_THING"] == hidden
782 assert env["JUPYTER_TOKEN"] == hidden
785 assert env["JUPYTER_TOKEN"] == hidden
783 assert env["VAR"] == "abc"
786 assert env["VAR"] == "abc"
784
787
785 def test_env_get_set_simple(self):
788 def test_env_get_set_simple(self):
786 env = _ip.magic("env var val1")
789 env = _ip.run_line_magic("env", "var val1")
787 self.assertEqual(env, None)
790 self.assertEqual(env, None)
788 self.assertEqual(os.environ['var'], 'val1')
791 self.assertEqual(os.environ["var"], "val1")
789 self.assertEqual(_ip.magic("env var"), 'val1')
792 self.assertEqual(_ip.run_line_magic("env", "var"), "val1")
790 env = _ip.magic("env var=val2")
793 env = _ip.run_line_magic("env", "var=val2")
791 self.assertEqual(env, None)
794 self.assertEqual(env, None)
792 self.assertEqual(os.environ['var'], 'val2')
795 self.assertEqual(os.environ['var'], 'val2')
793
796
794 def test_env_get_set_complex(self):
797 def test_env_get_set_complex(self):
795 env = _ip.magic("env var 'val1 '' 'val2")
798 env = _ip.run_line_magic("env", "var 'val1 '' 'val2")
796 self.assertEqual(env, None)
799 self.assertEqual(env, None)
797 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
800 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
798 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
801 self.assertEqual(_ip.run_line_magic("env", "var"), "'val1 '' 'val2")
799 env = _ip.magic('env var=val2 val3="val4')
802 env = _ip.run_line_magic("env", 'var=val2 val3="val4')
800 self.assertEqual(env, None)
803 self.assertEqual(env, None)
801 self.assertEqual(os.environ['var'], 'val2 val3="val4')
804 self.assertEqual(os.environ['var'], 'val2 val3="val4')
802
805
803 def test_env_set_bad_input(self):
806 def test_env_set_bad_input(self):
804 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
807 self.assertRaises(UsageError, lambda: _ip.run_line_magic("set_env", "var"))
805
808
806 def test_env_set_whitespace(self):
809 def test_env_set_whitespace(self):
807 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
810 self.assertRaises(UsageError, lambda: _ip.run_line_magic("env", "var A=B"))
808
811
809
812
810 class CellMagicTestCase(TestCase):
813 class CellMagicTestCase(TestCase):
@@ -1308,7 +1311,7 b' def test_ls_magic():'
1308 ip = get_ipython()
1311 ip = get_ipython()
1309 json_formatter = ip.display_formatter.formatters['application/json']
1312 json_formatter = ip.display_formatter.formatters['application/json']
1310 json_formatter.enabled = True
1313 json_formatter.enabled = True
1311 lsmagic = ip.magic('lsmagic')
1314 lsmagic = ip.run_line_magic("lsmagic", "")
1312 with warnings.catch_warnings(record=True) as w:
1315 with warnings.catch_warnings(record=True) as w:
1313 j = json_formatter(lsmagic)
1316 j = json_formatter(lsmagic)
1314 assert sorted(j) == ["cell", "line"]
1317 assert sorted(j) == ["cell", "line"]
@@ -1358,16 +1361,16 b' def test_logging_magic_not_quiet():'
1358
1361
1359
1362
1360 def test_time_no_var_expand():
1363 def test_time_no_var_expand():
1361 _ip.user_ns['a'] = 5
1364 _ip.user_ns["a"] = 5
1362 _ip.user_ns['b'] = []
1365 _ip.user_ns["b"] = []
1363 _ip.magic('time b.append("{a}")')
1366 _ip.run_line_magic("time", 'b.append("{a}")')
1364 assert _ip.user_ns['b'] == ['{a}']
1367 assert _ip.user_ns["b"] == ["{a}"]
1365
1368
1366
1369
1367 # this is slow, put at the end for local testing.
1370 # this is slow, put at the end for local testing.
1368 def test_timeit_arguments():
1371 def test_timeit_arguments():
1369 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1372 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1370 _ip.magic("timeit -n1 -r1 a=('#')")
1373 _ip.run_line_magic("timeit", "-n1 -r1 a=('#')")
1371
1374
1372
1375
1373 MINIMAL_LAZY_MAGIC = """
1376 MINIMAL_LAZY_MAGIC = """
@@ -1442,7 +1445,7 b' def test_run_module_from_import_hook():'
1442 sys.meta_path.insert(0, MyTempImporter())
1445 sys.meta_path.insert(0, MyTempImporter())
1443
1446
1444 with capture_output() as captured:
1447 with capture_output() as captured:
1445 _ip.magic("run -m my_tmp")
1448 _ip.run_line_magic("run", "-m my_tmp")
1446 _ip.run_cell("import my_tmp")
1449 _ip.run_cell("import my_tmp")
1447
1450
1448 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1451 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
@@ -5,6 +5,7 b''
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7
7
8 from contextlib import contextmanager
8 from inspect import signature, Signature, Parameter
9 from inspect import signature, Signature, Parameter
9 import inspect
10 import inspect
10 import os
11 import os
@@ -43,7 +44,7 b' class SourceModuleMainTest:'
43 # defined, if any code is inserted above, the following line will need to be
44 # defined, if any code is inserted above, the following line will need to be
44 # updated. Do NOT insert any whitespace between the next line and the function
45 # updated. Do NOT insert any whitespace between the next line and the function
45 # definition below.
46 # definition below.
46 THIS_LINE_NUMBER = 46 # Put here the actual number of this line
47 THIS_LINE_NUMBER = 47 # Put here the actual number of this line
47
48
48
49
49 def test_find_source_lines():
50 def test_find_source_lines():
@@ -345,6 +346,70 b' def test_pdef():'
345 inspector.pdef(foo, 'foo')
346 inspector.pdef(foo, 'foo')
346
347
347
348
349 @contextmanager
350 def cleanup_user_ns(**kwargs):
351 """
352 On exit delete all the keys that were not in user_ns before entering.
353
354 It does not restore old values !
355
356 Parameters
357 ----------
358
359 **kwargs
360 used to update ip.user_ns
361
362 """
363 try:
364 known = set(ip.user_ns.keys())
365 ip.user_ns.update(kwargs)
366 yield
367 finally:
368 added = set(ip.user_ns.keys()) - known
369 for k in added:
370 del ip.user_ns[k]
371
372
373 def test_pinfo_getindex():
374 def dummy():
375 """
376 MARKER
377 """
378
379 container = [dummy]
380 with cleanup_user_ns(container=container):
381 with AssertPrints("MARKER"):
382 ip._inspect("pinfo", "container[0]", detail_level=0)
383 assert "container" not in ip.user_ns.keys()
384
385
386 def test_qmark_getindex():
387 def dummy():
388 """
389 MARKER 2
390 """
391
392 container = [dummy]
393 with cleanup_user_ns(container=container):
394 with AssertPrints("MARKER 2"):
395 ip.run_cell("container[0]?")
396 assert "container" not in ip.user_ns.keys()
397
398
399 def test_qmark_getindex_negatif():
400 def dummy():
401 """
402 MARKER 3
403 """
404
405 container = [dummy]
406 with cleanup_user_ns(container=container):
407 with AssertPrints("MARKER 3"):
408 ip.run_cell("container[-1]?")
409 assert "container" not in ip.user_ns.keys()
410
411
412
348 def test_pinfo_nonascii():
413 def test_pinfo_nonascii():
349 # See gh-1177
414 # See gh-1177
350 from . import nonascii2
415 from . import nonascii2
@@ -180,13 +180,13 b' class TestMagicRunPass(tt.TempFileMixin):'
180 _ip = get_ipython()
180 _ip = get_ipython()
181 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
181 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
182 # See below and ticket https://bugs.launchpad.net/bugs/366353
182 # See below and ticket https://bugs.launchpad.net/bugs/366353
183 _ip.magic('run %s' % self.fname)
183 _ip.run_line_magic("run", self.fname)
184
184
185 def run_tmpfile_p(self):
185 def run_tmpfile_p(self):
186 _ip = get_ipython()
186 _ip = get_ipython()
187 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
187 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
188 # See below and ticket https://bugs.launchpad.net/bugs/366353
188 # See below and ticket https://bugs.launchpad.net/bugs/366353
189 _ip.magic('run -p %s' % self.fname)
189 _ip.run_line_magic("run", "-p %s" % self.fname)
190
190
191 def test_builtins_id(self):
191 def test_builtins_id(self):
192 """Check that %run doesn't damage __builtins__ """
192 """Check that %run doesn't damage __builtins__ """
@@ -216,20 +216,20 b' class TestMagicRunPass(tt.TempFileMixin):'
216 def test_run_debug_twice(self):
216 def test_run_debug_twice(self):
217 # https://github.com/ipython/ipython/issues/10028
217 # https://github.com/ipython/ipython/issues/10028
218 _ip = get_ipython()
218 _ip = get_ipython()
219 with tt.fake_input(['c']):
219 with tt.fake_input(["c"]):
220 _ip.magic('run -d %s' % self.fname)
220 _ip.run_line_magic("run", "-d %s" % self.fname)
221 with tt.fake_input(['c']):
221 with tt.fake_input(["c"]):
222 _ip.magic('run -d %s' % self.fname)
222 _ip.run_line_magic("run", "-d %s" % self.fname)
223
223
224 def test_run_debug_twice_with_breakpoint(self):
224 def test_run_debug_twice_with_breakpoint(self):
225 """Make a valid python temp file."""
225 """Make a valid python temp file."""
226 _ip = get_ipython()
226 _ip = get_ipython()
227 with tt.fake_input(['b 2', 'c', 'c']):
227 with tt.fake_input(["b 2", "c", "c"]):
228 _ip.magic('run -d %s' % self.fname)
228 _ip.run_line_magic("run", "-d %s" % self.fname)
229
229
230 with tt.fake_input(['c']):
230 with tt.fake_input(["c"]):
231 with tt.AssertNotPrints('KeyError'):
231 with tt.AssertNotPrints("KeyError"):
232 _ip.magic('run -d %s' % self.fname)
232 _ip.run_line_magic("run", "-d %s" % self.fname)
233
233
234
234
235 class TestMagicRunSimple(tt.TempFileMixin):
235 class TestMagicRunSimple(tt.TempFileMixin):
@@ -239,7 +239,7 b' class TestMagicRunSimple(tt.TempFileMixin):'
239 src = ("class foo: pass\n"
239 src = ("class foo: pass\n"
240 "def f(): return foo()")
240 "def f(): return foo()")
241 self.mktmp(src)
241 self.mktmp(src)
242 _ip.magic("run %s" % self.fname)
242 _ip.run_line_magic("run", str(self.fname))
243 _ip.run_cell("t = isinstance(f(), foo)")
243 _ip.run_cell("t = isinstance(f(), foo)")
244 assert _ip.user_ns["t"] is True
244 assert _ip.user_ns["t"] is True
245
245
@@ -277,7 +277,7 b' class TestMagicRunSimple(tt.TempFileMixin):'
277 " break\n" % ("run " + empty.fname)
277 " break\n" % ("run " + empty.fname)
278 )
278 )
279 self.mktmp(src)
279 self.mktmp(src)
280 _ip.magic("run %s" % self.fname)
280 _ip.run_line_magic("run", str(self.fname))
281 _ip.run_cell("ip == get_ipython()")
281 _ip.run_cell("ip == get_ipython()")
282 assert _ip.user_ns["i"] == 4
282 assert _ip.user_ns["i"] == 4
283
283
@@ -288,8 +288,8 b' class TestMagicRunSimple(tt.TempFileMixin):'
288 with tt.TempFileMixin() as empty:
288 with tt.TempFileMixin() as empty:
289 empty.mktmp("")
289 empty.mktmp("")
290
290
291 _ip.magic("run %s" % self.fname)
291 _ip.run_line_magic("run", self.fname)
292 _ip.magic("run %s" % empty.fname)
292 _ip.run_line_magic("run", empty.fname)
293 assert _ip.user_ns["afunc"]() == 1
293 assert _ip.user_ns["afunc"]() == 1
294
294
295 def test_tclass(self):
295 def test_tclass(self):
@@ -323,22 +323,22 b' tclass.py: deleting object: C-third'
323 self.mktmp(src)
323 self.mktmp(src)
324 _ip.run_cell("zz = 23")
324 _ip.run_cell("zz = 23")
325 try:
325 try:
326 _ip.magic("run -i %s" % self.fname)
326 _ip.run_line_magic("run", "-i %s" % self.fname)
327 assert _ip.user_ns["yy"] == 23
327 assert _ip.user_ns["yy"] == 23
328 finally:
328 finally:
329 _ip.magic('reset -f')
329 _ip.run_line_magic("reset", "-f")
330
330
331 _ip.run_cell("zz = 23")
331 _ip.run_cell("zz = 23")
332 try:
332 try:
333 _ip.magic("run -i %s" % self.fname)
333 _ip.run_line_magic("run", "-i %s" % self.fname)
334 assert _ip.user_ns["yy"] == 23
334 assert _ip.user_ns["yy"] == 23
335 finally:
335 finally:
336 _ip.magic('reset -f')
336 _ip.run_line_magic("reset", "-f")
337
337
338 def test_unicode(self):
338 def test_unicode(self):
339 """Check that files in odd encodings are accepted."""
339 """Check that files in odd encodings are accepted."""
340 mydir = os.path.dirname(__file__)
340 mydir = os.path.dirname(__file__)
341 na = os.path.join(mydir, 'nonascii.py')
341 na = os.path.join(mydir, "nonascii.py")
342 _ip.magic('run "%s"' % na)
342 _ip.magic('run "%s"' % na)
343 assert _ip.user_ns["u"] == "Ўт№Ф"
343 assert _ip.user_ns["u"] == "Ўт№Ф"
344
344
@@ -347,9 +347,9 b' tclass.py: deleting object: C-third'
347 src = "t = __file__\n"
347 src = "t = __file__\n"
348 self.mktmp(src)
348 self.mktmp(src)
349 _missing = object()
349 _missing = object()
350 file1 = _ip.user_ns.get('__file__', _missing)
350 file1 = _ip.user_ns.get("__file__", _missing)
351 _ip.magic('run %s' % self.fname)
351 _ip.run_line_magic("run", self.fname)
352 file2 = _ip.user_ns.get('__file__', _missing)
352 file2 = _ip.user_ns.get("__file__", _missing)
353
353
354 # Check that __file__ was equal to the filename in the script's
354 # Check that __file__ was equal to the filename in the script's
355 # namespace.
355 # namespace.
@@ -363,9 +363,9 b' tclass.py: deleting object: C-third'
363 src = "t = __file__\n"
363 src = "t = __file__\n"
364 self.mktmp(src, ext='.ipy')
364 self.mktmp(src, ext='.ipy')
365 _missing = object()
365 _missing = object()
366 file1 = _ip.user_ns.get('__file__', _missing)
366 file1 = _ip.user_ns.get("__file__", _missing)
367 _ip.magic('run %s' % self.fname)
367 _ip.run_line_magic("run", self.fname)
368 file2 = _ip.user_ns.get('__file__', _missing)
368 file2 = _ip.user_ns.get("__file__", _missing)
369
369
370 # Check that __file__ was equal to the filename in the script's
370 # Check that __file__ was equal to the filename in the script's
371 # namespace.
371 # namespace.
@@ -378,18 +378,18 b' tclass.py: deleting object: C-third'
378 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
378 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
379 src = "pass"
379 src = "pass"
380 self.mktmp(src)
380 self.mktmp(src)
381 _ip.magic('run -t -N 1 %s' % self.fname)
381 _ip.run_line_magic("run", "-t -N 1 %s" % self.fname)
382 _ip.magic('run -t -N 10 %s' % self.fname)
382 _ip.run_line_magic("run", "-t -N 10 %s" % self.fname)
383
383
384 def test_ignore_sys_exit(self):
384 def test_ignore_sys_exit(self):
385 """Test the -e option to ignore sys.exit()"""
385 """Test the -e option to ignore sys.exit()"""
386 src = "import sys; sys.exit(1)"
386 src = "import sys; sys.exit(1)"
387 self.mktmp(src)
387 self.mktmp(src)
388 with tt.AssertPrints('SystemExit'):
388 with tt.AssertPrints("SystemExit"):
389 _ip.magic('run %s' % self.fname)
389 _ip.run_line_magic("run", self.fname)
390
390
391 with tt.AssertNotPrints('SystemExit'):
391 with tt.AssertNotPrints("SystemExit"):
392 _ip.magic('run -e %s' % self.fname)
392 _ip.run_line_magic("run", "-e %s" % self.fname)
393
393
394 def test_run_nb(self):
394 def test_run_nb(self):
395 """Test %run notebook.ipynb"""
395 """Test %run notebook.ipynb"""
@@ -404,7 +404,7 b' tclass.py: deleting object: C-third'
404 src = writes(nb, version=4)
404 src = writes(nb, version=4)
405 self.mktmp(src, ext='.ipynb')
405 self.mktmp(src, ext='.ipynb')
406
406
407 _ip.magic("run %s" % self.fname)
407 _ip.run_line_magic("run", self.fname)
408
408
409 assert _ip.user_ns["answer"] == 42
409 assert _ip.user_ns["answer"] == 42
410
410
@@ -478,12 +478,16 b' class TestMagicRunWithPackage(unittest.TestCase):'
478 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
478 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
479 self.tempdir.cleanup()
479 self.tempdir.cleanup()
480
480
481 def check_run_submodule(self, submodule, opts=''):
481 def check_run_submodule(self, submodule, opts=""):
482 _ip.user_ns.pop('x', None)
482 _ip.user_ns.pop("x", None)
483 _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
483 _ip.run_line_magic(
484 self.assertEqual(_ip.user_ns['x'], self.value,
484 "run", "{2} -m {0}.{1}".format(self.package, submodule, opts)
485 'Variable `x` is not loaded from module `{0}`.'
485 )
486 .format(submodule))
486 self.assertEqual(
487 _ip.user_ns["x"],
488 self.value,
489 "Variable `x` is not loaded from module `{0}`.".format(submodule),
490 )
487
491
488 def test_run_submodule_with_absolute_import(self):
492 def test_run_submodule_with_absolute_import(self):
489 self.check_run_submodule('absolute')
493 self.check_run_submodule('absolute')
@@ -533,17 +537,17 b' def test_run__name__():'
533 f.write("q = __name__")
537 f.write("q = __name__")
534
538
535 _ip.user_ns.pop("q", None)
539 _ip.user_ns.pop("q", None)
536 _ip.magic("run {}".format(path))
540 _ip.run_line_magic("run", "{}".format(path))
537 assert _ip.user_ns.pop("q") == "__main__"
541 assert _ip.user_ns.pop("q") == "__main__"
538
542
539 _ip.magic("run -n {}".format(path))
543 _ip.run_line_magic("run", "-n {}".format(path))
540 assert _ip.user_ns.pop("q") == "foo"
544 assert _ip.user_ns.pop("q") == "foo"
541
545
542 try:
546 try:
543 _ip.magic("run -i -n {}".format(path))
547 _ip.run_line_magic("run", "-i -n {}".format(path))
544 assert _ip.user_ns.pop("q") == "foo"
548 assert _ip.user_ns.pop("q") == "foo"
545 finally:
549 finally:
546 _ip.magic('reset -f')
550 _ip.run_line_magic("reset", "-f")
547
551
548
552
549 def test_run_tb():
553 def test_run_tb():
@@ -563,7 +567,7 b' def test_run_tb():'
563 )
567 )
564 )
568 )
565 with capture_output() as io:
569 with capture_output() as io:
566 _ip.magic('run {}'.format(path))
570 _ip.run_line_magic("run", "{}".format(path))
567 out = io.stdout
571 out = io.stdout
568 assert "execfile" not in out
572 assert "execfile" not in out
569 assert "RuntimeError" in out
573 assert "RuntimeError" in out
@@ -610,6 +610,8 b' class VerboseTB(TBTools):'
610 traceback, to be used with alternate interpreters (because their own code
610 traceback, to be used with alternate interpreters (because their own code
611 would appear in the traceback)."""
611 would appear in the traceback)."""
612
612
613 _tb_highlight = "bg:ansiyellow"
614
613 def __init__(
615 def __init__(
614 self,
616 self,
615 color_scheme: str = "Linux",
617 color_scheme: str = "Linux",
@@ -642,10 +644,8 b' class VerboseTB(TBTools):'
642 self.long_header = long_header
644 self.long_header = long_header
643 self.include_vars = include_vars
645 self.include_vars = include_vars
644 # By default we use linecache.checkcache, but the user can provide a
646 # By default we use linecache.checkcache, but the user can provide a
645 # different check_cache implementation. This is used by the IPython
647 # different check_cache implementation. This was formerly used by the
646 # kernel to provide tracebacks for interactive code that is cached,
648 # IPython kernel for interactive code, but is no longer necessary.
647 # by a compiler instance that flushes the linecache but preserves its
648 # own code cache.
649 if check_cache is None:
649 if check_cache is None:
650 check_cache = linecache.checkcache
650 check_cache = linecache.checkcache
651 self.check_cache = check_cache
651 self.check_cache = check_cache
@@ -836,7 +836,7 b' class VerboseTB(TBTools):'
836 before = context - after
836 before = context - after
837 if self.has_colors:
837 if self.has_colors:
838 style = get_style_by_name("default")
838 style = get_style_by_name("default")
839 style = stack_data.style_with_executing_node(style, "bg:ansiyellow")
839 style = stack_data.style_with_executing_node(style, self._tb_highlight)
840 formatter = Terminal256Formatter(style=style)
840 formatter = Terminal256Formatter(style=style)
841 else:
841 else:
842 formatter = None
842 formatter = None
@@ -107,6 +107,10 b' Some of the known remaining caveats are:'
107 before it is reloaded are not upgraded.
107 before it is reloaded are not upgraded.
108
108
109 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
109 - C extension modules cannot be reloaded, and so cannot be autoreloaded.
110
111 - While comparing Enum and Flag, the 'is' Identity Operator is used (even in the case '==' has been used (Similar to the 'None' keyword)).
112
113 - Reloading a module, or importing the same module by a different name, creates new Enums. These may look the same, but are not.
110 """
114 """
111
115
112 from IPython.core.magic import Magics, magics_class, line_magic
116 from IPython.core.magic import Magics, magics_class, line_magic
@@ -1,11 +1,14 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 from IPython.testing import decorators as dec
2 from IPython.testing import decorators as dec
3
3
4
4 def test_import_backgroundjobs():
5 def test_import_backgroundjobs():
5 from IPython.lib import backgroundjobs
6 from IPython.lib import backgroundjobs
6
7
8
7 def test_import_deepreload():
9 def test_import_deepreload():
8 from IPython.lib import deepreload
10 from IPython.lib import deepreload
9
11
12
10 def test_import_demo():
13 def test_import_demo():
11 from IPython.lib import demo
14 from IPython.lib import demo
@@ -981,8 +981,9 b' class IPythonDirective(Directive):'
981 self.shell.warning_is_error = warning_is_error
981 self.shell.warning_is_error = warning_is_error
982
982
983 # setup bookmark for saving figures directory
983 # setup bookmark for saving figures directory
984 self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir,
984 self.shell.process_input_line(
985 store_history=False)
985 'bookmark ipy_savedir "%s"' % savefig_dir, store_history=False
986 )
986 self.shell.clear_cout()
987 self.shell.clear_cout()
987
988
988 return rgxin, rgxout, promptin, promptout
989 return rgxin, rgxout, promptin, promptout
@@ -405,14 +405,14 b' class TerminalInteractiveShell(InteractiveShell):'
405 @observe('term_title')
405 @observe('term_title')
406 def init_term_title(self, change=None):
406 def init_term_title(self, change=None):
407 # Enable or disable the terminal title.
407 # Enable or disable the terminal title.
408 if self.term_title:
408 if self.term_title and _is_tty:
409 toggle_set_term_title(True)
409 toggle_set_term_title(True)
410 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
410 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
411 else:
411 else:
412 toggle_set_term_title(False)
412 toggle_set_term_title(False)
413
413
414 def restore_term_title(self):
414 def restore_term_title(self):
415 if self.term_title:
415 if self.term_title and _is_tty:
416 restore_term_title()
416 restore_term_title()
417
417
418 def init_display_formatter(self):
418 def init_display_formatter(self):
@@ -711,9 +711,8 b' class TerminalInteractiveShell(InteractiveShell):'
711
711
712 active_eventloop = None
712 active_eventloop = None
713 def enable_gui(self, gui=None):
713 def enable_gui(self, gui=None):
714 if gui and (gui != 'inline') :
714 if gui and (gui not in {"inline", "webagg"}):
715 self.active_eventloop, self._inputhook =\
715 self.active_eventloop, self._inputhook = get_inputhook_name_and_func(gui)
716 get_inputhook_name_and_func(gui)
717 else:
716 else:
718 self.active_eventloop = self._inputhook = None
717 self.active_eventloop = self._inputhook = None
719
718
@@ -318,6 +318,7 b' class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):'
318 self.shell.mainloop()
318 self.shell.mainloop()
319 else:
319 else:
320 self.log.debug("IPython not interactive...")
320 self.log.debug("IPython not interactive...")
321 self.shell.restore_term_title()
321 if not self.shell.last_execution_succeeded:
322 if not self.shell.last_execution_succeeded:
322 sys.exit(1)
323 sys.exit(1)
323
324
@@ -41,7 +41,7 b' class TerminalMagics(Magics):'
41 def __init__(self, shell):
41 def __init__(self, shell):
42 super(TerminalMagics, self).__init__(shell)
42 super(TerminalMagics, self).__init__(shell)
43
43
44 def store_or_execute(self, block, name):
44 def store_or_execute(self, block, name, store_history=False):
45 """ Execute a block, or store it in a variable, per the user's request.
45 """ Execute a block, or store it in a variable, per the user's request.
46 """
46 """
47 if name:
47 if name:
@@ -53,7 +53,7 b' class TerminalMagics(Magics):'
53 self.shell.user_ns['pasted_block'] = b
53 self.shell.user_ns['pasted_block'] = b
54 self.shell.using_paste_magics = True
54 self.shell.using_paste_magics = True
55 try:
55 try:
56 self.shell.run_cell(b, store_history=True)
56 self.shell.run_cell(b, store_history)
57 finally:
57 finally:
58 self.shell.using_paste_magics = False
58 self.shell.using_paste_magics = False
59
59
@@ -147,7 +147,7 b' class TerminalMagics(Magics):'
147
147
148 sentinel = opts.get('s', u'--')
148 sentinel = opts.get('s', u'--')
149 block = '\n'.join(get_pasted_lines(sentinel, quiet=quiet))
149 block = '\n'.join(get_pasted_lines(sentinel, quiet=quiet))
150 self.store_or_execute(block, name)
150 self.store_or_execute(block, name, store_history=False)
151
151
152 @line_magic
152 @line_magic
153 def paste(self, parameter_s=''):
153 def paste(self, parameter_s=''):
@@ -203,7 +203,7 b' class TerminalMagics(Magics):'
203 sys.stdout.write("\n")
203 sys.stdout.write("\n")
204 sys.stdout.write("## -- End pasted text --\n")
204 sys.stdout.write("## -- End pasted text --\n")
205
205
206 self.store_or_execute(block, name)
206 self.store_or_execute(block, name, store_history=True)
207
207
208 # Class-level: add a '%cls' magic only on Windows
208 # Class-level: add a '%cls' magic only on Windows
209 if sys.platform == 'win32':
209 if sys.platform == 'win32':
@@ -31,8 +31,7 b' from prompt_toolkit import __version__ as ptk_version'
31
31
32 from IPython.core.async_helpers import get_asyncio_loop
32 from IPython.core.async_helpers import get_asyncio_loop
33
33
34 PTK3 = ptk_version.startswith('3.')
34 PTK3 = ptk_version.startswith("3.")
35
36
35
37
36
38 def inputhook(context):
37 def inputhook(context):
@@ -41,6 +41,7 b' import gtk, gobject'
41 # Enable threading in GTK. (Otherwise, GTK will keep the GIL.)
41 # Enable threading in GTK. (Otherwise, GTK will keep the GIL.)
42 gtk.gdk.threads_init()
42 gtk.gdk.threads_init()
43
43
44
44 def inputhook(context):
45 def inputhook(context):
45 """
46 """
46 When the eventloop of prompt-toolkit is idle, call this inputhook.
47 When the eventloop of prompt-toolkit is idle, call this inputhook.
@@ -50,6 +51,7 b' def inputhook(context):'
50
51
51 :param context: An `InputHookContext` instance.
52 :param context: An `InputHookContext` instance.
52 """
53 """
54
53 def _main_quit(*a, **kw):
55 def _main_quit(*a, **kw):
54 gtk.main_quit()
56 gtk.main_quit()
55 return False
57 return False
@@ -3,10 +3,12 b''
3
3
4 from gi.repository import Gtk, GLib
4 from gi.repository import Gtk, GLib
5
5
6
6 def _main_quit(*args, **kwargs):
7 def _main_quit(*args, **kwargs):
7 Gtk.main_quit()
8 Gtk.main_quit()
8 return False
9 return False
9
10
11
10 def inputhook(context):
12 def inputhook(context):
11 GLib.io_add_watch(context.fileno(), GLib.PRIORITY_DEFAULT, GLib.IO_IN, _main_quit)
13 GLib.io_add_watch(context.fileno(), GLib.PRIORITY_DEFAULT, GLib.IO_IN, _main_quit)
12 Gtk.main()
14 Gtk.main()
@@ -782,7 +782,7 b' def _init_checker_class() -> Type["IPDoctestOutputChecker"]:'
782 precision = 0 if fraction is None else len(fraction)
782 precision = 0 if fraction is None else len(fraction)
783 if exponent is not None:
783 if exponent is not None:
784 precision -= int(exponent)
784 precision -= int(exponent)
785 if float(w.group()) == approx(float(g.group()), abs=10 ** -precision):
785 if float(w.group()) == approx(float(g.group()), abs=10**-precision):
786 # They're close enough. Replace the text we actually
786 # They're close enough. Replace the text we actually
787 # got with the text we want, so that it will match when we
787 # got with the text we want, so that it will match when we
788 # check the string literally.
788 # check the string literally.
@@ -1,2 +1,2 b''
1 x = 1
1 x = 1
2 print('x is:',x)
2 print("x is:", x)
@@ -1,2 +1,2 b''
1 # GENERATED BY setup.py
1 # GENERATED BY setup.py
2 commit = u""
2 commit = ""
@@ -2,7 +2,7 b''
2 """Decorators that don't go anywhere else.
2 """Decorators that don't go anywhere else.
3
3
4 This module contains misc. decorators that don't really go with another module
4 This module contains misc. decorators that don't really go with another module
5 in :mod:`IPython.utils`. Beore putting something here please see if it should
5 in :mod:`IPython.utils`. Before putting something here please see if it should
6 go into another topical module in :mod:`IPython.utils`.
6 go into another topical module in :mod:`IPython.utils`.
7 """
7 """
8
8
@@ -16,6 +16,10 b' go into another topical module in :mod:`IPython.utils`.'
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 from typing import Sequence
20
21 from IPython.utils.docs import GENERATING_DOCUMENTATION
22
19
23
20 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
21 # Code
25 # Code
@@ -48,6 +52,7 b' def flag_calls(func):'
48 wrapper.__doc__ = func.__doc__
52 wrapper.__doc__ = func.__doc__
49 return wrapper
53 return wrapper
50
54
55
51 def undoc(func):
56 def undoc(func):
52 """Mark a function or class as undocumented.
57 """Mark a function or class as undocumented.
53
58
@@ -56,3 +61,23 b' def undoc(func):'
56 """
61 """
57 return func
62 return func
58
63
64
65 def sphinx_options(
66 show_inheritance: bool = True,
67 show_inherited_members: bool = False,
68 exclude_inherited_from: Sequence[str] = tuple(),
69 ):
70 """Set sphinx options"""
71
72 def wrapper(func):
73 if not GENERATING_DOCUMENTATION:
74 return func
75
76 func._sphinx_options = dict(
77 show_inheritance=show_inheritance,
78 show_inherited_members=show_inherited_members,
79 exclude_inherited_from=exclude_inherited_from,
80 )
81 return func
82
83 return wrapper
@@ -62,15 +62,27 b' def _restore_term_title():'
62 pass
62 pass
63
63
64
64
65 _xterm_term_title_saved = False
66
67
65 def _set_term_title_xterm(title):
68 def _set_term_title_xterm(title):
66 """ Change virtual terminal title in xterm-workalikes """
69 """ Change virtual terminal title in xterm-workalikes """
67 # save the current title to the xterm "stack"
70 global _xterm_term_title_saved
68 sys.stdout.write('\033[22;0t')
71 # Only save the title the first time we set, otherwise restore will only
72 # go back one title (probably undoing a %cd title change).
73 if not _xterm_term_title_saved:
74 # save the current title to the xterm "stack"
75 sys.stdout.write("\033[22;0t")
76 _xterm_term_title_saved = True
69 sys.stdout.write('\033]0;%s\007' % title)
77 sys.stdout.write('\033]0;%s\007' % title)
70
78
71
79
72 def _restore_term_title_xterm():
80 def _restore_term_title_xterm():
81 # Make sure the restore has at least one accompanying set.
82 global _xterm_term_title_saved
83 assert _xterm_term_title_saved
73 sys.stdout.write('\033[23;0t')
84 sys.stdout.write('\033[23;0t')
85 _xterm_term_title_saved = False
74
86
75
87
76 if os.name == 'posix':
88 if os.name == 'posix':
@@ -19,7 +19,6 b' def test_base():'
19
19
20
20
21 def test_SubClass():
21 def test_SubClass():
22
23 class SubClass(Base):
22 class SubClass(Base):
24 y = 2
23 y = 2
25
24
@@ -53,7 +52,7 b' def test_misbehaving_object_without_trait_names():'
53
52
54 class SillierWithDir(MisbehavingGetattr):
53 class SillierWithDir(MisbehavingGetattr):
55 def __dir__(self):
54 def __dir__(self):
56 return ['some_method']
55 return ["some_method"]
57
56
58 for bad_klass in (MisbehavingGetattr, SillierWithDir):
57 for bad_klass in (MisbehavingGetattr, SillierWithDir):
59 obj = bad_klass()
58 obj = bad_klass()
@@ -13,8 +13,8 b''
13 .. image:: https://raster.shields.io/badge/Follows-NEP29-brightgreen.png
13 .. image:: https://raster.shields.io/badge/Follows-NEP29-brightgreen.png
14 :target: https://numpy.org/neps/nep-0029-deprecation_policy.html
14 :target: https://numpy.org/neps/nep-0029-deprecation_policy.html
15
15
16 .. image:: https://tidelift.com/subscription/pkg/pypi-ipython
16 .. image:: https://tidelift.com/badges/package/pypi/ipython?style=flat
17 :target: https://tidelift.com/badges/package/pypi/ipython?style=flat
17 :target: https://tidelift.com/subscription/pkg/pypi-ipython
18
18
19
19
20 ===========================================
20 ===========================================
@@ -41,6 +41,14 b' else:'
41 html_theme = "sphinx_rtd_theme"
41 html_theme = "sphinx_rtd_theme"
42 html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
42 html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
43
43
44 # Allow Python scripts to change behaviour during sphinx run
45 os.environ["IN_SPHINX_RUN"] = "True"
46
47 autodoc_type_aliases = {
48 "Matcher": " IPython.core.completer.Matcher",
49 "MatcherAPIv1": " IPython.core.completer.MatcherAPIv1",
50 }
51
44 # If your extensions are in another directory, add it here. If the directory
52 # If your extensions are in another directory, add it here. If the directory
45 # is relative to the documentation root, use os.path.abspath to make it
53 # is relative to the documentation root, use os.path.abspath to make it
46 # absolute, like shown here.
54 # absolute, like shown here.
@@ -69,7 +69,7 b' shell:'
69
69
70 /home/bob >>> # it works
70 /home/bob >>> # it works
71
71
72 See ``IPython/example/utils/cwd_prompt.py`` for an example of how to write an
72 See ``IPython/example/utils/cwd_prompt.py`` for an example of how to write
73 extensions to customise prompts.
73 extensions to customise prompts.
74
74
75 Inside IPython or in a startup script, you can use a custom prompts class
75 Inside IPython or in a startup script, you can use a custom prompts class
@@ -6,8 +6,7 b' IPython extensions'
6
6
7 A level above configuration are IPython extensions, Python modules which modify
7 A level above configuration are IPython extensions, Python modules which modify
8 the behaviour of the shell. They are referred to by an importable module name,
8 the behaviour of the shell. They are referred to by an importable module name,
9 and can be placed anywhere you'd normally import from, or in
9 and can be placed anywhere you'd normally import from.
10 ``.ipython/extensions/``.
11
10
12 Getting extensions
11 Getting extensions
13 ==================
12 ==================
@@ -71,10 +70,7 b' Useful :class:`InteractiveShell` methods include :meth:`~IPython.core.interactiv'
71 :ref:`defining_magics`
70 :ref:`defining_magics`
72
71
73 You can put your extension modules anywhere you want, as long as they can be
72 You can put your extension modules anywhere you want, as long as they can be
74 imported by Python's standard import mechanism. However, to make it easy to
73 imported by Python's standard import mechanism.
75 write extensions, you can also put your extensions in :file:`extensions/`
76 within the :ref:`IPython directory <ipythondir>`. This directory is
77 added to :data:`sys.path` automatically.
78
74
79 When your extension is ready for general use, please add it to the `extensions
75 When your extension is ready for general use, please add it to the `extensions
80 index <https://github.com/ipython/ipython/wiki/Extensions-Index>`_. We also
76 index <https://github.com/ipython/ipython/wiki/Extensions-Index>`_. We also
@@ -2,6 +2,70 b''
2 8.x Series
2 8.x Series
3 ============
3 ============
4
4
5 .. _version 8.6.0:
6
7 IPython 8.6.0
8 -------------
9
10 Back to a more regular release schedule (at least I try), as Friday is
11 already over by more than 24h hours. This is a slightly bigger release with a
12 few new features that contain no less then 25 PRs.
13
14 We'll notably found a couple of non negligible changes:
15
16 The ``install_ext`` and related functions have been removed after being
17 deprecated for years. You can use pip to install extensions. ``pip`` did not
18 exists when ``install_ext`` was introduced. You can still load local extensions
19 without installing them. Just set your ``sys.path`` for example. :ghpull:`13744`
20
21 IPython now have extra entry points that that the major *and minor* version of
22 python. For some of you this mean that you can do a quick ``ipython3.10`` to
23 launch IPython from the Python 3.10 interpreter, while still using Python 3.11
24 as your main Python. :ghpull:`13743`
25
26 The completer matcher API have been improved. See :ghpull:`13745`. This should
27 improve the type inference and improve dict keys completions in many use case.
28 Tanks ``@krassowski`` for all the works, and the D.E. Shaw group for sponsoring
29 it.
30
31 The color of error nodes in tracebacks can now be customized. See
32 :ghpull:`13756`. This is a private attribute until someone find the time to
33 properly add a configuration option. Note that with Python 3.11 that also show
34 the relevant nodes in traceback, it would be good to leverage this informations
35 (plus the "did you mean" info added on attribute errors). But that's likely work
36 I won't have time to do before long, so contributions welcome.
37
38 As we follow NEP 29, we removed support for numpy 1.19 :ghpull:`13760`.
39
40
41 The ``open()`` function present in the user namespace by default will now refuse
42 to open the file descriptors 0,1,2 (stdin, out, err), to avoid crashing IPython.
43 This mostly occurs in teaching context when incorrect values get passed around.
44
45
46 The ``?``, ``??``, and corresponding ``pinfo``, ``pinfo2`` magics can now find
47 objects insides arrays. That is to say, the following now works::
48
49
50 >>> def my_func(*arg, **kwargs):pass
51 >>> container = [my_func]
52 >>> container[0]?
53
54
55 If ``container`` define a custom ``getitem``, this __will__ trigger the custom
56 method. So don't put side effects in your ``getitems``. Thanks the D.E. Shaw
57 group for the request and sponsoring the work.
58
59
60 As usual you can find the full list of PRs on GitHub under `the 8.6 milestone
61 <https://github.com/ipython/ipython/pulls?q=milestone%3A8.6>`__.
62
63 Thanks to all hacktoberfest contributors, please contribute to
64 `closember.org <https://closember.org/>`__.
65
66 Thanks to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring
67 work on IPython and related libraries.
68
5 .. _version 8.5.0:
69 .. _version 8.5.0:
6
70
7 IPython 8.5.0
71 IPython 8.5.0
@@ -24,14 +24,9 b' import inspect'
24 import os
24 import os
25 import re
25 import re
26 from importlib import import_module
26 from importlib import import_module
27 from types import SimpleNamespace as Obj
27
28
28
29
29 class Obj(object):
30 '''Namespace to hold arbitrary information.'''
31 def __init__(self, **kwargs):
32 for k, v in kwargs.items():
33 setattr(self, k, v)
34
35 class FuncClsScanner(ast.NodeVisitor):
30 class FuncClsScanner(ast.NodeVisitor):
36 """Scan a module for top-level functions and classes.
31 """Scan a module for top-level functions and classes.
37
32
@@ -42,7 +37,7 b' class FuncClsScanner(ast.NodeVisitor):'
42 self.classes = []
37 self.classes = []
43 self.classes_seen = set()
38 self.classes_seen = set()
44 self.functions = []
39 self.functions = []
45
40
46 @staticmethod
41 @staticmethod
47 def has_undoc_decorator(node):
42 def has_undoc_decorator(node):
48 return any(isinstance(d, ast.Name) and d.id == 'undoc' \
43 return any(isinstance(d, ast.Name) and d.id == 'undoc' \
@@ -62,11 +57,15 b' class FuncClsScanner(ast.NodeVisitor):'
62 self.functions.append(node.name)
57 self.functions.append(node.name)
63
58
64 def visit_ClassDef(self, node):
59 def visit_ClassDef(self, node):
65 if not (node.name.startswith('_') or self.has_undoc_decorator(node)) \
60 if (
66 and node.name not in self.classes_seen:
61 not (node.name.startswith("_") or self.has_undoc_decorator(node))
67 cls = Obj(name=node.name)
62 and node.name not in self.classes_seen
68 cls.has_init = any(isinstance(n, ast.FunctionDef) and \
63 ):
69 n.name=='__init__' for n in node.body)
64 cls = Obj(name=node.name, sphinx_options={})
65 cls.has_init = any(
66 isinstance(n, ast.FunctionDef) and n.name == "__init__"
67 for n in node.body
68 )
70 self.classes.append(cls)
69 self.classes.append(cls)
71 self.classes_seen.add(node.name)
70 self.classes_seen.add(node.name)
72
71
@@ -221,7 +220,11 b' class ApiDocWriter(object):'
221 funcs, classes = [], []
220 funcs, classes = [], []
222 for name, obj in ns.items():
221 for name, obj in ns.items():
223 if inspect.isclass(obj):
222 if inspect.isclass(obj):
224 cls = Obj(name=name, has_init='__init__' in obj.__dict__)
223 cls = Obj(
224 name=name,
225 has_init="__init__" in obj.__dict__,
226 sphinx_options=getattr(obj, "_sphinx_options", {}),
227 )
225 classes.append(cls)
228 classes.append(cls)
226 elif inspect.isfunction(obj):
229 elif inspect.isfunction(obj):
227 funcs.append(name)
230 funcs.append(name)
@@ -279,10 +282,18 b' class ApiDocWriter(object):'
279 self.rst_section_levels[2] * len(subhead) + '\n'
282 self.rst_section_levels[2] * len(subhead) + '\n'
280
283
281 for c in classes:
284 for c in classes:
282 ad += '\n.. autoclass:: ' + c.name + '\n'
285 opts = c.sphinx_options
286 ad += "\n.. autoclass:: " + c.name + "\n"
283 # must NOT exclude from index to keep cross-refs working
287 # must NOT exclude from index to keep cross-refs working
284 ad += ' :members:\n' \
288 ad += " :members:\n"
285 ' :show-inheritance:\n'
289 if opts.get("show_inheritance", True):
290 ad += " :show-inheritance:\n"
291 if opts.get("show_inherited_members", False):
292 exclusions_list = opts.get("exclude_inherited_from", [])
293 exclusions = (
294 (" " + " ".join(exclusions_list)) if exclusions_list else ""
295 )
296 ad += f" :inherited-members:{exclusions}\n"
286 if c.has_init:
297 if c.has_init:
287 ad += '\n .. automethod:: __init__\n'
298 ad += '\n .. automethod:: __init__\n'
288
299
@@ -54,6 +54,7 b' doc ='
54 matplotlib
54 matplotlib
55 stack_data
55 stack_data
56 pytest<7
56 pytest<7
57 typing_extensions
57 %(test)s
58 %(test)s
58 kernel =
59 kernel =
59 ipykernel
60 ipykernel
@@ -78,7 +79,7 b' test_extra ='
78 curio
79 curio
79 matplotlib!=3.2.0
80 matplotlib!=3.2.0
80 nbformat
81 nbformat
81 numpy>=1.19
82 numpy>=1.20
82 pandas
83 pandas
83 trio
84 trio
84 all =
85 all =
@@ -211,14 +211,20 b' def find_entry_points():'
211 use, our own build_scripts_entrypt class below parses these and builds
211 use, our own build_scripts_entrypt class below parses these and builds
212 command line scripts.
212 command line scripts.
213
213
214 Each of our entry points gets both a plain name, e.g. ipython, and one
214 Each of our entry points gets a plain name, e.g. ipython, a name
215 suffixed with the Python major version number, e.g. ipython3.
215 suffixed with the Python major version number, e.g. ipython3, and
216 a name suffixed with the Python major.minor version number, eg. ipython3.8.
216 """
217 """
217 ep = [
218 ep = [
218 'ipython%s = IPython:start_ipython',
219 'ipython%s = IPython:start_ipython',
219 ]
220 ]
220 suffix = str(sys.version_info[0])
221 major_suffix = str(sys.version_info[0])
221 return [e % '' for e in ep] + [e % suffix for e in ep]
222 minor_suffix = ".".join([str(sys.version_info[0]), str(sys.version_info[1])])
223 return (
224 [e % "" for e in ep]
225 + [e % major_suffix for e in ep]
226 + [e % minor_suffix for e in ep]
227 )
222
228
223 class install_lib_symlink(Command):
229 class install_lib_symlink(Command):
224 user_options = [
230 user_options = [
@@ -340,7 +346,7 b' def git_prebuild(pkg_dir, build_cmd=build_py):'
340 out_file.writelines(
346 out_file.writelines(
341 [
347 [
342 "# GENERATED BY setup.py\n",
348 "# GENERATED BY setup.py\n",
343 'commit = u"%s"\n' % repo_commit,
349 'commit = "%s"\n' % repo_commit,
344 ]
350 ]
345 )
351 )
346
352
General Comments 0
You need to be logged in to leave comments. Login now