##// END OF EJS Templates
Merge branch 'master' into madbird1304/issue-13073-fix-paste-magic
Artur Svistunov -
r27661:91d34770 merge
parent child Browse files
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 """ Will be used to set _ip point to current ipython instance b/f call
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 # laziliy initialize it on first use. Consuming code should access this
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 coudl only give a us the single current line.
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 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
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 reversed(list(cur)[1:])
369 return reversed(list(cur))
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, next_input=None):
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, next_input=None):
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, next_input=next_input)
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 format_screen, LSString, SList, DollarFormatter
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 if len(self.raw_cell) > 50 else self.raw_cell)
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 stb = value._render_traceback_()
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 (module_completer,
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", "ls")
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://www.python.org/peps/pep-0215.html. This is the library used by
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 = 1
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('alias commetarg echo this is %%s a commented out arg')
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('commetarg')
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('autocall 0')
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('%hist 2-500')
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("%%hist -f %s" % (tmp_path / "test1"))
57 ip.magic("%%hist -pf %s" % (tmp_path / "test2"))
58 ip.magic("%%hist -nf %s" % (tmp_path / "test3"))
59 ip.magic("%%save %s 1-10" % (tmp_path / "test4"))
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 " + str(testfilename) + " ~1/1-3")
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 ('!!ls', "get_ipython().getoutput('ls')"),
88 ],
89
90 # Help/object info
91 escaped_help =
92 [ ('?', 'get_ipython().show_usage()'),
93 ('?x1', "get_ipython().run_line_magic('pinfo', 'x1')"),
94 ('??x2', "get_ipython().run_line_magic('pinfo2', 'x2')"),
95 ('?a.*s', "get_ipython().run_line_magic('psearch', 'a.*s')"),
96 ('?%hist1', "get_ipython().run_line_magic('pinfo', '%hist1')"),
97 ('?%%hist2', "get_ipython().run_line_magic('pinfo', '%%hist2')"),
98 ('?abc = qwe', "get_ipython().run_line_magic('pinfo', 'abc')"),
99 ],
100
101 end_help =
102 [ ('x3?', "get_ipython().run_line_magic('pinfo', 'x3')"),
103 ('x4??', "get_ipython().run_line_magic('pinfo2', 'x4')"),
104 ('%hist1?', "get_ipython().run_line_magic('pinfo', '%hist1')"),
105 ('%hist2??', "get_ipython().run_line_magic('pinfo2', '%hist2')"),
106 ('%%hist3?', "get_ipython().run_line_magic('pinfo', '%%hist3')"),
107 ('%%hist4??', "get_ipython().run_line_magic('pinfo2', '%%hist4')"),
108 ('π.foo?', "get_ipython().run_line_magic('pinfo', 'π.foo')"),
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 "get_ipython().run_line_magic('pinfo2', 'abc.qe')"),
115 ('a = *.items?', "get_ipython().set_next_input('a = *.items');"
116 "get_ipython().run_line_magic('psearch', '*.items')"),
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 (' ,f y', ' f("y")'),
136 (',f a b', 'f("a", "b")'),
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 a = f()
19 20 %foo \\
20 21 bar
21 22 g()
22 """.splitlines(keepends=True), (2, 0), """\
23 """.splitlines(
24 keepends=True
25 ),
26 (2, 0),
27 """\
23 28 a = f()
24 29 get_ipython().run_line_magic('foo', ' bar')
25 30 g()
26 """.splitlines(keepends=True))
31 """.splitlines(
32 keepends=True
33 ),
34 )
27 35
28 INDENTED_MAGIC = ("""\
36 INDENTED_MAGIC = (
37 """\
29 38 for a in range(5):
30 39 %ls
31 """.splitlines(keepends=True), (2, 4), """\
40 """.splitlines(
41 keepends=True
42 ),
43 (2, 4),
44 """\
32 45 for a in range(5):
33 46 get_ipython().run_line_magic('ls', '')
34 """.splitlines(keepends=True))
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 "g()\n"
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 a = f()
48 61 b = %foo \\
49 62 bar
50 63 g()
51 """.splitlines(keepends=True), (2, 4), """\
64 """.splitlines(
65 keepends=True
66 ),
67 (2, 4),
68 """\
52 69 a = f()
53 70 b = get_ipython().run_line_magic('foo', ' bar')
54 71 g()
55 """.splitlines(keepends=True))
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 def test():
72 93 for i in range(1):
73 94 print(i)
74 95 res =! ls
75 """.splitlines(keepends=True), (4, 7), '''\
96 """.splitlines(
97 keepends=True
98 ),
99 (4, 7),
100 """\
76 101 def test():
77 102 for i in range(1):
78 103 print(i)
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"], (1, 0),
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"], (1, 0),
116 ["get_ipython().set_next_input('a = b + c');"
117 "get_ipython().run_line_magic('pinfo', 'c')\n"]
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 zip?
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 (a,
129 148 b) = zip?
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"], (1, 0),
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, _ = case
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 if True:
307 a=1""" )
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 = '12abAB'+string.printable[62:]
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") == ('complete', None)
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") == ('complete', None)
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, u"åäötestscript.py"), "w", encoding="utf-8"
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('SHELL', None)
590 os.environ['SHELL'] = find_cmd("csh")
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 ("ls", "pip", "conda", "cd"):
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('-34'):
685 ip.run_cell('print (12 + 22)')
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['n'] = 55
689 with tt.AssertNotPrints('-55'):
690 ip.run_cell('print (n)')
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(dedent("""\
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 >>> x = [1,2,3]
127 128 >>> y = []
128 129 >>> for i in x:
@@ -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 >> def foo(x):
150 152 >> return x + 1
151 153 >> xx = foo(1.1)"""
@@ -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 > > def foo(x):
159 162 > > return x + 1
160 163 > > yy = foo(2.1) """
@@ -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 >> >>> def f(x):
168 172 >> ... return x+1
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 / u"profile_ünicode").mkdir(parents=True)
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.Pdb()
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('/path/to/sound.wav') # doctest: +SKIP
96 >>> Audio(filename='/path/to/sound.ogg') # doctest: +SKIP
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 AUdio widget with more more flexibility and options.
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(self,
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", [(u"$$x^2$$", False), (u"x^2", True)])
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 == u"[%s]" % u
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 continue
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 InMemoryHistory
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 "black",
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 = InMemoryHistory()
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:`~IPython.core.application.Application` object for the command
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('c-\\')(force_exit)
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 force_exit(event):
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 'ipfunc'
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: py.path.local,
129 file_path: Path,
130 130 parent: Collector,
131 131 ) -> Optional[Union["IPDoctestModule", "IPDoctestTextfile"]]:
132 132 config = parent.config
133 if path.ext == ".py":
134 if config.option.ipdoctestmodules and not _is_setup_py(path):
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, fspath=path)
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_binary()
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: py.path.local, parent) -> bool:
151 if path.ext in (".txt", ".rst") and parent.session.isinitpath(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 to imposed limits on sublcass
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 not None:
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.fspath, self.dtest.lineno, "[ipdoctest] %s" % self.name
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.fspath.read_text(encoding)
478 filename = str(self.fspath)
479 name = self.fspath.basename
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 DocTestFinder._find_lineno( # type: ignore
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 DocTestFinder._find( # type: ignore
584 self, tests, obj, name, module, source_lines, globs, seen
631 super()._find( # type:ignore[misc]
632 tests, obj, name, module, source_lines, globs, seen
585 633 )
586 634
587 if self.fspath.basename == "conftest.py":
588 module = self.config.pluginmanager._importconftest(
589 self.fspath, self.config.getoption("importmode")
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.fspath)
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.fspath)
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 IPDoctestOutputChecker.check_output(self, want, got, optionflags):
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 IPDoctestOutputChecker.check_output(self, want, got, optionflags)
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=' ', displaywidth=80, spread=False):
694 """ Transform a list of strings into a single string with columns.
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 informations on the Jupyter website. https://jupyter.org/security
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('Cell code: "%s"' % info.raw_cell)
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 print('Error before execution: %s' % result.error_before_exec)
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 my extensions and
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://www.python.org/dev/peps/pep-0008
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://www.python.org/dev/peps/pep-0484/>`_ leading to clearer code
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 `<https://github.com/ipython/ipython/issues?q=label%3A%22Numfocus+SDG+2021%22+>`__
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://www.python.org/dev/peps/pep-0302/) into the import machinery,\n",
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"comming back to $BRANCH"$NOR
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