Show More
@@ -0,0 +1,7 b'' | |||
|
1 | Added shortcut for accepting auto suggestion | |
|
2 | ============================================ | |
|
3 | ||
|
4 | Added End key shortcut for accepting auto-suggestion | |
|
5 | This binding works in Vi mode too, provided | |
|
6 | TerminalInteractiveShell.emacs_bindings_in_vi_insert_mode is set to be True. | |
|
7 |
@@ -39,7 +39,7 b' jobs:' | |||
|
39 | 39 | - name: Install and update Python dependencies |
|
40 | 40 | run: | |
|
41 | 41 | python -m pip install --upgrade -e file://$PWD#egg=ipython[test] |
|
42 | # we must instal IPython after ipykernel to get the right versions. | |
|
42 | # we must install IPython after ipykernel to get the right versions. | |
|
43 | 43 | python -m pip install --upgrade --upgrade-strategy eager flaky ipyparallel |
|
44 | 44 | python -m pip install --upgrade 'pytest<7' |
|
45 | 45 | - name: pytest |
@@ -17,6 +17,7 b' jobs:' | |||
|
17 | 17 | test: |
|
18 | 18 | runs-on: ${{ matrix.os }} |
|
19 | 19 | strategy: |
|
20 | fail-fast: false | |
|
20 | 21 | matrix: |
|
21 | 22 | os: [ubuntu-latest, windows-latest] |
|
22 | 23 | python-version: ["3.8", "3.9", "3.10"] |
@@ -28,5 +28,13 b' __pycache__' | |||
|
28 | 28 | .pytest_cache |
|
29 | 29 | .python-version |
|
30 | 30 | venv*/ |
|
31 | .idea/ | |
|
32 | 31 | .mypy_cache/ |
|
32 | ||
|
33 | # jetbrains ide stuff | |
|
34 | *.iml | |
|
35 | .idea/ | |
|
36 | ||
|
37 | # vscode ide stuff | |
|
38 | *.code-workspace | |
|
39 | .history | |
|
40 | .vscode |
@@ -28,7 +28,7 b' import sys' | |||
|
28 | 28 | # Don't forget to also update setup.py when this changes! |
|
29 | 29 | if sys.version_info < (3, 8): |
|
30 | 30 | raise ImportError( |
|
31 | """ | |
|
31 | """ | |
|
32 | 32 | IPython 8+ supports Python 3.8 and above, following NEP 29. |
|
33 | 33 | When using Python 2.7, please install IPython 5.x LTS Long Term Support version. |
|
34 | 34 | Python 3.3 and 3.4 were supported up to IPython 6.x. |
@@ -40,7 +40,8 b' See IPython `README.rst` file for more information:' | |||
|
40 | 40 | |
|
41 | 41 | https://github.com/ipython/ipython/blob/master/README.rst |
|
42 | 42 | |
|
43 |
""" |
|
|
43 | """ | |
|
44 | ) | |
|
44 | 45 | |
|
45 | 46 | #----------------------------------------------------------------------------- |
|
46 | 47 | # Setup the top level names |
@@ -40,7 +40,7 b' class IPyAutocall(object):' | |||
|
40 | 40 | self._ip = ip |
|
41 | 41 | |
|
42 | 42 | def set_ip(self, ip): |
|
43 |
""" |
|
|
43 | """Will be used to set _ip point to current ipython instance b/f call | |
|
44 | 44 | |
|
45 | 45 | Override this method if you don't want this to happen. |
|
46 | 46 |
@@ -41,7 +41,7 b' or using unicode completion:' | |||
|
41 | 41 | |
|
42 | 42 | Only valid Python identifiers will complete. Combining characters (like arrow or |
|
43 | 43 | dots) are also available, unlike latex they need to be put after the their |
|
44 | counterpart that is to say, `F\\\\vec<tab>` is correct, not `\\\\vec<tab>F`. | |
|
44 | counterpart that is to say, ``F\\\\vec<tab>`` is correct, not ``\\\\vec<tab>F``. | |
|
45 | 45 | |
|
46 | 46 | Some browsers are known to display combining characters incorrectly. |
|
47 | 47 | |
@@ -50,7 +50,7 b' Backward latex completion' | |||
|
50 | 50 | |
|
51 | 51 | It is sometime challenging to know how to type a character, if you are using |
|
52 | 52 | IPython, or any compatible frontend you can prepend backslash to the character |
|
53 | and press `<tab>` to expand it to its latex form. | |
|
53 | and press ``<tab>`` to expand it to its latex form. | |
|
54 | 54 | |
|
55 | 55 | .. code:: |
|
56 | 56 | |
@@ -589,7 +589,7 b' class Completer(Configurable):' | |||
|
589 | 589 | |
|
590 | 590 | This will enable completion on elements of lists, results of function calls, etc., |
|
591 | 591 | but can be unsafe because the code is actually evaluated on TAB. |
|
592 | """ | |
|
592 | """, | |
|
593 | 593 | ).tag(config=True) |
|
594 | 594 | |
|
595 | 595 | use_jedi = Bool(default_value=JEDI_INSTALLED, |
@@ -1182,7 +1182,7 b' class IPCompleter(Completer):' | |||
|
1182 | 1182 | |
|
1183 | 1183 | # This is a list of names of unicode characters that can be completed |
|
1184 | 1184 | # into their corresponding unicode value. The list is large, so we |
|
1185 |
# lazil |
|
|
1185 | # lazily initialize it on first use. Consuming code should access this | |
|
1186 | 1186 | # attribute through the `@unicode_names` property. |
|
1187 | 1187 | self._unicode_names = None |
|
1188 | 1188 | |
@@ -2070,7 +2070,7 b' class IPCompleter(Completer):' | |||
|
2070 | 2070 | indexed. |
|
2071 | 2071 | line_buffer : optional, str |
|
2072 | 2072 | The current line the cursor is in, this is mostly due to legacy |
|
2073 |
reason that readline cou |
|
|
2073 | reason that readline could only give a us the single current line. | |
|
2074 | 2074 | Prefer `full_text`. |
|
2075 | 2075 | text : str |
|
2076 | 2076 | The current "token" the cursor is in, mostly also for historical |
@@ -2139,8 +2139,9 b' class IPCompleter(Completer):' | |||
|
2139 | 2139 | # different types of objects. The rlcomplete() method could then |
|
2140 | 2140 | # simply collapse the dict into a list for readline, but we'd have |
|
2141 | 2141 | # richer completion semantics in other environments. |
|
2142 | completions:Iterable[Any] = [] | |
|
2143 | if self.use_jedi: | |
|
2142 | is_magic_prefix = len(text) > 0 and text[0] == "%" | |
|
2143 | completions: Iterable[Any] = [] | |
|
2144 | if self.use_jedi and not is_magic_prefix: | |
|
2144 | 2145 | if not full_text: |
|
2145 | 2146 | full_text = line_buffer |
|
2146 | 2147 | completions = self._jedi_matches( |
@@ -101,7 +101,6 b' All the changes since then are under the same license as IPython.' | |||
|
101 | 101 | # |
|
102 | 102 | #***************************************************************************** |
|
103 | 103 | |
|
104 | import bdb | |
|
105 | 104 | import inspect |
|
106 | 105 | import linecache |
|
107 | 106 | import sys |
@@ -884,7 +884,7 b' class Image(DisplayObject):' | |||
|
884 | 884 | a URL, or a filename from which to load image data. |
|
885 | 885 | The result is always embedding image data for inline images. |
|
886 | 886 | |
|
887 | >>> Image('http://www.google.fr/images/srpr/logo3w.png') | |
|
887 | >>> Image('https://www.google.fr/images/srpr/logo3w.png') # doctest: +SKIP | |
|
888 | 888 | <IPython.core.display.Image object> |
|
889 | 889 | |
|
890 | 890 | >>> Image('/path/to/image.jpg') |
@@ -897,7 +897,7 b' class Image(DisplayObject):' | |||
|
897 | 897 | it only generates ``<img>`` tag with a link to the source. |
|
898 | 898 | This will not work in the qtconsole or offline. |
|
899 | 899 | |
|
900 | >>> Image(url='http://www.google.fr/images/srpr/logo3w.png') | |
|
900 | >>> Image(url='https://www.google.fr/images/srpr/logo3w.png') | |
|
901 | 901 | <IPython.core.display.Image object> |
|
902 | 902 | |
|
903 | 903 | """ |
@@ -166,7 +166,7 b' class HistoryAccessor(HistoryAccessorBase):' | |||
|
166 | 166 | in which case there will be no stored history, no SQLite connection, |
|
167 | 167 | and no background saving thread. This may be necessary in some |
|
168 | 168 | threaded environments where IPython is embedded. |
|
169 | """ | |
|
169 | """, | |
|
170 | 170 | ).tag(config=True) |
|
171 | 171 | |
|
172 | 172 | connection_options = Dict( |
@@ -296,8 +296,8 b' class HistoryAccessor(HistoryAccessorBase):' | |||
|
296 | 296 | toget = "history.%s, output_history.output" % toget |
|
297 | 297 | if latest: |
|
298 | 298 | toget += ", MAX(session * 128 * 1024 + line)" |
|
299 |
|
|
|
300 | (toget, sqlfrom) + sql, params) | |
|
299 | this_querry = "SELECT session, line, %s FROM %s " % (toget, sqlfrom) + sql | |
|
300 | cur = self.db.execute(this_querry, params) | |
|
301 | 301 | if latest: |
|
302 | 302 | cur = (row[:-1] for row in cur) |
|
303 | 303 | if output: # Regroup into 3-tuples, and parse JSON |
@@ -344,6 +344,11 b' class HistoryAccessor(HistoryAccessorBase):' | |||
|
344 | 344 | def get_tail(self, n=10, raw=True, output=False, include_latest=False): |
|
345 | 345 | """Get the last n lines from the history database. |
|
346 | 346 | |
|
347 | Most recent entry last. | |
|
348 | ||
|
349 | Completion will be reordered so that that the last ones are when | |
|
350 | possible from current session. | |
|
351 | ||
|
347 | 352 | Parameters |
|
348 | 353 | ---------- |
|
349 | 354 | n : int |
@@ -362,11 +367,31 b' class HistoryAccessor(HistoryAccessorBase):' | |||
|
362 | 367 | self.writeout_cache() |
|
363 | 368 | if not include_latest: |
|
364 | 369 | n += 1 |
|
365 | cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?", | |
|
366 | (n,), raw=raw, output=output) | |
|
370 | # cursor/line/entry | |
|
371 | this_cur = list( | |
|
372 | self._run_sql( | |
|
373 | "WHERE session == ? ORDER BY line DESC LIMIT ? ", | |
|
374 | (self.session_number, n), | |
|
375 | raw=raw, | |
|
376 | output=output, | |
|
377 | ) | |
|
378 | ) | |
|
379 | other_cur = list( | |
|
380 | self._run_sql( | |
|
381 | "WHERE session != ? ORDER BY session DESC, line DESC LIMIT ?", | |
|
382 | (self.session_number, n), | |
|
383 | raw=raw, | |
|
384 | output=output, | |
|
385 | ) | |
|
386 | ) | |
|
387 | ||
|
388 | everything = this_cur + other_cur | |
|
389 | ||
|
390 | everything = everything[:n] | |
|
391 | ||
|
367 | 392 | if not include_latest: |
|
368 |
return |
|
|
369 |
return |
|
|
393 | return list(everything)[:0:-1] | |
|
394 | return list(everything)[::-1] | |
|
370 | 395 | |
|
371 | 396 | @catch_corrupt_db |
|
372 | 397 | def search(self, pattern="*", raw=True, search_raw=True, |
@@ -193,7 +193,7 b' def assemble_logical_lines():' | |||
|
193 | 193 | line = ''.join(parts) |
|
194 | 194 | |
|
195 | 195 | # Utilities |
|
196 |
def _make_help_call(target, esc, lspace |
|
|
196 | def _make_help_call(target, esc, lspace): | |
|
197 | 197 | """Prepares a pinfo(2)/psearch call from a target name and the escape |
|
198 | 198 | (i.e. ? or ??)""" |
|
199 | 199 | method = 'pinfo2' if esc == '??' \ |
@@ -203,12 +203,13 b' def _make_help_call(target, esc, lspace, next_input=None):' | |||
|
203 | 203 | #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args) |
|
204 | 204 | t_magic_name, _, t_magic_arg_s = arg.partition(' ') |
|
205 | 205 | t_magic_name = t_magic_name.lstrip(ESC_MAGIC) |
|
206 | if next_input is None: | |
|
207 | return '%sget_ipython().run_line_magic(%r, %r)' % (lspace, t_magic_name, t_magic_arg_s) | |
|
208 | else: | |
|
209 | return '%sget_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \ | |
|
210 | (lspace, next_input, t_magic_name, t_magic_arg_s) | |
|
211 | ||
|
206 | return "%sget_ipython().run_line_magic(%r, %r)" % ( | |
|
207 | lspace, | |
|
208 | t_magic_name, | |
|
209 | t_magic_arg_s, | |
|
210 | ) | |
|
211 | ||
|
212 | ||
|
212 | 213 | # These define the transformations for the different escape characters. |
|
213 | 214 | def _tr_system(line_info): |
|
214 | 215 | "Translate lines escaped with: !" |
@@ -349,10 +350,7 b' def help_end(line):' | |||
|
349 | 350 | esc = m.group(3) |
|
350 | 351 | lspace = _initial_space_re.match(line).group(0) |
|
351 | 352 | |
|
352 | # If we're mid-command, put it back on the next prompt for the user. | |
|
353 | next_input = line.rstrip('?') if line.strip() != m.group(0) else None | |
|
354 | ||
|
355 | return _make_help_call(target, esc, lspace, next_input) | |
|
353 | return _make_help_call(target, esc, lspace) | |
|
356 | 354 | |
|
357 | 355 | |
|
358 | 356 | @CoroutineInputTransformer.wrap |
@@ -325,7 +325,7 b" ESC_PAREN = '/' # Call first argument with rest of line as arguments" | |||
|
325 | 325 | ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'} |
|
326 | 326 | ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately |
|
327 | 327 | |
|
328 |
def _make_help_call(target, esc |
|
|
328 | def _make_help_call(target, esc): | |
|
329 | 329 | """Prepares a pinfo(2)/psearch call from a target name and the escape |
|
330 | 330 | (i.e. ? or ??)""" |
|
331 | 331 | method = 'pinfo2' if esc == '??' \ |
@@ -335,11 +335,8 b' def _make_help_call(target, esc, next_input=None):' | |||
|
335 | 335 | #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args) |
|
336 | 336 | t_magic_name, _, t_magic_arg_s = arg.partition(' ') |
|
337 | 337 | t_magic_name = t_magic_name.lstrip(ESC_MAGIC) |
|
338 | if next_input is None: | |
|
339 | return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s) | |
|
340 | else: | |
|
341 | return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \ | |
|
342 | (next_input, t_magic_name, t_magic_arg_s) | |
|
338 | return "get_ipython().run_line_magic(%r, %r)" % (t_magic_name, t_magic_arg_s) | |
|
339 | ||
|
343 | 340 | |
|
344 | 341 | def _tr_help(content): |
|
345 | 342 | """Translate lines escaped with: ? |
@@ -480,13 +477,8 b' class HelpEnd(TokenTransformBase):' | |||
|
480 | 477 | target = m.group(1) |
|
481 | 478 | esc = m.group(3) |
|
482 | 479 | |
|
483 | # If we're mid-command, put it back on the next prompt for the user. | |
|
484 | next_input = None | |
|
485 | if (not lines_before) and (not lines_after) \ | |
|
486 | and content.strip() != m.group(0): | |
|
487 | next_input = content.rstrip('?\n') | |
|
488 | 480 | |
|
489 |
call = _make_help_call(target, esc |
|
|
481 | call = _make_help_call(target, esc) | |
|
490 | 482 | new_line = indent + call + '\n' |
|
491 | 483 | |
|
492 | 484 | return lines_before + [new_line] + lines_after |
@@ -14,40 +14,61 b'' | |||
|
14 | 14 | import abc |
|
15 | 15 | import ast |
|
16 | 16 | import atexit |
|
17 | import bdb | |
|
17 | 18 | import builtins as builtin_mod |
|
19 | import dis | |
|
18 | 20 | import functools |
|
19 | 21 | import inspect |
|
20 | 22 | import os |
|
21 | 23 | import re |
|
22 | 24 | import runpy |
|
25 | import subprocess | |
|
23 | 26 | import sys |
|
24 | 27 | import tempfile |
|
25 | 28 | import traceback |
|
26 | 29 | import types |
|
27 | import subprocess | |
|
28 | 30 | import warnings |
|
31 | from ast import stmt | |
|
29 | 32 | from io import open as io_open |
|
30 | ||
|
33 | from logging import error | |
|
31 | 34 | from pathlib import Path |
|
32 | from pickleshare import PickleShareDB | |
|
35 | from typing import Callable | |
|
36 | from typing import List as ListType | |
|
37 | from typing import Optional, Tuple | |
|
38 | from warnings import warn | |
|
33 | 39 | |
|
40 | from pickleshare import PickleShareDB | |
|
41 | from tempfile import TemporaryDirectory | |
|
42 | from traitlets import ( | |
|
43 | Any, | |
|
44 | Bool, | |
|
45 | CaselessStrEnum, | |
|
46 | Dict, | |
|
47 | Enum, | |
|
48 | Instance, | |
|
49 | Integer, | |
|
50 | List, | |
|
51 | Type, | |
|
52 | Unicode, | |
|
53 | default, | |
|
54 | observe, | |
|
55 | validate, | |
|
56 | ) | |
|
34 | 57 | from traitlets.config.configurable import SingletonConfigurable |
|
35 | 58 | from traitlets.utils.importstring import import_item |
|
36 | from IPython.core import oinspect | |
|
37 | from IPython.core import magic | |
|
38 | from IPython.core import page | |
|
39 | from IPython.core import prefilter | |
|
40 | from IPython.core import ultratb | |
|
59 | ||
|
60 | import IPython.core.hooks | |
|
61 | from IPython.core import magic, oinspect, page, prefilter, ultratb | |
|
41 | 62 | from IPython.core.alias import Alias, AliasManager |
|
42 | 63 | from IPython.core.autocall import ExitAutocall |
|
43 | 64 | from IPython.core.builtin_trap import BuiltinTrap |
|
44 | from IPython.core.events import EventManager, available_events | |
|
45 | 65 | from IPython.core.compilerop import CachingCompiler, check_linecache_ipython |
|
46 | 66 | from IPython.core.debugger import InterruptiblePdb |
|
47 | 67 | from IPython.core.display_trap import DisplayTrap |
|
48 | 68 | from IPython.core.displayhook import DisplayHook |
|
49 | 69 | from IPython.core.displaypub import DisplayPublisher |
|
50 | 70 | from IPython.core.error import InputRejected, UsageError |
|
71 | from IPython.core.events import EventManager, available_events | |
|
51 | 72 | from IPython.core.extensions import ExtensionManager |
|
52 | 73 | from IPython.core.formatters import DisplayFormatter |
|
53 | 74 | from IPython.core.history import HistoryManager |
@@ -59,31 +80,17 b' from IPython.core.prefilter import PrefilterManager' | |||
|
59 | 80 | from IPython.core.profiledir import ProfileDir |
|
60 | 81 | from IPython.core.usage import default_banner |
|
61 | 82 | from IPython.display import display |
|
83 | from IPython.paths import get_ipython_dir | |
|
62 | 84 | from IPython.testing.skipdoctest import skip_doctest |
|
63 | from IPython.utils import PyColorize | |
|
64 | from IPython.utils import io | |
|
65 | from IPython.utils import py3compat | |
|
66 | from IPython.utils import openpy | |
|
85 | from IPython.utils import PyColorize, io, openpy, py3compat | |
|
67 | 86 | from IPython.utils.decorators import undoc |
|
68 | 87 | from IPython.utils.io import ask_yes_no |
|
69 | 88 | from IPython.utils.ipstruct import Struct |
|
70 | from IPython.paths import get_ipython_dir | |
|
71 | from IPython.utils.path import get_home_dir, get_py_filename, ensure_dir_exists | |
|
72 | from IPython.utils.process import system, getoutput | |
|
89 | from IPython.utils.path import ensure_dir_exists, get_home_dir, get_py_filename | |
|
90 | from IPython.utils.process import getoutput, system | |
|
73 | 91 | from IPython.utils.strdispatch import StrDispatch |
|
74 | 92 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
75 |
from IPython.utils.text import |
|
|
76 | from IPython.utils.tempdir import TemporaryDirectory | |
|
77 | from traitlets import ( | |
|
78 | Integer, Bool, CaselessStrEnum, Enum, List, Dict, Unicode, Instance, Type, | |
|
79 | observe, default, validate, Any | |
|
80 | ) | |
|
81 | from warnings import warn | |
|
82 | from logging import error | |
|
83 | import IPython.core.hooks | |
|
84 | ||
|
85 | from typing import List as ListType, Tuple, Optional, Callable | |
|
86 | from ast import stmt | |
|
93 | from IPython.utils.text import DollarFormatter, LSString, SList, format_screen | |
|
87 | 94 | |
|
88 | 95 | sphinxify: Optional[Callable] |
|
89 | 96 | |
@@ -122,8 +129,13 b' _single_targets_nodes = (ast.AugAssign, ast.AnnAssign)' | |||
|
122 | 129 | |
|
123 | 130 | # we still need to run things using the asyncio eventloop, but there is no |
|
124 | 131 | # async integration |
|
125 | from .async_helpers import _asyncio_runner, _pseudo_sync_runner | |
|
126 | from .async_helpers import _curio_runner, _trio_runner, _should_be_async | |
|
132 | from .async_helpers import ( | |
|
133 | _asyncio_runner, | |
|
134 | _curio_runner, | |
|
135 | _pseudo_sync_runner, | |
|
136 | _should_be_async, | |
|
137 | _trio_runner, | |
|
138 | ) | |
|
127 | 139 | |
|
128 | 140 | #----------------------------------------------------------------------------- |
|
129 | 141 | # Globals |
@@ -188,19 +200,29 b' class ExecutionInfo(object):' | |||
|
188 | 200 | store_history = False |
|
189 | 201 | silent = False |
|
190 | 202 | shell_futures = True |
|
203 | cell_id = None | |
|
191 | 204 | |
|
192 | def __init__(self, raw_cell, store_history, silent, shell_futures): | |
|
205 | def __init__(self, raw_cell, store_history, silent, shell_futures, cell_id): | |
|
193 | 206 | self.raw_cell = raw_cell |
|
194 | 207 | self.store_history = store_history |
|
195 | 208 | self.silent = silent |
|
196 | 209 | self.shell_futures = shell_futures |
|
210 | self.cell_id = cell_id | |
|
197 | 211 | |
|
198 | 212 | def __repr__(self): |
|
199 | 213 | name = self.__class__.__qualname__ |
|
200 | raw_cell = ((self.raw_cell[:50] + '..') | |
|
201 |
|
|
|
202 | return '<%s object at %x, raw_cell="%s" store_history=%s silent=%s shell_futures=%s>' %\ | |
|
203 | (name, id(self), raw_cell, self.store_history, self.silent, self.shell_futures) | |
|
214 | raw_cell = ( | |
|
215 | (self.raw_cell[:50] + "..") if len(self.raw_cell) > 50 else self.raw_cell | |
|
216 | ) | |
|
217 | return '<%s object at %x, raw_cell="%s" store_history=%s silent=%s shell_futures=%s cell_id=%s>' % ( | |
|
218 | name, | |
|
219 | id(self), | |
|
220 | raw_cell, | |
|
221 | self.store_history, | |
|
222 | self.silent, | |
|
223 | self.shell_futures, | |
|
224 | self.cell_id, | |
|
225 | ) | |
|
204 | 226 | |
|
205 | 227 | |
|
206 | 228 | class ExecutionResult(object): |
@@ -747,6 +769,33 b' class InteractiveShell(SingletonConfigurable):' | |||
|
747 | 769 | # the appropriate time. |
|
748 | 770 | self.display_trap = DisplayTrap(hook=self.displayhook) |
|
749 | 771 | |
|
772 | @staticmethod | |
|
773 | def get_path_links(p: Path): | |
|
774 | """Gets path links including all symlinks | |
|
775 | ||
|
776 | Examples | |
|
777 | -------- | |
|
778 | In [1]: from IPython.core.interactiveshell import InteractiveShell | |
|
779 | ||
|
780 | In [2]: import sys, pathlib | |
|
781 | ||
|
782 | In [3]: paths = InteractiveShell.get_path_links(pathlib.Path(sys.executable)) | |
|
783 | ||
|
784 | In [4]: len(paths) == len(set(paths)) | |
|
785 | Out[4]: True | |
|
786 | ||
|
787 | In [5]: bool(paths) | |
|
788 | Out[5]: True | |
|
789 | """ | |
|
790 | paths = [p] | |
|
791 | while p.is_symlink(): | |
|
792 | new_path = Path(os.readlink(p)) | |
|
793 | if not new_path.is_absolute(): | |
|
794 | new_path = p.parent / new_path | |
|
795 | p = new_path | |
|
796 | paths.append(p) | |
|
797 | return paths | |
|
798 | ||
|
750 | 799 | def init_virtualenv(self): |
|
751 | 800 | """Add the current virtualenv to sys.path so the user can import modules from it. |
|
752 | 801 | This isn't perfect: it doesn't use the Python interpreter with which the |
@@ -772,10 +821,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
772 | 821 | # stdlib venv may symlink sys.executable, so we can't use realpath. |
|
773 | 822 | # but others can symlink *to* the venv Python, so we can't just use sys.executable. |
|
774 | 823 | # So we just check every item in the symlink tree (generally <= 3) |
|
775 | paths = [p] | |
|
776 | while p.is_symlink(): | |
|
777 | p = Path(os.readlink(p)) | |
|
778 | paths.append(p.resolve()) | |
|
824 | paths = self.get_path_links(p) | |
|
779 | 825 | |
|
780 | 826 | # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible |
|
781 | 827 | if p_venv.parts[1] == "cygdrive": |
@@ -1941,10 +1987,19 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1941 | 1987 | # Exception classes can customise their traceback - we |
|
1942 | 1988 | # use this in IPython.parallel for exceptions occurring |
|
1943 | 1989 | # in the engines. This should return a list of strings. |
|
1944 |
|
|
|
1990 | if hasattr(value, "_render_traceback_"): | |
|
1991 | stb = value._render_traceback_() | |
|
1992 | else: | |
|
1993 | stb = self.InteractiveTB.structured_traceback( | |
|
1994 | etype, value, tb, tb_offset=tb_offset | |
|
1995 | ) | |
|
1996 | ||
|
1945 | 1997 | except Exception: |
|
1946 | stb = self.InteractiveTB.structured_traceback(etype, | |
|
1947 | value, tb, tb_offset=tb_offset) | |
|
1998 | print( | |
|
1999 | "Unexpected exception formatting exception. Falling back to standard exception" | |
|
2000 | ) | |
|
2001 | traceback.print_exc() | |
|
2002 | return None | |
|
1948 | 2003 | |
|
1949 | 2004 | self._showtraceback(etype, value, stb) |
|
1950 | 2005 | if self.call_pdb: |
@@ -2034,8 +2089,12 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2034 | 2089 | (typically over the network by remote frontends). |
|
2035 | 2090 | """ |
|
2036 | 2091 | from IPython.core.completer import IPCompleter |
|
2037 |
from IPython.core.completerlib import ( |
|
|
2038 | magic_run_completer, cd_completer, reset_completer) | |
|
2092 | from IPython.core.completerlib import ( | |
|
2093 | cd_completer, | |
|
2094 | magic_run_completer, | |
|
2095 | module_completer, | |
|
2096 | reset_completer, | |
|
2097 | ) | |
|
2039 | 2098 | |
|
2040 | 2099 | self.Completer = IPCompleter(shell=self, |
|
2041 | 2100 | namespace=self.user_ns, |
@@ -2414,14 +2473,9 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2414 | 2473 | cmd = self.var_expand(cmd, depth=1) |
|
2415 | 2474 | # warn if there is an IPython magic alternative. |
|
2416 | 2475 | main_cmd = cmd.split()[0] |
|
2417 |
has_magic_alternatives = ("pip", "conda", "cd" |
|
|
2476 | has_magic_alternatives = ("pip", "conda", "cd") | |
|
2418 | 2477 | |
|
2419 | # had to check if the command was an alias expanded because of `ls` | |
|
2420 | is_alias_expanded = self.alias_manager.is_alias(main_cmd) and ( | |
|
2421 | self.alias_manager.retrieve_alias(main_cmd).strip() == cmd.strip() | |
|
2422 | ) | |
|
2423 | ||
|
2424 | if main_cmd in has_magic_alternatives and not is_alias_expanded: | |
|
2478 | if main_cmd in has_magic_alternatives: | |
|
2425 | 2479 | warnings.warn( |
|
2426 | 2480 | ( |
|
2427 | 2481 | "You executed the system command !{0} which may not work " |
@@ -2791,7 +2845,14 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2791 | 2845 | self.showtraceback() |
|
2792 | 2846 | warn('Unknown failure executing module: <%s>' % mod_name) |
|
2793 | 2847 | |
|
2794 | def run_cell(self, raw_cell, store_history=False, silent=False, shell_futures=True): | |
|
2848 | def run_cell( | |
|
2849 | self, | |
|
2850 | raw_cell, | |
|
2851 | store_history=False, | |
|
2852 | silent=False, | |
|
2853 | shell_futures=True, | |
|
2854 | cell_id=None, | |
|
2855 | ): | |
|
2795 | 2856 | """Run a complete IPython cell. |
|
2796 | 2857 | |
|
2797 | 2858 | Parameters |
@@ -2818,14 +2879,22 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2818 | 2879 | result = None |
|
2819 | 2880 | try: |
|
2820 | 2881 | result = self._run_cell( |
|
2821 |
raw_cell, store_history, silent, shell_futures |
|
|
2882 | raw_cell, store_history, silent, shell_futures, cell_id | |
|
2883 | ) | |
|
2822 | 2884 | finally: |
|
2823 | 2885 | self.events.trigger('post_execute') |
|
2824 | 2886 | if not silent: |
|
2825 | 2887 | self.events.trigger('post_run_cell', result) |
|
2826 | 2888 | return result |
|
2827 | 2889 | |
|
2828 | def _run_cell(self, raw_cell:str, store_history:bool, silent:bool, shell_futures:bool) -> ExecutionResult: | |
|
2890 | def _run_cell( | |
|
2891 | self, | |
|
2892 | raw_cell: str, | |
|
2893 | store_history: bool, | |
|
2894 | silent: bool, | |
|
2895 | shell_futures: bool, | |
|
2896 | cell_id: str, | |
|
2897 | ) -> ExecutionResult: | |
|
2829 | 2898 | """Internal method to run a complete IPython cell.""" |
|
2830 | 2899 | |
|
2831 | 2900 | # we need to avoid calling self.transform_cell multiple time on the same thing |
@@ -2845,6 +2914,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2845 | 2914 | shell_futures=shell_futures, |
|
2846 | 2915 | transformed_cell=transformed_cell, |
|
2847 | 2916 | preprocessing_exc_tuple=preprocessing_exc_tuple, |
|
2917 | cell_id=cell_id, | |
|
2848 | 2918 | ) |
|
2849 | 2919 | |
|
2850 | 2920 | # run_cell_async is async, but may not actually need an eventloop. |
@@ -2865,7 +2935,9 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2865 | 2935 | try: |
|
2866 | 2936 | return runner(coro) |
|
2867 | 2937 | except BaseException as e: |
|
2868 | info = ExecutionInfo(raw_cell, store_history, silent, shell_futures) | |
|
2938 | info = ExecutionInfo( | |
|
2939 | raw_cell, store_history, silent, shell_futures, cell_id | |
|
2940 | ) | |
|
2869 | 2941 | result = ExecutionResult(info) |
|
2870 | 2942 | result.error_in_exec = e |
|
2871 | 2943 | self.showtraceback(running_compiled_code=True) |
@@ -2921,7 +2993,8 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2921 | 2993 | shell_futures=True, |
|
2922 | 2994 | *, |
|
2923 | 2995 | transformed_cell: Optional[str] = None, |
|
2924 | preprocessing_exc_tuple: Optional[Any] = None | |
|
2996 | preprocessing_exc_tuple: Optional[Any] = None, | |
|
2997 | cell_id=None, | |
|
2925 | 2998 | ) -> ExecutionResult: |
|
2926 | 2999 | """Run a complete IPython cell asynchronously. |
|
2927 | 3000 | |
@@ -2952,8 +3025,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2952 | 3025 | |
|
2953 | 3026 | .. versionadded:: 7.0 |
|
2954 | 3027 | """ |
|
2955 | info = ExecutionInfo( | |
|
2956 | raw_cell, store_history, silent, shell_futures) | |
|
3028 | info = ExecutionInfo(raw_cell, store_history, silent, shell_futures, cell_id) | |
|
2957 | 3029 | result = ExecutionResult(info) |
|
2958 | 3030 | |
|
2959 | 3031 | if (not raw_cell) or raw_cell.isspace(): |
@@ -3008,9 +3080,8 b' class InteractiveShell(SingletonConfigurable):' | |||
|
3008 | 3080 | cell = raw_cell |
|
3009 | 3081 | |
|
3010 | 3082 | # Store raw and processed history |
|
3011 | if store_history: | |
|
3012 | self.history_manager.store_inputs(self.execution_count, | |
|
3013 | cell, raw_cell) | |
|
3083 | if store_history and raw_cell.strip(" %") != "paste": | |
|
3084 | self.history_manager.store_inputs(self.execution_count, cell, raw_cell) | |
|
3014 | 3085 | if not silent: |
|
3015 | 3086 | self.logger.log(cell, raw_cell) |
|
3016 | 3087 | |
@@ -3141,6 +3212,29 b' class InteractiveShell(SingletonConfigurable):' | |||
|
3141 | 3212 | ast.fix_missing_locations(node) |
|
3142 | 3213 | return node |
|
3143 | 3214 | |
|
3215 | def _update_code_co_name(self, code): | |
|
3216 | """Python 3.10 changed the behaviour so that whenever a code object | |
|
3217 | is assembled in the compile(ast) the co_firstlineno would be == 1. | |
|
3218 | ||
|
3219 | This makes pydevd/debugpy think that all cells invoked are the same | |
|
3220 | since it caches information based on (co_firstlineno, co_name, co_filename). | |
|
3221 | ||
|
3222 | Given that, this function changes the code 'co_name' to be unique | |
|
3223 | based on the first real lineno of the code (which also has a nice | |
|
3224 | side effect of customizing the name so that it's not always <module>). | |
|
3225 | ||
|
3226 | See: https://github.com/ipython/ipykernel/issues/841 | |
|
3227 | """ | |
|
3228 | if not hasattr(code, "replace"): | |
|
3229 | # It may not be available on older versions of Python (only | |
|
3230 | # available for 3.8 onwards). | |
|
3231 | return code | |
|
3232 | try: | |
|
3233 | first_real_line = next(dis.findlinestarts(code))[1] | |
|
3234 | except StopIteration: | |
|
3235 | return code | |
|
3236 | return code.replace(co_name="<cell line: %s>" % (first_real_line,)) | |
|
3237 | ||
|
3144 | 3238 | async def run_ast_nodes( |
|
3145 | 3239 | self, |
|
3146 | 3240 | nodelist: ListType[stmt], |
@@ -3239,6 +3333,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
3239 | 3333 | else 0x0 |
|
3240 | 3334 | ): |
|
3241 | 3335 | code = compiler(mod, cell_name, mode) |
|
3336 | code = self._update_code_co_name(code) | |
|
3242 | 3337 | asy = compare(code) |
|
3243 | 3338 | if await self.run_code(code, result, async_=asy): |
|
3244 | 3339 | return True |
@@ -3309,6 +3404,11 b' class InteractiveShell(SingletonConfigurable):' | |||
|
3309 | 3404 | result.error_in_exec = e |
|
3310 | 3405 | self.showtraceback(exception_only=True) |
|
3311 | 3406 | warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1) |
|
3407 | except bdb.BdbQuit: | |
|
3408 | etype, value, tb = sys.exc_info() | |
|
3409 | if result is not None: | |
|
3410 | result.error_in_exec = value | |
|
3411 | # the BdbQuit stops here | |
|
3312 | 3412 | except self.custom_exceptions: |
|
3313 | 3413 | etype, value, tb = sys.exc_info() |
|
3314 | 3414 | if result is not None: |
@@ -3375,8 +3475,9 b' class InteractiveShell(SingletonConfigurable):' | |||
|
3375 | 3475 | make sense in all contexts, for example a terminal ipython can't |
|
3376 | 3476 | display figures inline. |
|
3377 | 3477 | """ |
|
3378 | from IPython.core import pylabtools as pt | |
|
3379 | 3478 | from matplotlib_inline.backend_inline import configure_inline_support |
|
3479 | ||
|
3480 | from IPython.core import pylabtools as pt | |
|
3380 | 3481 | gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select) |
|
3381 | 3482 | |
|
3382 | 3483 | if gui != 'inline': |
@@ -3679,6 +3780,10 b' class InteractiveShell(SingletonConfigurable):' | |||
|
3679 | 3780 | pass |
|
3680 | 3781 | del self.tempdirs |
|
3681 | 3782 | |
|
3783 | # Restore user's cursor | |
|
3784 | if hasattr(self, "editing_mode") and self.editing_mode == "vi": | |
|
3785 | sys.stdout.write("\x1b[0 q") | |
|
3786 | sys.stdout.flush() | |
|
3682 | 3787 | |
|
3683 | 3788 | def cleanup(self): |
|
3684 | 3789 | self.restore_sys_module_state() |
@@ -311,7 +311,7 b' class MagicsManager(Configurable):' | |||
|
311 | 311 | |
|
312 | 312 | For example:: |
|
313 | 313 | |
|
314 | c.MagicsManger.lazy_magics = { | |
|
314 | c.MagicsManager.lazy_magics = { | |
|
315 | 315 | "my_magic": "slow.to.import", |
|
316 | 316 | "my_other_magic": "also.slow", |
|
317 | 317 | } |
@@ -511,7 +511,7 b' class ExecutionMagics(Magics):' | |||
|
511 | 511 | """Run the named file inside IPython as a program. |
|
512 | 512 | |
|
513 | 513 | Usage:: |
|
514 | ||
|
514 | ||
|
515 | 515 | %run [-n -i -e -G] |
|
516 | 516 | [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )] |
|
517 | 517 | ( -m mod | filename ) [args] |
@@ -552,7 +552,7 b' class ExecutionMagics(Magics):' | |||
|
552 | 552 | *two* back slashes (e.g. ``\\\\*``) to suppress expansions. |
|
553 | 553 | To completely disable these expansions, you can use -G flag. |
|
554 | 554 | |
|
555 |
On Windows systems, the use of single quotes `'` when specifying |
|
|
555 | On Windows systems, the use of single quotes `'` when specifying | |
|
556 | 556 | a file is not supported. Use double quotes `"`. |
|
557 | 557 | |
|
558 | 558 | Options: |
@@ -127,7 +127,7 b' class OSMagics(Magics):' | |||
|
127 | 127 | Aliases expand Python variables just like system calls using ! or !! |
|
128 | 128 | do: all expressions prefixed with '$' get expanded. For details of |
|
129 | 129 | the semantic rules, see PEP-215: |
|
130 |
http |
|
|
130 | https://peps.python.org/pep-0215/. This is the library used by | |
|
131 | 131 | IPython for variable expansion. If you want to access a true shell |
|
132 | 132 | variable, an extra $ is necessary to prevent its expansion by |
|
133 | 133 | IPython:: |
@@ -58,8 +58,8 b' def script_args(f):' | |||
|
58 | 58 | '--no-raise-error', action="store_false", dest='raise_error', |
|
59 | 59 | help="""Whether you should raise an error message in addition to |
|
60 | 60 | a stream on stderr if you get a nonzero exit code. |
|
61 | """ | |
|
62 | ) | |
|
61 | """, | |
|
62 | ), | |
|
63 | 63 | ] |
|
64 | 64 | for arg in args: |
|
65 | 65 | f = arg(f) |
@@ -518,12 +518,12 b' class Inspector(Colorable):' | |||
|
518 | 518 | """Return a mime bundle representation of the input text. |
|
519 | 519 | |
|
520 | 520 | - if `formatter` is None, the returned mime bundle has |
|
521 | a `text/plain` field, with the input text. | |
|
522 | a `text/html` field with a `<pre>` tag containing the input text. | |
|
521 | a ``text/plain`` field, with the input text. | |
|
522 | a ``text/html`` field with a ``<pre>`` tag containing the input text. | |
|
523 | 523 | |
|
524 | - if `formatter` is not None, it must be a callable transforming the | |
|
525 | input text into a mime bundle. Default values for `text/plain` and | |
|
526 | `text/html` representations are the ones described above. | |
|
524 | - if ``formatter`` is not None, it must be a callable transforming the | |
|
525 | input text into a mime bundle. Default values for ``text/plain`` and | |
|
526 | ``text/html`` representations are the ones described above. | |
|
527 | 527 | |
|
528 | 528 | Note: |
|
529 | 529 |
@@ -16,7 +16,7 b'' | |||
|
16 | 16 | # release. 'dev' as a _version_extra string means this is a development |
|
17 | 17 | # version |
|
18 | 18 | _version_major = 8 |
|
19 |
_version_minor = |
|
|
19 | _version_minor = 4 | |
|
20 | 20 | _version_patch = 0 |
|
21 | 21 | _version_extra = ".dev" |
|
22 | 22 | # _version_extra = "rc1" |
@@ -43,11 +43,11 b' def test_alias_args_error():' | |||
|
43 | 43 | |
|
44 | 44 | def test_alias_args_commented(): |
|
45 | 45 | """Check that alias correctly ignores 'commented out' args""" |
|
46 |
_ip.magic( |
|
|
47 | ||
|
46 | _ip.run_line_magic("alias", "commentarg echo this is %%s a commented out arg") | |
|
47 | ||
|
48 | 48 | with capture_output() as cap: |
|
49 |
_ip.run_cell( |
|
|
50 | ||
|
49 | _ip.run_cell("commentarg") | |
|
50 | ||
|
51 | 51 | # strip() is for pytest compat; testing via iptest patch IPython shell |
|
52 | 52 | # in testing.globalipapp and replace the system call which messed up the |
|
53 | 53 | # \r\n |
@@ -4,11 +4,11 b'' | |||
|
4 | 4 | import os |
|
5 | 5 | import tempfile |
|
6 | 6 | |
|
7 | from tempfile import TemporaryDirectory | |
|
7 | 8 | from traitlets import Unicode |
|
8 | 9 | |
|
9 | 10 | from IPython.core.application import BaseIPythonApplication |
|
10 | 11 | from IPython.testing import decorators as dec |
|
11 | from IPython.utils.tempdir import TemporaryDirectory | |
|
12 | 12 | |
|
13 | 13 | |
|
14 | 14 | @dec.onlyif_unicode_paths |
@@ -1262,3 +1262,14 b' class TestCompleter(unittest.TestCase):' | |||
|
1262 | 1262 | _, matches = ip.complete(None, "test.meth(") |
|
1263 | 1263 | self.assertIn("meth_arg1=", matches) |
|
1264 | 1264 | self.assertNotIn("meth2_arg1=", matches) |
|
1265 | ||
|
1266 | def test_percent_symbol_restrict_to_magic_completions(self): | |
|
1267 | ip = get_ipython() | |
|
1268 | completer = ip.Completer | |
|
1269 | text = "%a" | |
|
1270 | ||
|
1271 | with provisionalcompleter(): | |
|
1272 | completer.use_jedi = True | |
|
1273 | completions = completer.completions(text, len(text)) | |
|
1274 | for c in completions: | |
|
1275 | self.assertEqual(c.text[0], "%") |
@@ -14,8 +14,9 b' import tempfile' | |||
|
14 | 14 | import unittest |
|
15 | 15 | from os.path import join |
|
16 | 16 | |
|
17 | from tempfile import TemporaryDirectory | |
|
18 | ||
|
17 | 19 | from IPython.core.completerlib import magic_run_completer, module_completion, try_import |
|
18 | from IPython.utils.tempdir import TemporaryDirectory | |
|
19 | 20 | from IPython.testing.decorators import onlyif_unicode_paths |
|
20 | 21 | |
|
21 | 22 |
@@ -1,8 +1,9 b'' | |||
|
1 | 1 | import os.path |
|
2 | 2 | |
|
3 | from tempfile import TemporaryDirectory | |
|
4 | ||
|
3 | 5 | import IPython.testing.tools as tt |
|
4 | 6 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
5 | from IPython.utils.tempdir import TemporaryDirectory | |
|
6 | 7 | |
|
7 | 8 | ext1_content = """ |
|
8 | 9 | def load_ipython_extension(ip): |
@@ -56,36 +56,42 b' def test_handlers():' | |||
|
56 | 56 | ip.user_ns['autocallable'] = autocallable |
|
57 | 57 | |
|
58 | 58 | # auto |
|
59 |
ip.magic( |
|
|
59 | ip.run_line_magic("autocall", "0") | |
|
60 | 60 | # Only explicit escapes or instances of IPyAutocallable should get |
|
61 | 61 | # expanded |
|
62 |
run( |
|
|
63 | ('len "abc"', 'len "abc"'), | |
|
64 | ('autocallable', 'autocallable()'), | |
|
65 | # Don't add extra brackets (gh-1117) | |
|
66 | ('autocallable()', 'autocallable()'), | |
|
67 | ]) | |
|
68 | ip.magic('autocall 1') | |
|
69 | run([ | |
|
70 | ('len "abc"', 'len("abc")'), | |
|
71 | ('len "abc";', 'len("abc");'), # ; is special -- moves out of parens | |
|
72 | # Autocall is turned off if first arg is [] and the object | |
|
73 | # is both callable and indexable. Like so: | |
|
74 | ('len [1,2]', 'len([1,2])'), # len doesn't support __getitem__... | |
|
75 | ('call_idx [1]', 'call_idx [1]'), # call_idx *does*.. | |
|
76 | ('call_idx 1', 'call_idx(1)'), | |
|
77 | ('len', 'len'), # only at 2 does it auto-call on single args | |
|
78 | ]) | |
|
79 | ip.magic('autocall 2') | |
|
80 | run([ | |
|
81 | ('len "abc"', 'len("abc")'), | |
|
82 | ('len "abc";', 'len("abc");'), | |
|
83 | ('len [1,2]', 'len([1,2])'), | |
|
84 | ('call_idx [1]', 'call_idx [1]'), | |
|
85 | ('call_idx 1', 'call_idx(1)'), | |
|
86 | # This is what's different: | |
|
87 | ('len', 'len()'), # only at 2 does it auto-call on single args | |
|
88 | ]) | |
|
89 | ip.magic('autocall 1') | |
|
62 | run( | |
|
63 | [ | |
|
64 | ('len "abc"', 'len "abc"'), | |
|
65 | ("autocallable", "autocallable()"), | |
|
66 | # Don't add extra brackets (gh-1117) | |
|
67 | ("autocallable()", "autocallable()"), | |
|
68 | ] | |
|
69 | ) | |
|
70 | ip.run_line_magic("autocall", "1") | |
|
71 | run( | |
|
72 | [ | |
|
73 | ('len "abc"', 'len("abc")'), | |
|
74 | ('len "abc";', 'len("abc");'), # ; is special -- moves out of parens | |
|
75 | # Autocall is turned off if first arg is [] and the object | |
|
76 | # is both callable and indexable. Like so: | |
|
77 | ("len [1,2]", "len([1,2])"), # len doesn't support __getitem__... | |
|
78 | ("call_idx [1]", "call_idx [1]"), # call_idx *does*.. | |
|
79 | ("call_idx 1", "call_idx(1)"), | |
|
80 | ("len", "len"), # only at 2 does it auto-call on single args | |
|
81 | ] | |
|
82 | ) | |
|
83 | ip.run_line_magic("autocall", "2") | |
|
84 | run( | |
|
85 | [ | |
|
86 | ('len "abc"', 'len("abc")'), | |
|
87 | ('len "abc";', 'len("abc");'), | |
|
88 | ("len [1,2]", "len([1,2])"), | |
|
89 | ("call_idx [1]", "call_idx [1]"), | |
|
90 | ("call_idx 1", "call_idx(1)"), | |
|
91 | # This is what's different: | |
|
92 | ("len", "len()"), # only at 2 does it auto-call on single args | |
|
93 | ] | |
|
94 | ) | |
|
95 | ip.run_line_magic("autocall", "1") | |
|
90 | 96 | |
|
91 | 97 | assert failures == [] |
@@ -7,17 +7,19 b'' | |||
|
7 | 7 | |
|
8 | 8 | # stdlib |
|
9 | 9 | import io |
|
10 | from pathlib import Path | |
|
10 | import sqlite3 | |
|
11 | 11 | import sys |
|
12 | 12 | import tempfile |
|
13 | 13 | from datetime import datetime |
|
14 | import sqlite3 | |
|
14 | from pathlib import Path | |
|
15 | 15 | |
|
16 | from tempfile import TemporaryDirectory | |
|
16 | 17 | # our own packages |
|
17 | 18 | from traitlets.config.loader import Config |
|
18 | from IPython.utils.tempdir import TemporaryDirectory | |
|
19 | ||
|
19 | 20 | from IPython.core.history import HistoryManager, extract_hist_ranges |
|
20 | 21 | |
|
22 | ||
|
21 | 23 | def test_proper_default_encoding(): |
|
22 | 24 | assert sys.getdefaultencoding() == "utf-8" |
|
23 | 25 | |
@@ -50,13 +52,13 b' def test_history():' | |||
|
50 | 52 | |
|
51 | 53 | # Check whether specifying a range beyond the end of the current |
|
52 | 54 | # session results in an error (gh-804) |
|
53 |
ip.magic( |
|
|
55 | ip.run_line_magic("hist", "2-500") | |
|
54 | 56 | |
|
55 | 57 | # Check that we can write non-ascii characters to a file |
|
56 |
ip.magic(" |
|
|
57 |
ip.magic(" |
|
|
58 |
ip.magic(" |
|
|
59 |
ip.magic(" |
|
|
58 | ip.run_line_magic("hist", "-f %s" % (tmp_path / "test1")) | |
|
59 | ip.run_line_magic("hist", "-pf %s" % (tmp_path / "test2")) | |
|
60 | ip.run_line_magic("hist", "-nf %s" % (tmp_path / "test3")) | |
|
61 | ip.run_line_magic("save", "%s 1-10" % (tmp_path / "test4")) | |
|
60 | 62 | |
|
61 | 63 | # New session |
|
62 | 64 | ip.history_manager.reset() |
@@ -122,7 +124,7 b' def test_history():' | |||
|
122 | 124 | |
|
123 | 125 | # Cross testing: check that magic %save can get previous session. |
|
124 | 126 | testfilename = (tmp_path / "test.py").resolve() |
|
125 |
ip.magic("save |
|
|
127 | ip.run_line_magic("save", str(testfilename) + " ~1/1-3") | |
|
126 | 128 | with io.open(testfilename, encoding="utf-8") as testfile: |
|
127 | 129 | assert testfile.read() == "# coding: utf-8\n" + "\n".join(hist) + "\n" |
|
128 | 130 |
@@ -59,108 +59,93 b' syntax = \\' | |||
|
59 | 59 | ('x=1', 'x=1'), # normal input is unmodified |
|
60 | 60 | (' ',' '), # blank lines are kept intact |
|
61 | 61 | ("a, b = %foo", "a, b = get_ipython().run_line_magic('foo', '')"), |
|
62 |
|
|
|
63 | ||
|
64 | classic_prompt = | |
|
65 | [('>>> x=1', 'x=1'), | |
|
66 | ('x=1', 'x=1'), # normal input is unmodified | |
|
67 | (' ', ' '), # blank lines are kept intact | |
|
68 | ], | |
|
69 | ||
|
70 | ipy_prompt = | |
|
71 | [('In [1]: x=1', 'x=1'), | |
|
72 | ('x=1', 'x=1'), # normal input is unmodified | |
|
73 | (' ',' '), # blank lines are kept intact | |
|
74 | ], | |
|
75 | ||
|
76 | # Tests for the escape transformer to leave normal code alone | |
|
77 | escaped_noesc = | |
|
78 | [ (' ', ' '), | |
|
79 | ('x=1', 'x=1'), | |
|
80 | ], | |
|
81 | ||
|
82 | # System calls | |
|
83 | escaped_shell = | |
|
84 | [ ('!ls', "get_ipython().system('ls')"), | |
|
85 | # Double-escape shell, this means to capture the output of the | |
|
86 | # subprocess and return it | |
|
87 |
|
|
|
88 | ], | |
|
89 | ||
|
90 | # Help/object info | |
|
91 | escaped_help = | |
|
92 | [ ('?', 'get_ipython().show_usage()'), | |
|
93 |
|
|
|
94 | ('??x2', "get_ipython().run_line_magic('pinfo2', 'x2')"), | |
|
95 | ('?a.*s', "get_ipython().run_line_magic('psearch', 'a.*s')"), | |
|
96 |
|
|
|
97 |
|
|
|
98 |
|
|
|
99 | ], | |
|
100 | ||
|
101 | end_help = | |
|
102 |
|
|
|
103 |
( |
|
|
104 |
( |
|
|
105 |
( |
|
|
106 |
( |
|
|
107 |
( |
|
|
108 |
( |
|
|
109 | ('f*?', "get_ipython().run_line_magic('psearch', 'f*')"), | |
|
110 | ('ax.*aspe*?', "get_ipython().run_line_magic('psearch', 'ax.*aspe*')"), | |
|
111 | ('a = abc?', "get_ipython().set_next_input('a = abc');" | |
|
112 | "get_ipython().run_line_magic('pinfo', 'abc')"), | |
|
113 | ('a = abc.qe??', "get_ipython().set_next_input('a = abc.qe');" | |
|
114 |
|
|
|
115 | ('a = *.items?', "get_ipython().set_next_input('a = *.items');" | |
|
116 |
|
|
|
117 | ('plot(a?', "get_ipython().set_next_input('plot(a');" | |
|
118 | "get_ipython().run_line_magic('pinfo', 'a')"), | |
|
119 | ('a*2 #comment?', 'a*2 #comment?'), | |
|
120 | ], | |
|
121 | ||
|
122 | # Explicit magic calls | |
|
123 | escaped_magic = | |
|
124 | [ ('%cd', "get_ipython().run_line_magic('cd', '')"), | |
|
125 | ('%cd /home', "get_ipython().run_line_magic('cd', '/home')"), | |
|
126 | # Backslashes need to be escaped. | |
|
127 | ('%cd C:\\User', "get_ipython().run_line_magic('cd', 'C:\\\\User')"), | |
|
128 | (' %magic', " get_ipython().run_line_magic('magic', '')"), | |
|
129 | ], | |
|
130 | ||
|
131 | # Quoting with separate arguments | |
|
132 | escaped_quote = | |
|
133 | [ (',f', 'f("")'), | |
|
134 | (',f x', 'f("x")'), | |
|
135 |
|
|
|
136 |
|
|
|
137 | ], | |
|
138 | ||
|
139 | # Quoting with single argument | |
|
140 | escaped_quote2 = | |
|
141 | [ (';f', 'f("")'), | |
|
142 | (';f x', 'f("x")'), | |
|
143 | (' ;f y', ' f("y")'), | |
|
144 | (';f a b', 'f("a b")'), | |
|
145 | ], | |
|
146 | ||
|
147 | # Simply apply parens | |
|
148 | escaped_paren = | |
|
149 | [ ('/f', 'f()'), | |
|
150 | ('/f x', 'f(x)'), | |
|
151 | (' /f y', ' f(y)'), | |
|
152 | ('/f a b', 'f(a, b)'), | |
|
153 | ], | |
|
154 | ||
|
155 | # Check that we transform prompts before other transforms | |
|
156 | mixed = | |
|
157 | [ ('In [1]: %lsmagic', "get_ipython().run_line_magic('lsmagic', '')"), | |
|
158 | ('>>> %lsmagic', "get_ipython().run_line_magic('lsmagic', '')"), | |
|
159 | ('In [2]: !ls', "get_ipython().system('ls')"), | |
|
160 | ('In [3]: abs?', "get_ipython().run_line_magic('pinfo', 'abs')"), | |
|
161 | ('In [4]: b = %who', "b = get_ipython().run_line_magic('who', '')"), | |
|
162 | ], | |
|
163 | ) | |
|
62 | ], | |
|
63 | classic_prompt=[ | |
|
64 | (">>> x=1", "x=1"), | |
|
65 | ("x=1", "x=1"), # normal input is unmodified | |
|
66 | (" ", " "), # blank lines are kept intact | |
|
67 | ], | |
|
68 | ipy_prompt=[ | |
|
69 | ("In [1]: x=1", "x=1"), | |
|
70 | ("x=1", "x=1"), # normal input is unmodified | |
|
71 | (" ", " "), # blank lines are kept intact | |
|
72 | ], | |
|
73 | # Tests for the escape transformer to leave normal code alone | |
|
74 | escaped_noesc=[ | |
|
75 | (" ", " "), | |
|
76 | ("x=1", "x=1"), | |
|
77 | ], | |
|
78 | # System calls | |
|
79 | escaped_shell=[ | |
|
80 | ("!ls", "get_ipython().system('ls')"), | |
|
81 | # Double-escape shell, this means to capture the output of the | |
|
82 | # subprocess and return it | |
|
83 | ("!!ls", "get_ipython().getoutput('ls')"), | |
|
84 | ], | |
|
85 | # Help/object info | |
|
86 | escaped_help=[ | |
|
87 | ("?", "get_ipython().show_usage()"), | |
|
88 | ("?x1", "get_ipython().run_line_magic('pinfo', 'x1')"), | |
|
89 | ("??x2", "get_ipython().run_line_magic('pinfo2', 'x2')"), | |
|
90 | ("?a.*s", "get_ipython().run_line_magic('psearch', 'a.*s')"), | |
|
91 | ("?%hist1", "get_ipython().run_line_magic('pinfo', '%hist1')"), | |
|
92 | ("?%%hist2", "get_ipython().run_line_magic('pinfo', '%%hist2')"), | |
|
93 | ("?abc = qwe", "get_ipython().run_line_magic('pinfo', 'abc')"), | |
|
94 | ], | |
|
95 | end_help=[ | |
|
96 | ("x3?", "get_ipython().run_line_magic('pinfo', 'x3')"), | |
|
97 | ("x4??", "get_ipython().run_line_magic('pinfo2', 'x4')"), | |
|
98 | ("%hist1?", "get_ipython().run_line_magic('pinfo', '%hist1')"), | |
|
99 | ("%hist2??", "get_ipython().run_line_magic('pinfo2', '%hist2')"), | |
|
100 | ("%%hist3?", "get_ipython().run_line_magic('pinfo', '%%hist3')"), | |
|
101 | ("%%hist4??", "get_ipython().run_line_magic('pinfo2', '%%hist4')"), | |
|
102 | ("π.foo?", "get_ipython().run_line_magic('pinfo', 'π.foo')"), | |
|
103 | ("f*?", "get_ipython().run_line_magic('psearch', 'f*')"), | |
|
104 | ("ax.*aspe*?", "get_ipython().run_line_magic('psearch', 'ax.*aspe*')"), | |
|
105 | ("a = abc?", "get_ipython().run_line_magic('pinfo', 'abc')"), | |
|
106 | ("a = abc.qe??", "get_ipython().run_line_magic('pinfo2', 'abc.qe')"), | |
|
107 | ("a = *.items?", "get_ipython().run_line_magic('psearch', '*.items')"), | |
|
108 | ("plot(a?", "get_ipython().run_line_magic('pinfo', 'a')"), | |
|
109 | ("a*2 #comment?", "a*2 #comment?"), | |
|
110 | ], | |
|
111 | # Explicit magic calls | |
|
112 | escaped_magic=[ | |
|
113 | ("%cd", "get_ipython().run_line_magic('cd', '')"), | |
|
114 | ("%cd /home", "get_ipython().run_line_magic('cd', '/home')"), | |
|
115 | # Backslashes need to be escaped. | |
|
116 | ("%cd C:\\User", "get_ipython().run_line_magic('cd', 'C:\\\\User')"), | |
|
117 | (" %magic", " get_ipython().run_line_magic('magic', '')"), | |
|
118 | ], | |
|
119 | # Quoting with separate arguments | |
|
120 | escaped_quote=[ | |
|
121 | (",f", 'f("")'), | |
|
122 | (",f x", 'f("x")'), | |
|
123 | (" ,f y", ' f("y")'), | |
|
124 | (",f a b", 'f("a", "b")'), | |
|
125 | ], | |
|
126 | # Quoting with single argument | |
|
127 | escaped_quote2=[ | |
|
128 | (";f", 'f("")'), | |
|
129 | (";f x", 'f("x")'), | |
|
130 | (" ;f y", ' f("y")'), | |
|
131 | (";f a b", 'f("a b")'), | |
|
132 | ], | |
|
133 | # Simply apply parens | |
|
134 | escaped_paren=[ | |
|
135 | ("/f", "f()"), | |
|
136 | ("/f x", "f(x)"), | |
|
137 | (" /f y", " f(y)"), | |
|
138 | ("/f a b", "f(a, b)"), | |
|
139 | ], | |
|
140 | # Check that we transform prompts before other transforms | |
|
141 | mixed=[ | |
|
142 | ("In [1]: %lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), | |
|
143 | (">>> %lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), | |
|
144 | ("In [2]: !ls", "get_ipython().system('ls')"), | |
|
145 | ("In [3]: abs?", "get_ipython().run_line_magic('pinfo', 'abs')"), | |
|
146 | ("In [4]: b = %who", "b = get_ipython().run_line_magic('who', '')"), | |
|
147 | ], | |
|
148 | ) | |
|
164 | 149 | |
|
165 | 150 | # multiline syntax examples. Each of these should be a list of lists, with |
|
166 | 151 | # each entry itself having pairs of raw/transformed input. The union (with |
@@ -14,45 +14,65 b' import pytest' | |||
|
14 | 14 | from IPython.core import inputtransformer2 as ipt2 |
|
15 | 15 | from IPython.core.inputtransformer2 import _find_assign_op, make_tokens_by_line |
|
16 | 16 | |
|
17 |
MULTILINE_MAGIC = ( |
|
|
17 | MULTILINE_MAGIC = ( | |
|
18 | """\ | |
|
18 | 19 |
|
|
19 | 20 | %foo \\ |
|
20 | 21 |
|
|
21 | 22 |
|
|
22 | """.splitlines(keepends=True), (2, 0), """\ | |
|
23 | """.splitlines( | |
|
24 | keepends=True | |
|
25 | ), | |
|
26 | (2, 0), | |
|
27 | """\ | |
|
23 | 28 |
|
|
24 | 29 | get_ipython().run_line_magic('foo', ' bar') |
|
25 | 30 |
|
|
26 |
|
|
|
31 | """.splitlines( | |
|
32 | keepends=True | |
|
33 | ), | |
|
34 | ) | |
|
27 | 35 | |
|
28 |
INDENTED_MAGIC = ( |
|
|
36 | INDENTED_MAGIC = ( | |
|
37 | """\ | |
|
29 | 38 |
|
|
30 | 39 | %ls |
|
31 | """.splitlines(keepends=True), (2, 4), """\ | |
|
40 | """.splitlines( | |
|
41 | keepends=True | |
|
42 | ), | |
|
43 | (2, 4), | |
|
44 | """\ | |
|
32 | 45 |
|
|
33 | 46 | get_ipython().run_line_magic('ls', '') |
|
34 |
|
|
|
47 | """.splitlines( | |
|
48 | keepends=True | |
|
49 | ), | |
|
50 | ) | |
|
35 | 51 | |
|
36 |
CRLF_MAGIC = ( |
|
|
37 | "a = f()\n", | |
|
38 | "%ls\r\n", | |
|
39 | "g()\n" | |
|
40 | ], (2, 0), [ | |
|
41 | "a = f()\n", | |
|
42 | "get_ipython().run_line_magic('ls', '')\n", | |
|
43 |
" |
|
|
44 | ]) | |
|
45 | ||
|
46 | MULTILINE_MAGIC_ASSIGN = ("""\ | |
|
52 | CRLF_MAGIC = ( | |
|
53 | ["a = f()\n", "%ls\r\n", "g()\n"], | |
|
54 | (2, 0), | |
|
55 | ["a = f()\n", "get_ipython().run_line_magic('ls', '')\n", "g()\n"], | |
|
56 | ) | |
|
57 | ||
|
58 | MULTILINE_MAGIC_ASSIGN = ( | |
|
59 | """\ | |
|
47 | 60 |
|
|
48 | 61 | b = %foo \\ |
|
49 | 62 |
|
|
50 | 63 |
|
|
51 | """.splitlines(keepends=True), (2, 4), """\ | |
|
64 | """.splitlines( | |
|
65 | keepends=True | |
|
66 | ), | |
|
67 | (2, 4), | |
|
68 | """\ | |
|
52 | 69 |
|
|
53 | 70 | b = get_ipython().run_line_magic('foo', ' bar') |
|
54 | 71 |
|
|
55 |
|
|
|
72 | """.splitlines( | |
|
73 | keepends=True | |
|
74 | ), | |
|
75 | ) | |
|
56 | 76 | |
|
57 | 77 | MULTILINE_SYSTEM_ASSIGN = ("""\ |
|
58 | 78 | a = f() |
@@ -67,73 +87,76 b' g()' | |||
|
67 | 87 | |
|
68 | 88 | ##### |
|
69 | 89 | |
|
70 |
MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT = ( |
|
|
90 | MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT = ( | |
|
91 | """\ | |
|
71 | 92 |
|
|
72 | 93 |
|
|
73 | 94 |
|
|
74 | 95 |
|
|
75 | """.splitlines(keepends=True), (4, 7), '''\ | |
|
96 | """.splitlines( | |
|
97 | keepends=True | |
|
98 | ), | |
|
99 | (4, 7), | |
|
100 | """\ | |
|
76 | 101 |
|
|
77 | 102 |
|
|
78 | 103 |
|
|
79 | 104 | res =get_ipython().getoutput(\' ls\') |
|
80 | '''.splitlines(keepends=True)) | |
|
105 | """.splitlines( | |
|
106 | keepends=True | |
|
107 | ), | |
|
108 | ) | |
|
81 | 109 | |
|
82 | 110 | ###### |
|
83 | 111 | |
|
84 | AUTOCALL_QUOTE = ( | |
|
85 | [",f 1 2 3\n"], (1, 0), | |
|
86 | ['f("1", "2", "3")\n'] | |
|
87 | ) | |
|
112 | AUTOCALL_QUOTE = ([",f 1 2 3\n"], (1, 0), ['f("1", "2", "3")\n']) | |
|
88 | 113 | |
|
89 | AUTOCALL_QUOTE2 = ( | |
|
90 | [";f 1 2 3\n"], (1, 0), | |
|
91 | ['f("1 2 3")\n'] | |
|
92 | ) | |
|
114 | AUTOCALL_QUOTE2 = ([";f 1 2 3\n"], (1, 0), ['f("1 2 3")\n']) | |
|
93 | 115 | |
|
94 | AUTOCALL_PAREN = ( | |
|
95 | ["/f 1 2 3\n"], (1, 0), | |
|
96 | ['f(1, 2, 3)\n'] | |
|
97 | ) | |
|
116 | AUTOCALL_PAREN = (["/f 1 2 3\n"], (1, 0), ["f(1, 2, 3)\n"]) | |
|
98 | 117 | |
|
99 | SIMPLE_HELP = ( | |
|
100 | ["foo?\n"], (1, 0), | |
|
101 | ["get_ipython().run_line_magic('pinfo', 'foo')\n"] | |
|
102 | ) | |
|
118 | SIMPLE_HELP = (["foo?\n"], (1, 0), ["get_ipython().run_line_magic('pinfo', 'foo')\n"]) | |
|
103 | 119 | |
|
104 | 120 | DETAILED_HELP = ( |
|
105 |
["foo??\n"], |
|
|
106 | ["get_ipython().run_line_magic('pinfo2', 'foo')\n"] | |
|
121 | ["foo??\n"], | |
|
122 | (1, 0), | |
|
123 | ["get_ipython().run_line_magic('pinfo2', 'foo')\n"], | |
|
107 | 124 | ) |
|
108 | 125 | |
|
109 | MAGIC_HELP = ( | |
|
110 | ["%foo?\n"], (1, 0), | |
|
111 | ["get_ipython().run_line_magic('pinfo', '%foo')\n"] | |
|
112 | ) | |
|
126 | MAGIC_HELP = (["%foo?\n"], (1, 0), ["get_ipython().run_line_magic('pinfo', '%foo')\n"]) | |
|
113 | 127 | |
|
114 | 128 | HELP_IN_EXPR = ( |
|
115 |
["a = b + c?\n"], |
|
|
116 | ["get_ipython().set_next_input('a = b + c');" | |
|
117 |
|
|
|
129 | ["a = b + c?\n"], | |
|
130 | (1, 0), | |
|
131 | ["get_ipython().run_line_magic('pinfo', 'c')\n"], | |
|
118 | 132 | ) |
|
119 | 133 | |
|
120 |
HELP_CONTINUED_LINE = ( |
|
|
134 | HELP_CONTINUED_LINE = ( | |
|
135 | """\ | |
|
121 | 136 | a = \\ |
|
122 | 137 |
|
|
123 | """.splitlines(keepends=True), (1, 0), | |
|
124 | [r"get_ipython().set_next_input('a = \\\nzip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"] | |
|
138 | """.splitlines( | |
|
139 | keepends=True | |
|
140 | ), | |
|
141 | (1, 0), | |
|
142 | [r"get_ipython().run_line_magic('pinfo', 'zip')" + "\n"], | |
|
125 | 143 | ) |
|
126 | 144 | |
|
127 |
HELP_MULTILINE = ( |
|
|
145 | HELP_MULTILINE = ( | |
|
146 | """\ | |
|
128 | 147 |
|
|
129 | 148 |
|
|
130 | """.splitlines(keepends=True), (1, 0), | |
|
131 | [r"get_ipython().set_next_input('(a,\nb) = zip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"] | |
|
149 | """.splitlines( | |
|
150 | keepends=True | |
|
151 | ), | |
|
152 | (1, 0), | |
|
153 | [r"get_ipython().run_line_magic('pinfo', 'zip')" + "\n"], | |
|
132 | 154 | ) |
|
133 | 155 | |
|
134 | 156 | HELP_UNICODE = ( |
|
135 |
["π.foo?\n"], |
|
|
136 | ["get_ipython().run_line_magic('pinfo', 'π.foo')\n"] | |
|
157 | ["π.foo?\n"], | |
|
158 | (1, 0), | |
|
159 | ["get_ipython().run_line_magic('pinfo', 'π.foo')\n"], | |
|
137 | 160 | ) |
|
138 | 161 | |
|
139 | 162 | |
@@ -149,6 +172,7 b' def test_check_make_token_by_line_never_ends_empty():' | |||
|
149 | 172 | Check that not sequence of single or double characters ends up leading to en empty list of tokens |
|
150 | 173 | """ |
|
151 | 174 | from string import printable |
|
175 | ||
|
152 | 176 | for c in printable: |
|
153 | 177 | assert make_tokens_by_line(c)[-1] != [] |
|
154 | 178 | for k in printable: |
@@ -156,7 +180,7 b' def test_check_make_token_by_line_never_ends_empty():' | |||
|
156 | 180 | |
|
157 | 181 | |
|
158 | 182 | def check_find(transformer, case, match=True): |
|
159 |
sample, expected_start, _ |
|
|
183 | sample, expected_start, _ = case | |
|
160 | 184 | tbl = make_tokens_by_line(sample) |
|
161 | 185 | res = transformer.find(tbl) |
|
162 | 186 | if match: |
@@ -166,25 +190,30 b' def check_find(transformer, case, match=True):' | |||
|
166 | 190 | else: |
|
167 | 191 | assert res is None |
|
168 | 192 | |
|
193 | ||
|
169 | 194 | def check_transform(transformer_cls, case): |
|
170 | 195 | lines, start, expected = case |
|
171 | 196 | transformer = transformer_cls(start) |
|
172 | 197 | assert transformer.transform(lines) == expected |
|
173 | 198 | |
|
199 | ||
|
174 | 200 | def test_continued_line(): |
|
175 | 201 | lines = MULTILINE_MAGIC_ASSIGN[0] |
|
176 | 202 | assert ipt2.find_end_of_continued_line(lines, 1) == 2 |
|
177 | 203 | |
|
178 | 204 | assert ipt2.assemble_continued_line(lines, (1, 5), 2) == "foo bar" |
|
179 | 205 | |
|
206 | ||
|
180 | 207 | def test_find_assign_magic(): |
|
181 | 208 | check_find(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN) |
|
182 | 209 | check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN, match=False) |
|
183 | 210 | check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT, match=False) |
|
184 | 211 | |
|
212 | ||
|
185 | 213 | def test_transform_assign_magic(): |
|
186 | 214 | check_transform(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN) |
|
187 | 215 | |
|
216 | ||
|
188 | 217 | def test_find_assign_system(): |
|
189 | 218 | check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN) |
|
190 | 219 | check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT) |
@@ -192,30 +221,36 b' def test_find_assign_system():' | |||
|
192 | 221 | check_find(ipt2.SystemAssign, (["a=!ls\n"], (1, 2), None)) |
|
193 | 222 | check_find(ipt2.SystemAssign, MULTILINE_MAGIC_ASSIGN, match=False) |
|
194 | 223 | |
|
224 | ||
|
195 | 225 | def test_transform_assign_system(): |
|
196 | 226 | check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN) |
|
197 | 227 | check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT) |
|
198 | 228 | |
|
229 | ||
|
199 | 230 | def test_find_magic_escape(): |
|
200 | 231 | check_find(ipt2.EscapedCommand, MULTILINE_MAGIC) |
|
201 | 232 | check_find(ipt2.EscapedCommand, INDENTED_MAGIC) |
|
202 | 233 | check_find(ipt2.EscapedCommand, MULTILINE_MAGIC_ASSIGN, match=False) |
|
203 | 234 | |
|
235 | ||
|
204 | 236 | def test_transform_magic_escape(): |
|
205 | 237 | check_transform(ipt2.EscapedCommand, MULTILINE_MAGIC) |
|
206 | 238 | check_transform(ipt2.EscapedCommand, INDENTED_MAGIC) |
|
207 | 239 | check_transform(ipt2.EscapedCommand, CRLF_MAGIC) |
|
208 | 240 | |
|
241 | ||
|
209 | 242 | def test_find_autocalls(): |
|
210 | 243 | for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]: |
|
211 | 244 | print("Testing %r" % case[0]) |
|
212 | 245 | check_find(ipt2.EscapedCommand, case) |
|
213 | 246 | |
|
247 | ||
|
214 | 248 | def test_transform_autocall(): |
|
215 | 249 | for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]: |
|
216 | 250 | print("Testing %r" % case[0]) |
|
217 | 251 | check_transform(ipt2.EscapedCommand, case) |
|
218 | 252 | |
|
253 | ||
|
219 | 254 | def test_find_help(): |
|
220 | 255 | for case in [SIMPLE_HELP, DETAILED_HELP, MAGIC_HELP, HELP_IN_EXPR]: |
|
221 | 256 | check_find(ipt2.HelpEnd, case) |
@@ -233,6 +268,7 b' def test_find_help():' | |||
|
233 | 268 | # Nor in a string |
|
234 | 269 | check_find(ipt2.HelpEnd, (["foo = '''bar?\n"], None, None), match=False) |
|
235 | 270 | |
|
271 | ||
|
236 | 272 | def test_transform_help(): |
|
237 | 273 | tf = ipt2.HelpEnd((1, 0), (1, 9)) |
|
238 | 274 | assert tf.transform(HELP_IN_EXPR[0]) == HELP_IN_EXPR[2] |
@@ -246,10 +282,12 b' def test_transform_help():' | |||
|
246 | 282 | tf = ipt2.HelpEnd((1, 0), (1, 0)) |
|
247 | 283 | assert tf.transform(HELP_UNICODE[0]) == HELP_UNICODE[2] |
|
248 | 284 | |
|
285 | ||
|
249 | 286 | def test_find_assign_op_dedent(): |
|
250 | 287 | """ |
|
251 | 288 | be careful that empty token like dedent are not counted as parens |
|
252 | 289 | """ |
|
290 | ||
|
253 | 291 | class Tk: |
|
254 | 292 | def __init__(self, s): |
|
255 | 293 | self.string = s |
@@ -302,21 +340,23 b' def test_check_complete_param(code, expected, number):' | |||
|
302 | 340 | def test_check_complete(): |
|
303 | 341 | cc = ipt2.TransformerManager().check_complete |
|
304 | 342 | |
|
305 |
example = dedent( |
|
|
343 | example = dedent( | |
|
344 | """ | |
|
306 | 345 |
|
|
307 |
|
|
|
346 | a=1""" | |
|
347 | ) | |
|
308 | 348 | |
|
309 | 349 | assert cc(example) == ("incomplete", 4) |
|
310 | 350 | assert cc(example + "\n") == ("complete", None) |
|
311 | 351 | assert cc(example + "\n ") == ("complete", None) |
|
312 | 352 | |
|
313 | 353 | # no need to loop on all the letters/numbers. |
|
314 |
short = |
|
|
354 | short = "12abAB" + string.printable[62:] | |
|
315 | 355 | for c in short: |
|
316 | 356 | # test does not raise: |
|
317 | 357 | cc(c) |
|
318 | 358 | for k in short: |
|
319 | cc(c+k) | |
|
359 | cc(c + k) | |
|
320 | 360 | |
|
321 | 361 | assert cc("def f():\n x=0\n \\\n ") == ("incomplete", 2) |
|
322 | 362 | |
@@ -371,10 +411,9 b' def test_null_cleanup_transformer():' | |||
|
371 | 411 | assert manager.transform_cell("") == "" |
|
372 | 412 | |
|
373 | 413 | |
|
374 | ||
|
375 | ||
|
376 | 414 | def test_side_effects_I(): |
|
377 | 415 | count = 0 |
|
416 | ||
|
378 | 417 | def counter(lines): |
|
379 | 418 | nonlocal count |
|
380 | 419 | count += 1 |
@@ -384,14 +423,13 b' def test_side_effects_I():' | |||
|
384 | 423 | |
|
385 | 424 | manager = ipt2.TransformerManager() |
|
386 | 425 | manager.cleanup_transforms.insert(0, counter) |
|
387 |
assert manager.check_complete("a=1\n") == ( |
|
|
426 | assert manager.check_complete("a=1\n") == ("complete", None) | |
|
388 | 427 | assert count == 0 |
|
389 | 428 | |
|
390 | 429 | |
|
391 | ||
|
392 | ||
|
393 | 430 | def test_side_effects_II(): |
|
394 | 431 | count = 0 |
|
432 | ||
|
395 | 433 | def counter(lines): |
|
396 | 434 | nonlocal count |
|
397 | 435 | count += 1 |
@@ -401,5 +439,5 b' def test_side_effects_II():' | |||
|
401 | 439 | |
|
402 | 440 | manager = ipt2.TransformerManager() |
|
403 | 441 | manager.line_transforms.insert(0, counter) |
|
404 |
assert manager.check_complete("b=1\n") == ( |
|
|
442 | assert manager.check_complete("b=1\n") == ("complete", None) | |
|
405 | 443 | assert count == 0 |
@@ -380,7 +380,8 b' class InteractiveShellTestCase(unittest.TestCase):' | |||
|
380 | 380 | class A(object): |
|
381 | 381 | @property |
|
382 | 382 | def foo(self): |
|
383 | raise NotImplementedError() | |
|
383 | raise NotImplementedError() # pragma: no cover | |
|
384 | ||
|
384 | 385 | a = A() |
|
385 | 386 | |
|
386 | 387 | found = ip._ofind('a.foo', [('locals', locals())]) |
@@ -392,7 +393,7 b' class InteractiveShellTestCase(unittest.TestCase):' | |||
|
392 | 393 | class A(object): |
|
393 | 394 | @property |
|
394 | 395 | def foo(self): |
|
395 | raise NotImplementedError() | |
|
396 | raise NotImplementedError() # pragma: no cover | |
|
396 | 397 | |
|
397 | 398 | a = A() |
|
398 | 399 | a.a = A() |
@@ -546,7 +547,7 b' class TestSafeExecfileNonAsciiPath(unittest.TestCase):' | |||
|
546 | 547 | self.TESTDIR = join(self.BASETESTDIR, u"åäö") |
|
547 | 548 | os.mkdir(self.TESTDIR) |
|
548 | 549 | with open( |
|
549 |
join(self.TESTDIR, |
|
|
550 | join(self.TESTDIR, "åäötestscript.py"), "w", encoding="utf-8" | |
|
550 | 551 | ) as sfile: |
|
551 | 552 | sfile.write("pass\n") |
|
552 | 553 | self.oldpath = os.getcwd() |
@@ -585,9 +586,9 b' class ExitCodeChecks(tt.TempFileMixin):' | |||
|
585 | 586 | self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM) |
|
586 | 587 | |
|
587 | 588 | @onlyif_cmds_exist("csh") |
|
588 | def test_exit_code_signal_csh(self): | |
|
589 |
SHELL = os.environ.get( |
|
|
590 |
os.environ[ |
|
|
589 | def test_exit_code_signal_csh(self): # pragma: no cover | |
|
590 | SHELL = os.environ.get("SHELL", None) | |
|
591 | os.environ["SHELL"] = find_cmd("csh") | |
|
591 | 592 | try: |
|
592 | 593 | self.test_exit_code_signal() |
|
593 | 594 | finally: |
@@ -615,7 +616,7 b' class TestSystemRaw(ExitCodeChecks):' | |||
|
615 | 616 | def test_control_c(self, *mocks): |
|
616 | 617 | try: |
|
617 | 618 | self.system("sleep 1 # wont happen") |
|
618 | except KeyboardInterrupt: | |
|
619 | except KeyboardInterrupt: # pragma: no cove | |
|
619 | 620 | self.fail( |
|
620 | 621 | "system call should intercept " |
|
621 | 622 | "keyboard interrupt from subprocess.call" |
@@ -623,7 +624,7 b' class TestSystemRaw(ExitCodeChecks):' | |||
|
623 | 624 | self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT) |
|
624 | 625 | |
|
625 | 626 | def test_magic_warnings(self): |
|
626 |
for magic_cmd in ( |
|
|
627 | for magic_cmd in ("pip", "conda", "cd"): | |
|
627 | 628 | with self.assertWarnsRegex(Warning, "You executed the system command"): |
|
628 | 629 | ip.system_raw(magic_cmd) |
|
629 | 630 | |
@@ -679,16 +680,20 b' class TestAstTransform(unittest.TestCase):' | |||
|
679 | 680 | |
|
680 | 681 | def tearDown(self): |
|
681 | 682 | ip.ast_transformers.remove(self.negator) |
|
682 | ||
|
683 | ||
|
684 | def test_non_int_const(self): | |
|
685 | with tt.AssertPrints("hello"): | |
|
686 | ip.run_cell('print("hello")') | |
|
687 | ||
|
683 | 688 | def test_run_cell(self): |
|
684 |
with tt.AssertPrints( |
|
|
685 |
ip.run_cell( |
|
|
686 | ||
|
689 | with tt.AssertPrints("-34"): | |
|
690 | ip.run_cell("print(12 + 22)") | |
|
691 | ||
|
687 | 692 | # A named reference to a number shouldn't be transformed. |
|
688 |
ip.user_ns[ |
|
|
689 |
with tt.AssertNotPrints( |
|
|
690 |
ip.run_cell( |
|
|
691 | ||
|
693 | ip.user_ns["n"] = 55 | |
|
694 | with tt.AssertNotPrints("-55"): | |
|
695 | ip.run_cell("print(n)") | |
|
696 | ||
|
692 | 697 | def test_timeit(self): |
|
693 | 698 | called = set() |
|
694 | 699 | def f(x): |
@@ -796,7 +801,11 b' class TestAstTransform2(unittest.TestCase):' | |||
|
796 | 801 | # This shouldn't throw an error |
|
797 | 802 | ip.run_cell("o = 2.0") |
|
798 | 803 | self.assertEqual(ip.user_ns['o'], 2.0) |
|
799 | ||
|
804 | ||
|
805 | def test_run_cell_non_int(self): | |
|
806 | ip.run_cell("n = 'a'") | |
|
807 | assert self.calls == [] | |
|
808 | ||
|
800 | 809 | def test_timeit(self): |
|
801 | 810 | called = set() |
|
802 | 811 | def f(x): |
@@ -815,14 +824,9 b' class TestAstTransform2(unittest.TestCase):' | |||
|
815 | 824 | class ErrorTransformer(ast.NodeTransformer): |
|
816 | 825 | """Throws an error when it sees a number.""" |
|
817 | 826 | |
|
818 | # for Python 3.7 and earlier | |
|
819 | def visit_Num(self, node): | |
|
820 | raise ValueError("test") | |
|
821 | ||
|
822 | # for Python 3.8+ | |
|
823 | 827 | def visit_Constant(self, node): |
|
824 | 828 | if isinstance(node.value, int): |
|
825 | return self.visit_Num(node) | |
|
829 | raise ValueError("test") | |
|
826 | 830 | return node |
|
827 | 831 | |
|
828 | 832 | |
@@ -845,10 +849,6 b' class StringRejector(ast.NodeTransformer):' | |||
|
845 | 849 | not be executed by throwing an InputRejected. |
|
846 | 850 | """ |
|
847 | 851 | |
|
848 | #for python 3.7 and earlier | |
|
849 | def visit_Str(self, node): | |
|
850 | raise InputRejected("test") | |
|
851 | ||
|
852 | 852 | # 3.8 only |
|
853 | 853 | def visit_Constant(self, node): |
|
854 | 854 | if isinstance(node.value, str): |
@@ -236,7 +236,8 b' def test_run_cell():' | |||
|
236 | 236 | if 4: |
|
237 | 237 | print "bar" |
|
238 | 238 | |
|
239 |
""" |
|
|
239 | """ | |
|
240 | ) | |
|
240 | 241 | # Simply verifies that this kind of input is run |
|
241 | 242 | ip.run_cell(complex) |
|
242 | 243 |
@@ -2,9 +2,10 b'' | |||
|
2 | 2 | """Test IPython.core.logger""" |
|
3 | 3 | |
|
4 | 4 | import os.path |
|
5 | ||
|
5 | 6 | import pytest |
|
7 | from tempfile import TemporaryDirectory | |
|
6 | 8 | |
|
7 | from IPython.utils.tempdir import TemporaryDirectory | |
|
8 | 9 | |
|
9 | 10 | def test_logstart_inaccessible_file(): |
|
10 | 11 | with pytest.raises(IOError): |
@@ -448,7 +448,9 b' def test_multiline_time():' | |||
|
448 | 448 | ip = get_ipython() |
|
449 | 449 | ip.user_ns.pop('run', None) |
|
450 | 450 | |
|
451 |
ip.run_cell( |
|
|
451 | ip.run_cell( | |
|
452 | dedent( | |
|
453 | """\ | |
|
452 | 454 | %%time |
|
453 | 455 | a = "ho" |
|
454 | 456 | b = "hey" |
@@ -122,7 +122,8 b' class PasteTestCase(TestCase):' | |||
|
122 | 122 | ip.user_ns.pop("x") |
|
123 | 123 | |
|
124 | 124 | def test_paste_py_multi(self): |
|
125 |
self.paste( |
|
|
125 | self.paste( | |
|
126 | """ | |
|
126 | 127 |
|
|
127 | 128 |
|
|
128 | 129 |
|
@@ -145,7 +146,8 b' class PasteTestCase(TestCase):' | |||
|
145 | 146 | |
|
146 | 147 | def test_paste_email(self): |
|
147 | 148 | "Test pasting of email-quoted contents" |
|
148 |
self.paste( |
|
|
149 | self.paste( | |
|
150 | """\ | |
|
149 | 151 |
|
|
150 | 152 |
|
|
151 | 153 |
|
@@ -154,7 +156,8 b' class PasteTestCase(TestCase):' | |||
|
154 | 156 | |
|
155 | 157 | def test_paste_email2(self): |
|
156 | 158 | "Email again; some programs add a space also at each quoting level" |
|
157 |
self.paste( |
|
|
159 | self.paste( | |
|
160 | """\ | |
|
158 | 161 |
|
|
159 | 162 |
|
|
160 | 163 |
|
@@ -163,7 +166,8 b' class PasteTestCase(TestCase):' | |||
|
163 | 166 | |
|
164 | 167 | def test_paste_email_py(self): |
|
165 | 168 | "Email quoting of interactive input" |
|
166 |
self.paste( |
|
|
169 | self.paste( | |
|
170 | """\ | |
|
167 | 171 |
|
|
168 | 172 |
|
|
169 | 173 |
|
@@ -6,11 +6,11 b' import tempfile' | |||
|
6 | 6 | import warnings |
|
7 | 7 | from unittest.mock import patch |
|
8 | 8 | |
|
9 | from testpath import modified_env, assert_isdir, assert_isfile | |
|
9 | from tempfile import TemporaryDirectory | |
|
10 | from testpath import assert_isdir, assert_isfile, modified_env | |
|
10 | 11 | |
|
11 | 12 | from IPython import paths |
|
12 | 13 | from IPython.testing.decorators import skip_win32 |
|
13 | from IPython.utils.tempdir import TemporaryDirectory | |
|
14 | 14 | |
|
15 | 15 | TMP_TEST_DIR = os.path.realpath(tempfile.mkdtemp()) |
|
16 | 16 | HOME_TEST_DIR = os.path.join(TMP_TEST_DIR, "home_test_dir") |
@@ -23,17 +23,16 b' Authors' | |||
|
23 | 23 | import shutil |
|
24 | 24 | import sys |
|
25 | 25 | import tempfile |
|
26 | ||
|
27 | 26 | from pathlib import Path |
|
28 | 27 | from unittest import TestCase |
|
29 | 28 | |
|
30 | from IPython.core.profileapp import list_profiles_in, list_bundled_profiles | |
|
31 | from IPython.core.profiledir import ProfileDir | |
|
29 | from tempfile import TemporaryDirectory | |
|
32 | 30 | |
|
31 | from IPython.core.profileapp import list_bundled_profiles, list_profiles_in | |
|
32 | from IPython.core.profiledir import ProfileDir | |
|
33 | 33 | from IPython.testing import decorators as dec |
|
34 | 34 | from IPython.testing import tools as tt |
|
35 | 35 | from IPython.utils.process import getoutput |
|
36 | from IPython.utils.tempdir import TemporaryDirectory | |
|
37 | 36 | |
|
38 | 37 | #----------------------------------------------------------------------------- |
|
39 | 38 | # Globals |
@@ -109,7 +108,7 b' def test_list_profiles_in():' | |||
|
109 | 108 | for name in ("profile_foo", "profile_hello", "not_a_profile"): |
|
110 | 109 | Path(td / name).mkdir(parents=True) |
|
111 | 110 | if dec.unicode_paths: |
|
112 |
Path(td / |
|
|
111 | Path(td / "profile_ünicode").mkdir(parents=True) | |
|
113 | 112 | |
|
114 | 113 | with open(td / "profile_file", "w", encoding="utf-8") as f: |
|
115 | 114 | f.write("I am not a profile directory") |
@@ -19,21 +19,22 b' as otherwise it may influence later tests.' | |||
|
19 | 19 | import functools |
|
20 | 20 | import os |
|
21 | 21 | import platform |
|
22 | from os.path import join as pjoin | |
|
23 | 22 | import random |
|
24 | 23 | import string |
|
25 | 24 | import sys |
|
26 | 25 | import textwrap |
|
27 | 26 | import unittest |
|
27 | from os.path import join as pjoin | |
|
28 | 28 | from unittest.mock import patch |
|
29 | 29 | |
|
30 | 30 | import pytest |
|
31 | from tempfile import TemporaryDirectory | |
|
31 | 32 | |
|
33 | from IPython.core import debugger | |
|
32 | 34 | from IPython.testing import decorators as dec |
|
33 | 35 | from IPython.testing import tools as tt |
|
34 | 36 | from IPython.utils.io import capture_output |
|
35 | from IPython.utils.tempdir import TemporaryDirectory | |
|
36 | from IPython.core import debugger | |
|
37 | ||
|
37 | 38 | |
|
38 | 39 | def doctest_refbug(): |
|
39 | 40 | """Very nasty problem with references held by multiple runs of a script. |
@@ -411,6 +412,7 b' tclass.py: deleting object: C-third' | |||
|
411 | 412 | """Test %run notebook.ipynb error""" |
|
412 | 413 | pytest.importorskip("nbformat") |
|
413 | 414 | from nbformat import v4, writes |
|
415 | ||
|
414 | 416 | # %run when a file name isn't provided |
|
415 | 417 | pytest.raises(Exception, _ip.magic, "run") |
|
416 | 418 |
@@ -3,21 +3,20 b'' | |||
|
3 | 3 | """ |
|
4 | 4 | import io |
|
5 | 5 | import logging |
|
6 | import os.path | |
|
6 | 7 | import platform |
|
7 | 8 | import re |
|
8 | 9 | import sys |
|
9 | import os.path | |
|
10 | from textwrap import dedent | |
|
11 | 10 | import traceback |
|
12 | 11 | import unittest |
|
12 | from textwrap import dedent | |
|
13 | 13 | |
|
14 | from IPython.core.ultratb import ColorTB, VerboseTB | |
|
15 | ||
|
14 | from tempfile import TemporaryDirectory | |
|
16 | 15 | |
|
16 | from IPython.core.ultratb import ColorTB, VerboseTB | |
|
17 | 17 | from IPython.testing import tools as tt |
|
18 | 18 | from IPython.testing.decorators import onlyif_unicode_paths |
|
19 | 19 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
20 | from IPython.utils.tempdir import TemporaryDirectory | |
|
21 | 20 | |
|
22 | 21 | file_1 = """1 |
|
23 | 22 | 2 |
@@ -207,7 +207,16 b' class TBTools(colorable.Colorable):' | |||
|
207 | 207 | # Number of frames to skip when reporting tracebacks |
|
208 | 208 | tb_offset = 0 |
|
209 | 209 | |
|
210 | def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None): | |
|
210 | def __init__( | |
|
211 | self, | |
|
212 | color_scheme="NoColor", | |
|
213 | call_pdb=False, | |
|
214 | ostream=None, | |
|
215 | parent=None, | |
|
216 | config=None, | |
|
217 | *, | |
|
218 | debugger_cls=None, | |
|
219 | ): | |
|
211 | 220 | # Whether to call the interactive pdb debugger after printing |
|
212 | 221 | # tracebacks or not |
|
213 | 222 | super(TBTools, self).__init__(parent=parent, config=config) |
@@ -227,9 +236,10 b' class TBTools(colorable.Colorable):' | |||
|
227 | 236 | |
|
228 | 237 | self.set_colors(color_scheme) |
|
229 | 238 | self.old_scheme = color_scheme # save initial value for toggles |
|
239 | self.debugger_cls = debugger_cls or debugger.Pdb | |
|
230 | 240 | |
|
231 | 241 | if call_pdb: |
|
232 |
self.pdb = debugger |
|
|
242 | self.pdb = debugger_cls() | |
|
233 | 243 | else: |
|
234 | 244 | self.pdb = None |
|
235 | 245 | |
@@ -350,9 +360,6 b' class ListTB(TBTools):' | |||
|
350 | 360 | Because they are meant to be called without a full traceback (only a |
|
351 | 361 | list), instances of this class can't call the interactive pdb debugger.""" |
|
352 | 362 | |
|
353 | def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None): | |
|
354 | TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb, | |
|
355 | ostream=ostream, parent=parent,config=config) | |
|
356 | 363 | |
|
357 | 364 | def __call__(self, etype, value, elist): |
|
358 | 365 | self.ostream.flush() |
@@ -628,8 +635,15 b' class VerboseTB(TBTools):' | |||
|
628 | 635 | tb_offset=1 allows use of this handler in interpreters which will have |
|
629 | 636 | their own code at the top of the traceback (VerboseTB will first |
|
630 | 637 | remove that frame before printing the traceback info).""" |
|
631 | TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb, | |
|
632 | ostream=ostream, parent=parent, config=config) | |
|
638 | TBTools.__init__( | |
|
639 | self, | |
|
640 | color_scheme=color_scheme, | |
|
641 | call_pdb=call_pdb, | |
|
642 | ostream=ostream, | |
|
643 | parent=parent, | |
|
644 | config=config, | |
|
645 | debugger_cls=debugger_cls, | |
|
646 | ) | |
|
633 | 647 | self.tb_offset = tb_offset |
|
634 | 648 | self.long_header = long_header |
|
635 | 649 | self.include_vars = include_vars |
@@ -642,7 +656,6 b' class VerboseTB(TBTools):' | |||
|
642 | 656 | check_cache = linecache.checkcache |
|
643 | 657 | self.check_cache = check_cache |
|
644 | 658 | |
|
645 | self.debugger_cls = debugger_cls or debugger.Pdb | |
|
646 | 659 | self.skip_hidden = True |
|
647 | 660 | |
|
648 | 661 | def format_record(self, frame_info): |
@@ -763,7 +776,7 b' class VerboseTB(TBTools):' | |||
|
763 | 776 | self, |
|
764 | 777 | etype: type, |
|
765 | 778 | evalue: BaseException, |
|
766 | etb: TracebackType, | |
|
779 | etb: Optional[TracebackType], | |
|
767 | 780 | number_of_lines_of_context, |
|
768 | 781 | tb_offset: Optional[int], |
|
769 | 782 | ): |
@@ -772,7 +785,6 b' class VerboseTB(TBTools):' | |||
|
772 | 785 | This may be called multiple times by Python 3 exception chaining |
|
773 | 786 | (PEP 3134). |
|
774 | 787 | """ |
|
775 | assert etb is not None | |
|
776 | 788 | # some locals |
|
777 | 789 | orig_etype = etype |
|
778 | 790 | try: |
@@ -783,7 +795,9 b' class VerboseTB(TBTools):' | |||
|
783 | 795 | tb_offset = self.tb_offset if tb_offset is None else tb_offset |
|
784 | 796 | assert isinstance(tb_offset, int) |
|
785 | 797 | head = self.prepare_header(etype, self.long_header) |
|
786 | records = self.get_records(etb, number_of_lines_of_context, tb_offset) | |
|
798 | records = ( | |
|
799 | self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else [] | |
|
800 | ) | |
|
787 | 801 | |
|
788 | 802 | frames = [] |
|
789 | 803 | skipped = 0 |
@@ -822,6 +836,7 b' class VerboseTB(TBTools):' | |||
|
822 | 836 | def get_records( |
|
823 | 837 | self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int |
|
824 | 838 | ): |
|
839 | assert etb is not None | |
|
825 | 840 | context = number_of_lines_of_context - 1 |
|
826 | 841 | after = context // 2 |
|
827 | 842 | before = context - after |
@@ -836,19 +851,17 b' class VerboseTB(TBTools):' | |||
|
836 | 851 | after=after, |
|
837 | 852 | pygments_formatter=formatter, |
|
838 | 853 | ) |
|
839 | assert etb is not None | |
|
840 | 854 | return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:] |
|
841 | 855 | |
|
842 | 856 | def structured_traceback( |
|
843 | 857 | self, |
|
844 | 858 | etype: type, |
|
845 | 859 | evalue: Optional[BaseException], |
|
846 | etb: TracebackType, | |
|
860 | etb: Optional[TracebackType], | |
|
847 | 861 | tb_offset: Optional[int] = None, |
|
848 | 862 | number_of_lines_of_context: int = 5, |
|
849 | 863 | ): |
|
850 | 864 | """Return a nice text document describing the traceback.""" |
|
851 | assert etb is not None | |
|
852 | 865 | formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context, |
|
853 | 866 | tb_offset) |
|
854 | 867 |
@@ -33,7 +33,7 b' from IPython.testing.decorators import skipif_not_numpy' | |||
|
33 | 33 | |
|
34 | 34 | if platform.python_implementation() == "PyPy": |
|
35 | 35 | pytest.skip( |
|
36 | "Current autoreload implementation is extremly slow on PyPy", | |
|
36 | "Current autoreload implementation is extremely slow on PyPy", | |
|
37 | 37 | allow_module_level=True, |
|
38 | 38 | ) |
|
39 | 39 |
@@ -66,7 +66,9 b' class ImportDenier(importlib.abc.MetaPathFinder):' | |||
|
66 | 66 | """ |
|
67 | 67 | Importing %s disabled by IPython, which has |
|
68 | 68 | already imported an Incompatible QT Binding: %s |
|
69 | """ % (fullname, loaded_api())) | |
|
69 | """ | |
|
70 | % (fullname, loaded_api()) | |
|
71 | ) | |
|
70 | 72 | |
|
71 | 73 | |
|
72 | 74 | ID = ImportDenier() |
@@ -9,13 +9,3 b' Extra capabilities for IPython' | |||
|
9 | 9 | # Distributed under the terms of the BSD License. The full license is in |
|
10 | 10 | # the file COPYING, distributed as part of this software. |
|
11 | 11 | #----------------------------------------------------------------------------- |
|
12 | ||
|
13 | #----------------------------------------------------------------------------- | |
|
14 | # Imports | |
|
15 | #----------------------------------------------------------------------------- | |
|
16 | ||
|
17 | from IPython.lib.security import passwd | |
|
18 | ||
|
19 | #----------------------------------------------------------------------------- | |
|
20 | # Code | |
|
21 | #----------------------------------------------------------------------------- |
@@ -92,8 +92,8 b' class Audio(DisplayObject):' | |||
|
92 | 92 | |
|
93 | 93 | From a File: |
|
94 | 94 | |
|
95 |
>>> Audio(' |
|
|
96 |
>>> Audio(filename=' |
|
|
95 | >>> Audio('IPython/lib/tests/test.wav') # doctest: +SKIP | |
|
96 | >>> Audio(filename='IPython/lib/tests/test.wav') # doctest: +SKIP | |
|
97 | 97 | |
|
98 | 98 | From Bytes: |
|
99 | 99 | |
@@ -103,9 +103,9 b' class Audio(DisplayObject):' | |||
|
103 | 103 | See Also |
|
104 | 104 | -------- |
|
105 | 105 | ipywidgets.Audio |
|
106 | ||
|
107 |
A |
|
|
108 | ||
|
106 | ||
|
107 | Audio widget with more more flexibility and options. | |
|
108 | ||
|
109 | 109 | """ |
|
110 | 110 | _read_flags = 'rb' |
|
111 | 111 | |
@@ -510,12 +510,10 b' class FileLinks(FileLink):' | |||
|
510 | 510 | |
|
511 | 511 | self.recursive = recursive |
|
512 | 512 | |
|
513 |
def _get_display_formatter( |
|
|
514 | dirname_output_format, | |
|
515 | fname_output_format, | |
|
516 | fp_format, | |
|
517 | fp_cleaner=None): | |
|
518 | """ generate built-in formatter function | |
|
513 | def _get_display_formatter( | |
|
514 | self, dirname_output_format, fname_output_format, fp_format, fp_cleaner=None | |
|
515 | ): | |
|
516 | """generate built-in formatter function | |
|
519 | 517 | |
|
520 | 518 | this is used to define both the notebook and terminal built-in |
|
521 | 519 | formatters as they only differ by some wrapper text for each entry |
@@ -4,14 +4,15 b'' | |||
|
4 | 4 | # Copyright (c) IPython Development Team. |
|
5 | 5 | # Distributed under the terms of the Modified BSD License. |
|
6 | 6 | |
|
7 | import pytest | |
|
8 | 7 | import types |
|
9 | ||
|
10 | 8 | from pathlib import Path |
|
11 | 9 | |
|
10 | import pytest | |
|
11 | from tempfile import TemporaryDirectory | |
|
12 | ||
|
13 | from IPython.lib.deepreload import modules_reloading | |
|
14 | from IPython.lib.deepreload import reload as dreload | |
|
12 | 15 | from IPython.utils.syspathcontext import prepended_to_syspath |
|
13 | from IPython.utils.tempdir import TemporaryDirectory | |
|
14 | from IPython.lib.deepreload import reload as dreload, modules_reloading | |
|
15 | 16 | |
|
16 | 17 | |
|
17 | 18 | def test_deepreload(): |
@@ -31,7 +31,7 b' def no_op(*args, **kwargs):' | |||
|
31 | 31 | |
|
32 | 32 | |
|
33 | 33 | @onlyif_cmds_exist("latex", "dvipng") |
|
34 |
@pytest.mark.parametrize("s, wrap", [( |
|
|
34 | @pytest.mark.parametrize("s, wrap", [("$$x^2$$", False), ("x^2", True)]) | |
|
35 | 35 | def test_latex_to_png_dvipng_runs(s, wrap): |
|
36 | 36 | """ |
|
37 | 37 | Test that latex_to_png_dvipng just runs without error. |
@@ -273,7 +273,7 b' def test_unicode_repr():' | |||
|
273 | 273 | p = pretty.pretty(c) |
|
274 | 274 | assert p == u |
|
275 | 275 | p = pretty.pretty([c]) |
|
276 |
assert p == |
|
|
276 | assert p == "[%s]" % u | |
|
277 | 277 | |
|
278 | 278 | |
|
279 | 279 | def test_basic_class(): |
@@ -220,6 +220,8 b' except Exception:' | |||
|
220 | 220 | # for tokenizing blocks |
|
221 | 221 | COMMENT, INPUT, OUTPUT = range(3) |
|
222 | 222 | |
|
223 | PSEUDO_DECORATORS = ["suppress", "verbatim", "savefig", "doctest"] | |
|
224 | ||
|
223 | 225 | #----------------------------------------------------------------------------- |
|
224 | 226 | # Functions and class declarations |
|
225 | 227 | #----------------------------------------------------------------------------- |
@@ -263,11 +265,17 b' def block_parser(part, rgxin, rgxout, fmtin, fmtout):' | |||
|
263 | 265 | block.append((COMMENT, line)) |
|
264 | 266 | continue |
|
265 | 267 | |
|
266 | if line_stripped.startswith('@'): | |
|
267 | # Here is where we assume there is, at most, one decorator. | |
|
268 | # Might need to rethink this. | |
|
269 | decorator = line_stripped | |
|
270 |
|
|
|
268 | if any( | |
|
269 | line_stripped.startswith("@" + pseudo_decorator) | |
|
270 | for pseudo_decorator in PSEUDO_DECORATORS | |
|
271 | ): | |
|
272 | if decorator: | |
|
273 | raise RuntimeError( | |
|
274 | "Applying multiple pseudo-decorators on one line is not supported" | |
|
275 | ) | |
|
276 | else: | |
|
277 | decorator = line_stripped | |
|
278 | continue | |
|
271 | 279 | |
|
272 | 280 | # does this look like an input line? |
|
273 | 281 | matchin = rgxin.match(line) |
@@ -68,6 +68,8 b' class TerminalPdb(Pdb):' | |||
|
68 | 68 | self.debugger_history = FileHistory(os.path.expanduser(str(p))) |
|
69 | 69 | else: |
|
70 | 70 | self.debugger_history = InMemoryHistory() |
|
71 | else: | |
|
72 | self.debugger_history = self.shell.debugger_history | |
|
71 | 73 | |
|
72 | 74 | options = dict( |
|
73 | 75 | message=(lambda: PygmentsTokens(get_prompt_tokens())), |
@@ -3,12 +3,10 b'' | |||
|
3 | 3 | import asyncio |
|
4 | 4 | import os |
|
5 | 5 | import sys |
|
6 | import warnings | |
|
7 | 6 | from warnings import warn |
|
8 | 7 | |
|
9 | 8 | from IPython.core.async_helpers import get_asyncio_loop |
|
10 | 9 | from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC |
|
11 | from IPython.utils import io | |
|
12 | 10 | from IPython.utils.py3compat import input |
|
13 | 11 | from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title |
|
14 | 12 | from IPython.utils.process import abbrev_cwd |
@@ -32,7 +30,7 b' from prompt_toolkit.auto_suggest import AutoSuggestFromHistory' | |||
|
32 | 30 | from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode |
|
33 | 31 | from prompt_toolkit.filters import (HasFocus, Condition, IsDone) |
|
34 | 32 | from prompt_toolkit.formatted_text import PygmentsTokens |
|
35 |
from prompt_toolkit.history import |
|
|
33 | from prompt_toolkit.history import History | |
|
36 | 34 | from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor |
|
37 | 35 | from prompt_toolkit.output import ColorDepth |
|
38 | 36 | from prompt_toolkit.patch_stdout import patch_stdout |
@@ -116,6 +114,59 b' def black_reformat_handler(text_before_cursor):' | |||
|
116 | 114 | return formatted_text |
|
117 | 115 | |
|
118 | 116 | |
|
117 | def yapf_reformat_handler(text_before_cursor): | |
|
118 | from yapf.yapflib import file_resources | |
|
119 | from yapf.yapflib import yapf_api | |
|
120 | ||
|
121 | style_config = file_resources.GetDefaultStyleForDir(os.getcwd()) | |
|
122 | formatted_text, was_formatted = yapf_api.FormatCode( | |
|
123 | text_before_cursor, style_config=style_config | |
|
124 | ) | |
|
125 | if was_formatted: | |
|
126 | if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"): | |
|
127 | formatted_text = formatted_text[:-1] | |
|
128 | return formatted_text | |
|
129 | else: | |
|
130 | return text_before_cursor | |
|
131 | ||
|
132 | ||
|
133 | class PtkHistoryAdapter(History): | |
|
134 | """ | |
|
135 | Prompt toolkit has it's own way of handling history, Where it assumes it can | |
|
136 | Push/pull from history. | |
|
137 | ||
|
138 | """ | |
|
139 | ||
|
140 | def __init__(self, shell): | |
|
141 | super().__init__() | |
|
142 | self.shell = shell | |
|
143 | self._refresh() | |
|
144 | ||
|
145 | def append_string(self, string): | |
|
146 | # we rely on sql for that. | |
|
147 | self._loaded = False | |
|
148 | self._refresh() | |
|
149 | ||
|
150 | def _refresh(self): | |
|
151 | if not self._loaded: | |
|
152 | self._loaded_strings = list(self.load_history_strings()) | |
|
153 | ||
|
154 | def load_history_strings(self): | |
|
155 | last_cell = "" | |
|
156 | res = [] | |
|
157 | for __, ___, cell in self.shell.history_manager.get_tail( | |
|
158 | self.shell.history_load_length, include_latest=True | |
|
159 | ): | |
|
160 | # Ignore blank lines and consecutive duplicates | |
|
161 | cell = cell.rstrip() | |
|
162 | if cell and (cell != last_cell): | |
|
163 | res.append(cell) | |
|
164 | last_cell = cell | |
|
165 | yield from res[::-1] | |
|
166 | ||
|
167 | def store_string(self, string: str) -> None: | |
|
168 | pass | |
|
169 | ||
|
119 | 170 | class TerminalInteractiveShell(InteractiveShell): |
|
120 | 171 | mime_renderers = Dict().tag(config=True) |
|
121 | 172 | |
@@ -184,8 +235,8 b' class TerminalInteractiveShell(InteractiveShell):' | |||
|
184 | 235 | ).tag(config=True) |
|
185 | 236 | |
|
186 | 237 | autoformatter = Unicode( |
|
187 |
|
|
|
188 | help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`", | |
|
238 | None, | |
|
239 | help="Autoformatter to reformat Terminal code. Can be `'black'`, `'yapf'` or `None`", | |
|
189 | 240 | allow_none=True |
|
190 | 241 | ).tag(config=True) |
|
191 | 242 | |
@@ -232,6 +283,8 b' class TerminalInteractiveShell(InteractiveShell):' | |||
|
232 | 283 | self.reformat_handler = lambda x:x |
|
233 | 284 | elif formatter == 'black': |
|
234 | 285 | self.reformat_handler = black_reformat_handler |
|
286 | elif formatter == "yapf": | |
|
287 | self.reformat_handler = yapf_reformat_handler | |
|
235 | 288 | else: |
|
236 | 289 | raise ValueError |
|
237 | 290 | |
@@ -379,16 +432,9 b' class TerminalInteractiveShell(InteractiveShell):' | |||
|
379 | 432 | # Set up keyboard shortcuts |
|
380 | 433 | key_bindings = create_ipython_shortcuts(self) |
|
381 | 434 | |
|
435 | ||
|
382 | 436 | # Pre-populate history from IPython's history database |
|
383 |
history = |
|
|
384 | last_cell = u"" | |
|
385 | for __, ___, cell in self.history_manager.get_tail(self.history_load_length, | |
|
386 | include_latest=True): | |
|
387 | # Ignore blank lines and consecutive duplicates | |
|
388 | cell = cell.rstrip() | |
|
389 | if cell and (cell != last_cell): | |
|
390 | history.append_string(cell) | |
|
391 | last_cell = cell | |
|
437 | history = PtkHistoryAdapter(self) | |
|
392 | 438 | |
|
393 | 439 | self._style = self._make_style_from_name_or_cls(self.highlighting_style) |
|
394 | 440 | self.style = DynamicStyle(lambda: self._style) |
@@ -568,7 +614,6 b' class TerminalInteractiveShell(InteractiveShell):' | |||
|
568 | 614 | def enable_win_unicode_console(self): |
|
569 | 615 | # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows |
|
570 | 616 | # console by default, so WUC shouldn't be needed. |
|
571 | from warnings import warn | |
|
572 | 617 | warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future", |
|
573 | 618 | DeprecationWarning, |
|
574 | 619 | stacklevel=2) |
@@ -1,7 +1,7 b'' | |||
|
1 | 1 | #!/usr/bin/env python |
|
2 | 2 | # encoding: utf-8 |
|
3 | 3 | """ |
|
4 |
The :class:`~ |
|
|
4 | The :class:`~traitlets.config.application.Application` object for the command | |
|
5 | 5 | line :command:`ipython` program. |
|
6 | 6 | """ |
|
7 | 7 |
@@ -53,7 +53,7 b' class TerminalMagics(Magics):' | |||
|
53 | 53 | self.shell.user_ns['pasted_block'] = b |
|
54 | 54 | self.shell.using_paste_magics = True |
|
55 | 55 | try: |
|
56 | self.shell.run_cell(b) | |
|
56 | self.shell.run_cell(b, store_history=True) | |
|
57 | 57 | finally: |
|
58 | 58 | self.shell.using_paste_magics = False |
|
59 | 59 |
@@ -10,6 +10,7 b' import warnings' | |||
|
10 | 10 | import signal |
|
11 | 11 | import sys |
|
12 | 12 | import re |
|
13 | import os | |
|
13 | 14 | from typing import Callable |
|
14 | 15 | |
|
15 | 16 | |
@@ -56,7 +57,7 b' def create_ipython_shortcuts(shell):' | |||
|
56 | 57 | & insert_mode |
|
57 | 58 | ))(reformat_and_execute) |
|
58 | 59 | |
|
59 |
kb.add( |
|
|
60 | kb.add("c-\\")(quit) | |
|
60 | 61 | |
|
61 | 62 | kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER)) |
|
62 | 63 | )(previous_history_or_previous_completion) |
@@ -139,12 +140,24 b' def create_ipython_shortcuts(shell):' | |||
|
139 | 140 | event.current_buffer.insert_text("{}") |
|
140 | 141 | event.current_buffer.cursor_left() |
|
141 | 142 | |
|
142 | @kb.add('"', filter=focused_insert & auto_match & following_text(r"[,)}\]]|$")) | |
|
143 | @kb.add( | |
|
144 | '"', | |
|
145 | filter=focused_insert | |
|
146 | & auto_match | |
|
147 | & preceding_text(r'^([^"]+|"[^"]*")*$') | |
|
148 | & following_text(r"[,)}\]]|$"), | |
|
149 | ) | |
|
143 | 150 | def _(event): |
|
144 | 151 | event.current_buffer.insert_text('""') |
|
145 | 152 | event.current_buffer.cursor_left() |
|
146 | 153 | |
|
147 | @kb.add("'", filter=focused_insert & auto_match & following_text(r"[,)}\]]|$")) | |
|
154 | @kb.add( | |
|
155 | "'", | |
|
156 | filter=focused_insert | |
|
157 | & auto_match | |
|
158 | & preceding_text(r"^([^']+|'[^']*')*$") | |
|
159 | & following_text(r"[,)}\]]|$"), | |
|
160 | ) | |
|
148 | 161 | def _(event): |
|
149 | 162 | event.current_buffer.insert_text("''") |
|
150 | 163 | event.current_buffer.cursor_left() |
@@ -186,16 +199,6 b' def create_ipython_shortcuts(shell):' | |||
|
186 | 199 | event.current_buffer.insert_text("{}" + dashes) |
|
187 | 200 | event.current_buffer.cursor_left(len(dashes) + 1) |
|
188 | 201 | |
|
189 | @kb.add('"', filter=focused_insert & auto_match & preceding_text(r".*(r|R)$")) | |
|
190 | def _(event): | |
|
191 | event.current_buffer.insert_text('""') | |
|
192 | event.current_buffer.cursor_left() | |
|
193 | ||
|
194 | @kb.add("'", filter=focused_insert & auto_match & preceding_text(r".*(r|R)$")) | |
|
195 | def _(event): | |
|
196 | event.current_buffer.insert_text("''") | |
|
197 | event.current_buffer.cursor_left() | |
|
198 | ||
|
199 | 202 | # just move cursor |
|
200 | 203 | @kb.add(")", filter=focused_insert & auto_match & following_text(r"^\)")) |
|
201 | 204 | @kb.add("]", filter=focused_insert & auto_match & following_text(r"^\]")) |
@@ -265,15 +268,22 b' def create_ipython_shortcuts(shell):' | |||
|
265 | 268 | focused_insert_vi = has_focus(DEFAULT_BUFFER) & vi_insert_mode |
|
266 | 269 | |
|
267 | 270 | # Needed for to accept autosuggestions in vi insert mode |
|
268 | @kb.add("c-e", filter=focused_insert_vi & ebivim) | |
|
269 | def _(event): | |
|
271 | def _apply_autosuggest(event): | |
|
270 | 272 | b = event.current_buffer |
|
271 | 273 | suggestion = b.suggestion |
|
272 | if suggestion: | |
|
274 | if suggestion is not None and suggestion.text: | |
|
273 | 275 | b.insert_text(suggestion.text) |
|
274 | 276 | else: |
|
275 | 277 | nc.end_of_line(event) |
|
276 | 278 | |
|
279 | @kb.add("end", filter=has_focus(DEFAULT_BUFFER) & (ebivim | ~vi_insert_mode)) | |
|
280 | def _(event): | |
|
281 | _apply_autosuggest(event) | |
|
282 | ||
|
283 | @kb.add("c-e", filter=focused_insert_vi & ebivim) | |
|
284 | def _(event): | |
|
285 | _apply_autosuggest(event) | |
|
286 | ||
|
277 | 287 | @kb.add("c-f", filter=focused_insert_vi) |
|
278 | 288 | def _(event): |
|
279 | 289 | b = event.current_buffer |
@@ -336,12 +346,7 b' def create_ipython_shortcuts(shell):' | |||
|
336 | 346 | shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6) |
|
337 | 347 | cursor = "\x1b[{} q".format(shape) |
|
338 | 348 | |
|
339 | if hasattr(sys.stdout, "_cli"): | |
|
340 | write = sys.stdout._cli.output.write_raw | |
|
341 | else: | |
|
342 | write = sys.stdout.write | |
|
343 | ||
|
344 | write(cursor) | |
|
349 | sys.stdout.write(cursor) | |
|
345 | 350 | sys.stdout.flush() |
|
346 | 351 | |
|
347 | 352 | self._input_mode = mode |
@@ -454,11 +459,16 b' def reset_search_buffer(event):' | |||
|
454 | 459 | def suspend_to_bg(event): |
|
455 | 460 | event.app.suspend_to_background() |
|
456 | 461 | |
|
457 |
def |
|
|
462 | def quit(event): | |
|
458 | 463 | """ |
|
459 | Force exit (with a non-zero return value) | |
|
464 | On platforms that support SIGQUIT, send SIGQUIT to the current process. | |
|
465 | On other platforms, just exit the process with a message. | |
|
460 | 466 | """ |
|
461 | sys.exit("Quit") | |
|
467 | sigquit = getattr(signal, "SIGQUIT", None) | |
|
468 | if sigquit is not None: | |
|
469 | os.kill(0, signal.SIGQUIT) | |
|
470 | else: | |
|
471 | sys.exit("Quit") | |
|
462 | 472 | |
|
463 | 473 | def indent_buffer(event): |
|
464 | 474 | event.current_buffer.insert_text(' ' * 4) |
@@ -38,7 +38,7 b' def ipfunc():' | |||
|
38 | 38 | ....: print(i, end=' ') |
|
39 | 39 | ....: print(i+1, end=' ') |
|
40 | 40 | ....: |
|
41 |
0 1 1 2 2 3 |
|
|
41 | 0 1 1 2 2 3 | |
|
42 | 42 | |
|
43 | 43 | |
|
44 | 44 | It's OK to use '_' for the last result, but do NOT try to use IPython's |
@@ -50,7 +50,7 b' def ipfunc():' | |||
|
50 | 50 | |
|
51 | 51 | In [8]: print(repr(_)) |
|
52 | 52 | 'hi' |
|
53 | ||
|
53 | ||
|
54 | 54 | In [7]: 3+4 |
|
55 | 55 | Out[7]: 7 |
|
56 | 56 | |
@@ -60,7 +60,7 b' def ipfunc():' | |||
|
60 | 60 | In [9]: ipfunc() |
|
61 | 61 | Out[9]: 'ipfunc' |
|
62 | 62 | """ |
|
63 |
return |
|
|
63 | return "ipfunc" | |
|
64 | 64 | |
|
65 | 65 | |
|
66 | 66 | def ipos(): |
@@ -14,6 +14,7 b' import traceback' | |||
|
14 | 14 | import types |
|
15 | 15 | import warnings |
|
16 | 16 | from contextlib import contextmanager |
|
17 | from pathlib import Path | |
|
17 | 18 | from typing import Any |
|
18 | 19 | from typing import Callable |
|
19 | 20 | from typing import Dict |
@@ -28,8 +29,6 b' from typing import Type' | |||
|
28 | 29 | from typing import TYPE_CHECKING |
|
29 | 30 | from typing import Union |
|
30 | 31 | |
|
31 | import py.path | |
|
32 | ||
|
33 | 32 | import pytest |
|
34 | 33 | from _pytest import outcomes |
|
35 | 34 | from _pytest._code.code import ExceptionInfo |
@@ -42,6 +41,7 b' from _pytest.config.argparsing import Parser' | |||
|
42 | 41 | from _pytest.fixtures import FixtureRequest |
|
43 | 42 | from _pytest.nodes import Collector |
|
44 | 43 | from _pytest.outcomes import OutcomeException |
|
44 | from _pytest.pathlib import fnmatch_ex | |
|
45 | 45 | from _pytest.pathlib import import_path |
|
46 | 46 | from _pytest.python_api import approx |
|
47 | 47 | from _pytest.warning_types import PytestWarning |
@@ -126,35 +126,55 b' def pytest_unconfigure() -> None:' | |||
|
126 | 126 | |
|
127 | 127 | |
|
128 | 128 | def pytest_collect_file( |
|
129 |
path: |
|
|
129 | file_path: Path, | |
|
130 | 130 | parent: Collector, |
|
131 | 131 | ) -> Optional[Union["IPDoctestModule", "IPDoctestTextfile"]]: |
|
132 | 132 | config = parent.config |
|
133 |
if path. |
|
|
134 |
if config.option.ipdoctestmodules and not |
|
|
135 | mod: IPDoctestModule = IPDoctestModule.from_parent(parent, fspath=path) | |
|
133 | if file_path.suffix == ".py": | |
|
134 | if config.option.ipdoctestmodules and not any( | |
|
135 | (_is_setup_py(file_path), _is_main_py(file_path)) | |
|
136 | ): | |
|
137 | mod: IPDoctestModule = IPDoctestModule.from_parent(parent, path=file_path) | |
|
136 | 138 | return mod |
|
137 | elif _is_ipdoctest(config, path, parent): | |
|
138 |
txt: IPDoctestTextfile = IPDoctestTextfile.from_parent(parent, |
|
|
139 | elif _is_ipdoctest(config, file_path, parent): | |
|
140 | txt: IPDoctestTextfile = IPDoctestTextfile.from_parent(parent, path=file_path) | |
|
139 | 141 | return txt |
|
140 | 142 | return None |
|
141 | 143 | |
|
142 | 144 | |
|
143 | def _is_setup_py(path: py.path.local) -> bool: | |
|
144 | if path.basename != "setup.py": | |
|
145 | if int(pytest.__version__.split(".")[0]) < 7: | |
|
146 | _collect_file = pytest_collect_file | |
|
147 | ||
|
148 | def pytest_collect_file( | |
|
149 | path, | |
|
150 | parent: Collector, | |
|
151 | ) -> Optional[Union["IPDoctestModule", "IPDoctestTextfile"]]: | |
|
152 | return _collect_file(Path(path), parent) | |
|
153 | ||
|
154 | _import_path = import_path | |
|
155 | ||
|
156 | def import_path(path, root): | |
|
157 | import py.path | |
|
158 | ||
|
159 | return _import_path(py.path.local(path)) | |
|
160 | ||
|
161 | ||
|
162 | def _is_setup_py(path: Path) -> bool: | |
|
163 | if path.name != "setup.py": | |
|
145 | 164 | return False |
|
146 |
contents = path.read_b |
|
|
165 | contents = path.read_bytes() | |
|
147 | 166 | return b"setuptools" in contents or b"distutils" in contents |
|
148 | 167 | |
|
149 | 168 | |
|
150 |
def _is_ipdoctest(config: Config, path: |
|
|
151 |
if path. |
|
|
169 | def _is_ipdoctest(config: Config, path: Path, parent: Collector) -> bool: | |
|
170 | if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path): | |
|
152 | 171 | return True |
|
153 | 172 | globs = config.getoption("ipdoctestglob") or ["test*.txt"] |
|
154 | for glob in globs: | |
|
155 | if path.check(fnmatch=glob): | |
|
156 | return True | |
|
157 | return False | |
|
173 | return any(fnmatch_ex(glob, path) for glob in globs) | |
|
174 | ||
|
175 | ||
|
176 | def _is_main_py(path: Path) -> bool: | |
|
177 | return path.name == "__main__.py" | |
|
158 | 178 | |
|
159 | 179 | |
|
160 | 180 | class ReprFailDoctest(TerminalRepr): |
@@ -273,7 +293,7 b' class IPDoctestItem(pytest.Item):' | |||
|
273 | 293 | runner: "IPDocTestRunner", |
|
274 | 294 | dtest: "doctest.DocTest", |
|
275 | 295 | ): |
|
276 |
# incompatible signature due to |
|
|
296 | # incompatible signature due to imposed limits on subclass | |
|
277 | 297 | """The public named constructor.""" |
|
278 | 298 | return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest) |
|
279 | 299 | |
@@ -372,61 +392,63 b' class IPDoctestItem(pytest.Item):' | |||
|
372 | 392 | elif isinstance(excinfo.value, MultipleDoctestFailures): |
|
373 | 393 | failures = excinfo.value.failures |
|
374 | 394 | |
|
375 |
if failures is |
|
|
376 | reprlocation_lines = [] | |
|
377 | for failure in failures: | |
|
378 | example = failure.example | |
|
379 | test = failure.test | |
|
380 | filename = test.filename | |
|
381 | if test.lineno is None: | |
|
382 | lineno = None | |
|
383 | else: | |
|
384 | lineno = test.lineno + example.lineno + 1 | |
|
385 | message = type(failure).__name__ | |
|
386 | # TODO: ReprFileLocation doesn't expect a None lineno. | |
|
387 | reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type] | |
|
388 | checker = _get_checker() | |
|
389 | report_choice = _get_report_choice( | |
|
390 | self.config.getoption("ipdoctestreport") | |
|
391 | ) | |
|
392 | if lineno is not None: | |
|
393 | assert failure.test.docstring is not None | |
|
394 | lines = failure.test.docstring.splitlines(False) | |
|
395 | # add line numbers to the left of the error message | |
|
396 | assert test.lineno is not None | |
|
397 | lines = [ | |
|
398 | "%03d %s" % (i + test.lineno + 1, x) | |
|
399 | for (i, x) in enumerate(lines) | |
|
400 | ] | |
|
401 | # trim docstring error lines to 10 | |
|
402 | lines = lines[max(example.lineno - 9, 0) : example.lineno + 1] | |
|
403 | else: | |
|
404 | lines = [ | |
|
405 | "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example" | |
|
406 | ] | |
|
407 | indent = ">>>" | |
|
408 | for line in example.source.splitlines(): | |
|
409 | lines.append(f"??? {indent} {line}") | |
|
410 | indent = "..." | |
|
411 | if isinstance(failure, doctest.DocTestFailure): | |
|
412 | lines += checker.output_difference( | |
|
413 | example, failure.got, report_choice | |
|
414 | ).split("\n") | |
|
415 | else: | |
|
416 | inner_excinfo = ExceptionInfo(failure.exc_info) | |
|
417 | lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)] | |
|
418 | lines += [ | |
|
419 | x.strip("\n") | |
|
420 | for x in traceback.format_exception(*failure.exc_info) | |
|
421 | ] | |
|
422 | reprlocation_lines.append((reprlocation, lines)) | |
|
423 | return ReprFailDoctest(reprlocation_lines) | |
|
424 | else: | |
|
395 | if failures is None: | |
|
425 | 396 | return super().repr_failure(excinfo) |
|
426 | 397 | |
|
427 | def reportinfo(self): | |
|
398 | reprlocation_lines = [] | |
|
399 | for failure in failures: | |
|
400 | example = failure.example | |
|
401 | test = failure.test | |
|
402 | filename = test.filename | |
|
403 | if test.lineno is None: | |
|
404 | lineno = None | |
|
405 | else: | |
|
406 | lineno = test.lineno + example.lineno + 1 | |
|
407 | message = type(failure).__name__ | |
|
408 | # TODO: ReprFileLocation doesn't expect a None lineno. | |
|
409 | reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type] | |
|
410 | checker = _get_checker() | |
|
411 | report_choice = _get_report_choice(self.config.getoption("ipdoctestreport")) | |
|
412 | if lineno is not None: | |
|
413 | assert failure.test.docstring is not None | |
|
414 | lines = failure.test.docstring.splitlines(False) | |
|
415 | # add line numbers to the left of the error message | |
|
416 | assert test.lineno is not None | |
|
417 | lines = [ | |
|
418 | "%03d %s" % (i + test.lineno + 1, x) for (i, x) in enumerate(lines) | |
|
419 | ] | |
|
420 | # trim docstring error lines to 10 | |
|
421 | lines = lines[max(example.lineno - 9, 0) : example.lineno + 1] | |
|
422 | else: | |
|
423 | lines = [ | |
|
424 | "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example" | |
|
425 | ] | |
|
426 | indent = ">>>" | |
|
427 | for line in example.source.splitlines(): | |
|
428 | lines.append(f"??? {indent} {line}") | |
|
429 | indent = "..." | |
|
430 | if isinstance(failure, doctest.DocTestFailure): | |
|
431 | lines += checker.output_difference( | |
|
432 | example, failure.got, report_choice | |
|
433 | ).split("\n") | |
|
434 | else: | |
|
435 | inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info) | |
|
436 | lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)] | |
|
437 | lines += [ | |
|
438 | x.strip("\n") for x in traceback.format_exception(*failure.exc_info) | |
|
439 | ] | |
|
440 | reprlocation_lines.append((reprlocation, lines)) | |
|
441 | return ReprFailDoctest(reprlocation_lines) | |
|
442 | ||
|
443 | def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]: | |
|
428 | 444 | assert self.dtest is not None |
|
429 |
return self. |
|
|
445 | return self.path, self.dtest.lineno, "[ipdoctest] %s" % self.name | |
|
446 | ||
|
447 | if int(pytest.__version__.split(".")[0]) < 7: | |
|
448 | ||
|
449 | @property | |
|
450 | def path(self) -> Path: | |
|
451 | return Path(self.fspath) | |
|
430 | 452 | |
|
431 | 453 | |
|
432 | 454 | def _get_flag_lookup() -> Dict[str, int]: |
@@ -474,9 +496,9 b' class IPDoctestTextfile(pytest.Module):' | |||
|
474 | 496 | # Inspired by doctest.testfile; ideally we would use it directly, |
|
475 | 497 | # but it doesn't support passing a custom checker. |
|
476 | 498 | encoding = self.config.getini("ipdoctest_encoding") |
|
477 |
text = self. |
|
|
478 |
filename = str(self. |
|
|
479 |
name = self. |
|
|
499 | text = self.path.read_text(encoding) | |
|
500 | filename = str(self.path) | |
|
501 | name = self.path.name | |
|
480 | 502 | globs = {"__name__": "__main__"} |
|
481 | 503 | |
|
482 | 504 | optionflags = get_optionflags(self) |
@@ -495,6 +517,27 b' class IPDoctestTextfile(pytest.Module):' | |||
|
495 | 517 | self, name=test.name, runner=runner, dtest=test |
|
496 | 518 | ) |
|
497 | 519 | |
|
520 | if int(pytest.__version__.split(".")[0]) < 7: | |
|
521 | ||
|
522 | @property | |
|
523 | def path(self) -> Path: | |
|
524 | return Path(self.fspath) | |
|
525 | ||
|
526 | @classmethod | |
|
527 | def from_parent( | |
|
528 | cls, | |
|
529 | parent, | |
|
530 | *, | |
|
531 | fspath=None, | |
|
532 | path: Optional[Path] = None, | |
|
533 | **kw, | |
|
534 | ): | |
|
535 | if path is not None: | |
|
536 | import py.path | |
|
537 | ||
|
538 | fspath = py.path.local(path) | |
|
539 | return super().from_parent(parent=parent, fspath=fspath, **kw) | |
|
540 | ||
|
498 | 541 | |
|
499 | 542 | def _check_all_skipped(test: "doctest.DocTest") -> None: |
|
500 | 543 | """Raise pytest.skip() if all examples in the given DocTest have the SKIP |
@@ -559,15 +602,20 b' class IPDoctestModule(pytest.Module):' | |||
|
559 | 602 | |
|
560 | 603 | def _find_lineno(self, obj, source_lines): |
|
561 | 604 | """Doctest code does not take into account `@property`, this |
|
562 | is a hackish way to fix it. | |
|
605 | is a hackish way to fix it. https://bugs.python.org/issue17446 | |
|
563 | 606 | |
|
564 | https://bugs.python.org/issue17446 | |
|
607 | Wrapped Doctests will need to be unwrapped so the correct | |
|
608 | line number is returned. This will be reported upstream. #8796 | |
|
565 | 609 | """ |
|
566 | 610 | if isinstance(obj, property): |
|
567 | 611 | obj = getattr(obj, "fget", obj) |
|
612 | ||
|
613 | if hasattr(obj, "__wrapped__"): | |
|
614 | # Get the main obj in case of it being wrapped | |
|
615 | obj = inspect.unwrap(obj) | |
|
616 | ||
|
568 | 617 | # Type ignored because this is a private function. |
|
569 |
return |
|
|
570 | self, | |
|
618 | return super()._find_lineno( # type:ignore[misc] | |
|
571 | 619 | obj, |
|
572 | 620 | source_lines, |
|
573 | 621 | ) |
@@ -580,20 +628,28 b' class IPDoctestModule(pytest.Module):' | |||
|
580 | 628 | with _patch_unwrap_mock_aware(): |
|
581 | 629 | |
|
582 | 630 | # Type ignored because this is a private function. |
|
583 |
|
|
|
584 |
|
|
|
631 | super()._find( # type:ignore[misc] | |
|
632 | tests, obj, name, module, source_lines, globs, seen | |
|
585 | 633 | ) |
|
586 | 634 | |
|
587 |
if self. |
|
|
588 | module = self.config.pluginmanager._importconftest( | |
|
589 |
|
|
|
590 | ) | |
|
635 | if self.path.name == "conftest.py": | |
|
636 | if int(pytest.__version__.split(".")[0]) < 7: | |
|
637 | module = self.config.pluginmanager._importconftest( | |
|
638 | self.path, | |
|
639 | self.config.getoption("importmode"), | |
|
640 | ) | |
|
641 | else: | |
|
642 | module = self.config.pluginmanager._importconftest( | |
|
643 | self.path, | |
|
644 | self.config.getoption("importmode"), | |
|
645 | rootpath=self.config.rootpath, | |
|
646 | ) | |
|
591 | 647 | else: |
|
592 | 648 | try: |
|
593 |
module = import_path(self. |
|
|
649 | module = import_path(self.path, root=self.config.rootpath) | |
|
594 | 650 | except ImportError: |
|
595 | 651 | if self.config.getvalue("ipdoctest_ignore_import_errors"): |
|
596 |
pytest.skip("unable to import module %r" % self. |
|
|
652 | pytest.skip("unable to import module %r" % self.path) | |
|
597 | 653 | else: |
|
598 | 654 | raise |
|
599 | 655 | # Uses internal doctest module parsing mechanism. |
@@ -612,6 +668,27 b' class IPDoctestModule(pytest.Module):' | |||
|
612 | 668 | self, name=test.name, runner=runner, dtest=test |
|
613 | 669 | ) |
|
614 | 670 | |
|
671 | if int(pytest.__version__.split(".")[0]) < 7: | |
|
672 | ||
|
673 | @property | |
|
674 | def path(self) -> Path: | |
|
675 | return Path(self.fspath) | |
|
676 | ||
|
677 | @classmethod | |
|
678 | def from_parent( | |
|
679 | cls, | |
|
680 | parent, | |
|
681 | *, | |
|
682 | fspath=None, | |
|
683 | path: Optional[Path] = None, | |
|
684 | **kw, | |
|
685 | ): | |
|
686 | if path is not None: | |
|
687 | import py.path | |
|
688 | ||
|
689 | fspath = py.path.local(path) | |
|
690 | return super().from_parent(parent=parent, fspath=fspath, **kw) | |
|
691 | ||
|
615 | 692 | |
|
616 | 693 | def _setup_fixtures(doctest_item: IPDoctestItem) -> FixtureRequest: |
|
617 | 694 | """Used by IPDoctestTextfile and IPDoctestItem to setup fixture information.""" |
@@ -665,7 +742,7 b' def _init_checker_class() -> Type["IPDoctestOutputChecker"]:' | |||
|
665 | 742 | ) |
|
666 | 743 | |
|
667 | 744 | def check_output(self, want: str, got: str, optionflags: int) -> bool: |
|
668 |
if |
|
|
745 | if super().check_output(want, got, optionflags): | |
|
669 | 746 | return True |
|
670 | 747 | |
|
671 | 748 | allow_unicode = optionflags & _get_allow_unicode_flag() |
@@ -689,7 +766,7 b' def _init_checker_class() -> Type["IPDoctestOutputChecker"]:' | |||
|
689 | 766 | if allow_number: |
|
690 | 767 | got = self._remove_unwanted_precision(want, got) |
|
691 | 768 | |
|
692 |
return |
|
|
769 | return super().check_output(want, got, optionflags) | |
|
693 | 770 | |
|
694 | 771 | def _remove_unwanted_precision(self, want: str, got: str) -> str: |
|
695 | 772 | wants = list(self._number_re.finditer(want)) |
@@ -702,10 +779,7 b' def _init_checker_class() -> Type["IPDoctestOutputChecker"]:' | |||
|
702 | 779 | exponent: Optional[str] = w.group("exponent1") |
|
703 | 780 | if exponent is None: |
|
704 | 781 | exponent = w.group("exponent2") |
|
705 |
if fraction is None |
|
|
706 | precision = 0 | |
|
707 | else: | |
|
708 | precision = len(fraction) | |
|
782 | precision = 0 if fraction is None else len(fraction) | |
|
709 | 783 | if exponent is not None: |
|
710 | 784 | precision -= int(exponent) |
|
711 | 785 | if float(w.group()) == approx(float(g.group()), abs=10 ** -precision): |
@@ -10,8 +10,7 b' from tempfile import TemporaryDirectory' | |||
|
10 | 10 | |
|
11 | 11 | |
|
12 | 12 | class NamedFileInTemporaryDirectory(object): |
|
13 | ||
|
14 | def __init__(self, filename, mode='w+b', bufsize=-1, **kwds): | |
|
13 | def __init__(self, filename, mode="w+b", bufsize=-1, add_to_syspath=False, **kwds): | |
|
15 | 14 | """ |
|
16 | 15 | Open a file named `filename` in a temporary directory. |
|
17 | 16 |
@@ -10,24 +10,23 b' import sys' | |||
|
10 | 10 | import tempfile |
|
11 | 11 | import unittest |
|
12 | 12 | from contextlib import contextmanager |
|
13 | from unittest.mock import patch | |
|
14 | from os.path import join, abspath | |
|
15 | 13 | from importlib import reload |
|
14 | from os.path import abspath, join | |
|
15 | from unittest.mock import patch | |
|
16 | 16 | |
|
17 | 17 | import pytest |
|
18 | from tempfile import TemporaryDirectory | |
|
18 | 19 | |
|
19 | 20 | import IPython |
|
20 | 21 | from IPython import paths |
|
21 | 22 | from IPython.testing import decorators as dec |
|
22 | 23 | from IPython.testing.decorators import ( |
|
24 | onlyif_unicode_paths, | |
|
23 | 25 | skip_if_not_win32, |
|
24 | 26 | skip_win32, |
|
25 | onlyif_unicode_paths, | |
|
26 | 27 | ) |
|
27 | 28 | from IPython.testing.tools import make_tempfile |
|
28 | 29 | from IPython.utils import path |
|
29 | from IPython.utils.tempdir import TemporaryDirectory | |
|
30 | ||
|
31 | 30 | |
|
32 | 31 | # Platform-dependent imports |
|
33 | 32 | try: |
@@ -41,6 +40,7 b' except ImportError:' | |||
|
41 | 40 | import winreg as wreg |
|
42 | 41 | except ImportError: |
|
43 | 42 | import _winreg as wreg |
|
43 | ||
|
44 | 44 | #Add entries that needs to be stubbed by the testing code |
|
45 | 45 | (wreg.OpenKey, wreg.QueryValueEx,) = (None, None) |
|
46 | 46 |
@@ -470,11 +470,11 b' def strip_ansi(source):' | |||
|
470 | 470 | |
|
471 | 471 | class EvalFormatter(Formatter): |
|
472 | 472 | """A String Formatter that allows evaluation of simple expressions. |
|
473 | ||
|
474 | Note that this version interprets a : as specifying a format string (as per | |
|
473 | ||
|
474 | Note that this version interprets a `:` as specifying a format string (as per | |
|
475 | 475 | standard string formatting), so if slicing is required, you must explicitly |
|
476 | 476 | create a slice. |
|
477 | ||
|
477 | ||
|
478 | 478 | This is to be used in templating cases, such as the parallel batch |
|
479 | 479 | script templates, where simple arithmetic on arguments is useful. |
|
480 | 480 | |
@@ -690,8 +690,8 b' def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) :' | |||
|
690 | 690 | return ([[_get_or_default(items, c * nrow + r, default=empty) for c in range(ncol)] for r in range(nrow)], info) |
|
691 | 691 | |
|
692 | 692 | |
|
693 |
def columnize(items, row_first=False, separator= |
|
|
694 |
""" |
|
|
693 | def columnize(items, row_first=False, separator=" ", displaywidth=80, spread=False): | |
|
694 | """Transform a list of strings into a single string with columns. | |
|
695 | 695 | |
|
696 | 696 | Parameters |
|
697 | 697 | ---------- |
@@ -3,4 +3,4 b'' | |||
|
3 | 3 | ## Reporting a Vulnerability |
|
4 | 4 | |
|
5 | 5 | All IPython and Jupyter security are handled via security@ipython.org. |
|
6 |
You can find more information |
|
|
6 | You can find more information on the Jupyter website. https://jupyter.org/security |
@@ -5,7 +5,7 b' matrix:' | |||
|
5 | 5 | environment: |
|
6 | 6 | global: |
|
7 | 7 | APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' |
|
8 | COLUMNS: 120 # Appveyor web viwer window width is 130 chars | |
|
8 | COLUMNS: 120 # Appveyor web viewer window width is 130 chars | |
|
9 | 9 | |
|
10 | 10 | matrix: |
|
11 | 11 | - PYTHON: "C:\\Python38" |
@@ -17,22 +17,29 b' For example::' | |||
|
17 | 17 | def __init__(self, ip): |
|
18 | 18 | self.shell = ip |
|
19 | 19 | self.last_x = None |
|
20 | ||
|
20 | ||
|
21 | 21 | def pre_execute(self): |
|
22 | 22 | self.last_x = self.shell.user_ns.get('x', None) |
|
23 | ||
|
23 | ||
|
24 | 24 | def pre_run_cell(self, info): |
|
25 |
print(' |
|
|
26 | ||
|
25 | print('info.raw_cell =', info.raw_cell) | |
|
26 | print('info.store_history =', info.store_history) | |
|
27 | print('info.silent =', info.silent) | |
|
28 | print('info.shell_futures =', info.shell_futures) | |
|
29 | print('info.cell_id =', info.cell_id) | |
|
30 | print(dir(info)) | |
|
31 | ||
|
27 | 32 | def post_execute(self): |
|
28 | 33 | if self.shell.user_ns.get('x', None) != self.last_x: |
|
29 | 34 | print("x changed!") |
|
30 | ||
|
35 | ||
|
31 | 36 | def post_run_cell(self, result): |
|
32 | print('Cell code: "%s"' % result.info.raw_cell) | |
|
33 |
if result.error_before_exec |
|
|
34 |
|
|
|
35 | ||
|
37 | print('result.execution_count = ', result.execution_count) | |
|
38 | print('result.error_before_exec = ', result.error_before_exec) | |
|
39 | print('result.error_in_exec = ', result.error_in_exec) | |
|
40 | print('result.info = ', result.info) | |
|
41 | print('result.result = ', result.result) | |
|
42 | ||
|
36 | 43 | def load_ipython_extension(ip): |
|
37 | 44 | vw = VarWatcher(ip) |
|
38 | 45 | ip.events.register('pre_execute', vw.pre_execute) |
@@ -40,6 +47,13 b' For example::' | |||
|
40 | 47 | ip.events.register('post_execute', vw.post_execute) |
|
41 | 48 | ip.events.register('post_run_cell', vw.post_run_cell) |
|
42 | 49 | |
|
50 | .. versionadded:: 8.3 | |
|
51 | ||
|
52 | Since IPython 8.3 and ipykernel 6.12.1, the ``info`` objects in the callback | |
|
53 | now have a the ``cell_id`` that will be set to the value sent by the | |
|
54 | frontened, when those send it. | |
|
55 | ||
|
56 | ||
|
43 | 57 | |
|
44 | 58 | Events |
|
45 | 59 | ====== |
@@ -135,7 +135,7 b' Metadata' | |||
|
135 | 135 | ^^^^^^^^ |
|
136 | 136 | |
|
137 | 137 | We often want to provide frontends with guidance on how to display the data. To |
|
138 | support this, ``_repr_*_()`` methods (except `_repr_pretty_``?) can also return a ``(data, metadata)`` | |
|
138 | support this, ``_repr_*_()`` methods (except ``_repr_pretty_``?) can also return a ``(data, metadata)`` | |
|
139 | 139 | tuple where ``metadata`` is a dictionary containing arbitrary key-value pairs for |
|
140 | 140 | the frontend to interpret. An example use case is ``_repr_jpeg_()``, which can |
|
141 | 141 | be set to return a jpeg image and a ``{'height': 400, 'width': 600}`` dictionary |
@@ -10,7 +10,7 b' thought to render a number of mimetypes in the shell. This can be used to either' | |||
|
10 | 10 | display inline images if your terminal emulator supports it; or open some |
|
11 | 11 | display results with external file viewers. |
|
12 | 12 | |
|
13 |
Registering new mimetype handlers can so far only be done |
|
|
13 | Registering new mimetype handlers can so far only be done by extensions and | |
|
14 | 14 | requires 4 steps: |
|
15 | 15 | |
|
16 | 16 | - Define a callable that takes 2 parameters:``data`` and ``metadata``; return |
@@ -17,9 +17,9 b' interactively. Its main components are:' | |||
|
17 | 17 | * A powerful interactive Python shell. |
|
18 | 18 | |
|
19 | 19 | |
|
20 | .. image:: ./_images/ipython-6-screenshot.png | |
|
21 | :alt: Screenshot of IPython 6.0 | |
|
22 | :align: center | |
|
20 | .. image:: ./_images/ipython-6-screenshot.png | |
|
21 | :alt: Screenshot of IPython 6.0 | |
|
22 | :align: center | |
|
23 | 23 | |
|
24 | 24 | |
|
25 | 25 | * A `Jupyter <https://jupyter.org/>`_ kernel to work with Python code in Jupyter |
@@ -59,7 +59,7 b" while some other do not. We'll come to this later." | |||
|
59 | 59 | Depending on the exact command you are typing you might realize that sometimes |
|
60 | 60 | :kbd:`Enter` will add a new line, and sometimes it will execute the current |
|
61 | 61 | statement. IPython tries to guess what you are doing, so most of the time you |
|
62 | should not have to care. Though if by any chance IPython does not the right | |
|
62 | should not have to care. Though if by any chance IPython does not do the right | |
|
63 | 63 | thing you can force execution of the current code block by pressing in sequence |
|
64 | 64 | :kbd:`Esc` and :kbd:`Enter`. You can also force the insertion of a new line at |
|
65 | 65 | the position of the cursor by using :kbd:`Ctrl-o`. |
@@ -20,9 +20,6 b'' | |||
|
20 | 20 | .. _ipython: https://ipython.org |
|
21 | 21 | .. _`ipython manual`: https://ipython.org/documentation.html |
|
22 | 22 | .. _ipython_github: http://github.com/ipython/ipython/ |
|
23 | .. _ipython_github_repo: http://github.com/ipython/ipython/ | |
|
24 | .. _ipython_downloads: https://ipython.org/download.html | |
|
25 | .. _ipython_pypi: http://pypi.python.org/pypi/ipython | |
|
26 | 23 | .. _nbviewer: http://nbviewer.ipython.org |
|
27 | 24 | |
|
28 | 25 | .. _ZeroMQ: http://zeromq.org |
@@ -35,8 +32,7 b'' | |||
|
35 | 32 | .. _reST: http://docutils.sourceforge.net/rst.html |
|
36 | 33 | .. _docutils: http://docutils.sourceforge.net |
|
37 | 34 | .. _lyx: http://www.lyx.org |
|
38 |
.. _pep8: http |
|
|
39 | .. _numpy_coding_guide: https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt | |
|
35 | .. _pep8: https://peps.python.org/pep-0008/ | |
|
40 | 36 | |
|
41 | 37 | .. Licenses |
|
42 | 38 | .. _GPL: http://www.gnu.org/licenses/gpl.html |
@@ -204,7 +204,7 b" suppress the seed line so it doesn't show up in the rendered output" | |||
|
204 | 204 | [0.22591016, 0.77731835], |
|
205 | 205 | [0.0072729 , 0.34273127]]) |
|
206 | 206 | |
|
207 | For more information on @supress and @doctest decorators, please refer to the end of this file in | |
|
207 | For more information on @suppress and @doctest decorators, please refer to the end of this file in | |
|
208 | 208 | Pseudo-Decorators section. |
|
209 | 209 | |
|
210 | 210 | Another demonstration of multi-line input and output |
@@ -51,9 +51,9 b' Pull requests (226):' | |||
|
51 | 51 | * `542 <https://github.com/ipython/ipython/issues/542>`_: issue 440 |
|
52 | 52 | * `533 <https://github.com/ipython/ipython/issues/533>`_: Remove unused configobj and validate libraries from externals. |
|
53 | 53 | * `538 <https://github.com/ipython/ipython/issues/538>`_: fix various tests on Windows |
|
54 | * `540 <https://github.com/ipython/ipython/issues/540>`_: support `-pylab` flag with deprecation warning | |
|
54 | * `540 <https://github.com/ipython/ipython/issues/540>`_: support ``-pylab`` flag with deprecation warning | |
|
55 | 55 | * `537 <https://github.com/ipython/ipython/issues/537>`_: Docs update |
|
56 | * `536 <https://github.com/ipython/ipython/issues/536>`_: `setup.py install` depends on setuptools on Windows | |
|
56 | * `536 <https://github.com/ipython/ipython/issues/536>`_: ``setup.py install`` depends on setuptools on Windows | |
|
57 | 57 | * `480 <https://github.com/ipython/ipython/issues/480>`_: Get help mid-command |
|
58 | 58 | * `462 <https://github.com/ipython/ipython/issues/462>`_: Str and Bytes traitlets |
|
59 | 59 | * `534 <https://github.com/ipython/ipython/issues/534>`_: Handle unicode properly in IPython.zmq.iostream |
@@ -916,7 +916,7 b' Pull Requests (687):' | |||
|
916 | 916 | * :ghpull:`4444`: Css cleaning |
|
917 | 917 | * :ghpull:`4523`: Use username and password for MongoDB on ShiningPanda |
|
918 | 918 | * :ghpull:`4510`: Update whatsnew from PR files |
|
919 | * :ghpull:`4441`: add `setup.py jsversion` | |
|
919 | * :ghpull:`4441`: add ``setup.py jsversion`` | |
|
920 | 920 | * :ghpull:`4518`: Fix for race condition in url file decoding. |
|
921 | 921 | * :ghpull:`4497`: don't automatically unpack datetime objects in the message spec |
|
922 | 922 | * :ghpull:`4506`: wait for empty queues as well as load-balanced tasks |
@@ -1050,7 +1050,7 b' Pull Requests (687):' | |||
|
1050 | 1050 | * :ghpull:`4214`: engine ID metadata should be unicode, not bytes |
|
1051 | 1051 | * :ghpull:`4232`: no highlight if no language specified |
|
1052 | 1052 | * :ghpull:`4218`: Fix display of SyntaxError when .py file is modified |
|
1053 | * :ghpull:`4207`: add `setup.py css` command | |
|
1053 | * :ghpull:`4207`: add ``setup.py css`` command | |
|
1054 | 1054 | * :ghpull:`4224`: clear previous callbacks on execute |
|
1055 | 1055 | * :ghpull:`4180`: Iptest refactoring |
|
1056 | 1056 | * :ghpull:`4105`: JS output area misaligned |
@@ -81,7 +81,7 b' New features' | |||
|
81 | 81 | :mod:`IPython.kernel`, :mod:`IPython.kernel.core`, :mod:`traitlets.config`, |
|
82 | 82 | :mod:`IPython.tools` and :mod:`IPython.testing`. |
|
83 | 83 | |
|
84 | * As part of merging in the `ipython1-dev` stuff, the `setup.py` script and | |
|
84 | * As part of merging in the `ipython1-dev` stuff, the ``setup.py`` script and | |
|
85 | 85 | friends have been completely refactored. Now we are checking for |
|
86 | 86 | dependencies using the approach that matplotlib uses. |
|
87 | 87 | |
@@ -161,7 +161,7 b' Backwards incompatible changes' | |||
|
161 | 161 | `'basic'` to `'b'`. |
|
162 | 162 | |
|
163 | 163 | * IPython has a larger set of dependencies if you want all of its capabilities. |
|
164 | See the `setup.py` script for details. | |
|
164 | See the ``setup.py`` script for details. | |
|
165 | 165 | |
|
166 | 166 | * The constructors for :class:`IPython.kernel.client.MultiEngineClient` and |
|
167 | 167 | :class:`IPython.kernel.client.TaskClient` no longer take the (ip,port) tuple. |
@@ -277,7 +277,7 b' automatically upgrade to the latest version compatible with your system.' | |||
|
277 | 277 | |
|
278 | 278 | The ability to use only Python 3 on the code base of IPython brings a number |
|
279 | 279 | of advantages. Most of the newly written code make use of `optional function type |
|
280 |
annotation <https:// |
|
|
280 | annotation <https://peps.python.org/pep-0484/>`_ leading to clearer code | |
|
281 | 281 | and better documentation. |
|
282 | 282 | |
|
283 | 283 | The total size of the repository has also decreased by about 1500 lines (for the |
@@ -2,6 +2,17 b'' | |||
|
2 | 2 | 7.x Series |
|
3 | 3 | ============ |
|
4 | 4 | |
|
5 | .. _version 7.33: | |
|
6 | ||
|
7 | IPython 7.33 | |
|
8 | ============ | |
|
9 | ||
|
10 | - Allow IPython hooks to receive current cell ids when frontend support it. See | |
|
11 | :ghpull:`13600` | |
|
12 | ||
|
13 | - ``?`` does not trigger the insertion of a new cell anymore as most frontend | |
|
14 | allow proper multiline edition. :ghpull:`13625` | |
|
15 | ||
|
5 | 16 | |
|
6 | 17 | .. _version 7.32: |
|
7 | 18 | |
@@ -9,11 +20,15 b' IPython 7.32' | |||
|
9 | 20 | ============ |
|
10 | 21 | |
|
11 | 22 | |
|
23 | ||
|
24 | Autoload magic lazily | |
|
25 | --------------------- | |
|
26 | ||
|
12 | 27 | The ability to configure magics to be lazily loaded has been added to IPython. |
|
13 | 28 | See the ``ipython --help-all`` section on ``MagicsManager.lazy_magic``. |
|
14 | 29 | One can now use:: |
|
15 | 30 | |
|
16 | c.MagicsManger.lazy_magics = { | |
|
31 | c.MagicsManager.lazy_magics = { | |
|
17 | 32 | "my_magic": "slow.to.import", |
|
18 | 33 | "my_other_magic": "also.slow", |
|
19 | 34 | } |
@@ -21,6 +36,21 b' One can now use::' | |||
|
21 | 36 | And on first use of ``%my_magic``, or corresponding cell magic, or other line magic, |
|
22 | 37 | the corresponding ``load_ext`` will be called just before trying to invoke the magic. |
|
23 | 38 | |
|
39 | Misc | |
|
40 | ---- | |
|
41 | ||
|
42 | - Update sphinxify for Docrepr 0.2.0 :ghpull:`13503`. | |
|
43 | - Set co_name for cells run line by line (to fix debugging with Python 3.10) | |
|
44 | :ghpull:`13535` | |
|
45 | ||
|
46 | ||
|
47 | Many thanks to all the contributors to this release. You can find all individual | |
|
48 | contributions to this milestone `on github | |
|
49 | <https://github.com/ipython/ipython/milestone/99>`__. | |
|
50 | ||
|
51 | Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring | |
|
52 | work on IPython and related libraries. | |
|
53 | ||
|
24 | 54 | .. _version 7.31: |
|
25 | 55 | |
|
26 | 56 | IPython 7.31 |
@@ -3,6 +3,134 b'' | |||
|
3 | 3 | ============ |
|
4 | 4 | |
|
5 | 5 | |
|
6 | .. _version 8.3.0: | |
|
7 | ||
|
8 | IPython 8.3.0 | |
|
9 | ------------- | |
|
10 | ||
|
11 | - :ghpull:`13625`, using ``?``, ``??``, ``*?`` will not call | |
|
12 | ``set_next_input`` as most frontend allow proper multiline editing and it was | |
|
13 | causing issues for many users of multi-cell frontends. This has been backported to 7.33 | |
|
14 | ||
|
15 | ||
|
16 | - :ghpull:`13600`, ``pre_run_*``-hooks will now have a ``cell_id`` attribute on | |
|
17 | the info object when frontend provide it. This has been backported to 7.33 | |
|
18 | ||
|
19 | - :ghpull:`13624`, fixed :kbd:`End` key being broken after accepting an | |
|
20 | auto-suggestion. | |
|
21 | ||
|
22 | - :ghpull:`13657` fix issue where history from different sessions would be mixed. | |
|
23 | ||
|
24 | .. _version 8.2.0: | |
|
25 | ||
|
26 | IPython 8.2.0 | |
|
27 | ------------- | |
|
28 | ||
|
29 | IPython 8.2 mostly bring bugfixes to IPython. | |
|
30 | ||
|
31 | - Auto-suggestion can now be elected with the ``end`` key. :ghpull:`13566` | |
|
32 | - Some traceback issues with ``assert etb is not None`` have been fixed. :ghpull:`13588` | |
|
33 | - History is now pulled from the sqitel database and not from in-memory. | |
|
34 | In particular when using the ``%paste`` magic, the content of the pasted text will | |
|
35 | be part of the history and not the verbatim text ``%paste`` anymore. :ghpull:`13592` | |
|
36 | - Fix ``Ctrl-\\`` exit cleanup :ghpull:`13603` | |
|
37 | - Fixes to ``ultratb`` ipdb support when used outside of IPython. :ghpull:`13498` | |
|
38 | ||
|
39 | ||
|
40 | I am still trying to fix and investigate :ghissue:`13598`, which seem to be | |
|
41 | random, and would appreciate help if you find reproducible minimal case. I've | |
|
42 | tried to make various changes to the codebase to mitigate it, but a proper fix | |
|
43 | will be difficult without understanding the cause. | |
|
44 | ||
|
45 | ||
|
46 | All the issues on pull-requests for this release can be found in the `8.2 | |
|
47 | milestone. <https://github.com/ipython/ipython/milestone/100>`__ . And some | |
|
48 | documentation only PR can be found as part of the `7.33 milestone | |
|
49 | <https://github.com/ipython/ipython/milestone/101>`__ (currently not released). | |
|
50 | ||
|
51 | Thanks to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring | |
|
52 | work on IPython and related libraries. | |
|
53 | ||
|
54 | .. _version 8.1.1: | |
|
55 | ||
|
56 | IPython 8.1.1 | |
|
57 | ------------- | |
|
58 | ||
|
59 | Fix an issue with virtualenv and Python 3.8 introduced in 8.1 | |
|
60 | ||
|
61 | Revert :ghpull:`13537` (fix an issue with symlinks in virtualenv) that raises an | |
|
62 | error in Python 3.8, and fixed in a different way in :ghpull:`13559`. | |
|
63 | ||
|
64 | .. _version 8.1: | |
|
65 | ||
|
66 | IPython 8.1.0 | |
|
67 | ------------- | |
|
68 | ||
|
69 | IPython 8.1 is the first minor release after 8.0 and fixes a number of bugs and | |
|
70 | Update a few behavior that were problematic with the 8.0 as with many new major | |
|
71 | release. | |
|
72 | ||
|
73 | Note that beyond the changes listed here, IPython 8.1.0 also contains all the | |
|
74 | features listed in :ref:`version 7.32`. | |
|
75 | ||
|
76 | - Misc and multiple fixes around quotation auto-closing. It is now disabled by | |
|
77 | default. Run with ``TerminalInteractiveShell.auto_match=True`` to re-enabled | |
|
78 | - Require pygments>=2.4.0 :ghpull:`13459`, this was implicit in the code, but | |
|
79 | is now explicit in ``setup.cfg``/``setup.py`` | |
|
80 | - Docs improvement of ``core.magic_arguments`` examples. :ghpull:`13433` | |
|
81 | - Multi-line edit executes too early with await. :ghpull:`13424` | |
|
82 | ||
|
83 | - ``black`` is back as an optional dependency, and autoformatting disabled by | |
|
84 | default until some fixes are implemented (black improperly reformat magics). | |
|
85 | :ghpull:`13471` Additionally the ability to use ``yapf`` as a code | |
|
86 | reformatter has been added :ghpull:`13528` . You can use | |
|
87 | ``TerminalInteractiveShell.autoformatter="black"``, | |
|
88 | ``TerminalInteractiveShell.autoformatter="yapf"`` to re-enable auto formating | |
|
89 | with black, or switch to yapf. | |
|
90 | ||
|
91 | - Fix and issue where ``display`` was not defined. | |
|
92 | ||
|
93 | - Auto suggestions are now configurable. Currently only | |
|
94 | ``AutoSuggestFromHistory`` (default) and ``None``. new provider contribution | |
|
95 | welcomed. :ghpull:`13475` | |
|
96 | ||
|
97 | - multiple packaging/testing improvement to simplify downstream packaging | |
|
98 | (xfail with reasons, try to not access network...). | |
|
99 | ||
|
100 | - Update deprecation. ``InteractiveShell.magic`` internal method has been | |
|
101 | deprecated for many years but did not emit a warning until now. | |
|
102 | ||
|
103 | - internal ``appended_to_syspath`` context manager has been deprecated. | |
|
104 | ||
|
105 | - fix an issue with symlinks in virtualenv :ghpull:`13537` (Reverted in 8.1.1) | |
|
106 | ||
|
107 | - Fix an issue with vim mode, where cursor would not be reset on exit :ghpull:`13472` | |
|
108 | ||
|
109 | - ipython directive now remove only known pseudo-decorators :ghpull:`13532` | |
|
110 | ||
|
111 | - ``IPython/lib/security`` which used to be used for jupyter notebook has been | |
|
112 | removed. | |
|
113 | ||
|
114 | - Fix an issue where ``async with`` would execute on new lines. :ghpull:`13436` | |
|
115 | ||
|
116 | ||
|
117 | We want to remind users that IPython is part of the Jupyter organisations, and | |
|
118 | thus governed by a Code of Conduct. Some of the behavior we have seen on GitHub is not acceptable. | |
|
119 | Abuse and non-respectful comments on discussion will not be tolerated. | |
|
120 | ||
|
121 | Many thanks to all the contributors to this release, many of the above fixed issue and | |
|
122 | new features where done by first time contributors, showing there is still | |
|
123 | plenty of easy contribution possible in IPython | |
|
124 | . You can find all individual contributions | |
|
125 | to this milestone `on github <https://github.com/ipython/ipython/milestone/91>`__. | |
|
126 | ||
|
127 | Thanks as well to the `D. E. Shaw group <https://deshaw.com/>`__ for sponsoring | |
|
128 | work on IPython and related libraries. In particular the Lazy autoloading of | |
|
129 | magics that you will find described in the 7.32 release notes. | |
|
130 | ||
|
131 | ||
|
132 | .. _version 8.0.1: | |
|
133 | ||
|
6 | 134 | IPython 8.0.1 (CVE-2022-21699) |
|
7 | 135 | ------------------------------ |
|
8 | 136 | |
@@ -45,6 +173,7 b' Thus starting with this version:' | |||
|
45 | 173 | Further details can be read on the `GitHub Advisory <https://github.com/ipython/ipython/security/advisories/GHSA-pq7m-3gw7-gq5x>`__ |
|
46 | 174 | |
|
47 | 175 | |
|
176 | .. _version 8.0: | |
|
48 | 177 | |
|
49 | 178 | IPython 8.0 |
|
50 | 179 | ----------- |
@@ -344,12 +473,11 b' For more information please see the following unit test : ``extensions/tests/tes' | |||
|
344 | 473 | Auto formatting with black in the CLI |
|
345 | 474 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
346 | 475 | |
|
347 | If ``black`` is installed in the same environment as IPython, terminal IPython | |
|
348 | will now *by default* reformat the code in the CLI when possible. You can | |
|
349 | disable this with ``--TerminalInteractiveShell.autoformatter=None``. | |
|
350 | ||
|
351 | 476 | This feature was present in 7.x, but disabled by default. |
|
352 | 477 | |
|
478 | In 8.0, input was automatically reformatted with Black when black was installed. | |
|
479 | This feature has been reverted for the time being. | |
|
480 | You can re-enable it by setting ``TerminalInteractiveShell.autoformatter`` to ``"black"`` | |
|
353 | 481 | |
|
354 | 482 | History Range Glob feature |
|
355 | 483 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
@@ -393,8 +521,8 b' Automatic Vi prompt stripping' | |||
|
393 | 521 | |
|
394 | 522 | When pasting code into IPython, it will strip the leading prompt characters if |
|
395 | 523 | there are any. For example, you can paste the following code into the console - |
|
396 | it will still work, even though each line is prefixed with prompts (`In`, | |
|
397 | `Out`):: | |
|
524 | it will still work, even though each line is prefixed with prompts (``In``, | |
|
525 | ``Out``):: | |
|
398 | 526 | |
|
399 | 527 | In [1]: 2 * 2 == 4 |
|
400 | 528 | Out[1]: True |
@@ -489,7 +617,7 b' who did a fantastic job at updating our code base, migrating to pytest, pushing' | |||
|
489 | 617 | our coverage, and fixing a large number of bugs. I highly recommend contacting |
|
490 | 618 | them if you need help with C++ and Python projects. |
|
491 | 619 | |
|
492 |
You can find all relevant issues and PRs with the SDG 2021 tag |
|
|
620 | You can find all relevant issues and PRs with `the SDG 2021 tag <https://github.com/ipython/ipython/issues?q=label%3A%22Numfocus+SDG+2021%22+>`__ | |
|
493 | 621 | |
|
494 | 622 | Removing support for older Python versions |
|
495 | 623 | ------------------------------------------ |
@@ -15,7 +15,7 b'' | |||
|
15 | 15 | "This is made difficult by the fact that Notebooks are not plain Python files,\n", |
|
16 | 16 | "and thus cannot be imported by the regular Python machinery.\n", |
|
17 | 17 | "\n", |
|
18 |
"Fortunately, Python provides some fairly sophisticated [hooks](http |
|
|
18 | "Fortunately, Python provides some fairly sophisticated [hooks](https://peps.python.org/pep-0302/) into the import machinery,\n", | |
|
19 | 19 | "so we can actually make IPython notebooks importable without much difficulty,\n", |
|
20 | 20 | "and only using public APIs." |
|
21 | 21 | ] |
@@ -45,3 +45,4 b' addopts = --durations=10' | |||
|
45 | 45 | --ignore=IPython/utils/version.py |
|
46 | 46 | doctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS |
|
47 | 47 | ipdoctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS |
|
48 | asyncio_mode = strict |
@@ -63,17 +63,16 b' qtconsole =' | |||
|
63 | 63 | qtconsole |
|
64 | 64 | terminal = |
|
65 | 65 | test = |
|
66 | pytest<7 | |
|
66 | pytest<7.1 | |
|
67 | 67 | pytest-asyncio |
|
68 | 68 | testpath |
|
69 | 69 | test_extra = |
|
70 | %(test)s | |
|
70 | 71 | curio |
|
71 | 72 | matplotlib!=3.2.0 |
|
72 | 73 | nbformat |
|
73 | 74 | numpy>=1.19 |
|
74 | 75 | pandas |
|
75 | pytest<7 | |
|
76 | testpath | |
|
77 | 76 | trio |
|
78 | 77 | all = |
|
79 | 78 | %(black)s |
@@ -18,7 +18,6 b' requires utilities which are not available under Windows."""' | |||
|
18 | 18 | |
|
19 | 19 | import os |
|
20 | 20 | import sys |
|
21 | from itertools import chain | |
|
22 | 21 | |
|
23 | 22 | # **Python version check** |
|
24 | 23 | # |
@@ -53,7 +52,9 b' See IPython `README.rst` file for more information:' | |||
|
53 | 52 | |
|
54 | 53 | Python {py} detected. |
|
55 | 54 | {pip} |
|
56 | """.format(py=sys.version_info, pip=pip_message ) | |
|
55 | """.format( | |
|
56 | py=sys.version_info, pip=pip_message | |
|
57 | ) | |
|
57 | 58 | |
|
58 | 59 | print(error, file=sys.stderr) |
|
59 | 60 | sys.exit(1) |
@@ -111,7 +111,7 b' then' | |||
|
111 | 111 | sleep 1 |
|
112 | 112 | echo $BLUE"Saving API to file $PREV_RELEASE"$NOR |
|
113 | 113 | frappuccino IPython IPython.kernel IPython.lib IPython.qt IPython.lib.kernel IPython.html IPython.frontend IPython.external --save IPython-$PREV_RELEASE.json |
|
114 |
echo $BLUE"com |
|
|
114 | echo $BLUE"coming back to $BRANCH"$NOR | |
|
115 | 115 | git checkout $BRANCH |
|
116 | 116 | sleep 1 |
|
117 | 117 | echo $BLUE"comparing ..."$NOR |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now