From 91d34770641f9ea210df40df5a8e9e58e73722b1 2022-05-06 00:13:23 From: Artur Svistunov <18216480+madbird1304@users.noreply.github.com> Date: 2022-05-06 00:13:23 Subject: [PATCH] Merge branch 'master' into madbird1304/issue-13073-fix-paste-magic --- diff --git a/.github/workflows/downstream.yml b/.github/workflows/downstream.yml index 5c5adc1..309d03a 100644 --- a/.github/workflows/downstream.yml +++ b/.github/workflows/downstream.yml @@ -39,7 +39,7 @@ jobs: - name: Install and update Python dependencies run: | python -m pip install --upgrade -e file://$PWD#egg=ipython[test] - # we must instal IPython after ipykernel to get the right versions. + # we must install IPython after ipykernel to get the right versions. python -m pip install --upgrade --upgrade-strategy eager flaky ipyparallel python -m pip install --upgrade 'pytest<7' - name: pytest diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 601828a..1bbddbf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,6 +17,7 @@ jobs: test: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [ubuntu-latest, windows-latest] python-version: ["3.8", "3.9", "3.10"] diff --git a/.gitignore b/.gitignore index 5b45fd4..f473653 100644 --- a/.gitignore +++ b/.gitignore @@ -28,5 +28,13 @@ __pycache__ .pytest_cache .python-version venv*/ -.idea/ .mypy_cache/ + +# jetbrains ide stuff +*.iml +.idea/ + +# vscode ide stuff +*.code-workspace +.history +.vscode diff --git a/IPython/__init__.py b/IPython/__init__.py index e12da90..7ebb80b 100644 --- a/IPython/__init__.py +++ b/IPython/__init__.py @@ -28,7 +28,7 @@ import sys # Don't forget to also update setup.py when this changes! if sys.version_info < (3, 8): raise ImportError( -""" + """ IPython 8+ supports Python 3.8 and above, following NEP 29. When using Python 2.7, please install IPython 5.x LTS Long Term Support version. Python 3.3 and 3.4 were supported up to IPython 6.x. @@ -40,7 +40,8 @@ See IPython `README.rst` file for more information: https://github.com/ipython/ipython/blob/master/README.rst -""") +""" + ) #----------------------------------------------------------------------------- # Setup the top level names diff --git a/IPython/core/autocall.py b/IPython/core/autocall.py index 5f7720b..54beec3 100644 --- a/IPython/core/autocall.py +++ b/IPython/core/autocall.py @@ -40,7 +40,7 @@ class IPyAutocall(object): self._ip = ip def set_ip(self, ip): - """ Will be used to set _ip point to current ipython instance b/f call + """Will be used to set _ip point to current ipython instance b/f call Override this method if you don't want this to happen. diff --git a/IPython/core/completer.py b/IPython/core/completer.py index 9774fd5..59d3e99 100644 --- a/IPython/core/completer.py +++ b/IPython/core/completer.py @@ -41,7 +41,7 @@ or using unicode completion: Only valid Python identifiers will complete. Combining characters (like arrow or dots) are also available, unlike latex they need to be put after the their -counterpart that is to say, `F\\\\vec` is correct, not `\\\\vecF`. +counterpart that is to say, ``F\\\\vec`` is correct, not ``\\\\vecF``. Some browsers are known to display combining characters incorrectly. @@ -50,7 +50,7 @@ Backward latex completion It is sometime challenging to know how to type a character, if you are using IPython, or any compatible frontend you can prepend backslash to the character -and press `` to expand it to its latex form. +and press ```` to expand it to its latex form. .. code:: @@ -589,7 +589,7 @@ class Completer(Configurable): This will enable completion on elements of lists, results of function calls, etc., but can be unsafe because the code is actually evaluated on TAB. - """ + """, ).tag(config=True) use_jedi = Bool(default_value=JEDI_INSTALLED, @@ -1182,7 +1182,7 @@ class IPCompleter(Completer): # This is a list of names of unicode characters that can be completed # into their corresponding unicode value. The list is large, so we - # laziliy initialize it on first use. Consuming code should access this + # lazily initialize it on first use. Consuming code should access this # attribute through the `@unicode_names` property. self._unicode_names = None @@ -2070,7 +2070,7 @@ class IPCompleter(Completer): indexed. line_buffer : optional, str The current line the cursor is in, this is mostly due to legacy - reason that readline coudl only give a us the single current line. + reason that readline could only give a us the single current line. Prefer `full_text`. text : str The current "token" the cursor is in, mostly also for historical @@ -2139,8 +2139,9 @@ class IPCompleter(Completer): # different types of objects. The rlcomplete() method could then # simply collapse the dict into a list for readline, but we'd have # richer completion semantics in other environments. - completions:Iterable[Any] = [] - if self.use_jedi: + is_magic_prefix = len(text) > 0 and text[0] == "%" + completions: Iterable[Any] = [] + if self.use_jedi and not is_magic_prefix: if not full_text: full_text = line_buffer completions = self._jedi_matches( diff --git a/IPython/core/debugger.py b/IPython/core/debugger.py index 8e3dd96..ba12e3e 100644 --- a/IPython/core/debugger.py +++ b/IPython/core/debugger.py @@ -101,7 +101,6 @@ All the changes since then are under the same license as IPython. # #***************************************************************************** -import bdb import inspect import linecache import sys diff --git a/IPython/core/display.py b/IPython/core/display.py index d36a176..933295a 100644 --- a/IPython/core/display.py +++ b/IPython/core/display.py @@ -884,7 +884,7 @@ class Image(DisplayObject): a URL, or a filename from which to load image data. The result is always embedding image data for inline images. - >>> Image('http://www.google.fr/images/srpr/logo3w.png') + >>> Image('https://www.google.fr/images/srpr/logo3w.png') # doctest: +SKIP >>> Image('/path/to/image.jpg') @@ -897,7 +897,7 @@ class Image(DisplayObject): it only generates ```` tag with a link to the source. This will not work in the qtconsole or offline. - >>> Image(url='http://www.google.fr/images/srpr/logo3w.png') + >>> Image(url='https://www.google.fr/images/srpr/logo3w.png') """ diff --git a/IPython/core/history.py b/IPython/core/history.py index 41a0433..9b0b2cb 100644 --- a/IPython/core/history.py +++ b/IPython/core/history.py @@ -166,7 +166,7 @@ class HistoryAccessor(HistoryAccessorBase): in which case there will be no stored history, no SQLite connection, and no background saving thread. This may be necessary in some threaded environments where IPython is embedded. - """ + """, ).tag(config=True) connection_options = Dict( @@ -296,8 +296,8 @@ class HistoryAccessor(HistoryAccessorBase): toget = "history.%s, output_history.output" % toget if latest: toget += ", MAX(session * 128 * 1024 + line)" - cur = self.db.execute("SELECT session, line, %s FROM %s " %\ - (toget, sqlfrom) + sql, params) + this_querry = "SELECT session, line, %s FROM %s " % (toget, sqlfrom) + sql + cur = self.db.execute(this_querry, params) if latest: cur = (row[:-1] for row in cur) if output: # Regroup into 3-tuples, and parse JSON @@ -344,6 +344,11 @@ class HistoryAccessor(HistoryAccessorBase): def get_tail(self, n=10, raw=True, output=False, include_latest=False): """Get the last n lines from the history database. + Most recent entry last. + + Completion will be reordered so that that the last ones are when + possible from current session. + Parameters ---------- n : int @@ -362,11 +367,31 @@ class HistoryAccessor(HistoryAccessorBase): self.writeout_cache() if not include_latest: n += 1 - cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?", - (n,), raw=raw, output=output) + # cursor/line/entry + this_cur = list( + self._run_sql( + "WHERE session == ? ORDER BY line DESC LIMIT ? ", + (self.session_number, n), + raw=raw, + output=output, + ) + ) + other_cur = list( + self._run_sql( + "WHERE session != ? ORDER BY session DESC, line DESC LIMIT ?", + (self.session_number, n), + raw=raw, + output=output, + ) + ) + + everything = this_cur + other_cur + + everything = everything[:n] + if not include_latest: - return reversed(list(cur)[1:]) - return reversed(list(cur)) + return list(everything)[:0:-1] + return list(everything)[::-1] @catch_corrupt_db def search(self, pattern="*", raw=True, search_raw=True, diff --git a/IPython/core/inputtransformer.py b/IPython/core/inputtransformer.py index f668f46..77f69f3 100644 --- a/IPython/core/inputtransformer.py +++ b/IPython/core/inputtransformer.py @@ -193,7 +193,7 @@ def assemble_logical_lines(): line = ''.join(parts) # Utilities -def _make_help_call(target, esc, lspace, next_input=None): +def _make_help_call(target, esc, lspace): """Prepares a pinfo(2)/psearch call from a target name and the escape (i.e. ? or ??)""" method = 'pinfo2' if esc == '??' \ @@ -203,12 +203,13 @@ def _make_help_call(target, esc, lspace, next_input=None): #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args) t_magic_name, _, t_magic_arg_s = arg.partition(' ') t_magic_name = t_magic_name.lstrip(ESC_MAGIC) - if next_input is None: - return '%sget_ipython().run_line_magic(%r, %r)' % (lspace, t_magic_name, t_magic_arg_s) - else: - return '%sget_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \ - (lspace, next_input, t_magic_name, t_magic_arg_s) - + return "%sget_ipython().run_line_magic(%r, %r)" % ( + lspace, + t_magic_name, + t_magic_arg_s, + ) + + # These define the transformations for the different escape characters. def _tr_system(line_info): "Translate lines escaped with: !" @@ -349,10 +350,7 @@ def help_end(line): esc = m.group(3) lspace = _initial_space_re.match(line).group(0) - # If we're mid-command, put it back on the next prompt for the user. - next_input = line.rstrip('?') if line.strip() != m.group(0) else None - - return _make_help_call(target, esc, lspace, next_input) + return _make_help_call(target, esc, lspace) @CoroutineInputTransformer.wrap diff --git a/IPython/core/inputtransformer2.py b/IPython/core/inputtransformer2.py index 3a56007..a8f676f 100644 --- a/IPython/core/inputtransformer2.py +++ b/IPython/core/inputtransformer2.py @@ -325,7 +325,7 @@ ESC_PAREN = '/' # Call first argument with rest of line as arguments ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'} ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately -def _make_help_call(target, esc, next_input=None): +def _make_help_call(target, esc): """Prepares a pinfo(2)/psearch call from a target name and the escape (i.e. ? or ??)""" method = 'pinfo2' if esc == '??' \ @@ -335,11 +335,8 @@ def _make_help_call(target, esc, next_input=None): #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args) t_magic_name, _, t_magic_arg_s = arg.partition(' ') t_magic_name = t_magic_name.lstrip(ESC_MAGIC) - if next_input is None: - return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s) - else: - return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \ - (next_input, t_magic_name, t_magic_arg_s) + return "get_ipython().run_line_magic(%r, %r)" % (t_magic_name, t_magic_arg_s) + def _tr_help(content): """Translate lines escaped with: ? @@ -480,13 +477,8 @@ class HelpEnd(TokenTransformBase): target = m.group(1) esc = m.group(3) - # If we're mid-command, put it back on the next prompt for the user. - next_input = None - if (not lines_before) and (not lines_after) \ - and content.strip() != m.group(0): - next_input = content.rstrip('?\n') - call = _make_help_call(target, esc, next_input=next_input) + call = _make_help_call(target, esc) new_line = indent + call + '\n' return lines_before + [new_line] + lines_after diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 61e1541..371a3da 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -14,40 +14,61 @@ import abc import ast import atexit +import bdb import builtins as builtin_mod +import dis import functools import inspect import os import re import runpy +import subprocess import sys import tempfile import traceback import types -import subprocess import warnings +from ast import stmt from io import open as io_open - +from logging import error from pathlib import Path -from pickleshare import PickleShareDB +from typing import Callable +from typing import List as ListType +from typing import Optional, Tuple +from warnings import warn +from pickleshare import PickleShareDB +from tempfile import TemporaryDirectory +from traitlets import ( + Any, + Bool, + CaselessStrEnum, + Dict, + Enum, + Instance, + Integer, + List, + Type, + Unicode, + default, + observe, + validate, +) from traitlets.config.configurable import SingletonConfigurable from traitlets.utils.importstring import import_item -from IPython.core import oinspect -from IPython.core import magic -from IPython.core import page -from IPython.core import prefilter -from IPython.core import ultratb + +import IPython.core.hooks +from IPython.core import magic, oinspect, page, prefilter, ultratb from IPython.core.alias import Alias, AliasManager from IPython.core.autocall import ExitAutocall from IPython.core.builtin_trap import BuiltinTrap -from IPython.core.events import EventManager, available_events from IPython.core.compilerop import CachingCompiler, check_linecache_ipython from IPython.core.debugger import InterruptiblePdb from IPython.core.display_trap import DisplayTrap from IPython.core.displayhook import DisplayHook from IPython.core.displaypub import DisplayPublisher from IPython.core.error import InputRejected, UsageError +from IPython.core.events import EventManager, available_events from IPython.core.extensions import ExtensionManager from IPython.core.formatters import DisplayFormatter from IPython.core.history import HistoryManager @@ -59,31 +80,17 @@ from IPython.core.prefilter import PrefilterManager from IPython.core.profiledir import ProfileDir from IPython.core.usage import default_banner from IPython.display import display +from IPython.paths import get_ipython_dir from IPython.testing.skipdoctest import skip_doctest -from IPython.utils import PyColorize -from IPython.utils import io -from IPython.utils import py3compat -from IPython.utils import openpy +from IPython.utils import PyColorize, io, openpy, py3compat from IPython.utils.decorators import undoc from IPython.utils.io import ask_yes_no from IPython.utils.ipstruct import Struct -from IPython.paths import get_ipython_dir -from IPython.utils.path import get_home_dir, get_py_filename, ensure_dir_exists -from IPython.utils.process import system, getoutput +from IPython.utils.path import ensure_dir_exists, get_home_dir, get_py_filename +from IPython.utils.process import getoutput, system from IPython.utils.strdispatch import StrDispatch from IPython.utils.syspathcontext import prepended_to_syspath -from IPython.utils.text import format_screen, LSString, SList, DollarFormatter -from IPython.utils.tempdir import TemporaryDirectory -from traitlets import ( - Integer, Bool, CaselessStrEnum, Enum, List, Dict, Unicode, Instance, Type, - observe, default, validate, Any -) -from warnings import warn -from logging import error -import IPython.core.hooks - -from typing import List as ListType, Tuple, Optional, Callable -from ast import stmt +from IPython.utils.text import DollarFormatter, LSString, SList, format_screen sphinxify: Optional[Callable] @@ -122,8 +129,13 @@ _single_targets_nodes = (ast.AugAssign, ast.AnnAssign) # we still need to run things using the asyncio eventloop, but there is no # async integration -from .async_helpers import _asyncio_runner, _pseudo_sync_runner -from .async_helpers import _curio_runner, _trio_runner, _should_be_async +from .async_helpers import ( + _asyncio_runner, + _curio_runner, + _pseudo_sync_runner, + _should_be_async, + _trio_runner, +) #----------------------------------------------------------------------------- # Globals @@ -188,19 +200,29 @@ class ExecutionInfo(object): store_history = False silent = False shell_futures = True + cell_id = None - def __init__(self, raw_cell, store_history, silent, shell_futures): + def __init__(self, raw_cell, store_history, silent, shell_futures, cell_id): self.raw_cell = raw_cell self.store_history = store_history self.silent = silent self.shell_futures = shell_futures + self.cell_id = cell_id def __repr__(self): name = self.__class__.__qualname__ - raw_cell = ((self.raw_cell[:50] + '..') - if len(self.raw_cell) > 50 else self.raw_cell) - return '<%s object at %x, raw_cell="%s" store_history=%s silent=%s shell_futures=%s>' %\ - (name, id(self), raw_cell, self.store_history, self.silent, self.shell_futures) + raw_cell = ( + (self.raw_cell[:50] + "..") if len(self.raw_cell) > 50 else self.raw_cell + ) + return '<%s object at %x, raw_cell="%s" store_history=%s silent=%s shell_futures=%s cell_id=%s>' % ( + name, + id(self), + raw_cell, + self.store_history, + self.silent, + self.shell_futures, + self.cell_id, + ) class ExecutionResult(object): @@ -747,6 +769,33 @@ class InteractiveShell(SingletonConfigurable): # the appropriate time. self.display_trap = DisplayTrap(hook=self.displayhook) + @staticmethod + def get_path_links(p: Path): + """Gets path links including all symlinks + + Examples + -------- + In [1]: from IPython.core.interactiveshell import InteractiveShell + + In [2]: import sys, pathlib + + In [3]: paths = InteractiveShell.get_path_links(pathlib.Path(sys.executable)) + + In [4]: len(paths) == len(set(paths)) + Out[4]: True + + In [5]: bool(paths) + Out[5]: True + """ + paths = [p] + while p.is_symlink(): + new_path = Path(os.readlink(p)) + if not new_path.is_absolute(): + new_path = p.parent / new_path + p = new_path + paths.append(p) + return paths + def init_virtualenv(self): """Add the current virtualenv to sys.path so the user can import modules from it. This isn't perfect: it doesn't use the Python interpreter with which the @@ -772,10 +821,7 @@ class InteractiveShell(SingletonConfigurable): # stdlib venv may symlink sys.executable, so we can't use realpath. # but others can symlink *to* the venv Python, so we can't just use sys.executable. # So we just check every item in the symlink tree (generally <= 3) - paths = [p] - while p.is_symlink(): - p = Path(os.readlink(p)) - paths.append(p.resolve()) + paths = self.get_path_links(p) # In Cygwin paths like "c:\..." and '\cygdrive\c\...' are possible if p_venv.parts[1] == "cygdrive": @@ -1941,10 +1987,19 @@ class InteractiveShell(SingletonConfigurable): # Exception classes can customise their traceback - we # use this in IPython.parallel for exceptions occurring # in the engines. This should return a list of strings. - stb = value._render_traceback_() + if hasattr(value, "_render_traceback_"): + stb = value._render_traceback_() + else: + stb = self.InteractiveTB.structured_traceback( + etype, value, tb, tb_offset=tb_offset + ) + except Exception: - stb = self.InteractiveTB.structured_traceback(etype, - value, tb, tb_offset=tb_offset) + print( + "Unexpected exception formatting exception. Falling back to standard exception" + ) + traceback.print_exc() + return None self._showtraceback(etype, value, stb) if self.call_pdb: @@ -2034,8 +2089,12 @@ class InteractiveShell(SingletonConfigurable): (typically over the network by remote frontends). """ from IPython.core.completer import IPCompleter - from IPython.core.completerlib import (module_completer, - magic_run_completer, cd_completer, reset_completer) + from IPython.core.completerlib import ( + cd_completer, + magic_run_completer, + module_completer, + reset_completer, + ) self.Completer = IPCompleter(shell=self, namespace=self.user_ns, @@ -2414,14 +2473,9 @@ class InteractiveShell(SingletonConfigurable): cmd = self.var_expand(cmd, depth=1) # warn if there is an IPython magic alternative. main_cmd = cmd.split()[0] - has_magic_alternatives = ("pip", "conda", "cd", "ls") + has_magic_alternatives = ("pip", "conda", "cd") - # had to check if the command was an alias expanded because of `ls` - is_alias_expanded = self.alias_manager.is_alias(main_cmd) and ( - self.alias_manager.retrieve_alias(main_cmd).strip() == cmd.strip() - ) - - if main_cmd in has_magic_alternatives and not is_alias_expanded: + if main_cmd in has_magic_alternatives: warnings.warn( ( "You executed the system command !{0} which may not work " @@ -2791,7 +2845,14 @@ class InteractiveShell(SingletonConfigurable): self.showtraceback() warn('Unknown failure executing module: <%s>' % mod_name) - def run_cell(self, raw_cell, store_history=False, silent=False, shell_futures=True): + def run_cell( + self, + raw_cell, + store_history=False, + silent=False, + shell_futures=True, + cell_id=None, + ): """Run a complete IPython cell. Parameters @@ -2818,14 +2879,22 @@ class InteractiveShell(SingletonConfigurable): result = None try: result = self._run_cell( - raw_cell, store_history, silent, shell_futures) + raw_cell, store_history, silent, shell_futures, cell_id + ) finally: self.events.trigger('post_execute') if not silent: self.events.trigger('post_run_cell', result) return result - def _run_cell(self, raw_cell:str, store_history:bool, silent:bool, shell_futures:bool) -> ExecutionResult: + def _run_cell( + self, + raw_cell: str, + store_history: bool, + silent: bool, + shell_futures: bool, + cell_id: str, + ) -> ExecutionResult: """Internal method to run a complete IPython cell.""" # we need to avoid calling self.transform_cell multiple time on the same thing @@ -2845,6 +2914,7 @@ class InteractiveShell(SingletonConfigurable): shell_futures=shell_futures, transformed_cell=transformed_cell, preprocessing_exc_tuple=preprocessing_exc_tuple, + cell_id=cell_id, ) # run_cell_async is async, but may not actually need an eventloop. @@ -2865,7 +2935,9 @@ class InteractiveShell(SingletonConfigurable): try: return runner(coro) except BaseException as e: - info = ExecutionInfo(raw_cell, store_history, silent, shell_futures) + info = ExecutionInfo( + raw_cell, store_history, silent, shell_futures, cell_id + ) result = ExecutionResult(info) result.error_in_exec = e self.showtraceback(running_compiled_code=True) @@ -2921,7 +2993,8 @@ class InteractiveShell(SingletonConfigurable): shell_futures=True, *, transformed_cell: Optional[str] = None, - preprocessing_exc_tuple: Optional[Any] = None + preprocessing_exc_tuple: Optional[Any] = None, + cell_id=None, ) -> ExecutionResult: """Run a complete IPython cell asynchronously. @@ -2952,8 +3025,7 @@ class InteractiveShell(SingletonConfigurable): .. versionadded:: 7.0 """ - info = ExecutionInfo( - raw_cell, store_history, silent, shell_futures) + info = ExecutionInfo(raw_cell, store_history, silent, shell_futures, cell_id) result = ExecutionResult(info) if (not raw_cell) or raw_cell.isspace(): @@ -3008,9 +3080,8 @@ class InteractiveShell(SingletonConfigurable): cell = raw_cell # Store raw and processed history - if store_history: - self.history_manager.store_inputs(self.execution_count, - cell, raw_cell) + if store_history and raw_cell.strip(" %") != "paste": + self.history_manager.store_inputs(self.execution_count, cell, raw_cell) if not silent: self.logger.log(cell, raw_cell) @@ -3141,6 +3212,29 @@ class InteractiveShell(SingletonConfigurable): ast.fix_missing_locations(node) return node + def _update_code_co_name(self, code): + """Python 3.10 changed the behaviour so that whenever a code object + is assembled in the compile(ast) the co_firstlineno would be == 1. + + This makes pydevd/debugpy think that all cells invoked are the same + since it caches information based on (co_firstlineno, co_name, co_filename). + + Given that, this function changes the code 'co_name' to be unique + based on the first real lineno of the code (which also has a nice + side effect of customizing the name so that it's not always ). + + See: https://github.com/ipython/ipykernel/issues/841 + """ + if not hasattr(code, "replace"): + # It may not be available on older versions of Python (only + # available for 3.8 onwards). + return code + try: + first_real_line = next(dis.findlinestarts(code))[1] + except StopIteration: + return code + return code.replace(co_name="" % (first_real_line,)) + async def run_ast_nodes( self, nodelist: ListType[stmt], @@ -3239,6 +3333,7 @@ class InteractiveShell(SingletonConfigurable): else 0x0 ): code = compiler(mod, cell_name, mode) + code = self._update_code_co_name(code) asy = compare(code) if await self.run_code(code, result, async_=asy): return True @@ -3309,6 +3404,11 @@ class InteractiveShell(SingletonConfigurable): result.error_in_exec = e self.showtraceback(exception_only=True) warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1) + except bdb.BdbQuit: + etype, value, tb = sys.exc_info() + if result is not None: + result.error_in_exec = value + # the BdbQuit stops here except self.custom_exceptions: etype, value, tb = sys.exc_info() if result is not None: @@ -3375,8 +3475,9 @@ class InteractiveShell(SingletonConfigurable): make sense in all contexts, for example a terminal ipython can't display figures inline. """ - from IPython.core import pylabtools as pt from matplotlib_inline.backend_inline import configure_inline_support + + from IPython.core import pylabtools as pt gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select) if gui != 'inline': @@ -3679,6 +3780,10 @@ class InteractiveShell(SingletonConfigurable): pass del self.tempdirs + # Restore user's cursor + if hasattr(self, "editing_mode") and self.editing_mode == "vi": + sys.stdout.write("\x1b[0 q") + sys.stdout.flush() def cleanup(self): self.restore_sys_module_state() diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 79983df..cedba61 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -311,7 +311,7 @@ class MagicsManager(Configurable): For example:: - c.MagicsManger.lazy_magics = { + c.MagicsManager.lazy_magics = { "my_magic": "slow.to.import", "my_other_magic": "also.slow", } diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py index 371da5b..da7f780 100644 --- a/IPython/core/magics/execution.py +++ b/IPython/core/magics/execution.py @@ -511,7 +511,7 @@ class ExecutionMagics(Magics): """Run the named file inside IPython as a program. Usage:: - + %run [-n -i -e -G] [( -t [-N] | -d [-b] | -p [profile options] )] ( -m mod | filename ) [args] @@ -552,7 +552,7 @@ class ExecutionMagics(Magics): *two* back slashes (e.g. ``\\\\*``) to suppress expansions. To completely disable these expansions, you can use -G flag. - On Windows systems, the use of single quotes `'` when specifying + On Windows systems, the use of single quotes `'` when specifying a file is not supported. Use double quotes `"`. Options: diff --git a/IPython/core/magics/osm.py b/IPython/core/magics/osm.py index 11fec81..41957a2 100644 --- a/IPython/core/magics/osm.py +++ b/IPython/core/magics/osm.py @@ -127,7 +127,7 @@ class OSMagics(Magics): Aliases expand Python variables just like system calls using ! or !! do: all expressions prefixed with '$' get expanded. For details of the semantic rules, see PEP-215: - http://www.python.org/peps/pep-0215.html. This is the library used by + https://peps.python.org/pep-0215/. This is the library used by IPython for variable expansion. If you want to access a true shell variable, an extra $ is necessary to prevent its expansion by IPython:: diff --git a/IPython/core/magics/script.py b/IPython/core/magics/script.py index a0028c2..9fd2fc6 100644 --- a/IPython/core/magics/script.py +++ b/IPython/core/magics/script.py @@ -58,8 +58,8 @@ def script_args(f): '--no-raise-error', action="store_false", dest='raise_error', help="""Whether you should raise an error message in addition to a stream on stderr if you get a nonzero exit code. - """ - ) + """, + ), ] for arg in args: f = arg(f) diff --git a/IPython/core/oinspect.py b/IPython/core/oinspect.py index 0096b9c..1a5c0ae 100644 --- a/IPython/core/oinspect.py +++ b/IPython/core/oinspect.py @@ -518,12 +518,12 @@ class Inspector(Colorable): """Return a mime bundle representation of the input text. - if `formatter` is None, the returned mime bundle has - a `text/plain` field, with the input text. - a `text/html` field with a `
` tag containing the input text.
+           a ``text/plain`` field, with the input text.
+           a ``text/html`` field with a ``
`` tag containing the input text.
 
-        - if `formatter` is not None, it must be a callable transforming the
-          input text into a mime bundle. Default values for `text/plain` and
-          `text/html` representations are the ones described above.
+        - if ``formatter`` is not None, it must be a callable transforming the
+          input text into a mime bundle. Default values for ``text/plain`` and
+          ``text/html`` representations are the ones described above.
 
         Note:
 
diff --git a/IPython/core/release.py b/IPython/core/release.py
index c1c90b1..9d23699 100644
--- a/IPython/core/release.py
+++ b/IPython/core/release.py
@@ -16,7 +16,7 @@
 # release.  'dev' as a _version_extra string means this is a development
 # version
 _version_major = 8
-_version_minor = 1
+_version_minor = 4
 _version_patch = 0
 _version_extra = ".dev"
 # _version_extra = "rc1"
diff --git a/IPython/core/tests/test_alias.py b/IPython/core/tests/test_alias.py
index a84b009..32d2e2f 100644
--- a/IPython/core/tests/test_alias.py
+++ b/IPython/core/tests/test_alias.py
@@ -43,11 +43,11 @@ def test_alias_args_error():
 
 def test_alias_args_commented():
     """Check that alias correctly ignores 'commented out' args"""
-    _ip.magic('alias commetarg echo this is %%s a commented out arg')
-    
+    _ip.run_line_magic("alias", "commentarg echo this is %%s a commented out arg")
+
     with capture_output() as cap:
-        _ip.run_cell('commetarg')
-    
+        _ip.run_cell("commentarg")
+
     # strip() is for pytest compat; testing via iptest patch IPython shell
     # in testing.globalipapp and replace the system call which messed up the
     # \r\n
diff --git a/IPython/core/tests/test_application.py b/IPython/core/tests/test_application.py
index 74ea0f7..827a1e3 100644
--- a/IPython/core/tests/test_application.py
+++ b/IPython/core/tests/test_application.py
@@ -4,11 +4,11 @@
 import os
 import tempfile
 
+from tempfile import TemporaryDirectory
 from traitlets import Unicode
 
 from IPython.core.application import BaseIPythonApplication
 from IPython.testing import decorators as dec
-from IPython.utils.tempdir import TemporaryDirectory
 
 
 @dec.onlyif_unicode_paths
diff --git a/IPython/core/tests/test_completer.py b/IPython/core/tests/test_completer.py
index 5f791e8..746a1e6 100644
--- a/IPython/core/tests/test_completer.py
+++ b/IPython/core/tests/test_completer.py
@@ -1262,3 +1262,14 @@ class TestCompleter(unittest.TestCase):
         _, matches = ip.complete(None, "test.meth(")
         self.assertIn("meth_arg1=", matches)
         self.assertNotIn("meth2_arg1=", matches)
+
+    def test_percent_symbol_restrict_to_magic_completions(self):
+        ip = get_ipython()
+        completer = ip.Completer
+        text = "%a"
+
+        with provisionalcompleter():
+            completer.use_jedi = True
+            completions = completer.completions(text, len(text))
+            for c in completions:
+                self.assertEqual(c.text[0], "%")
diff --git a/IPython/core/tests/test_completerlib.py b/IPython/core/tests/test_completerlib.py
index fbbc258..0e8bf19 100644
--- a/IPython/core/tests/test_completerlib.py
+++ b/IPython/core/tests/test_completerlib.py
@@ -14,8 +14,9 @@ import tempfile
 import unittest
 from os.path import join
 
+from tempfile import TemporaryDirectory
+
 from IPython.core.completerlib import magic_run_completer, module_completion, try_import
-from IPython.utils.tempdir import TemporaryDirectory
 from IPython.testing.decorators import onlyif_unicode_paths
 
 
diff --git a/IPython/core/tests/test_extension.py b/IPython/core/tests/test_extension.py
index 59e21dc..24ecf7e 100644
--- a/IPython/core/tests/test_extension.py
+++ b/IPython/core/tests/test_extension.py
@@ -1,8 +1,9 @@
 import os.path
 
+from tempfile import TemporaryDirectory
+
 import IPython.testing.tools as tt
 from IPython.utils.syspathcontext import prepended_to_syspath
-from IPython.utils.tempdir import TemporaryDirectory
 
 ext1_content = """
 def load_ipython_extension(ip):
diff --git a/IPython/core/tests/test_handlers.py b/IPython/core/tests/test_handlers.py
index e151e70..604dade 100644
--- a/IPython/core/tests/test_handlers.py
+++ b/IPython/core/tests/test_handlers.py
@@ -56,36 +56,42 @@ def test_handlers():
     ip.user_ns['autocallable'] = autocallable
 
     # auto
-    ip.magic('autocall 0')
+    ip.run_line_magic("autocall", "0")
     # Only explicit escapes or instances of IPyAutocallable should get
     # expanded
-    run([
-        ('len "abc"',       'len "abc"'),
-        ('autocallable',    'autocallable()'),
-        # Don't add extra brackets (gh-1117)
-        ('autocallable()',    'autocallable()'),
-        ])
-    ip.magic('autocall 1')
-    run([
-        ('len "abc"', 'len("abc")'),
-        ('len "abc";', 'len("abc");'),  # ; is special -- moves out of parens
-        # Autocall is turned off if first arg is [] and the object
-        # is both callable and indexable.  Like so:
-        ('len [1,2]', 'len([1,2])'),      # len doesn't support __getitem__...
-        ('call_idx [1]', 'call_idx [1]'), # call_idx *does*..
-        ('call_idx 1', 'call_idx(1)'),
-        ('len', 'len'), # only at 2 does it auto-call on single args
-        ])
-    ip.magic('autocall 2')
-    run([
-        ('len "abc"', 'len("abc")'),
-        ('len "abc";', 'len("abc");'),
-        ('len [1,2]', 'len([1,2])'),
-        ('call_idx [1]', 'call_idx [1]'),
-        ('call_idx 1', 'call_idx(1)'),
-        # This is what's different:
-        ('len', 'len()'), # only at 2 does it auto-call on single args
-        ])
-    ip.magic('autocall 1')
+    run(
+        [
+            ('len "abc"', 'len "abc"'),
+            ("autocallable", "autocallable()"),
+            # Don't add extra brackets (gh-1117)
+            ("autocallable()", "autocallable()"),
+        ]
+    )
+    ip.run_line_magic("autocall", "1")
+    run(
+        [
+            ('len "abc"', 'len("abc")'),
+            ('len "abc";', 'len("abc");'),  # ; is special -- moves out of parens
+            # Autocall is turned off if first arg is [] and the object
+            # is both callable and indexable.  Like so:
+            ("len [1,2]", "len([1,2])"),  # len doesn't support __getitem__...
+            ("call_idx [1]", "call_idx [1]"),  # call_idx *does*..
+            ("call_idx 1", "call_idx(1)"),
+            ("len", "len"),  # only at 2 does it auto-call on single args
+        ]
+    )
+    ip.run_line_magic("autocall", "2")
+    run(
+        [
+            ('len "abc"', 'len("abc")'),
+            ('len "abc";', 'len("abc");'),
+            ("len [1,2]", "len([1,2])"),
+            ("call_idx [1]", "call_idx [1]"),
+            ("call_idx 1", "call_idx(1)"),
+            # This is what's different:
+            ("len", "len()"),  # only at 2 does it auto-call on single args
+        ]
+    )
+    ip.run_line_magic("autocall", "1")
 
     assert failures == []
diff --git a/IPython/core/tests/test_history.py b/IPython/core/tests/test_history.py
index 6d6a1b1..73d50c8 100644
--- a/IPython/core/tests/test_history.py
+++ b/IPython/core/tests/test_history.py
@@ -7,17 +7,19 @@
 
 # stdlib
 import io
-from pathlib import Path
+import sqlite3
 import sys
 import tempfile
 from datetime import datetime
-import sqlite3
+from pathlib import Path
 
+from tempfile import TemporaryDirectory
 # our own packages
 from traitlets.config.loader import Config
-from IPython.utils.tempdir import TemporaryDirectory
+
 from IPython.core.history import HistoryManager, extract_hist_ranges
 
+
 def test_proper_default_encoding():
     assert sys.getdefaultencoding() == "utf-8"
 
@@ -50,13 +52,13 @@ def test_history():
 
             # Check whether specifying a range beyond the end of the current
             # session results in an error (gh-804)
-            ip.magic('%hist 2-500')
+            ip.run_line_magic("hist", "2-500")
 
             # Check that we can write non-ascii characters to a file
-            ip.magic("%%hist -f %s" % (tmp_path / "test1"))
-            ip.magic("%%hist -pf %s" % (tmp_path / "test2"))
-            ip.magic("%%hist -nf %s" % (tmp_path / "test3"))
-            ip.magic("%%save %s 1-10" % (tmp_path / "test4"))
+            ip.run_line_magic("hist", "-f %s" % (tmp_path / "test1"))
+            ip.run_line_magic("hist", "-pf %s" % (tmp_path / "test2"))
+            ip.run_line_magic("hist", "-nf %s" % (tmp_path / "test3"))
+            ip.run_line_magic("save", "%s 1-10" % (tmp_path / "test4"))
 
             # New session
             ip.history_manager.reset()
@@ -122,7 +124,7 @@ def test_history():
 
             # Cross testing: check that magic %save can get previous session.
             testfilename = (tmp_path / "test.py").resolve()
-            ip.magic("save " + str(testfilename) + " ~1/1-3")
+            ip.run_line_magic("save", str(testfilename) + " ~1/1-3")
             with io.open(testfilename, encoding="utf-8") as testfile:
                 assert testfile.read() == "# coding: utf-8\n" + "\n".join(hist) + "\n"
 
diff --git a/IPython/core/tests/test_inputtransformer.py b/IPython/core/tests/test_inputtransformer.py
index 4de97b8..bfc936d 100644
--- a/IPython/core/tests/test_inputtransformer.py
+++ b/IPython/core/tests/test_inputtransformer.py
@@ -59,108 +59,93 @@ syntax = \
         ('x=1', 'x=1'), # normal input is unmodified
         ('    ','    '),  # blank lines are kept intact
         ("a, b = %foo", "a, b = get_ipython().run_line_magic('foo', '')"),
-        ],
-
-       classic_prompt =
-       [('>>> x=1', 'x=1'),
-        ('x=1', 'x=1'), # normal input is unmodified
-        ('    ', '    '),  # blank lines are kept intact
-        ],
-
-       ipy_prompt =
-       [('In [1]: x=1', 'x=1'),
-        ('x=1', 'x=1'), # normal input is unmodified
-        ('    ','    '),  # blank lines are kept intact
-        ],
-
-       # Tests for the escape transformer to leave normal code alone
-       escaped_noesc =
-       [ ('    ', '    '),
-         ('x=1', 'x=1'),
-         ],
-
-       # System calls
-       escaped_shell =
-       [ ('!ls', "get_ipython().system('ls')"),
-         # Double-escape shell, this means to capture the output of the
-         # subprocess and return it
-         ('!!ls', "get_ipython().getoutput('ls')"),
-         ],
-
-       # Help/object info
-       escaped_help =
-       [ ('?', 'get_ipython().show_usage()'),
-         ('?x1', "get_ipython().run_line_magic('pinfo', 'x1')"),
-         ('??x2', "get_ipython().run_line_magic('pinfo2', 'x2')"),
-         ('?a.*s', "get_ipython().run_line_magic('psearch', 'a.*s')"),
-         ('?%hist1', "get_ipython().run_line_magic('pinfo', '%hist1')"),
-         ('?%%hist2', "get_ipython().run_line_magic('pinfo', '%%hist2')"),
-         ('?abc = qwe', "get_ipython().run_line_magic('pinfo', 'abc')"),
-         ],
-
-      end_help =
-      [ ('x3?', "get_ipython().run_line_magic('pinfo', 'x3')"),
-        ('x4??', "get_ipython().run_line_magic('pinfo2', 'x4')"),
-        ('%hist1?', "get_ipython().run_line_magic('pinfo', '%hist1')"),
-        ('%hist2??', "get_ipython().run_line_magic('pinfo2', '%hist2')"),
-        ('%%hist3?', "get_ipython().run_line_magic('pinfo', '%%hist3')"),
-        ('%%hist4??', "get_ipython().run_line_magic('pinfo2', '%%hist4')"),
-        ('π.foo?', "get_ipython().run_line_magic('pinfo', 'π.foo')"),
-        ('f*?', "get_ipython().run_line_magic('psearch', 'f*')"),
-        ('ax.*aspe*?', "get_ipython().run_line_magic('psearch', 'ax.*aspe*')"),
-        ('a = abc?', "get_ipython().set_next_input('a = abc');"
-                      "get_ipython().run_line_magic('pinfo', 'abc')"),
-        ('a = abc.qe??', "get_ipython().set_next_input('a = abc.qe');"
-                          "get_ipython().run_line_magic('pinfo2', 'abc.qe')"),
-        ('a = *.items?', "get_ipython().set_next_input('a = *.items');"
-                          "get_ipython().run_line_magic('psearch', '*.items')"),
-        ('plot(a?', "get_ipython().set_next_input('plot(a');"
-                     "get_ipython().run_line_magic('pinfo', 'a')"),
-        ('a*2 #comment?', 'a*2 #comment?'),
-        ],
-
-       # Explicit magic calls
-       escaped_magic =
-       [ ('%cd', "get_ipython().run_line_magic('cd', '')"),
-         ('%cd /home', "get_ipython().run_line_magic('cd', '/home')"),
-         # Backslashes need to be escaped.
-         ('%cd C:\\User', "get_ipython().run_line_magic('cd', 'C:\\\\User')"),
-         ('    %magic', "    get_ipython().run_line_magic('magic', '')"),
-         ],
-
-       # Quoting with separate arguments
-       escaped_quote =
-       [ (',f', 'f("")'),
-         (',f x', 'f("x")'),
-         ('  ,f y', '  f("y")'),
-         (',f a b', 'f("a", "b")'),
-         ],
-
-       # Quoting with single argument
-       escaped_quote2 =
-       [ (';f', 'f("")'),
-         (';f x', 'f("x")'),
-         ('  ;f y', '  f("y")'),
-         (';f a b', 'f("a b")'),
-         ],
-
-       # Simply apply parens
-       escaped_paren =
-       [ ('/f', 'f()'),
-         ('/f x', 'f(x)'),
-         ('  /f y', '  f(y)'),
-         ('/f a b', 'f(a, b)'),
-         ],
-
-       # Check that we transform prompts before other transforms
-       mixed =
-       [ ('In [1]: %lsmagic', "get_ipython().run_line_magic('lsmagic', '')"),
-         ('>>> %lsmagic', "get_ipython().run_line_magic('lsmagic', '')"),
-         ('In [2]: !ls', "get_ipython().system('ls')"),
-         ('In [3]: abs?', "get_ipython().run_line_magic('pinfo', 'abs')"),
-         ('In [4]: b = %who', "b = get_ipython().run_line_magic('who', '')"),
-         ],
-       )
+    ],
+    classic_prompt=[
+        (">>> x=1", "x=1"),
+        ("x=1", "x=1"),  # normal input is unmodified
+        ("    ", "    "),  # blank lines are kept intact
+    ],
+    ipy_prompt=[
+        ("In [1]: x=1", "x=1"),
+        ("x=1", "x=1"),  # normal input is unmodified
+        ("    ", "    "),  # blank lines are kept intact
+    ],
+    # Tests for the escape transformer to leave normal code alone
+    escaped_noesc=[
+        ("    ", "    "),
+        ("x=1", "x=1"),
+    ],
+    # System calls
+    escaped_shell=[
+        ("!ls", "get_ipython().system('ls')"),
+        # Double-escape shell, this means to capture the output of the
+        # subprocess and return it
+        ("!!ls", "get_ipython().getoutput('ls')"),
+    ],
+    # Help/object info
+    escaped_help=[
+        ("?", "get_ipython().show_usage()"),
+        ("?x1", "get_ipython().run_line_magic('pinfo', 'x1')"),
+        ("??x2", "get_ipython().run_line_magic('pinfo2', 'x2')"),
+        ("?a.*s", "get_ipython().run_line_magic('psearch', 'a.*s')"),
+        ("?%hist1", "get_ipython().run_line_magic('pinfo', '%hist1')"),
+        ("?%%hist2", "get_ipython().run_line_magic('pinfo', '%%hist2')"),
+        ("?abc = qwe", "get_ipython().run_line_magic('pinfo', 'abc')"),
+    ],
+    end_help=[
+        ("x3?", "get_ipython().run_line_magic('pinfo', 'x3')"),
+        ("x4??", "get_ipython().run_line_magic('pinfo2', 'x4')"),
+        ("%hist1?", "get_ipython().run_line_magic('pinfo', '%hist1')"),
+        ("%hist2??", "get_ipython().run_line_magic('pinfo2', '%hist2')"),
+        ("%%hist3?", "get_ipython().run_line_magic('pinfo', '%%hist3')"),
+        ("%%hist4??", "get_ipython().run_line_magic('pinfo2', '%%hist4')"),
+        ("π.foo?", "get_ipython().run_line_magic('pinfo', 'π.foo')"),
+        ("f*?", "get_ipython().run_line_magic('psearch', 'f*')"),
+        ("ax.*aspe*?", "get_ipython().run_line_magic('psearch', 'ax.*aspe*')"),
+        ("a = abc?", "get_ipython().run_line_magic('pinfo', 'abc')"),
+        ("a = abc.qe??", "get_ipython().run_line_magic('pinfo2', 'abc.qe')"),
+        ("a = *.items?", "get_ipython().run_line_magic('psearch', '*.items')"),
+        ("plot(a?", "get_ipython().run_line_magic('pinfo', 'a')"),
+        ("a*2 #comment?", "a*2 #comment?"),
+    ],
+    # Explicit magic calls
+    escaped_magic=[
+        ("%cd", "get_ipython().run_line_magic('cd', '')"),
+        ("%cd /home", "get_ipython().run_line_magic('cd', '/home')"),
+        # Backslashes need to be escaped.
+        ("%cd C:\\User", "get_ipython().run_line_magic('cd', 'C:\\\\User')"),
+        ("    %magic", "    get_ipython().run_line_magic('magic', '')"),
+    ],
+    # Quoting with separate arguments
+    escaped_quote=[
+        (",f", 'f("")'),
+        (",f x", 'f("x")'),
+        ("  ,f y", '  f("y")'),
+        (",f a b", 'f("a", "b")'),
+    ],
+    # Quoting with single argument
+    escaped_quote2=[
+        (";f", 'f("")'),
+        (";f x", 'f("x")'),
+        ("  ;f y", '  f("y")'),
+        (";f a b", 'f("a b")'),
+    ],
+    # Simply apply parens
+    escaped_paren=[
+        ("/f", "f()"),
+        ("/f x", "f(x)"),
+        ("  /f y", "  f(y)"),
+        ("/f a b", "f(a, b)"),
+    ],
+    # Check that we transform prompts before other transforms
+    mixed=[
+        ("In [1]: %lsmagic", "get_ipython().run_line_magic('lsmagic', '')"),
+        (">>> %lsmagic", "get_ipython().run_line_magic('lsmagic', '')"),
+        ("In [2]: !ls", "get_ipython().system('ls')"),
+        ("In [3]: abs?", "get_ipython().run_line_magic('pinfo', 'abs')"),
+        ("In [4]: b = %who", "b = get_ipython().run_line_magic('who', '')"),
+    ],
+)
 
 # multiline syntax examples.  Each of these should be a list of lists, with
 # each entry itself having pairs of raw/transformed input.  The union (with
diff --git a/IPython/core/tests/test_inputtransformer2.py b/IPython/core/tests/test_inputtransformer2.py
index abc6303..cddb32f 100644
--- a/IPython/core/tests/test_inputtransformer2.py
+++ b/IPython/core/tests/test_inputtransformer2.py
@@ -14,45 +14,65 @@ import pytest
 from IPython.core import inputtransformer2 as ipt2
 from IPython.core.inputtransformer2 import _find_assign_op, make_tokens_by_line
 
-MULTILINE_MAGIC = ("""\
+MULTILINE_MAGIC = (
+    """\
 a = f()
 %foo \\
 bar
 g()
-""".splitlines(keepends=True), (2, 0), """\
+""".splitlines(
+        keepends=True
+    ),
+    (2, 0),
+    """\
 a = f()
 get_ipython().run_line_magic('foo', ' bar')
 g()
-""".splitlines(keepends=True))
+""".splitlines(
+        keepends=True
+    ),
+)
 
-INDENTED_MAGIC = ("""\
+INDENTED_MAGIC = (
+    """\
 for a in range(5):
     %ls
-""".splitlines(keepends=True), (2, 4), """\
+""".splitlines(
+        keepends=True
+    ),
+    (2, 4),
+    """\
 for a in range(5):
     get_ipython().run_line_magic('ls', '')
-""".splitlines(keepends=True))
+""".splitlines(
+        keepends=True
+    ),
+)
 
-CRLF_MAGIC = ([
-    "a = f()\n",
-    "%ls\r\n",
-    "g()\n"
-], (2, 0), [
-    "a = f()\n",
-    "get_ipython().run_line_magic('ls', '')\n",
-    "g()\n"
-])
-
-MULTILINE_MAGIC_ASSIGN = ("""\
+CRLF_MAGIC = (
+    ["a = f()\n", "%ls\r\n", "g()\n"],
+    (2, 0),
+    ["a = f()\n", "get_ipython().run_line_magic('ls', '')\n", "g()\n"],
+)
+
+MULTILINE_MAGIC_ASSIGN = (
+    """\
 a = f()
 b = %foo \\
   bar
 g()
-""".splitlines(keepends=True), (2, 4), """\
+""".splitlines(
+        keepends=True
+    ),
+    (2, 4),
+    """\
 a = f()
 b = get_ipython().run_line_magic('foo', '   bar')
 g()
-""".splitlines(keepends=True))
+""".splitlines(
+        keepends=True
+    ),
+)
 
 MULTILINE_SYSTEM_ASSIGN = ("""\
 a = f()
@@ -67,73 +87,76 @@ g()
 
 #####
 
-MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT = ("""\
+MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT = (
+    """\
 def test():
   for i in range(1):
     print(i)
   res =! ls
-""".splitlines(keepends=True), (4, 7), '''\
+""".splitlines(
+        keepends=True
+    ),
+    (4, 7),
+    """\
 def test():
   for i in range(1):
     print(i)
   res =get_ipython().getoutput(\' ls\')
-'''.splitlines(keepends=True))
+""".splitlines(
+        keepends=True
+    ),
+)
 
 ######
 
-AUTOCALL_QUOTE = (
-    [",f 1 2 3\n"], (1, 0),
-    ['f("1", "2", "3")\n']
-)
+AUTOCALL_QUOTE = ([",f 1 2 3\n"], (1, 0), ['f("1", "2", "3")\n'])
 
-AUTOCALL_QUOTE2 = (
-    [";f 1 2 3\n"], (1, 0),
-    ['f("1 2 3")\n']
-)
+AUTOCALL_QUOTE2 = ([";f 1 2 3\n"], (1, 0), ['f("1 2 3")\n'])
 
-AUTOCALL_PAREN = (
-    ["/f 1 2 3\n"], (1, 0),
-    ['f(1, 2, 3)\n']
-)
+AUTOCALL_PAREN = (["/f 1 2 3\n"], (1, 0), ["f(1, 2, 3)\n"])
 
-SIMPLE_HELP = (
-    ["foo?\n"], (1, 0),
-    ["get_ipython().run_line_magic('pinfo', 'foo')\n"]
-)
+SIMPLE_HELP = (["foo?\n"], (1, 0), ["get_ipython().run_line_magic('pinfo', 'foo')\n"])
 
 DETAILED_HELP = (
-    ["foo??\n"], (1, 0),
-    ["get_ipython().run_line_magic('pinfo2', 'foo')\n"]
+    ["foo??\n"],
+    (1, 0),
+    ["get_ipython().run_line_magic('pinfo2', 'foo')\n"],
 )
 
-MAGIC_HELP = (
-    ["%foo?\n"], (1, 0),
-    ["get_ipython().run_line_magic('pinfo', '%foo')\n"]
-)
+MAGIC_HELP = (["%foo?\n"], (1, 0), ["get_ipython().run_line_magic('pinfo', '%foo')\n"])
 
 HELP_IN_EXPR = (
-    ["a = b + c?\n"], (1, 0),
-    ["get_ipython().set_next_input('a = b + c');"
-     "get_ipython().run_line_magic('pinfo', 'c')\n"]
+    ["a = b + c?\n"],
+    (1, 0),
+    ["get_ipython().run_line_magic('pinfo', 'c')\n"],
 )
 
-HELP_CONTINUED_LINE = ("""\
+HELP_CONTINUED_LINE = (
+    """\
 a = \\
 zip?
-""".splitlines(keepends=True), (1, 0),
-[r"get_ipython().set_next_input('a = \\\nzip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"]
+""".splitlines(
+        keepends=True
+    ),
+    (1, 0),
+    [r"get_ipython().run_line_magic('pinfo', 'zip')" + "\n"],
 )
 
-HELP_MULTILINE = ("""\
+HELP_MULTILINE = (
+    """\
 (a,
 b) = zip?
-""".splitlines(keepends=True), (1, 0),
-[r"get_ipython().set_next_input('(a,\nb) = zip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"]
+""".splitlines(
+        keepends=True
+    ),
+    (1, 0),
+    [r"get_ipython().run_line_magic('pinfo', 'zip')" + "\n"],
 )
 
 HELP_UNICODE = (
-    ["π.foo?\n"], (1, 0),
-    ["get_ipython().run_line_magic('pinfo', 'π.foo')\n"]
+    ["π.foo?\n"],
+    (1, 0),
+    ["get_ipython().run_line_magic('pinfo', 'π.foo')\n"],
 )
 
 
@@ -149,6 +172,7 @@ def test_check_make_token_by_line_never_ends_empty():
     Check that not sequence of single or double characters ends up leading to en empty list of tokens
     """
     from string import printable
+
     for c in printable:
         assert make_tokens_by_line(c)[-1] != []
         for k in printable:
@@ -156,7 +180,7 @@ def test_check_make_token_by_line_never_ends_empty():
 
 
 def check_find(transformer, case, match=True):
-    sample, expected_start, _  = case
+    sample, expected_start, _ = case
     tbl = make_tokens_by_line(sample)
     res = transformer.find(tbl)
     if match:
@@ -166,25 +190,30 @@ def check_find(transformer, case, match=True):
     else:
         assert res is None
 
+
 def check_transform(transformer_cls, case):
     lines, start, expected = case
     transformer = transformer_cls(start)
     assert transformer.transform(lines) == expected
 
+
 def test_continued_line():
     lines = MULTILINE_MAGIC_ASSIGN[0]
     assert ipt2.find_end_of_continued_line(lines, 1) == 2
 
     assert ipt2.assemble_continued_line(lines, (1, 5), 2) == "foo    bar"
 
+
 def test_find_assign_magic():
     check_find(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN)
     check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN, match=False)
     check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT, match=False)
 
+
 def test_transform_assign_magic():
     check_transform(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN)
 
+
 def test_find_assign_system():
     check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN)
     check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT)
@@ -192,30 +221,36 @@ def test_find_assign_system():
     check_find(ipt2.SystemAssign, (["a=!ls\n"], (1, 2), None))
     check_find(ipt2.SystemAssign, MULTILINE_MAGIC_ASSIGN, match=False)
 
+
 def test_transform_assign_system():
     check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN)
     check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT)
 
+
 def test_find_magic_escape():
     check_find(ipt2.EscapedCommand, MULTILINE_MAGIC)
     check_find(ipt2.EscapedCommand, INDENTED_MAGIC)
     check_find(ipt2.EscapedCommand, MULTILINE_MAGIC_ASSIGN, match=False)
 
+
 def test_transform_magic_escape():
     check_transform(ipt2.EscapedCommand, MULTILINE_MAGIC)
     check_transform(ipt2.EscapedCommand, INDENTED_MAGIC)
     check_transform(ipt2.EscapedCommand, CRLF_MAGIC)
 
+
 def test_find_autocalls():
     for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]:
         print("Testing %r" % case[0])
         check_find(ipt2.EscapedCommand, case)
 
+
 def test_transform_autocall():
     for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]:
         print("Testing %r" % case[0])
         check_transform(ipt2.EscapedCommand, case)
 
+
 def test_find_help():
     for case in [SIMPLE_HELP, DETAILED_HELP, MAGIC_HELP, HELP_IN_EXPR]:
         check_find(ipt2.HelpEnd, case)
@@ -233,6 +268,7 @@ def test_find_help():
     # Nor in a string
     check_find(ipt2.HelpEnd, (["foo = '''bar?\n"], None, None), match=False)
 
+
 def test_transform_help():
     tf = ipt2.HelpEnd((1, 0), (1, 9))
     assert tf.transform(HELP_IN_EXPR[0]) == HELP_IN_EXPR[2]
@@ -246,10 +282,12 @@ def test_transform_help():
     tf = ipt2.HelpEnd((1, 0), (1, 0))
     assert tf.transform(HELP_UNICODE[0]) == HELP_UNICODE[2]
 
+
 def test_find_assign_op_dedent():
     """
     be careful that empty token like dedent are not counted as parens
     """
+
     class Tk:
         def __init__(self, s):
             self.string = s
@@ -302,21 +340,23 @@ def test_check_complete_param(code, expected, number):
 def test_check_complete():
     cc = ipt2.TransformerManager().check_complete
 
-    example = dedent("""
+    example = dedent(
+        """
         if True:
-            a=1""" )
+            a=1"""
+    )
 
     assert cc(example) == ("incomplete", 4)
     assert cc(example + "\n") == ("complete", None)
     assert cc(example + "\n    ") == ("complete", None)
 
     # no need to loop on all the letters/numbers.
-    short = '12abAB'+string.printable[62:]
+    short = "12abAB" + string.printable[62:]
     for c in short:
         # test does not raise:
         cc(c)
         for k in short:
-            cc(c+k)
+            cc(c + k)
 
     assert cc("def f():\n  x=0\n  \\\n  ") == ("incomplete", 2)
 
@@ -371,10 +411,9 @@ def test_null_cleanup_transformer():
     assert manager.transform_cell("") == ""
 
 
-
-
 def test_side_effects_I():
     count = 0
+
     def counter(lines):
         nonlocal count
         count += 1
@@ -384,14 +423,13 @@ def test_side_effects_I():
 
     manager = ipt2.TransformerManager()
     manager.cleanup_transforms.insert(0, counter)
-    assert manager.check_complete("a=1\n") == ('complete', None)
+    assert manager.check_complete("a=1\n") == ("complete", None)
     assert count == 0
 
 
-
-
 def test_side_effects_II():
     count = 0
+
     def counter(lines):
         nonlocal count
         count += 1
@@ -401,5 +439,5 @@ def test_side_effects_II():
 
     manager = ipt2.TransformerManager()
     manager.line_transforms.insert(0, counter)
-    assert manager.check_complete("b=1\n") == ('complete', None)
+    assert manager.check_complete("b=1\n") == ("complete", None)
     assert count == 0
diff --git a/IPython/core/tests/test_interactiveshell.py b/IPython/core/tests/test_interactiveshell.py
index 09dbd96..10827b5 100644
--- a/IPython/core/tests/test_interactiveshell.py
+++ b/IPython/core/tests/test_interactiveshell.py
@@ -380,7 +380,8 @@ class InteractiveShellTestCase(unittest.TestCase):
         class A(object):
             @property
             def foo(self):
-                raise NotImplementedError()
+                raise NotImplementedError()  # pragma: no cover
+
         a = A()
 
         found = ip._ofind('a.foo', [('locals', locals())])
@@ -392,7 +393,7 @@ class InteractiveShellTestCase(unittest.TestCase):
         class A(object):
             @property
             def foo(self):
-                raise NotImplementedError()
+                raise NotImplementedError()  # pragma: no cover
 
         a = A()
         a.a = A()
@@ -546,7 +547,7 @@ class TestSafeExecfileNonAsciiPath(unittest.TestCase):
         self.TESTDIR = join(self.BASETESTDIR, u"åäö")
         os.mkdir(self.TESTDIR)
         with open(
-            join(self.TESTDIR, u"åäötestscript.py"), "w", encoding="utf-8"
+            join(self.TESTDIR, "åäötestscript.py"), "w", encoding="utf-8"
         ) as sfile:
             sfile.write("pass\n")
         self.oldpath = os.getcwd()
@@ -585,9 +586,9 @@ class ExitCodeChecks(tt.TempFileMixin):
         self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
     
     @onlyif_cmds_exist("csh")
-    def test_exit_code_signal_csh(self):
-        SHELL = os.environ.get('SHELL', None)
-        os.environ['SHELL'] = find_cmd("csh")
+    def test_exit_code_signal_csh(self):  # pragma: no cover
+        SHELL = os.environ.get("SHELL", None)
+        os.environ["SHELL"] = find_cmd("csh")
         try:
             self.test_exit_code_signal()
         finally:
@@ -615,7 +616,7 @@ class TestSystemRaw(ExitCodeChecks):
     def test_control_c(self, *mocks):
         try:
             self.system("sleep 1 # wont happen")
-        except KeyboardInterrupt:
+        except KeyboardInterrupt:  # pragma: no cove
             self.fail(
                 "system call should intercept "
                 "keyboard interrupt from subprocess.call"
@@ -623,7 +624,7 @@ class TestSystemRaw(ExitCodeChecks):
         self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
 
     def test_magic_warnings(self):
-        for magic_cmd in ("ls", "pip", "conda", "cd"):
+        for magic_cmd in ("pip", "conda", "cd"):
             with self.assertWarnsRegex(Warning, "You executed the system command"):
                 ip.system_raw(magic_cmd)
 
@@ -679,16 +680,20 @@ class TestAstTransform(unittest.TestCase):
     
     def tearDown(self):
         ip.ast_transformers.remove(self.negator)
-    
+
+    def test_non_int_const(self):
+        with tt.AssertPrints("hello"):
+            ip.run_cell('print("hello")')
+
     def test_run_cell(self):
-        with tt.AssertPrints('-34'):
-            ip.run_cell('print (12 + 22)')
-        
+        with tt.AssertPrints("-34"):
+            ip.run_cell("print(12 + 22)")
+
         # A named reference to a number shouldn't be transformed.
-        ip.user_ns['n'] = 55
-        with tt.AssertNotPrints('-55'):
-            ip.run_cell('print (n)')
-    
+        ip.user_ns["n"] = 55
+        with tt.AssertNotPrints("-55"):
+            ip.run_cell("print(n)")
+
     def test_timeit(self):
         called = set()
         def f(x):
@@ -796,7 +801,11 @@ class TestAstTransform2(unittest.TestCase):
         # This shouldn't throw an error
         ip.run_cell("o = 2.0")
         self.assertEqual(ip.user_ns['o'], 2.0)
-    
+
+    def test_run_cell_non_int(self):
+        ip.run_cell("n = 'a'")
+        assert self.calls == []
+
     def test_timeit(self):
         called = set()
         def f(x):
@@ -815,14 +824,9 @@ class TestAstTransform2(unittest.TestCase):
 class ErrorTransformer(ast.NodeTransformer):
     """Throws an error when it sees a number."""
 
-    # for Python 3.7 and earlier
-    def visit_Num(self, node):
-        raise ValueError("test")
-
-    # for Python 3.8+
     def visit_Constant(self, node):
         if isinstance(node.value, int):
-            return self.visit_Num(node)
+            raise ValueError("test")
         return node
 
 
@@ -845,10 +849,6 @@ class StringRejector(ast.NodeTransformer):
     not be executed by throwing an InputRejected.
     """
     
-    #for python 3.7 and earlier
-    def visit_Str(self, node):
-        raise InputRejected("test")
-
     # 3.8 only
     def visit_Constant(self, node):
         if isinstance(node.value, str):
diff --git a/IPython/core/tests/test_iplib.py b/IPython/core/tests/test_iplib.py
index 94ce518..ec7007e 100644
--- a/IPython/core/tests/test_iplib.py
+++ b/IPython/core/tests/test_iplib.py
@@ -236,7 +236,8 @@ def test_run_cell():
     if 4:
         print "bar"
     
-    """)
+    """
+    )
     # Simply verifies that this kind of input is run
     ip.run_cell(complex)
     
diff --git a/IPython/core/tests/test_logger.py b/IPython/core/tests/test_logger.py
index 71c4e6b..10e4620 100644
--- a/IPython/core/tests/test_logger.py
+++ b/IPython/core/tests/test_logger.py
@@ -2,9 +2,10 @@
 """Test IPython.core.logger"""
 
 import os.path
+
 import pytest
+from tempfile import TemporaryDirectory
 
-from IPython.utils.tempdir import TemporaryDirectory
 
 def test_logstart_inaccessible_file():
     with pytest.raises(IOError):
diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py
index b46a9e8..850e227 100644
--- a/IPython/core/tests/test_magic.py
+++ b/IPython/core/tests/test_magic.py
@@ -448,7 +448,9 @@ def test_multiline_time():
     ip = get_ipython()
     ip.user_ns.pop('run', None)
 
-    ip.run_cell(dedent("""\
+    ip.run_cell(
+        dedent(
+            """\
         %%time
         a = "ho"
         b = "hey"
diff --git a/IPython/core/tests/test_magic_terminal.py b/IPython/core/tests/test_magic_terminal.py
index f090147..5dfa0f0 100644
--- a/IPython/core/tests/test_magic_terminal.py
+++ b/IPython/core/tests/test_magic_terminal.py
@@ -122,7 +122,8 @@ class PasteTestCase(TestCase):
         ip.user_ns.pop("x")
 
     def test_paste_py_multi(self):
-        self.paste("""
+        self.paste(
+            """
         >>> x = [1,2,3]
         >>> y = []
         >>> for i in x:
@@ -145,7 +146,8 @@ class PasteTestCase(TestCase):
 
     def test_paste_email(self):
         "Test pasting of email-quoted contents"
-        self.paste("""\
+        self.paste(
+            """\
         >> def foo(x):
         >>     return x + 1
         >> xx = foo(1.1)"""
@@ -154,7 +156,8 @@ class PasteTestCase(TestCase):
 
     def test_paste_email2(self):
         "Email again; some programs add a space also at each quoting level"
-        self.paste("""\
+        self.paste(
+            """\
         > > def foo(x):
         > >     return x + 1
         > > yy = foo(2.1)     """
@@ -163,7 +166,8 @@ class PasteTestCase(TestCase):
 
     def test_paste_email_py(self):
         "Email quoting of interactive input"
-        self.paste("""\
+        self.paste(
+            """\
         >> >>> def f(x):
         >> ...   return x+1
         >> ... 
diff --git a/IPython/core/tests/test_paths.py b/IPython/core/tests/test_paths.py
index d1366ee..eb754b8 100644
--- a/IPython/core/tests/test_paths.py
+++ b/IPython/core/tests/test_paths.py
@@ -6,11 +6,11 @@ import tempfile
 import warnings
 from unittest.mock import patch
 
-from testpath import modified_env, assert_isdir, assert_isfile
+from tempfile import TemporaryDirectory
+from testpath import assert_isdir, assert_isfile, modified_env
 
 from IPython import paths
 from IPython.testing.decorators import skip_win32
-from IPython.utils.tempdir import TemporaryDirectory
 
 TMP_TEST_DIR = os.path.realpath(tempfile.mkdtemp())
 HOME_TEST_DIR = os.path.join(TMP_TEST_DIR, "home_test_dir")
diff --git a/IPython/core/tests/test_profile.py b/IPython/core/tests/test_profile.py
index d034b50..876c7fd 100644
--- a/IPython/core/tests/test_profile.py
+++ b/IPython/core/tests/test_profile.py
@@ -23,17 +23,16 @@ Authors
 import shutil
 import sys
 import tempfile
-
 from pathlib import Path
 from unittest import TestCase
 
-from IPython.core.profileapp import list_profiles_in, list_bundled_profiles
-from IPython.core.profiledir import ProfileDir
+from tempfile import TemporaryDirectory
 
+from IPython.core.profileapp import list_bundled_profiles, list_profiles_in
+from IPython.core.profiledir import ProfileDir
 from IPython.testing import decorators as dec
 from IPython.testing import tools as tt
 from IPython.utils.process import getoutput
-from IPython.utils.tempdir import TemporaryDirectory
 
 #-----------------------------------------------------------------------------
 # Globals
@@ -109,7 +108,7 @@ def test_list_profiles_in():
     for name in ("profile_foo", "profile_hello", "not_a_profile"):
         Path(td / name).mkdir(parents=True)
     if dec.unicode_paths:
-        Path(td / u"profile_ünicode").mkdir(parents=True)
+        Path(td / "profile_ünicode").mkdir(parents=True)
 
     with open(td / "profile_file", "w", encoding="utf-8") as f:
         f.write("I am not a profile directory")
diff --git a/IPython/core/tests/test_run.py b/IPython/core/tests/test_run.py
index 9204e81..ae20ce6 100644
--- a/IPython/core/tests/test_run.py
+++ b/IPython/core/tests/test_run.py
@@ -19,21 +19,22 @@ as otherwise it may influence later tests.
 import functools
 import os
 import platform
-from os.path import join as pjoin
 import random
 import string
 import sys
 import textwrap
 import unittest
+from os.path import join as pjoin
 from unittest.mock import patch
 
 import pytest
+from tempfile import TemporaryDirectory
 
+from IPython.core import debugger
 from IPython.testing import decorators as dec
 from IPython.testing import tools as tt
 from IPython.utils.io import capture_output
-from IPython.utils.tempdir import TemporaryDirectory
-from IPython.core import debugger
+
 
 def doctest_refbug():
     """Very nasty problem with references held by multiple runs of a script.
@@ -411,6 +412,7 @@ tclass.py: deleting object: C-third
         """Test %run notebook.ipynb error"""
         pytest.importorskip("nbformat")
         from nbformat import v4, writes
+
         # %run when a file name isn't provided
         pytest.raises(Exception, _ip.magic, "run")
 
diff --git a/IPython/core/tests/test_ultratb.py b/IPython/core/tests/test_ultratb.py
index e10abb8..1f49603 100644
--- a/IPython/core/tests/test_ultratb.py
+++ b/IPython/core/tests/test_ultratb.py
@@ -3,21 +3,20 @@
 """
 import io
 import logging
+import os.path
 import platform
 import re
 import sys
-import os.path
-from textwrap import dedent
 import traceback
 import unittest
+from textwrap import dedent
 
-from IPython.core.ultratb import ColorTB, VerboseTB
-
+from tempfile import TemporaryDirectory
 
+from IPython.core.ultratb import ColorTB, VerboseTB
 from IPython.testing import tools as tt
 from IPython.testing.decorators import onlyif_unicode_paths
 from IPython.utils.syspathcontext import prepended_to_syspath
-from IPython.utils.tempdir import TemporaryDirectory
 
 file_1 = """1
 2
diff --git a/IPython/core/ultratb.py b/IPython/core/ultratb.py
index 1f08fa5..8569791 100644
--- a/IPython/core/ultratb.py
+++ b/IPython/core/ultratb.py
@@ -207,7 +207,16 @@ class TBTools(colorable.Colorable):
     # Number of frames to skip when reporting tracebacks
     tb_offset = 0
 
-    def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None):
+    def __init__(
+        self,
+        color_scheme="NoColor",
+        call_pdb=False,
+        ostream=None,
+        parent=None,
+        config=None,
+        *,
+        debugger_cls=None,
+    ):
         # Whether to call the interactive pdb debugger after printing
         # tracebacks or not
         super(TBTools, self).__init__(parent=parent, config=config)
@@ -227,9 +236,10 @@ class TBTools(colorable.Colorable):
 
         self.set_colors(color_scheme)
         self.old_scheme = color_scheme  # save initial value for toggles
+        self.debugger_cls = debugger_cls or debugger.Pdb
 
         if call_pdb:
-            self.pdb = debugger.Pdb()
+            self.pdb = debugger_cls()
         else:
             self.pdb = None
 
@@ -350,9 +360,6 @@ class ListTB(TBTools):
     Because they are meant to be called without a full traceback (only a
     list), instances of this class can't call the interactive pdb debugger."""
 
-    def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None):
-        TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
-                         ostream=ostream, parent=parent,config=config)
 
     def __call__(self, etype, value, elist):
         self.ostream.flush()
@@ -628,8 +635,15 @@ class VerboseTB(TBTools):
         tb_offset=1 allows use of this handler in interpreters which will have
         their own code at the top of the traceback (VerboseTB will first
         remove that frame before printing the traceback info)."""
-        TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
-                         ostream=ostream, parent=parent, config=config)
+        TBTools.__init__(
+            self,
+            color_scheme=color_scheme,
+            call_pdb=call_pdb,
+            ostream=ostream,
+            parent=parent,
+            config=config,
+            debugger_cls=debugger_cls,
+        )
         self.tb_offset = tb_offset
         self.long_header = long_header
         self.include_vars = include_vars
@@ -642,7 +656,6 @@ class VerboseTB(TBTools):
             check_cache = linecache.checkcache
         self.check_cache = check_cache
 
-        self.debugger_cls = debugger_cls or debugger.Pdb
         self.skip_hidden = True
 
     def format_record(self, frame_info):
@@ -763,7 +776,7 @@ class VerboseTB(TBTools):
         self,
         etype: type,
         evalue: BaseException,
-        etb: TracebackType,
+        etb: Optional[TracebackType],
         number_of_lines_of_context,
         tb_offset: Optional[int],
     ):
@@ -772,7 +785,6 @@ class VerboseTB(TBTools):
         This may be called multiple times by Python 3 exception chaining
         (PEP 3134).
         """
-        assert etb is not None
         # some locals
         orig_etype = etype
         try:
@@ -783,7 +795,9 @@ class VerboseTB(TBTools):
         tb_offset = self.tb_offset if tb_offset is None else tb_offset
         assert isinstance(tb_offset, int)
         head = self.prepare_header(etype, self.long_header)
-        records = self.get_records(etb, number_of_lines_of_context, tb_offset)
+        records = (
+            self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
+        )
 
         frames = []
         skipped = 0
@@ -822,6 +836,7 @@ class VerboseTB(TBTools):
     def get_records(
         self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
     ):
+        assert etb is not None
         context = number_of_lines_of_context - 1
         after = context // 2
         before = context - after
@@ -836,19 +851,17 @@ class VerboseTB(TBTools):
             after=after,
             pygments_formatter=formatter,
         )
-        assert etb is not None
         return list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
 
     def structured_traceback(
         self,
         etype: type,
         evalue: Optional[BaseException],
-        etb: TracebackType,
+        etb: Optional[TracebackType],
         tb_offset: Optional[int] = None,
         number_of_lines_of_context: int = 5,
     ):
         """Return a nice text document describing the traceback."""
-        assert etb is not None
         formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
                                                                tb_offset)
 
diff --git a/IPython/extensions/tests/test_autoreload.py b/IPython/extensions/tests/test_autoreload.py
index a0fe725..88637fb 100644
--- a/IPython/extensions/tests/test_autoreload.py
+++ b/IPython/extensions/tests/test_autoreload.py
@@ -33,7 +33,7 @@ from IPython.testing.decorators import skipif_not_numpy
 
 if platform.python_implementation() == "PyPy":
     pytest.skip(
-        "Current autoreload implementation is extremly slow on PyPy",
+        "Current autoreload implementation is extremely slow on PyPy",
         allow_module_level=True,
     )
 
diff --git a/IPython/external/qt_loaders.py b/IPython/external/qt_loaders.py
index 975855c..39ea298 100644
--- a/IPython/external/qt_loaders.py
+++ b/IPython/external/qt_loaders.py
@@ -66,7 +66,9 @@ class ImportDenier(importlib.abc.MetaPathFinder):
                 """
     Importing %s disabled by IPython, which has
     already imported an Incompatible QT Binding: %s
-    """ % (fullname, loaded_api()))
+    """
+                % (fullname, loaded_api())
+            )
 
 
 ID = ImportDenier()
diff --git a/IPython/lib/__init__.py b/IPython/lib/__init__.py
index 8eb8901..94b8ade 100644
--- a/IPython/lib/__init__.py
+++ b/IPython/lib/__init__.py
@@ -9,13 +9,3 @@ Extra capabilities for IPython
 #  Distributed under the terms of the BSD License.  The full license is in
 #  the file COPYING, distributed as part of this software.
 #-----------------------------------------------------------------------------
-
-#-----------------------------------------------------------------------------
-# Imports
-#-----------------------------------------------------------------------------
-
-from IPython.lib.security import passwd
-
-#-----------------------------------------------------------------------------
-# Code
-#-----------------------------------------------------------------------------
diff --git a/IPython/lib/display.py b/IPython/lib/display.py
index 52060da..5ff2983 100644
--- a/IPython/lib/display.py
+++ b/IPython/lib/display.py
@@ -92,8 +92,8 @@ class Audio(DisplayObject):
 
     From a File:
 
-    >>> Audio('/path/to/sound.wav')  # doctest: +SKIP
-    >>> Audio(filename='/path/to/sound.ogg')  # doctest: +SKIP
+    >>> Audio('IPython/lib/tests/test.wav')  # doctest: +SKIP
+    >>> Audio(filename='IPython/lib/tests/test.wav')  # doctest: +SKIP
 
     From Bytes:
 
@@ -103,9 +103,9 @@ class Audio(DisplayObject):
     See Also
     --------
     ipywidgets.Audio
-    
-         AUdio widget with more more flexibility and options.
-    
+
+         Audio widget with more more flexibility and options.
+
     """
     _read_flags = 'rb'
 
@@ -510,12 +510,10 @@ class FileLinks(FileLink):
 
         self.recursive = recursive
 
-    def _get_display_formatter(self,
-                               dirname_output_format,
-                               fname_output_format,
-                               fp_format,
-                               fp_cleaner=None):
-        """ generate built-in formatter function
+    def _get_display_formatter(
+        self, dirname_output_format, fname_output_format, fp_format, fp_cleaner=None
+    ):
+        """generate built-in formatter function
 
         this is used to define both the notebook and terminal built-in
          formatters as they only differ by some wrapper text for each entry
diff --git a/IPython/lib/security.py b/IPython/lib/security.py
deleted file mode 100644
index 152561d..0000000
--- a/IPython/lib/security.py
+++ /dev/null
@@ -1,114 +0,0 @@
-"""
-Password generation for the IPython notebook.
-"""
-#-----------------------------------------------------------------------------
-# Imports
-#-----------------------------------------------------------------------------
-# Stdlib
-import getpass
-import hashlib
-import random
-
-# Our own
-from IPython.core.error import UsageError
-from IPython.utils.py3compat import encode
-
-#-----------------------------------------------------------------------------
-# Globals
-#-----------------------------------------------------------------------------
-
-# Length of the salt in nr of hex chars, which implies salt_len * 4
-# bits of randomness.
-salt_len = 12
-
-#-----------------------------------------------------------------------------
-# Functions
-#-----------------------------------------------------------------------------
-
-def passwd(passphrase=None, algorithm='sha1'):
-    """Generate hashed password and salt for use in notebook configuration.
-
-    In the notebook configuration, set `c.NotebookApp.password` to
-    the generated string.
-
-    Parameters
-    ----------
-    passphrase : str
-        Password to hash.  If unspecified, the user is asked to input
-        and verify a password.
-    algorithm : str
-        Hashing algorithm to use (e.g, 'sha1' or any argument supported
-        by :func:`hashlib.new`).
-
-    Returns
-    -------
-    hashed_passphrase : str
-        Hashed password, in the format 'hash_algorithm:salt:passphrase_hash'.
-
-    Examples
-    --------
-    >>> passwd('mypassword')
-    'sha1:7cf3:b7d6da294ea9592a9480c8f52e63cd42cfb9dd12'  # random
-
-    """
-    if passphrase is None:
-        for i in range(3):
-            p0 = getpass.getpass('Enter password: ')
-            p1 = getpass.getpass('Verify password: ')
-            if p0 == p1:
-                passphrase = p0
-                break
-            else:
-                print('Passwords do not match.')
-        else:
-            raise UsageError('No matching passwords found. Giving up.')
-
-    h = hashlib.new(algorithm)
-    salt = ('%0' + str(salt_len) + 'x') % random.getrandbits(4 * salt_len)
-    h.update(encode(passphrase, 'utf-8') + encode(salt, 'ascii'))
-
-    return ':'.join((algorithm, salt, h.hexdigest()))
-
-
-def passwd_check(hashed_passphrase, passphrase):
-    """Verify that a given passphrase matches its hashed version.
-
-    Parameters
-    ----------
-    hashed_passphrase : str
-        Hashed password, in the format returned by `passwd`.
-    passphrase : str
-        Passphrase to validate.
-
-    Returns
-    -------
-    valid : bool
-        True if the passphrase matches the hash.
-
-    Examples
-    --------
-    >>> from IPython.lib.security import passwd_check
-    >>> passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a',
-    ...              'mypassword')
-    True
-
-    >>> passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a',
-    ...              'anotherpassword')
-    False
-    """
-    try:
-        algorithm, salt, pw_digest = hashed_passphrase.split(':', 2)
-    except (ValueError, TypeError):
-        return False
-
-    try:
-        h = hashlib.new(algorithm)
-    except ValueError:
-        return False
-
-    if len(pw_digest) == 0:
-        return False
-
-    h.update(encode(passphrase, 'utf-8') + encode(salt, 'ascii'))
-
-    return h.hexdigest() == pw_digest
diff --git a/IPython/lib/tests/test_deepreload.py b/IPython/lib/tests/test_deepreload.py
index 827249c..5da606c 100644
--- a/IPython/lib/tests/test_deepreload.py
+++ b/IPython/lib/tests/test_deepreload.py
@@ -4,14 +4,15 @@
 # Copyright (c) IPython Development Team.
 # Distributed under the terms of the Modified BSD License.
 
-import pytest
 import types
-
 from pathlib import Path
 
+import pytest
+from tempfile import TemporaryDirectory
+
+from IPython.lib.deepreload import modules_reloading
+from IPython.lib.deepreload import reload as dreload
 from IPython.utils.syspathcontext import prepended_to_syspath
-from IPython.utils.tempdir import TemporaryDirectory
-from IPython.lib.deepreload import reload as dreload, modules_reloading
 
 
 def test_deepreload():
diff --git a/IPython/lib/tests/test_latextools.py b/IPython/lib/tests/test_latextools.py
index ead73ab..d035752 100644
--- a/IPython/lib/tests/test_latextools.py
+++ b/IPython/lib/tests/test_latextools.py
@@ -31,7 +31,7 @@ def no_op(*args, **kwargs):
 
 
 @onlyif_cmds_exist("latex", "dvipng")
-@pytest.mark.parametrize("s, wrap", [(u"$$x^2$$", False), (u"x^2", True)])
+@pytest.mark.parametrize("s, wrap", [("$$x^2$$", False), ("x^2", True)])
 def test_latex_to_png_dvipng_runs(s, wrap):
     """
     Test that latex_to_png_dvipng just runs without error.
diff --git a/IPython/lib/tests/test_pretty.py b/IPython/lib/tests/test_pretty.py
index ca16924..8608516 100644
--- a/IPython/lib/tests/test_pretty.py
+++ b/IPython/lib/tests/test_pretty.py
@@ -273,7 +273,7 @@ def test_unicode_repr():
     p = pretty.pretty(c)
     assert p == u
     p = pretty.pretty([c])
-    assert p == u"[%s]" % u
+    assert p == "[%s]" % u
 
 
 def test_basic_class():
diff --git a/IPython/lib/tests/test_security.py b/IPython/lib/tests/test_security.py
deleted file mode 100644
index 27c32ab..0000000
--- a/IPython/lib/tests/test_security.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# coding: utf-8
-from IPython.lib import passwd
-from IPython.lib.security import passwd_check, salt_len
-
-def test_passwd_structure():
-    p = passwd("passphrase")
-    algorithm, salt, hashed = p.split(":")
-    assert algorithm == "sha1"
-    assert len(salt) == salt_len
-    assert len(hashed) == 40
-
-def test_roundtrip():
-    p = passwd("passphrase")
-    assert passwd_check(p, "passphrase") is True
-
-
-def test_bad():
-    p = passwd('passphrase')
-    assert passwd_check(p, p) is False
-    assert passwd_check(p, "a:b:c:d") is False
-    assert passwd_check(p, "a:b") is False
-
-
-def test_passwd_check_unicode():
-    # GH issue #4524
-    phash = u'sha1:23862bc21dd3:7a415a95ae4580582e314072143d9c382c491e4f'
-    assert passwd_check(phash, u"łe¶ŧ←↓→")
diff --git a/IPython/sphinxext/ipython_directive.py b/IPython/sphinxext/ipython_directive.py
index ac09640..18bdfca 100644
--- a/IPython/sphinxext/ipython_directive.py
+++ b/IPython/sphinxext/ipython_directive.py
@@ -220,6 +220,8 @@ except Exception:
 # for tokenizing blocks
 COMMENT, INPUT, OUTPUT =  range(3)
 
+PSEUDO_DECORATORS = ["suppress", "verbatim", "savefig", "doctest"]
+
 #-----------------------------------------------------------------------------
 # Functions and class declarations
 #-----------------------------------------------------------------------------
@@ -263,11 +265,17 @@ def block_parser(part, rgxin, rgxout, fmtin, fmtout):
             block.append((COMMENT, line))
             continue
 
-        if line_stripped.startswith('@'):
-            # Here is where we assume there is, at most, one decorator.
-            # Might need to rethink this.
-            decorator = line_stripped
-            continue
+        if any(
+            line_stripped.startswith("@" + pseudo_decorator)
+            for pseudo_decorator in PSEUDO_DECORATORS
+        ):
+            if decorator:
+                raise RuntimeError(
+                    "Applying multiple pseudo-decorators on one line is not supported"
+                )
+            else:
+                decorator = line_stripped
+                continue
 
         # does this look like an input line?
         matchin = rgxin.match(line)
diff --git a/IPython/terminal/debugger.py b/IPython/terminal/debugger.py
index d76550d..8448d96 100644
--- a/IPython/terminal/debugger.py
+++ b/IPython/terminal/debugger.py
@@ -68,6 +68,8 @@ class TerminalPdb(Pdb):
                 self.debugger_history = FileHistory(os.path.expanduser(str(p)))
             else:
                 self.debugger_history = InMemoryHistory()
+        else:
+            self.debugger_history = self.shell.debugger_history
 
         options = dict(
             message=(lambda: PygmentsTokens(get_prompt_tokens())),
diff --git a/IPython/terminal/interactiveshell.py b/IPython/terminal/interactiveshell.py
index 4a46f27..06724be 100644
--- a/IPython/terminal/interactiveshell.py
+++ b/IPython/terminal/interactiveshell.py
@@ -3,12 +3,10 @@
 import asyncio
 import os
 import sys
-import warnings
 from warnings import warn
 
 from IPython.core.async_helpers import get_asyncio_loop
 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
-from IPython.utils import io
 from IPython.utils.py3compat import input
 from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title
 from IPython.utils.process import abbrev_cwd
@@ -32,7 +30,7 @@ from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
 from prompt_toolkit.formatted_text import PygmentsTokens
-from prompt_toolkit.history import InMemoryHistory
+from prompt_toolkit.history import History
 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
 from prompt_toolkit.output import ColorDepth
 from prompt_toolkit.patch_stdout import patch_stdout
@@ -116,6 +114,59 @@ def black_reformat_handler(text_before_cursor):
     return formatted_text
 
 
+def yapf_reformat_handler(text_before_cursor):
+    from yapf.yapflib import file_resources
+    from yapf.yapflib import yapf_api
+
+    style_config = file_resources.GetDefaultStyleForDir(os.getcwd())
+    formatted_text, was_formatted = yapf_api.FormatCode(
+        text_before_cursor, style_config=style_config
+    )
+    if was_formatted:
+        if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"):
+            formatted_text = formatted_text[:-1]
+        return formatted_text
+    else:
+        return text_before_cursor
+
+
+class PtkHistoryAdapter(History):
+    """
+    Prompt toolkit has it's own way of handling history, Where it assumes it can
+    Push/pull from history.
+
+    """
+
+    def __init__(self, shell):
+        super().__init__()
+        self.shell = shell
+        self._refresh()
+
+    def append_string(self, string):
+        # we rely on sql for that.
+        self._loaded = False
+        self._refresh()
+
+    def _refresh(self):
+        if not self._loaded:
+            self._loaded_strings = list(self.load_history_strings())
+
+    def load_history_strings(self):
+        last_cell = ""
+        res = []
+        for __, ___, cell in self.shell.history_manager.get_tail(
+            self.shell.history_load_length, include_latest=True
+        ):
+            # Ignore blank lines and consecutive duplicates
+            cell = cell.rstrip()
+            if cell and (cell != last_cell):
+                res.append(cell)
+                last_cell = cell
+        yield from res[::-1]
+
+    def store_string(self, string: str) -> None:
+        pass
+
 class TerminalInteractiveShell(InteractiveShell):
     mime_renderers = Dict().tag(config=True)
 
@@ -184,8 +235,8 @@ class TerminalInteractiveShell(InteractiveShell):
     ).tag(config=True)
 
     autoformatter = Unicode(
-        "black",
-        help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
+        None,
+        help="Autoformatter to reformat Terminal code. Can be `'black'`, `'yapf'` or `None`",
         allow_none=True
     ).tag(config=True)
 
@@ -232,6 +283,8 @@ class TerminalInteractiveShell(InteractiveShell):
             self.reformat_handler = lambda x:x
         elif formatter == 'black':
             self.reformat_handler = black_reformat_handler
+        elif formatter == "yapf":
+            self.reformat_handler = yapf_reformat_handler
         else:
             raise ValueError
 
@@ -379,16 +432,9 @@ class TerminalInteractiveShell(InteractiveShell):
         # Set up keyboard shortcuts
         key_bindings = create_ipython_shortcuts(self)
 
+
         # Pre-populate history from IPython's history database
-        history = InMemoryHistory()
-        last_cell = u""
-        for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
-                                                        include_latest=True):
-            # Ignore blank lines and consecutive duplicates
-            cell = cell.rstrip()
-            if cell and (cell != last_cell):
-                history.append_string(cell)
-                last_cell = cell
+        history = PtkHistoryAdapter(self)
 
         self._style = self._make_style_from_name_or_cls(self.highlighting_style)
         self.style = DynamicStyle(lambda: self._style)
@@ -568,7 +614,6 @@ class TerminalInteractiveShell(InteractiveShell):
     def enable_win_unicode_console(self):
         # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
         # console by default, so WUC shouldn't be needed.
-        from warnings import warn
         warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
              DeprecationWarning,
              stacklevel=2)
diff --git a/IPython/terminal/ipapp.py b/IPython/terminal/ipapp.py
index e735a20..a87eb2f 100755
--- a/IPython/terminal/ipapp.py
+++ b/IPython/terminal/ipapp.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # encoding: utf-8
 """
-The :class:`~IPython.core.application.Application` object for the command
+The :class:`~traitlets.config.application.Application` object for the command
 line :command:`ipython` program.
 """
 
diff --git a/IPython/terminal/magics.py b/IPython/terminal/magics.py
index 498a050..206ff20 100644
--- a/IPython/terminal/magics.py
+++ b/IPython/terminal/magics.py
@@ -53,7 +53,7 @@ class TerminalMagics(Magics):
             self.shell.user_ns['pasted_block'] = b
             self.shell.using_paste_magics = True
             try:
-                self.shell.run_cell(b)
+                self.shell.run_cell(b, store_history=True)
             finally:
                 self.shell.using_paste_magics = False
 
diff --git a/IPython/terminal/shortcuts.py b/IPython/terminal/shortcuts.py
index ea204f8..615397a 100644
--- a/IPython/terminal/shortcuts.py
+++ b/IPython/terminal/shortcuts.py
@@ -10,6 +10,7 @@ import warnings
 import signal
 import sys
 import re
+import os
 from typing import Callable
 
 
@@ -56,7 +57,7 @@ def create_ipython_shortcuts(shell):
                             & insert_mode
                                       ))(reformat_and_execute)
 
-    kb.add('c-\\')(force_exit)
+    kb.add("c-\\")(quit)
 
     kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
                 )(previous_history_or_previous_completion)
@@ -139,12 +140,24 @@ def create_ipython_shortcuts(shell):
         event.current_buffer.insert_text("{}")
         event.current_buffer.cursor_left()
 
-    @kb.add('"', filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))
+    @kb.add(
+        '"',
+        filter=focused_insert
+        & auto_match
+        & preceding_text(r'^([^"]+|"[^"]*")*$')
+        & following_text(r"[,)}\]]|$"),
+    )
     def _(event):
         event.current_buffer.insert_text('""')
         event.current_buffer.cursor_left()
 
-    @kb.add("'", filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))
+    @kb.add(
+        "'",
+        filter=focused_insert
+        & auto_match
+        & preceding_text(r"^([^']+|'[^']*')*$")
+        & following_text(r"[,)}\]]|$"),
+    )
     def _(event):
         event.current_buffer.insert_text("''")
         event.current_buffer.cursor_left()
@@ -186,16 +199,6 @@ def create_ipython_shortcuts(shell):
         event.current_buffer.insert_text("{}" + dashes)
         event.current_buffer.cursor_left(len(dashes) + 1)
 
-    @kb.add('"', filter=focused_insert & auto_match & preceding_text(r".*(r|R)$"))
-    def _(event):
-        event.current_buffer.insert_text('""')
-        event.current_buffer.cursor_left()
-
-    @kb.add("'", filter=focused_insert & auto_match & preceding_text(r".*(r|R)$"))
-    def _(event):
-        event.current_buffer.insert_text("''")
-        event.current_buffer.cursor_left()
-
     # just move cursor
     @kb.add(")", filter=focused_insert & auto_match & following_text(r"^\)"))
     @kb.add("]", filter=focused_insert & auto_match & following_text(r"^\]"))
@@ -265,15 +268,22 @@ def create_ipython_shortcuts(shell):
     focused_insert_vi = has_focus(DEFAULT_BUFFER) & vi_insert_mode
 
     # Needed for to accept autosuggestions in vi insert mode
-    @kb.add("c-e", filter=focused_insert_vi & ebivim)
-    def _(event):
+    def _apply_autosuggest(event):
         b = event.current_buffer
         suggestion = b.suggestion
-        if suggestion:
+        if suggestion is not None and suggestion.text:
             b.insert_text(suggestion.text)
         else:
             nc.end_of_line(event)
 
+    @kb.add("end", filter=has_focus(DEFAULT_BUFFER) & (ebivim | ~vi_insert_mode))
+    def _(event):
+        _apply_autosuggest(event)
+
+    @kb.add("c-e", filter=focused_insert_vi & ebivim)
+    def _(event):
+        _apply_autosuggest(event)
+
     @kb.add("c-f", filter=focused_insert_vi)
     def _(event):
         b = event.current_buffer
@@ -336,12 +346,7 @@ def create_ipython_shortcuts(shell):
         shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6)
         cursor = "\x1b[{} q".format(shape)
 
-        if hasattr(sys.stdout, "_cli"):
-            write = sys.stdout._cli.output.write_raw
-        else:
-            write = sys.stdout.write
-
-        write(cursor)
+        sys.stdout.write(cursor)
         sys.stdout.flush()
 
         self._input_mode = mode
@@ -454,11 +459,16 @@ def reset_search_buffer(event):
 def suspend_to_bg(event):
     event.app.suspend_to_background()
 
-def force_exit(event):
+def quit(event):
     """
-    Force exit (with a non-zero return value)
+    On platforms that support SIGQUIT, send SIGQUIT to the current process.
+    On other platforms, just exit the process with a message.
     """
-    sys.exit("Quit")
+    sigquit = getattr(signal, "SIGQUIT", None)
+    if sigquit is not None:
+        os.kill(0, signal.SIGQUIT)
+    else:
+        sys.exit("Quit")
 
 def indent_buffer(event):
     event.current_buffer.insert_text(' ' * 4)
diff --git a/IPython/testing/plugin/dtexample.py b/IPython/testing/plugin/dtexample.py
index 119e0a0..68f7016 100644
--- a/IPython/testing/plugin/dtexample.py
+++ b/IPython/testing/plugin/dtexample.py
@@ -38,7 +38,7 @@ def ipfunc():
        ....:     print(i, end=' ')
        ....:     print(i+1, end=' ')
        ....:
-    0 1 1 2 2 3 
+    0 1 1 2 2 3
 
 
     It's OK to use '_' for the last result, but do NOT try to use IPython's
@@ -50,7 +50,7 @@ def ipfunc():
 
     In [8]: print(repr(_))
     'hi'
-    
+
     In [7]: 3+4
     Out[7]: 7
 
@@ -60,7 +60,7 @@ def ipfunc():
     In [9]: ipfunc()
     Out[9]: 'ipfunc'
     """
-    return 'ipfunc'
+    return "ipfunc"
 
 
 def ipos():
diff --git a/IPython/testing/plugin/pytest_ipdoctest.py b/IPython/testing/plugin/pytest_ipdoctest.py
index 603e33c..809713d 100644
--- a/IPython/testing/plugin/pytest_ipdoctest.py
+++ b/IPython/testing/plugin/pytest_ipdoctest.py
@@ -14,6 +14,7 @@ import traceback
 import types
 import warnings
 from contextlib import contextmanager
+from pathlib import Path
 from typing import Any
 from typing import Callable
 from typing import Dict
@@ -28,8 +29,6 @@ from typing import Type
 from typing import TYPE_CHECKING
 from typing import Union
 
-import py.path
-
 import pytest
 from _pytest import outcomes
 from _pytest._code.code import ExceptionInfo
@@ -42,6 +41,7 @@ from _pytest.config.argparsing import Parser
 from _pytest.fixtures import FixtureRequest
 from _pytest.nodes import Collector
 from _pytest.outcomes import OutcomeException
+from _pytest.pathlib import fnmatch_ex
 from _pytest.pathlib import import_path
 from _pytest.python_api import approx
 from _pytest.warning_types import PytestWarning
@@ -126,35 +126,55 @@ def pytest_unconfigure() -> None:
 
 
 def pytest_collect_file(
-    path: py.path.local,
+    file_path: Path,
     parent: Collector,
 ) -> Optional[Union["IPDoctestModule", "IPDoctestTextfile"]]:
     config = parent.config
-    if path.ext == ".py":
-        if config.option.ipdoctestmodules and not _is_setup_py(path):
-            mod: IPDoctestModule = IPDoctestModule.from_parent(parent, fspath=path)
+    if file_path.suffix == ".py":
+        if config.option.ipdoctestmodules and not any(
+            (_is_setup_py(file_path), _is_main_py(file_path))
+        ):
+            mod: IPDoctestModule = IPDoctestModule.from_parent(parent, path=file_path)
             return mod
-    elif _is_ipdoctest(config, path, parent):
-        txt: IPDoctestTextfile = IPDoctestTextfile.from_parent(parent, fspath=path)
+    elif _is_ipdoctest(config, file_path, parent):
+        txt: IPDoctestTextfile = IPDoctestTextfile.from_parent(parent, path=file_path)
         return txt
     return None
 
 
-def _is_setup_py(path: py.path.local) -> bool:
-    if path.basename != "setup.py":
+if int(pytest.__version__.split(".")[0]) < 7:
+    _collect_file = pytest_collect_file
+
+    def pytest_collect_file(
+        path,
+        parent: Collector,
+    ) -> Optional[Union["IPDoctestModule", "IPDoctestTextfile"]]:
+        return _collect_file(Path(path), parent)
+
+    _import_path = import_path
+
+    def import_path(path, root):
+        import py.path
+
+        return _import_path(py.path.local(path))
+
+
+def _is_setup_py(path: Path) -> bool:
+    if path.name != "setup.py":
         return False
-    contents = path.read_binary()
+    contents = path.read_bytes()
     return b"setuptools" in contents or b"distutils" in contents
 
 
-def _is_ipdoctest(config: Config, path: py.path.local, parent) -> bool:
-    if path.ext in (".txt", ".rst") and parent.session.isinitpath(path):
+def _is_ipdoctest(config: Config, path: Path, parent: Collector) -> bool:
+    if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path):
         return True
     globs = config.getoption("ipdoctestglob") or ["test*.txt"]
-    for glob in globs:
-        if path.check(fnmatch=glob):
-            return True
-    return False
+    return any(fnmatch_ex(glob, path) for glob in globs)
+
+
+def _is_main_py(path: Path) -> bool:
+    return path.name == "__main__.py"
 
 
 class ReprFailDoctest(TerminalRepr):
@@ -273,7 +293,7 @@ class IPDoctestItem(pytest.Item):
         runner: "IPDocTestRunner",
         dtest: "doctest.DocTest",
     ):
-        # incompatible signature due to to imposed limits on sublcass
+        # incompatible signature due to imposed limits on subclass
         """The public named constructor."""
         return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest)
 
@@ -372,61 +392,63 @@ class IPDoctestItem(pytest.Item):
         elif isinstance(excinfo.value, MultipleDoctestFailures):
             failures = excinfo.value.failures
 
-        if failures is not None:
-            reprlocation_lines = []
-            for failure in failures:
-                example = failure.example
-                test = failure.test
-                filename = test.filename
-                if test.lineno is None:
-                    lineno = None
-                else:
-                    lineno = test.lineno + example.lineno + 1
-                message = type(failure).__name__
-                # TODO: ReprFileLocation doesn't expect a None lineno.
-                reprlocation = ReprFileLocation(filename, lineno, message)  # type: ignore[arg-type]
-                checker = _get_checker()
-                report_choice = _get_report_choice(
-                    self.config.getoption("ipdoctestreport")
-                )
-                if lineno is not None:
-                    assert failure.test.docstring is not None
-                    lines = failure.test.docstring.splitlines(False)
-                    # add line numbers to the left of the error message
-                    assert test.lineno is not None
-                    lines = [
-                        "%03d %s" % (i + test.lineno + 1, x)
-                        for (i, x) in enumerate(lines)
-                    ]
-                    # trim docstring error lines to 10
-                    lines = lines[max(example.lineno - 9, 0) : example.lineno + 1]
-                else:
-                    lines = [
-                        "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example"
-                    ]
-                    indent = ">>>"
-                    for line in example.source.splitlines():
-                        lines.append(f"??? {indent} {line}")
-                        indent = "..."
-                if isinstance(failure, doctest.DocTestFailure):
-                    lines += checker.output_difference(
-                        example, failure.got, report_choice
-                    ).split("\n")
-                else:
-                    inner_excinfo = ExceptionInfo(failure.exc_info)
-                    lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)]
-                    lines += [
-                        x.strip("\n")
-                        for x in traceback.format_exception(*failure.exc_info)
-                    ]
-                reprlocation_lines.append((reprlocation, lines))
-            return ReprFailDoctest(reprlocation_lines)
-        else:
+        if failures is None:
             return super().repr_failure(excinfo)
 
-    def reportinfo(self):
+        reprlocation_lines = []
+        for failure in failures:
+            example = failure.example
+            test = failure.test
+            filename = test.filename
+            if test.lineno is None:
+                lineno = None
+            else:
+                lineno = test.lineno + example.lineno + 1
+            message = type(failure).__name__
+            # TODO: ReprFileLocation doesn't expect a None lineno.
+            reprlocation = ReprFileLocation(filename, lineno, message)  # type: ignore[arg-type]
+            checker = _get_checker()
+            report_choice = _get_report_choice(self.config.getoption("ipdoctestreport"))
+            if lineno is not None:
+                assert failure.test.docstring is not None
+                lines = failure.test.docstring.splitlines(False)
+                # add line numbers to the left of the error message
+                assert test.lineno is not None
+                lines = [
+                    "%03d %s" % (i + test.lineno + 1, x) for (i, x) in enumerate(lines)
+                ]
+                # trim docstring error lines to 10
+                lines = lines[max(example.lineno - 9, 0) : example.lineno + 1]
+            else:
+                lines = [
+                    "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example"
+                ]
+                indent = ">>>"
+                for line in example.source.splitlines():
+                    lines.append(f"??? {indent} {line}")
+                    indent = "..."
+            if isinstance(failure, doctest.DocTestFailure):
+                lines += checker.output_difference(
+                    example, failure.got, report_choice
+                ).split("\n")
+            else:
+                inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info)
+                lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)]
+                lines += [
+                    x.strip("\n") for x in traceback.format_exception(*failure.exc_info)
+                ]
+            reprlocation_lines.append((reprlocation, lines))
+        return ReprFailDoctest(reprlocation_lines)
+
+    def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]:
         assert self.dtest is not None
-        return self.fspath, self.dtest.lineno, "[ipdoctest] %s" % self.name
+        return self.path, self.dtest.lineno, "[ipdoctest] %s" % self.name
+
+    if int(pytest.__version__.split(".")[0]) < 7:
+
+        @property
+        def path(self) -> Path:
+            return Path(self.fspath)
 
 
 def _get_flag_lookup() -> Dict[str, int]:
@@ -474,9 +496,9 @@ class IPDoctestTextfile(pytest.Module):
         # Inspired by doctest.testfile; ideally we would use it directly,
         # but it doesn't support passing a custom checker.
         encoding = self.config.getini("ipdoctest_encoding")
-        text = self.fspath.read_text(encoding)
-        filename = str(self.fspath)
-        name = self.fspath.basename
+        text = self.path.read_text(encoding)
+        filename = str(self.path)
+        name = self.path.name
         globs = {"__name__": "__main__"}
 
         optionflags = get_optionflags(self)
@@ -495,6 +517,27 @@ class IPDoctestTextfile(pytest.Module):
                 self, name=test.name, runner=runner, dtest=test
             )
 
+    if int(pytest.__version__.split(".")[0]) < 7:
+
+        @property
+        def path(self) -> Path:
+            return Path(self.fspath)
+
+        @classmethod
+        def from_parent(
+            cls,
+            parent,
+            *,
+            fspath=None,
+            path: Optional[Path] = None,
+            **kw,
+        ):
+            if path is not None:
+                import py.path
+
+                fspath = py.path.local(path)
+            return super().from_parent(parent=parent, fspath=fspath, **kw)
+
 
 def _check_all_skipped(test: "doctest.DocTest") -> None:
     """Raise pytest.skip() if all examples in the given DocTest have the SKIP
@@ -559,15 +602,20 @@ class IPDoctestModule(pytest.Module):
 
             def _find_lineno(self, obj, source_lines):
                 """Doctest code does not take into account `@property`, this
-                is a hackish way to fix it.
+                is a hackish way to fix it. https://bugs.python.org/issue17446
 
-                https://bugs.python.org/issue17446
+                Wrapped Doctests will need to be unwrapped so the correct
+                line number is returned. This will be reported upstream. #8796
                 """
                 if isinstance(obj, property):
                     obj = getattr(obj, "fget", obj)
+
+                if hasattr(obj, "__wrapped__"):
+                    # Get the main obj in case of it being wrapped
+                    obj = inspect.unwrap(obj)
+
                 # Type ignored because this is a private function.
-                return DocTestFinder._find_lineno(  # type: ignore
-                    self,
+                return super()._find_lineno(  # type:ignore[misc]
                     obj,
                     source_lines,
                 )
@@ -580,20 +628,28 @@ class IPDoctestModule(pytest.Module):
                 with _patch_unwrap_mock_aware():
 
                     # Type ignored because this is a private function.
-                    DocTestFinder._find(  # type: ignore
-                        self, tests, obj, name, module, source_lines, globs, seen
+                    super()._find(  # type:ignore[misc]
+                        tests, obj, name, module, source_lines, globs, seen
                     )
 
-        if self.fspath.basename == "conftest.py":
-            module = self.config.pluginmanager._importconftest(
-                self.fspath, self.config.getoption("importmode")
-            )
+        if self.path.name == "conftest.py":
+            if int(pytest.__version__.split(".")[0]) < 7:
+                module = self.config.pluginmanager._importconftest(
+                    self.path,
+                    self.config.getoption("importmode"),
+                )
+            else:
+                module = self.config.pluginmanager._importconftest(
+                    self.path,
+                    self.config.getoption("importmode"),
+                    rootpath=self.config.rootpath,
+                )
         else:
             try:
-                module = import_path(self.fspath)
+                module = import_path(self.path, root=self.config.rootpath)
             except ImportError:
                 if self.config.getvalue("ipdoctest_ignore_import_errors"):
-                    pytest.skip("unable to import module %r" % self.fspath)
+                    pytest.skip("unable to import module %r" % self.path)
                 else:
                     raise
         # Uses internal doctest module parsing mechanism.
@@ -612,6 +668,27 @@ class IPDoctestModule(pytest.Module):
                     self, name=test.name, runner=runner, dtest=test
                 )
 
+    if int(pytest.__version__.split(".")[0]) < 7:
+
+        @property
+        def path(self) -> Path:
+            return Path(self.fspath)
+
+        @classmethod
+        def from_parent(
+            cls,
+            parent,
+            *,
+            fspath=None,
+            path: Optional[Path] = None,
+            **kw,
+        ):
+            if path is not None:
+                import py.path
+
+                fspath = py.path.local(path)
+            return super().from_parent(parent=parent, fspath=fspath, **kw)
+
 
 def _setup_fixtures(doctest_item: IPDoctestItem) -> FixtureRequest:
     """Used by IPDoctestTextfile and IPDoctestItem to setup fixture information."""
@@ -665,7 +742,7 @@ def _init_checker_class() -> Type["IPDoctestOutputChecker"]:
         )
 
         def check_output(self, want: str, got: str, optionflags: int) -> bool:
-            if IPDoctestOutputChecker.check_output(self, want, got, optionflags):
+            if super().check_output(want, got, optionflags):
                 return True
 
             allow_unicode = optionflags & _get_allow_unicode_flag()
@@ -689,7 +766,7 @@ def _init_checker_class() -> Type["IPDoctestOutputChecker"]:
             if allow_number:
                 got = self._remove_unwanted_precision(want, got)
 
-            return IPDoctestOutputChecker.check_output(self, want, got, optionflags)
+            return super().check_output(want, got, optionflags)
 
         def _remove_unwanted_precision(self, want: str, got: str) -> str:
             wants = list(self._number_re.finditer(want))
@@ -702,10 +779,7 @@ def _init_checker_class() -> Type["IPDoctestOutputChecker"]:
                 exponent: Optional[str] = w.group("exponent1")
                 if exponent is None:
                     exponent = w.group("exponent2")
-                if fraction is None:
-                    precision = 0
-                else:
-                    precision = len(fraction)
+                precision = 0 if fraction is None else len(fraction)
                 if exponent is not None:
                     precision -= int(exponent)
                 if float(w.group()) == approx(float(g.group()), abs=10 ** -precision):
diff --git a/IPython/utils/tempdir.py b/IPython/utils/tempdir.py
index c3918d0..5afc5d6 100644
--- a/IPython/utils/tempdir.py
+++ b/IPython/utils/tempdir.py
@@ -10,8 +10,7 @@ from tempfile import TemporaryDirectory
 
 
 class NamedFileInTemporaryDirectory(object):
-
-    def __init__(self, filename, mode='w+b', bufsize=-1, **kwds):
+    def __init__(self, filename, mode="w+b", bufsize=-1, add_to_syspath=False, **kwds):
         """
         Open a file named `filename` in a temporary directory.
 
diff --git a/IPython/utils/tests/test_path.py b/IPython/utils/tests/test_path.py
index 13e3223..8a61d2f 100644
--- a/IPython/utils/tests/test_path.py
+++ b/IPython/utils/tests/test_path.py
@@ -10,24 +10,23 @@ import sys
 import tempfile
 import unittest
 from contextlib import contextmanager
-from unittest.mock import patch
-from os.path import join, abspath
 from importlib import reload
+from os.path import abspath, join
+from unittest.mock import patch
 
 import pytest
+from tempfile import TemporaryDirectory
 
 import IPython
 from IPython import paths
 from IPython.testing import decorators as dec
 from IPython.testing.decorators import (
+    onlyif_unicode_paths,
     skip_if_not_win32,
     skip_win32,
-    onlyif_unicode_paths,
 )
 from IPython.testing.tools import make_tempfile
 from IPython.utils import path
-from IPython.utils.tempdir import TemporaryDirectory
-
 
 # Platform-dependent imports
 try:
@@ -41,6 +40,7 @@ except ImportError:
         import winreg as wreg
     except ImportError:
         import _winreg as wreg
+
         #Add entries that needs to be stubbed by the testing code
         (wreg.OpenKey, wreg.QueryValueEx,) = (None, None)
 
diff --git a/IPython/utils/text.py b/IPython/utils/text.py
index a1754d2..74bccdd 100644
--- a/IPython/utils/text.py
+++ b/IPython/utils/text.py
@@ -470,11 +470,11 @@ def strip_ansi(source):
 
 class EvalFormatter(Formatter):
     """A String Formatter that allows evaluation of simple expressions.
-    
-    Note that this version interprets a : as specifying a format string (as per
+
+    Note that this version interprets a `:`  as specifying a format string (as per
     standard string formatting), so if slicing is required, you must explicitly
     create a slice.
-    
+
     This is to be used in templating cases, such as the parallel batch
     script templates, where simple arithmetic on arguments is useful.
 
@@ -690,8 +690,8 @@ def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) :
         return ([[_get_or_default(items, c * nrow + r, default=empty) for c in range(ncol)] for r in range(nrow)], info)
 
 
-def columnize(items, row_first=False, separator='  ', displaywidth=80, spread=False):
-    """ Transform a list of strings into a single string with columns.
+def columnize(items, row_first=False, separator="  ", displaywidth=80, spread=False):
+    """Transform a list of strings into a single string with columns.
 
     Parameters
     ----------
diff --git a/SECURITY.md b/SECURITY.md
index a4b9435..dc5db66 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -3,4 +3,4 @@
 ## Reporting a Vulnerability
 
 All IPython and Jupyter security are handled via security@ipython.org. 
-You can find more informations on the Jupyter website. https://jupyter.org/security
+You can find more information on the Jupyter website. https://jupyter.org/security
diff --git a/appveyor.yml b/appveyor.yml
index b1c0abe..7637841 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -5,7 +5,7 @@ matrix:
 environment:
   global:
     APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022'
-    COLUMNS: 120  # Appveyor web viwer window width is 130 chars
+    COLUMNS: 120  # Appveyor web viewer window width is 130 chars
 
   matrix:
    - PYTHON: "C:\\Python38"
diff --git a/docs/source/config/callbacks.rst b/docs/source/config/callbacks.rst
index 6c75c73..60c7aba 100644
--- a/docs/source/config/callbacks.rst
+++ b/docs/source/config/callbacks.rst
@@ -17,22 +17,29 @@ For example::
         def __init__(self, ip):
             self.shell = ip
             self.last_x = None
-        
+
         def pre_execute(self):
             self.last_x = self.shell.user_ns.get('x', None)
-        
+
         def pre_run_cell(self, info):
-            print('Cell code: "%s"' % info.raw_cell)
-        
+            print('info.raw_cell =', info.raw_cell)
+            print('info.store_history =', info.store_history)
+            print('info.silent =', info.silent)
+            print('info.shell_futures =', info.shell_futures)
+            print('info.cell_id =', info.cell_id)
+            print(dir(info))
+
         def post_execute(self):
             if self.shell.user_ns.get('x', None) != self.last_x:
                 print("x changed!")
-        
+
         def post_run_cell(self, result):
-            print('Cell code: "%s"' % result.info.raw_cell)
-            if result.error_before_exec:
-                print('Error before execution: %s' % result.error_before_exec)
-        
+            print('result.execution_count = ', result.execution_count)
+            print('result.error_before_exec = ', result.error_before_exec)
+            print('result.error_in_exec = ', result.error_in_exec)
+            print('result.info = ', result.info)
+            print('result.result = ', result.result)
+
     def load_ipython_extension(ip):
         vw = VarWatcher(ip)
         ip.events.register('pre_execute', vw.pre_execute)
@@ -40,6 +47,13 @@ For example::
         ip.events.register('post_execute', vw.post_execute)
         ip.events.register('post_run_cell', vw.post_run_cell)
 
+.. versionadded:: 8.3
+
+   Since IPython 8.3 and ipykernel 6.12.1, the ``info`` objects in the callback
+   now have a the ``cell_id`` that will be set to the value sent by the
+   frontened, when those send it.
+
+
 
 Events
 ======
diff --git a/docs/source/config/integrating.rst b/docs/source/config/integrating.rst
index a045968..07429ef 100644
--- a/docs/source/config/integrating.rst
+++ b/docs/source/config/integrating.rst
@@ -135,7 +135,7 @@ Metadata
 ^^^^^^^^
 
 We often want to provide frontends with guidance on how to display the data. To
-support this, ``_repr_*_()`` methods (except `_repr_pretty_``?) can also return a ``(data, metadata)``
+support this, ``_repr_*_()`` methods (except ``_repr_pretty_``?) can also return a ``(data, metadata)``
 tuple where ``metadata`` is a dictionary containing arbitrary key-value pairs for
 the frontend to interpret. An example use case is ``_repr_jpeg_()``, which can
 be set to return a jpeg image and a ``{'height': 400, 'width': 600}`` dictionary
diff --git a/docs/source/config/shell_mimerenderer.rst b/docs/source/config/shell_mimerenderer.rst
index 0fb2ffd..75872ac 100644
--- a/docs/source/config/shell_mimerenderer.rst
+++ b/docs/source/config/shell_mimerenderer.rst
@@ -10,7 +10,7 @@ thought to render a number of mimetypes in the shell. This can be used to either
 display inline images if your terminal emulator supports it; or open some
 display results with external file viewers.
 
-Registering new mimetype handlers can so far only be done my extensions and
+Registering new mimetype handlers can so far only be done by extensions and
 requires 4 steps:
 
    - Define a callable that takes 2 parameters:``data`` and ``metadata``; return
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 9cec057..ebef06f 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -17,9 +17,9 @@ interactively.  Its main components are:
 * A powerful interactive Python shell.
 
 
-.. image:: ./_images/ipython-6-screenshot.png
-    :alt: Screenshot of IPython 6.0
-    :align: center
+    .. image:: ./_images/ipython-6-screenshot.png
+        :alt: Screenshot of IPython 6.0
+        :align: center
 
 
 * A `Jupyter `_ kernel to work with Python code in Jupyter
diff --git a/docs/source/interactive/tutorial.rst b/docs/source/interactive/tutorial.rst
index 511e746..947d6ad 100644
--- a/docs/source/interactive/tutorial.rst
+++ b/docs/source/interactive/tutorial.rst
@@ -59,7 +59,7 @@ while some other do not. We'll come to this later.
 Depending on the exact command you are typing you might realize that sometimes
 :kbd:`Enter` will add a new line, and sometimes it will execute the current
 statement. IPython tries to guess what you are doing, so most of the time you
-should not have to care. Though if by any chance IPython does not the right
+should not have to care. Though if by any chance IPython does not do the right
 thing you can force execution of the current code block by pressing in sequence
 :kbd:`Esc` and :kbd:`Enter`. You can also force the insertion of a new line at
 the position of the cursor by using :kbd:`Ctrl-o`.
diff --git a/docs/source/links.txt b/docs/source/links.txt
index 9379d1f..642389c 100644
--- a/docs/source/links.txt
+++ b/docs/source/links.txt
@@ -20,9 +20,6 @@
 .. _ipython: https://ipython.org
 .. _`ipython manual`: https://ipython.org/documentation.html
 .. _ipython_github: http://github.com/ipython/ipython/
-.. _ipython_github_repo: http://github.com/ipython/ipython/
-.. _ipython_downloads: https://ipython.org/download.html
-.. _ipython_pypi: http://pypi.python.org/pypi/ipython
 .. _nbviewer: http://nbviewer.ipython.org
 
 .. _ZeroMQ: http://zeromq.org
@@ -35,8 +32,7 @@
 .. _reST: http://docutils.sourceforge.net/rst.html
 .. _docutils: http://docutils.sourceforge.net
 .. _lyx: http://www.lyx.org
-.. _pep8: http://www.python.org/dev/peps/pep-0008
-.. _numpy_coding_guide: https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
+.. _pep8: https://peps.python.org/pep-0008/
 
 .. Licenses
 .. _GPL: http://www.gnu.org/licenses/gpl.html
diff --git a/docs/source/sphinxext.rst b/docs/source/sphinxext.rst
index b2012fa..093e04a 100644
--- a/docs/source/sphinxext.rst
+++ b/docs/source/sphinxext.rst
@@ -204,7 +204,7 @@ suppress the seed line so it doesn't show up in the rendered output
           [0.22591016, 0.77731835],
           [0.0072729 , 0.34273127]])
 
-For more information on @supress and @doctest decorators, please refer to the end of this file in
+For more information on @suppress and @doctest decorators, please refer to the end of this file in
 Pseudo-Decorators section.
 
 Another demonstration of multi-line input and output
diff --git a/docs/source/whatsnew/github-stats-0.11.rst b/docs/source/whatsnew/github-stats-0.11.rst
index 2f647ce..8fd4680 100644
--- a/docs/source/whatsnew/github-stats-0.11.rst
+++ b/docs/source/whatsnew/github-stats-0.11.rst
@@ -51,9 +51,9 @@ Pull requests (226):
 * `542 `_: issue 440
 * `533 `_: Remove unused configobj and validate libraries from externals.
 * `538 `_: fix various tests on Windows
-* `540 `_: support `-pylab` flag with deprecation warning
+* `540 `_: support ``-pylab`` flag with deprecation warning
 * `537 `_: Docs update
-* `536 `_: `setup.py install` depends on setuptools on Windows
+* `536 `_: ``setup.py install`` depends on setuptools on Windows
 * `480 `_: Get help mid-command
 * `462 `_: Str and Bytes traitlets
 * `534 `_: Handle unicode properly in IPython.zmq.iostream
diff --git a/docs/source/whatsnew/github-stats-2.0.rst b/docs/source/whatsnew/github-stats-2.0.rst
index 5033994..98e3d5e 100644
--- a/docs/source/whatsnew/github-stats-2.0.rst
+++ b/docs/source/whatsnew/github-stats-2.0.rst
@@ -916,7 +916,7 @@ Pull Requests (687):
 * :ghpull:`4444`: Css cleaning
 * :ghpull:`4523`: Use username and password for MongoDB on ShiningPanda
 * :ghpull:`4510`: Update whatsnew from PR files
-* :ghpull:`4441`: add `setup.py jsversion`
+* :ghpull:`4441`: add ``setup.py jsversion``
 * :ghpull:`4518`: Fix for race condition in url file decoding.
 * :ghpull:`4497`: don't automatically unpack datetime objects in the message spec
 * :ghpull:`4506`: wait for empty queues as well as load-balanced tasks
@@ -1050,7 +1050,7 @@ Pull Requests (687):
 * :ghpull:`4214`: engine ID metadata should be unicode, not bytes
 * :ghpull:`4232`: no highlight if no language specified
 * :ghpull:`4218`: Fix display of SyntaxError when .py file is modified
-* :ghpull:`4207`: add `setup.py css` command
+* :ghpull:`4207`: add ``setup.py css`` command
 * :ghpull:`4224`: clear previous callbacks on execute
 * :ghpull:`4180`: Iptest refactoring
 * :ghpull:`4105`: JS output area misaligned
diff --git a/docs/source/whatsnew/pr/end-shortcut-accept-suggestion.rst b/docs/source/whatsnew/pr/end-shortcut-accept-suggestion.rst
new file mode 100644
index 0000000..c04998e
--- /dev/null
+++ b/docs/source/whatsnew/pr/end-shortcut-accept-suggestion.rst
@@ -0,0 +1,7 @@
+Added shortcut for accepting auto suggestion
+============================================
+
+Added End key shortcut for accepting auto-suggestion
+This binding works in Vi mode too, provided 
+TerminalInteractiveShell.emacs_bindings_in_vi_insert_mode is set to be True.
+
diff --git a/docs/source/whatsnew/version0.9.rst b/docs/source/whatsnew/version0.9.rst
index 81ebba1..8d5f73c 100644
--- a/docs/source/whatsnew/version0.9.rst
+++ b/docs/source/whatsnew/version0.9.rst
@@ -81,7 +81,7 @@ New features
   :mod:`IPython.kernel`, :mod:`IPython.kernel.core`, :mod:`traitlets.config`,
   :mod:`IPython.tools` and :mod:`IPython.testing`.
 
-* As part of merging in the `ipython1-dev` stuff, the `setup.py` script and
+* As part of merging in the `ipython1-dev` stuff, the ``setup.py`` script and
   friends have been completely refactored.  Now we are checking for
   dependencies using the approach that matplotlib uses.
 
@@ -161,7 +161,7 @@ Backwards incompatible changes
   `'basic'` to `'b'`.
 
 * IPython has a larger set of dependencies if you want all of its capabilities.
-  See the `setup.py` script for details.
+  See the ``setup.py`` script for details.
 
 * The constructors for :class:`IPython.kernel.client.MultiEngineClient` and 
   :class:`IPython.kernel.client.TaskClient` no longer take the (ip,port) tuple.
diff --git a/docs/source/whatsnew/version6.rst b/docs/source/whatsnew/version6.rst
index 461132e..cb3b42d 100644
--- a/docs/source/whatsnew/version6.rst
+++ b/docs/source/whatsnew/version6.rst
@@ -277,7 +277,7 @@ automatically upgrade to the latest version compatible with your system.
 
 The ability to use only Python 3 on the code base of IPython brings a number
 of advantages. Most of the newly written code make use of `optional function type
-annotation `_ leading to clearer code
+annotation `_ leading to clearer code
 and better documentation.
 
 The total size of the repository has also decreased by about 1500 lines (for the 
diff --git a/docs/source/whatsnew/version7.rst b/docs/source/whatsnew/version7.rst
index 6597fe5..f6ecd25 100644
--- a/docs/source/whatsnew/version7.rst
+++ b/docs/source/whatsnew/version7.rst
@@ -2,6 +2,17 @@
  7.x Series
 ============
 
+.. _version 7.33:
+
+IPython 7.33
+============
+
+ - Allow IPython hooks to receive current cell ids when frontend support it. See
+   :ghpull:`13600`
+
+ - ``?`` does not trigger the insertion of a new cell anymore as most frontend
+   allow proper multiline edition. :ghpull:`13625`
+
 
 .. _version 7.32:
 
@@ -9,11 +20,15 @@ IPython 7.32
 ============
 
 
+
+Autoload magic lazily
+---------------------
+
 The ability to configure magics to be lazily loaded has been added to IPython.
 See the ``ipython --help-all`` section on ``MagicsManager.lazy_magic``.
 One can now use::
 
-    c.MagicsManger.lazy_magics = {
+    c.MagicsManager.lazy_magics = {
               "my_magic": "slow.to.import",
               "my_other_magic": "also.slow",
     }
@@ -21,6 +36,21 @@ One can now use::
 And on first use of ``%my_magic``, or corresponding cell magic, or other line magic,
 the corresponding ``load_ext`` will be called just before trying to invoke the magic.
 
+Misc
+----
+
+ - Update sphinxify  for Docrepr 0.2.0  :ghpull:`13503`.
+ - Set co_name for cells run line by line (to fix debugging with Python 3.10)
+   :ghpull:`13535`
+
+
+Many thanks to all the contributors to this release. You can find all individual
+contributions to this milestone `on github
+`__.
+
+Thanks as well to the `D. E. Shaw group `__ for sponsoring
+work on IPython and related libraries.
+
 .. _version 7.31:
 
 IPython 7.31
diff --git a/docs/source/whatsnew/version8.rst b/docs/source/whatsnew/version8.rst
index 7b2318f..f9636e4 100644
--- a/docs/source/whatsnew/version8.rst
+++ b/docs/source/whatsnew/version8.rst
@@ -3,6 +3,134 @@
 ============
 
 
+.. _version 8.3.0:
+
+IPython 8.3.0
+-------------
+
+ - :ghpull:`13625`, using ``?``, ``??``, ``*?`` will not call
+   ``set_next_input`` as most frontend allow proper multiline editing and it was
+   causing issues for many users of multi-cell frontends. This has been backported to 7.33
+
+
+ - :ghpull:`13600`, ``pre_run_*``-hooks will now have a ``cell_id`` attribute on
+   the info object when frontend provide it. This has been backported to 7.33
+
+ - :ghpull:`13624`, fixed :kbd:`End` key being broken after accepting an
+   auto-suggestion.
+
+ - :ghpull:`13657` fix issue where history from different sessions would be mixed.
+
+.. _version 8.2.0:
+
+IPython 8.2.0
+-------------
+
+IPython 8.2 mostly bring bugfixes to IPython.
+
+ - Auto-suggestion can now be elected with the ``end`` key. :ghpull:`13566`
+ - Some traceback issues with ``assert etb is not None`` have been fixed. :ghpull:`13588`
+ - History is now pulled from the sqitel database and not from in-memory.
+   In particular when using the ``%paste`` magic, the content of the pasted text will
+   be part of the history and not the verbatim text ``%paste`` anymore. :ghpull:`13592`
+ - Fix ``Ctrl-\\`` exit cleanup :ghpull:`13603`
+ - Fixes to ``ultratb`` ipdb support when used outside of IPython. :ghpull:`13498`
+
+
+I am still trying to fix and investigate :ghissue:`13598`, which seem to be
+random, and would appreciate help if you find  reproducible minimal case. I've
+tried to make various changes to the codebase to mitigate it, but a proper fix
+will be difficult without understanding the cause.
+
+
+All the issues on pull-requests for this release can be found in the `8.2
+milestone. `__ . And some
+documentation only PR can be found as part of the `7.33 milestone
+`__ (currently not released).
+
+Thanks to the `D. E. Shaw group `__ for sponsoring
+work on IPython and related libraries.
+
+.. _version 8.1.1:
+   
+IPython 8.1.1
+-------------
+
+Fix an issue with virtualenv and Python 3.8 introduced in 8.1
+
+Revert :ghpull:`13537` (fix an issue with symlinks in virtualenv) that raises an
+error in Python 3.8, and fixed in a different way in :ghpull:`13559`.
+
+.. _version 8.1:
+
+IPython 8.1.0
+-------------
+
+IPython 8.1 is the first minor release after 8.0 and fixes a number of bugs and
+Update a few behavior that were problematic with the 8.0 as with many new major
+release.
+
+Note that beyond the changes listed here, IPython 8.1.0 also contains all the
+features listed in :ref:`version 7.32`.
+
+ - Misc and multiple fixes around quotation auto-closing. It is now disabled by
+   default. Run with ``TerminalInteractiveShell.auto_match=True`` to re-enabled
+ - Require pygments>=2.4.0 :ghpull:`13459`, this was implicit in the code, but
+   is now explicit in ``setup.cfg``/``setup.py``
+ - Docs improvement of ``core.magic_arguments`` examples. :ghpull:`13433`
+ - Multi-line edit executes too early with await. :ghpull:`13424`
+
+ - ``black``  is back as an optional dependency, and autoformatting disabled by
+   default until some fixes are implemented (black improperly reformat magics).
+   :ghpull:`13471` Additionally the ability to use ``yapf`` as a code
+   reformatter has been added :ghpull:`13528` . You can use
+   ``TerminalInteractiveShell.autoformatter="black"``,
+   ``TerminalInteractiveShell.autoformatter="yapf"`` to re-enable auto formating
+   with black, or switch to yapf.
+
+ - Fix and issue where ``display`` was not defined.
+
+ - Auto suggestions are now configurable. Currently only
+   ``AutoSuggestFromHistory`` (default) and ``None``. new provider contribution
+   welcomed. :ghpull:`13475`
+
+ - multiple packaging/testing improvement to simplify downstream packaging
+   (xfail with reasons, try to not access network...).
+
+ - Update deprecation. ``InteractiveShell.magic`` internal method has been
+   deprecated for many years but did not emit a warning until now.
+
+ - internal ``appended_to_syspath`` context manager has been deprecated.
+
+ - fix an issue with symlinks in virtualenv :ghpull:`13537` (Reverted in 8.1.1)
+
+ - Fix an issue with vim mode, where cursor would not be reset on exit :ghpull:`13472`
+
+ - ipython directive now remove only known pseudo-decorators :ghpull:`13532`
+
+ - ``IPython/lib/security`` which used to be used for jupyter notebook has been
+   removed.
+
+ - Fix an issue where ``async with`` would execute on new lines. :ghpull:`13436`
+
+
+We want to remind users that IPython is part of the Jupyter organisations, and
+thus governed by a Code of Conduct. Some of the behavior we have seen on GitHub is not acceptable.
+Abuse and non-respectful comments on discussion will not be tolerated.
+
+Many thanks to all the contributors to this release, many of the above fixed issue and
+new features where done by first time contributors, showing there is still
+plenty of easy contribution possible in IPython
+. You can find all individual contributions
+to this milestone `on github `__.
+
+Thanks as well to the `D. E. Shaw group `__ for sponsoring
+work on IPython and related libraries. In particular the Lazy autoloading of
+magics that you will find described in the 7.32 release notes.
+
+
+.. _version 8.0.1:
+
 IPython 8.0.1 (CVE-2022-21699)
 ------------------------------
 
@@ -45,6 +173,7 @@ Thus starting with this version:
 Further details can be read on the `GitHub Advisory `__
 
 
+.. _version 8.0:
 
 IPython 8.0
 -----------
@@ -344,12 +473,11 @@ For more information please see the following unit test : ``extensions/tests/tes
 Auto formatting with black in the CLI
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-If ``black`` is installed in the same environment as IPython, terminal IPython
-will now *by default*  reformat the code in the CLI when possible. You can
-disable this with ``--TerminalInteractiveShell.autoformatter=None``.
-
 This feature was present in 7.x, but disabled by default.
 
+In 8.0, input was automatically reformatted with Black when black was installed.
+This feature has been reverted for the time being.
+You can re-enable it by setting ``TerminalInteractiveShell.autoformatter`` to ``"black"``
 
 History Range Glob feature
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -393,8 +521,8 @@ Automatic Vi prompt stripping
 
 When pasting code into IPython, it will strip the leading prompt characters if
 there are any. For example, you can paste the following code into the console -
-it will still work, even though each line is prefixed with prompts (`In`,
-`Out`)::
+it will still work, even though each line is prefixed with prompts (``In``,
+``Out``)::
 
     In [1]: 2 * 2 == 4
     Out[1]: True
@@ -489,7 +617,7 @@ who did a fantastic job at updating our code base, migrating to pytest, pushing
 our coverage, and fixing a large number of bugs. I highly recommend contacting
 them if you need help with C++ and Python projects.
 
-You can find all relevant issues and PRs with the SDG 2021 tag ``__
+You can find all relevant issues and PRs with `the SDG 2021 tag `__
 
 Removing support for older Python versions
 ------------------------------------------
diff --git a/examples/IPython Kernel/Importing Notebooks.ipynb b/examples/IPython Kernel/Importing Notebooks.ipynb
index 8a0aac7..9d091aa 100644
--- a/examples/IPython Kernel/Importing Notebooks.ipynb
+++ b/examples/IPython Kernel/Importing Notebooks.ipynb
@@ -15,7 +15,7 @@
     "This is made difficult by the fact that Notebooks are not plain Python files,\n",
     "and thus cannot be imported by the regular Python machinery.\n",
     "\n",
-    "Fortunately, Python provides some fairly sophisticated [hooks](http://www.python.org/dev/peps/pep-0302/) into the import machinery,\n",
+    "Fortunately, Python provides some fairly sophisticated [hooks](https://peps.python.org/pep-0302/) into the import machinery,\n",
     "so we can actually make IPython notebooks importable without much difficulty,\n",
     "and only using public APIs."
    ]
diff --git a/pytest.ini b/pytest.ini
index e0cfec0..81511e9 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -45,3 +45,4 @@ addopts = --durations=10
     --ignore=IPython/utils/version.py
 doctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS
 ipdoctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS
+asyncio_mode = strict
diff --git a/setup.cfg b/setup.cfg
index 4fbffaa..2027b53 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -63,17 +63,16 @@ qtconsole =
     qtconsole
 terminal =
 test =
-    pytest<7
+    pytest<7.1
     pytest-asyncio
     testpath
 test_extra =
+    %(test)s
     curio
     matplotlib!=3.2.0
     nbformat
     numpy>=1.19
     pandas
-    pytest<7
-    testpath
     trio
 all =
     %(black)s
diff --git a/setup.py b/setup.py
index 159f8f2..fbaa5f6 100644
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,6 @@ requires utilities which are not available under Windows."""
 
 import os
 import sys
-from itertools import chain
 
 # **Python version check**
 #
@@ -53,7 +52,9 @@ See IPython `README.rst` file for more information:
 
 Python {py} detected.
 {pip}
-""".format(py=sys.version_info, pip=pip_message )
+""".format(
+        py=sys.version_info, pip=pip_message
+    )
 
     print(error, file=sys.stderr)
     sys.exit(1)
diff --git a/tools/release_helper.sh b/tools/release_helper.sh
index a920844..54114d1 100644
--- a/tools/release_helper.sh
+++ b/tools/release_helper.sh
@@ -111,7 +111,7 @@ then
     sleep 1
     echo $BLUE"Saving API to file $PREV_RELEASE"$NOR
     frappuccino IPython IPython.kernel IPython.lib IPython.qt IPython.lib.kernel IPython.html IPython.frontend IPython.external --save IPython-$PREV_RELEASE.json
-    echo $BLUE"comming back to $BRANCH"$NOR
+    echo $BLUE"coming back to $BRANCH"$NOR
     git checkout $BRANCH
     sleep 1
     echo $BLUE"comparing ..."$NOR