aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/ipython/py3
diff options
context:
space:
mode:
authorrobot-contrib <robot-contrib@yandex-team.com>2023-09-30 10:27:28 +0300
committerrobot-contrib <robot-contrib@yandex-team.com>2023-09-30 10:47:10 +0300
commit5a6373c9d09bbfb7094f9992a4531477bb97829e (patch)
treeebea8fd55fee858876743312cdf789a1f01487b5 /contrib/python/ipython/py3
parent15f3c7493474de25a6b23296878bb8f49470d2e6 (diff)
downloadydb-5a6373c9d09bbfb7094f9992a4531477bb97829e.tar.gz
Update contrib/python/ipython/py3 to 8.15.0
Diffstat (limited to 'contrib/python/ipython/py3')
-rw-r--r--contrib/python/ipython/py3/.dist-info/METADATA43
-rw-r--r--contrib/python/ipython/py3/IPython/core/debugger.py118
-rw-r--r--contrib/python/ipython/py3/IPython/core/displayhook.py7
-rw-r--r--contrib/python/ipython/py3/IPython/core/history.py18
-rw-r--r--contrib/python/ipython/py3/IPython/core/inputsplitter.py18
-rw-r--r--contrib/python/ipython/py3/IPython/core/inputtransformer.py7
-rw-r--r--contrib/python/ipython/py3/IPython/core/inputtransformer2.py46
-rw-r--r--contrib/python/ipython/py3/IPython/core/interactiveshell.py58
-rw-r--r--contrib/python/ipython/py3/IPython/core/logger.py4
-rw-r--r--contrib/python/ipython/py3/IPython/core/magics/ast_mod.py320
-rw-r--r--contrib/python/ipython/py3/IPython/core/magics/basic.py4
-rw-r--r--contrib/python/ipython/py3/IPython/core/magics/execution.py106
-rw-r--r--contrib/python/ipython/py3/IPython/core/pylabtools.py26
-rw-r--r--contrib/python/ipython/py3/IPython/core/release.py2
-rw-r--r--contrib/python/ipython/py3/IPython/core/ultratb.py11
-rw-r--r--contrib/python/ipython/py3/IPython/sphinxext/ipython_directive.py8
-rw-r--r--contrib/python/ipython/py3/IPython/terminal/debugger.py16
-rw-r--r--contrib/python/ipython/py3/IPython/terminal/interactiveshell.py9
-rw-r--r--contrib/python/ipython/py3/IPython/terminal/pt_inputhooks/osx.py10
-rw-r--r--contrib/python/ipython/py3/IPython/utils/_sysinfo.py2
-rw-r--r--contrib/python/ipython/py3/IPython/utils/module_paths.py12
-rw-r--r--contrib/python/ipython/py3/IPython/utils/tokenutil.py32
-rw-r--r--contrib/python/ipython/py3/ya.make3
23 files changed, 773 insertions, 107 deletions
diff --git a/contrib/python/ipython/py3/.dist-info/METADATA b/contrib/python/ipython/py3/.dist-info/METADATA
index 694e2535e3..504e0953c2 100644
--- a/contrib/python/ipython/py3/.dist-info/METADATA
+++ b/contrib/python/ipython/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: ipython
-Version: 8.14.0
+Version: 8.15.0
Summary: IPython: Productive Interactive Computing
Home-page: https://ipython.org
Author: The IPython Development Team
@@ -28,29 +28,31 @@ Description-Content-Type: text/x-rst
License-File: LICENSE
Requires-Dist: backcall
Requires-Dist: decorator
-Requires-Dist: jedi (>=0.13)
+Requires-Dist: jedi >=0.13
Requires-Dist: matplotlib-inline
Requires-Dist: pickleshare
-Requires-Dist: prompt-toolkit (!=3.0.37,<3.1.0,>=3.0.30)
-Requires-Dist: pygments (>=2.4.0)
+Requires-Dist: prompt-toolkit !=3.0.37,<3.1.0,>=3.0.30
+Requires-Dist: pygments >=2.4.0
Requires-Dist: stack-data
-Requires-Dist: traitlets (>=5)
+Requires-Dist: traitlets >=5
Requires-Dist: typing-extensions ; python_version < "3.10"
-Requires-Dist: pexpect (>4.3) ; sys_platform != "win32"
+Requires-Dist: exceptiongroup ; python_version < "3.11"
+Requires-Dist: pexpect >4.3 ; sys_platform != "win32"
Requires-Dist: appnope ; sys_platform == "darwin"
Requires-Dist: colorama ; sys_platform == "win32"
Provides-Extra: all
Requires-Dist: black ; extra == 'all'
Requires-Dist: ipykernel ; extra == 'all'
-Requires-Dist: setuptools (>=18.5) ; extra == 'all'
-Requires-Dist: sphinx (>=1.3) ; extra == 'all'
+Requires-Dist: setuptools >=18.5 ; extra == 'all'
+Requires-Dist: sphinx >=1.3 ; extra == 'all'
Requires-Dist: sphinx-rtd-theme ; extra == 'all'
Requires-Dist: docrepr ; extra == 'all'
Requires-Dist: matplotlib ; extra == 'all'
Requires-Dist: stack-data ; extra == 'all'
-Requires-Dist: pytest (<7) ; extra == 'all'
+Requires-Dist: pytest <7 ; extra == 'all'
Requires-Dist: typing-extensions ; extra == 'all'
-Requires-Dist: pytest (<7.1) ; extra == 'all'
+Requires-Dist: exceptiongroup ; extra == 'all'
+Requires-Dist: pytest <7.1 ; extra == 'all'
Requires-Dist: pytest-asyncio ; extra == 'all'
Requires-Dist: testpath ; extra == 'all'
Requires-Dist: nbconvert ; extra == 'all'
@@ -60,23 +62,24 @@ Requires-Dist: notebook ; extra == 'all'
Requires-Dist: ipyparallel ; extra == 'all'
Requires-Dist: qtconsole ; extra == 'all'
Requires-Dist: curio ; extra == 'all'
-Requires-Dist: matplotlib (!=3.2.0) ; extra == 'all'
-Requires-Dist: numpy (>=1.21) ; extra == 'all'
+Requires-Dist: matplotlib !=3.2.0 ; extra == 'all'
+Requires-Dist: numpy >=1.21 ; extra == 'all'
Requires-Dist: pandas ; extra == 'all'
Requires-Dist: trio ; extra == 'all'
Provides-Extra: black
Requires-Dist: black ; extra == 'black'
Provides-Extra: doc
Requires-Dist: ipykernel ; extra == 'doc'
-Requires-Dist: setuptools (>=18.5) ; extra == 'doc'
-Requires-Dist: sphinx (>=1.3) ; extra == 'doc'
+Requires-Dist: setuptools >=18.5 ; extra == 'doc'
+Requires-Dist: sphinx >=1.3 ; extra == 'doc'
Requires-Dist: sphinx-rtd-theme ; extra == 'doc'
Requires-Dist: docrepr ; extra == 'doc'
Requires-Dist: matplotlib ; extra == 'doc'
Requires-Dist: stack-data ; extra == 'doc'
-Requires-Dist: pytest (<7) ; extra == 'doc'
+Requires-Dist: pytest <7 ; extra == 'doc'
Requires-Dist: typing-extensions ; extra == 'doc'
-Requires-Dist: pytest (<7.1) ; extra == 'doc'
+Requires-Dist: exceptiongroup ; extra == 'doc'
+Requires-Dist: pytest <7.1 ; extra == 'doc'
Requires-Dist: pytest-asyncio ; extra == 'doc'
Requires-Dist: testpath ; extra == 'doc'
Provides-Extra: kernel
@@ -94,17 +97,17 @@ Provides-Extra: qtconsole
Requires-Dist: qtconsole ; extra == 'qtconsole'
Provides-Extra: terminal
Provides-Extra: test
-Requires-Dist: pytest (<7.1) ; extra == 'test'
+Requires-Dist: pytest <7.1 ; extra == 'test'
Requires-Dist: pytest-asyncio ; extra == 'test'
Requires-Dist: testpath ; extra == 'test'
Provides-Extra: test_extra
-Requires-Dist: pytest (<7.1) ; extra == 'test_extra'
+Requires-Dist: pytest <7.1 ; extra == 'test_extra'
Requires-Dist: pytest-asyncio ; extra == 'test_extra'
Requires-Dist: testpath ; extra == 'test_extra'
Requires-Dist: curio ; extra == 'test_extra'
-Requires-Dist: matplotlib (!=3.2.0) ; extra == 'test_extra'
+Requires-Dist: matplotlib !=3.2.0 ; extra == 'test_extra'
Requires-Dist: nbformat ; extra == 'test_extra'
-Requires-Dist: numpy (>=1.21) ; extra == 'test_extra'
+Requires-Dist: numpy >=1.21 ; extra == 'test_extra'
Requires-Dist: pandas ; extra == 'test_extra'
Requires-Dist: trio ; extra == 'test_extra'
diff --git a/contrib/python/ipython/py3/IPython/core/debugger.py b/contrib/python/ipython/py3/IPython/core/debugger.py
index c8082e34e7..30be9fc0d1 100644
--- a/contrib/python/ipython/py3/IPython/core/debugger.py
+++ b/contrib/python/ipython/py3/IPython/core/debugger.py
@@ -108,6 +108,7 @@ import re
import os
from IPython import get_ipython
+from contextlib import contextmanager
from IPython.utils import PyColorize
from IPython.utils import coloransi, py3compat
from IPython.core.excolors import exception_colors
@@ -127,6 +128,11 @@ from pdb import Pdb as OldPdb
DEBUGGERSKIP = "__debuggerskip__"
+# this has been implemented in Pdb in Python 3.13 (https://github.com/python/cpython/pull/106676
+# on lower python versions, we backported the feature.
+CHAIN_EXCEPTIONS = sys.version_info < (3, 13)
+
+
def make_arrow(pad):
"""generate the leading arrow in front of traceback or debugger"""
if pad >= 2:
@@ -185,6 +191,9 @@ class Pdb(OldPdb):
"""
+ if CHAIN_EXCEPTIONS:
+ MAX_CHAINED_EXCEPTION_DEPTH = 999
+
default_predicates = {
"tbhide": True,
"readonly": False,
@@ -281,6 +290,10 @@ class Pdb(OldPdb):
# list of predicates we use to skip frames
self._predicates = self.default_predicates
+ if CHAIN_EXCEPTIONS:
+ self._chained_exceptions = tuple()
+ self._chained_exception_index = 0
+
#
def set_colors(self, scheme):
"""Shorthand access to the color table scheme selector method."""
@@ -330,9 +343,106 @@ class Pdb(OldPdb):
ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
return ip_hide
- def interaction(self, frame, traceback):
+ if CHAIN_EXCEPTIONS:
+
+ def _get_tb_and_exceptions(self, tb_or_exc):
+ """
+ Given a tracecack or an exception, return a tuple of chained exceptions
+ and current traceback to inspect.
+ This will deal with selecting the right ``__cause__`` or ``__context__``
+ as well as handling cycles, and return a flattened list of exceptions we
+ can jump to with do_exceptions.
+ """
+ _exceptions = []
+ if isinstance(tb_or_exc, BaseException):
+ traceback, current = tb_or_exc.__traceback__, tb_or_exc
+
+ while current is not None:
+ if current in _exceptions:
+ break
+ _exceptions.append(current)
+ if current.__cause__ is not None:
+ current = current.__cause__
+ elif (
+ current.__context__ is not None
+ and not current.__suppress_context__
+ ):
+ current = current.__context__
+
+ if len(_exceptions) >= self.MAX_CHAINED_EXCEPTION_DEPTH:
+ self.message(
+ f"More than {self.MAX_CHAINED_EXCEPTION_DEPTH}"
+ " chained exceptions found, not all exceptions"
+ "will be browsable with `exceptions`."
+ )
+ break
+ else:
+ traceback = tb_or_exc
+ return tuple(reversed(_exceptions)), traceback
+
+ @contextmanager
+ def _hold_exceptions(self, exceptions):
+ """
+ Context manager to ensure proper cleaning of exceptions references
+ When given a chained exception instead of a traceback,
+ pdb may hold references to many objects which may leak memory.
+ We use this context manager to make sure everything is properly cleaned
+ """
+ try:
+ self._chained_exceptions = exceptions
+ self._chained_exception_index = len(exceptions) - 1
+ yield
+ finally:
+ # we can't put those in forget as otherwise they would
+ # be cleared on exception change
+ self._chained_exceptions = tuple()
+ self._chained_exception_index = 0
+
+ def do_exceptions(self, arg):
+ """exceptions [number]
+ List or change current exception in an exception chain.
+ Without arguments, list all the current exception in the exception
+ chain. Exceptions will be numbered, with the current exception indicated
+ with an arrow.
+ If given an integer as argument, switch to the exception at that index.
+ """
+ if not self._chained_exceptions:
+ self.message(
+ "Did not find chained exceptions. To move between"
+ " exceptions, pdb/post_mortem must be given an exception"
+ " object rather than a traceback."
+ )
+ return
+ if not arg:
+ for ix, exc in enumerate(self._chained_exceptions):
+ prompt = ">" if ix == self._chained_exception_index else " "
+ rep = repr(exc)
+ if len(rep) > 80:
+ rep = rep[:77] + "..."
+ self.message(f"{prompt} {ix:>3} {rep}")
+ else:
+ try:
+ number = int(arg)
+ except ValueError:
+ self.error("Argument must be an integer")
+ return
+ if 0 <= number < len(self._chained_exceptions):
+ self._chained_exception_index = number
+ self.setup(None, self._chained_exceptions[number].__traceback__)
+ self.print_stack_entry(self.stack[self.curindex])
+ else:
+ self.error("No exception with that number")
+
+ def interaction(self, frame, tb_or_exc):
try:
- OldPdb.interaction(self, frame, traceback)
+ if CHAIN_EXCEPTIONS:
+ # this context manager is part of interaction in 3.13
+ _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc)
+ with self._hold_exceptions(_chained_exceptions):
+ OldPdb.interaction(self, frame, tb)
+ else:
+ OldPdb.interaction(self, frame, traceback)
+
except KeyboardInterrupt:
self.stdout.write("\n" + self.shell.get_exception_only())
@@ -640,7 +750,7 @@ class Pdb(OldPdb):
"""
self.lastcmd = 'list'
last = None
- if arg:
+ if arg and arg != ".":
try:
x = eval(arg, {}, {})
if type(x) == type(()):
@@ -655,7 +765,7 @@ class Pdb(OldPdb):
except:
print('*** Error in argument:', repr(arg), file=self.stdout)
return
- elif self.lineno is None:
+ elif self.lineno is None or arg == ".":
first = max(1, self.curframe.f_lineno - 5)
else:
first = self.lineno + 1
diff --git a/contrib/python/ipython/py3/IPython/core/displayhook.py b/contrib/python/ipython/py3/IPython/core/displayhook.py
index aba4f904d8..b411f11613 100644
--- a/contrib/python/ipython/py3/IPython/core/displayhook.py
+++ b/contrib/python/ipython/py3/IPython/core/displayhook.py
@@ -297,8 +297,13 @@ class DisplayHook(Configurable):
for n in range(1,self.prompt_count + 1):
key = '_'+repr(n)
try:
+ del self.shell.user_ns_hidden[key]
+ except KeyError:
+ pass
+ try:
del self.shell.user_ns[key]
- except: pass
+ except KeyError:
+ pass
# In some embedded circumstances, the user_ns doesn't have the
# '_oh' key set up.
oh = self.shell.user_ns.get('_oh', None)
diff --git a/contrib/python/ipython/py3/IPython/core/history.py b/contrib/python/ipython/py3/IPython/core/history.py
index fd5a8680bf..fb67d158ef 100644
--- a/contrib/python/ipython/py3/IPython/core/history.py
+++ b/contrib/python/ipython/py3/IPython/core/history.py
@@ -177,6 +177,10 @@ class HistoryAccessor(HistoryAccessorBase):
"""
).tag(config=True)
+ @default("connection_options")
+ def _default_connection_options(self):
+ return dict(check_same_thread=False)
+
# The SQLite database
db = Any()
@observe('db')
@@ -570,7 +574,7 @@ class HistoryManager(HistoryAccessor):
cur = conn.execute(
"""INSERT INTO sessions VALUES (NULL, ?, NULL,
NULL, '') """,
- (datetime.datetime.now(),),
+ (datetime.datetime.now().isoformat(" "),),
)
self.session_number = cur.lastrowid
@@ -578,9 +582,15 @@ class HistoryManager(HistoryAccessor):
"""Close the database session, filling in the end time and line count."""
self.writeout_cache()
with self.db:
- self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
- session==?""", (datetime.datetime.now(),
- len(self.input_hist_parsed)-1, self.session_number))
+ self.db.execute(
+ """UPDATE sessions SET end=?, num_cmds=? WHERE
+ session==?""",
+ (
+ datetime.datetime.now().isoformat(" "),
+ len(self.input_hist_parsed) - 1,
+ self.session_number,
+ ),
+ )
self.session_number = 0
def name_session(self, name):
diff --git a/contrib/python/ipython/py3/IPython/core/inputsplitter.py b/contrib/python/ipython/py3/IPython/core/inputsplitter.py
index 10707d3d6b..a4401184bd 100644
--- a/contrib/python/ipython/py3/IPython/core/inputsplitter.py
+++ b/contrib/python/ipython/py3/IPython/core/inputsplitter.py
@@ -44,6 +44,7 @@ from IPython.core.inputtransformer import (leading_indent,
assign_from_system,
assemble_python_lines,
)
+from IPython.utils import tokenutil
# These are available in this module for backwards compatibility.
from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP,
@@ -128,7 +129,7 @@ def partial_tokens(s):
readline = io.StringIO(s).readline
token = tokenize.TokenInfo(tokenize.NEWLINE, '', (1, 0), (1, 0), '')
try:
- for token in tokenize.generate_tokens(readline):
+ for token in tokenutil.generate_tokens_catch_errors(readline):
yield token
except tokenize.TokenError as e:
# catch EOF error
@@ -150,9 +151,22 @@ def find_next_indent(code):
tokens.pop()
if not tokens:
return 0
- while (tokens[-1].type in {tokenize.DEDENT, tokenize.NEWLINE, tokenize.COMMENT}):
+
+ while tokens[-1].type in {
+ tokenize.DEDENT,
+ tokenize.NEWLINE,
+ tokenize.COMMENT,
+ tokenize.ERRORTOKEN,
+ }:
tokens.pop()
+ # Starting in Python 3.12, the tokenize module adds implicit newlines at the end
+ # of input. We need to remove those if we're in a multiline statement
+ if tokens[-1].type == IN_MULTILINE_STATEMENT:
+ while tokens[-2].type in {tokenize.NL}:
+ tokens.pop(-2)
+
+
if tokens[-1].type == INCOMPLETE_STRING:
# Inside a multiline string
return 0
diff --git a/contrib/python/ipython/py3/IPython/core/inputtransformer.py b/contrib/python/ipython/py3/IPython/core/inputtransformer.py
index 77f69f388f..81cd1fa08c 100644
--- a/contrib/python/ipython/py3/IPython/core/inputtransformer.py
+++ b/contrib/python/ipython/py3/IPython/core/inputtransformer.py
@@ -9,10 +9,11 @@ import abc
import functools
import re
import tokenize
-from tokenize import generate_tokens, untokenize, TokenError
+from tokenize import untokenize, TokenError
from io import StringIO
from IPython.core.splitinput import LineInfo
+from IPython.utils import tokenutil
#-----------------------------------------------------------------------------
# Globals
@@ -127,7 +128,7 @@ class TokenInputTransformer(InputTransformer):
def reset_tokenizer(self):
it = iter(self.buf)
- self.tokenizer = generate_tokens(it.__next__)
+ self.tokenizer = tokenutil.generate_tokens_catch_errors(it.__next__)
def push(self, line):
self.buf.append(line + '\n')
@@ -295,7 +296,7 @@ def _line_tokens(line):
readline = StringIO(line).readline
toktypes = set()
try:
- for t in generate_tokens(readline):
+ for t in tokenutil.generate_tokens_catch_errors(readline):
toktypes.add(t[0])
except TokenError as e:
# There are only two cases where a TokenError is raised.
diff --git a/contrib/python/ipython/py3/IPython/core/inputtransformer2.py b/contrib/python/ipython/py3/IPython/core/inputtransformer2.py
index 37f0e7699c..949cf383e2 100644
--- a/contrib/python/ipython/py3/IPython/core/inputtransformer2.py
+++ b/contrib/python/ipython/py3/IPython/core/inputtransformer2.py
@@ -13,10 +13,13 @@ deprecated in 7.0.
import ast
from codeop import CommandCompiler, Compile
import re
+import sys
import tokenize
from typing import List, Tuple, Optional, Any
import warnings
+from IPython.utils import tokenutil
+
_indent_re = re.compile(r'^[ \t]+')
def leading_empty_lines(lines):
@@ -269,9 +272,7 @@ class MagicAssign(TokenTransformBase):
class SystemAssign(TokenTransformBase):
"""Transformer for assignments from system commands (a = !foo)"""
@classmethod
- def find(cls, tokens_by_line):
- """Find the first system assignment (a = !foo) in the cell.
- """
+ def find_pre_312(cls, tokens_by_line):
for line in tokens_by_line:
assign_ix = _find_assign_op(line)
if (assign_ix is not None) \
@@ -287,6 +288,26 @@ class SystemAssign(TokenTransformBase):
break
ix += 1
+ @classmethod
+ def find_post_312(cls, tokens_by_line):
+ for line in tokens_by_line:
+ assign_ix = _find_assign_op(line)
+ if (
+ (assign_ix is not None)
+ and not line[assign_ix].line.strip().startswith("=")
+ and (len(line) >= assign_ix + 2)
+ and (line[assign_ix + 1].type == tokenize.OP)
+ and (line[assign_ix + 1].string == "!")
+ ):
+ return cls(line[assign_ix + 1].start)
+
+ @classmethod
+ def find(cls, tokens_by_line):
+ """Find the first system assignment (a = !foo) in the cell."""
+ if sys.version_info < (3, 12):
+ return cls.find_pre_312(tokens_by_line)
+ return cls.find_post_312(tokens_by_line)
+
def transform(self, lines: List[str]):
"""Transform a system assignment found by the ``find()`` classmethod.
"""
@@ -511,7 +532,9 @@ def make_tokens_by_line(lines:List[str]):
)
parenlev = 0
try:
- for token in tokenize.generate_tokens(iter(lines).__next__):
+ for token in tokenutil.generate_tokens_catch_errors(
+ iter(lines).__next__, extra_errors_to_catch=["expected EOF"]
+ ):
tokens_by_line[-1].append(token)
if (token.type == NEWLINE) \
or ((token.type == NL) and (parenlev <= 0)):
@@ -677,9 +700,13 @@ class TransformerManager:
if not lines:
return 'complete', None
- if lines[-1].endswith('\\'):
- # Explicit backslash continuation
- return 'incomplete', find_last_indent(lines)
+ for line in reversed(lines):
+ if not line.strip():
+ continue
+ elif line.strip("\n").endswith("\\"):
+ return "incomplete", find_last_indent(lines)
+ else:
+ break
try:
for transform in self.cleanup_transforms:
@@ -717,7 +744,10 @@ class TransformerManager:
if not tokens_by_line:
return 'incomplete', find_last_indent(lines)
- if tokens_by_line[-1][-1].type != tokenize.ENDMARKER:
+ if (
+ tokens_by_line[-1][-1].type != tokenize.ENDMARKER
+ and tokens_by_line[-1][-1].type != tokenize.ERRORTOKEN
+ ):
# We're in a multiline string or expression
return 'incomplete', find_last_indent(lines)
diff --git a/contrib/python/ipython/py3/IPython/core/interactiveshell.py b/contrib/python/ipython/py3/IPython/core/interactiveshell.py
index 7392de7c02..894403f98b 100644
--- a/contrib/python/ipython/py3/IPython/core/interactiveshell.py
+++ b/contrib/python/ipython/py3/IPython/core/interactiveshell.py
@@ -112,6 +112,8 @@ try:
except ImportError:
sphinxify = None
+if sys.version_info[:2] < (3, 11):
+ from exceptiongroup import BaseExceptionGroup
class ProvisionalWarning(DeprecationWarning):
"""
@@ -2095,25 +2097,38 @@ class InteractiveShell(SingletonConfigurable):
stb.extend(self.InteractiveTB.get_exception_only(etype,
value))
else:
- try:
- # 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.
- if hasattr(value, "_render_traceback_"):
- stb = value._render_traceback_()
- else:
- stb = self.InteractiveTB.structured_traceback(
- etype, value, tb, tb_offset=tb_offset
- )
- except Exception:
- print(
- "Unexpected exception formatting exception. Falling back to standard exception"
- )
+ def contains_exceptiongroup(val):
+ if val is None:
+ return False
+ return isinstance(
+ val, BaseExceptionGroup
+ ) or contains_exceptiongroup(val.__context__)
+
+ if contains_exceptiongroup(value):
+ # fall back to native exception formatting until ultratb
+ # supports exception groups
traceback.print_exc()
- return None
+ else:
+ try:
+ # 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.
+ if hasattr(value, "_render_traceback_"):
+ stb = value._render_traceback_()
+ else:
+ stb = self.InteractiveTB.structured_traceback(
+ etype, value, tb, tb_offset=tb_offset
+ )
- self._showtraceback(etype, value, stb)
+ except Exception:
+ print(
+ "Unexpected exception formatting exception. Falling back to standard exception"
+ )
+ traceback.print_exc()
+ return None
+
+ self._showtraceback(etype, value, stb)
if self.call_pdb:
# drop into debugger
self.debugger(force=True)
@@ -2417,7 +2432,7 @@ class InteractiveShell(SingletonConfigurable):
result = fn(*args, **kwargs)
# The code below prevents the output from being displayed
- # when using magics with decodator @output_can_be_silenced
+ # when using magics with decorator @output_can_be_silenced
# when the last Python token in the expression is a ';'.
if getattr(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, False):
if DisplayHook.semicolon_at_end_of_expression(magic_arg_s):
@@ -2478,7 +2493,7 @@ class InteractiveShell(SingletonConfigurable):
result = fn(*args, **kwargs)
# The code below prevents the output from being displayed
- # when using magics with decodator @output_can_be_silenced
+ # when using magics with decorator @output_can_be_silenced
# when the last Python token in the expression is a ';'.
if getattr(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, False):
if DisplayHook.semicolon_at_end_of_expression(cell):
@@ -3338,8 +3353,11 @@ class InteractiveShell(SingletonConfigurable):
# an InputRejected. Short-circuit in this case so that we
# don't unregister the transform.
raise
- except Exception:
- warn("AST transformer %r threw an error. It will be unregistered." % transformer)
+ except Exception as e:
+ warn(
+ "AST transformer %r threw an error. It will be unregistered. %s"
+ % (transformer, e)
+ )
self.ast_transformers.remove(transformer)
if self.ast_transformers:
diff --git a/contrib/python/ipython/py3/IPython/core/logger.py b/contrib/python/ipython/py3/IPython/core/logger.py
index 99e7ce2918..ab12d10e22 100644
--- a/contrib/python/ipython/py3/IPython/core/logger.py
+++ b/contrib/python/ipython/py3/IPython/core/logger.py
@@ -15,10 +15,14 @@
# Python standard modules
import glob
import io
+import logging
import os
import time
+# prevent jedi/parso's debug messages pipe into interactiveshell
+logging.getLogger("parso").setLevel(logging.WARNING)
+
#****************************************************************************
# FIXME: This class isn't a mixin anymore, but it still needs attributes from
# ipython and does input cache management. Finish cleanup later...
diff --git a/contrib/python/ipython/py3/IPython/core/magics/ast_mod.py b/contrib/python/ipython/py3/IPython/core/magics/ast_mod.py
new file mode 100644
index 0000000000..e28b9f1231
--- /dev/null
+++ b/contrib/python/ipython/py3/IPython/core/magics/ast_mod.py
@@ -0,0 +1,320 @@
+"""
+This module contains utility function and classes to inject simple ast
+transformations based on code strings into IPython. While it is already possible
+with ast-transformers it is not easy to directly manipulate ast.
+
+
+IPython has pre-code and post-code hooks, but are ran from within the IPython
+machinery so may be inappropriate, for example for performance mesurement.
+
+This module give you tools to simplify this, and expose 2 classes:
+
+- `ReplaceCodeTransformer` which is a simple ast transformer based on code
+ template,
+
+and for advance case:
+
+- `Mangler` which is a simple ast transformer that mangle names in the ast.
+
+
+Example, let's try to make a simple version of the ``timeit`` magic, that run a
+code snippet 10 times and print the average time taken.
+
+Basically we want to run :
+
+.. code-block:: python
+
+ from time import perf_counter
+ now = perf_counter()
+ for i in range(10):
+ __code__ # our code
+ print(f"Time taken: {(perf_counter() - now)/10}")
+ __ret__ # the result of the last statement
+
+Where ``__code__`` is the code snippet we want to run, and ``__ret__`` is the
+result, so that if we for example run `dataframe.head()` IPython still display
+the head of dataframe instead of nothing.
+
+Here is a complete example of a file `timit2.py` that define such a magic:
+
+.. code-block:: python
+
+ from IPython.core.magic import (
+ Magics,
+ magics_class,
+ line_cell_magic,
+ )
+ from IPython.core.magics.ast_mod import ReplaceCodeTransformer
+ from textwrap import dedent
+ import ast
+
+ template = template = dedent('''
+ from time import perf_counter
+ now = perf_counter()
+ for i in range(10):
+ __code__
+ print(f"Time taken: {(perf_counter() - now)/10}")
+ __ret__
+ '''
+ )
+
+
+ @magics_class
+ class AstM(Magics):
+ @line_cell_magic
+ def t2(self, line, cell):
+ transformer = ReplaceCodeTransformer.from_string(template)
+ transformer.debug = True
+ transformer.mangler.debug = True
+ new_code = transformer.visit(ast.parse(cell))
+ return exec(compile(new_code, "<ast>", "exec"))
+
+
+ def load_ipython_extension(ip):
+ ip.register_magics(AstM)
+
+
+
+.. code-block:: python
+
+ In [1]: %load_ext timit2
+
+ In [2]: %%t2
+ ...: import time
+ ...: time.sleep(0.05)
+ ...:
+ ...:
+ Time taken: 0.05435649999999441
+
+
+If you wish to ran all the code enter in IPython in an ast transformer, you can
+do so as well:
+
+.. code-block:: python
+
+ In [1]: from IPython.core.magics.ast_mod import ReplaceCodeTransformer
+ ...:
+ ...: template = '''
+ ...: from time import perf_counter
+ ...: now = perf_counter()
+ ...: __code__
+ ...: print(f"Code ran in {perf_counter()-now}")
+ ...: __ret__'''
+ ...:
+ ...: get_ipython().ast_transformers.append(ReplaceCodeTransformer.from_string(template))
+
+ In [2]: 1+1
+ Code ran in 3.40410006174352e-05
+ Out[2]: 2
+
+
+
+Hygiene and Mangling
+--------------------
+
+The ast transformer above is not hygienic, it may not work if the user code use
+the same variable names as the ones used in the template. For example.
+
+To help with this by default the `ReplaceCodeTransformer` will mangle all names
+staring with 3 underscores. This is a simple heuristic that should work in most
+case, but can be cumbersome in some case. We provide a `Mangler` class that can
+be overridden to change the mangling heuristic, or simply use the `mangle_all`
+utility function. It will _try_ to mangle all names (except `__ret__` and
+`__code__`), but this include builtins (``print``, ``range``, ``type``) and
+replace those by invalid identifiers py prepending ``mangle-``:
+``mangle-print``, ``mangle-range``, ``mangle-type`` etc. This is not a problem
+as currently Python AST support invalid identifiers, but it may not be the case
+in the future.
+
+You can set `ReplaceCodeTransformer.debug=True` and
+`ReplaceCodeTransformer.mangler.debug=True` to see the code after mangling and
+transforming:
+
+.. code-block:: python
+
+
+ In [1]: from IPython.core.magics.ast_mod import ReplaceCodeTransformer, mangle_all
+ ...:
+ ...: template = '''
+ ...: from builtins import type, print
+ ...: from time import perf_counter
+ ...: now = perf_counter()
+ ...: __code__
+ ...: print(f"Code ran in {perf_counter()-now}")
+ ...: __ret__'''
+ ...:
+ ...: transformer = ReplaceCodeTransformer.from_string(template, mangling_predicate=mangle_all)
+
+
+ In [2]: transformer.debug = True
+ ...: transformer.mangler.debug = True
+ ...: get_ipython().ast_transformers.append(transformer)
+
+ In [3]: 1+1
+ Mangling Alias mangle-type
+ Mangling Alias mangle-print
+ Mangling Alias mangle-perf_counter
+ Mangling now
+ Mangling perf_counter
+ Not mangling __code__
+ Mangling print
+ Mangling perf_counter
+ Mangling now
+ Not mangling __ret__
+ ---- Transformed code ----
+ from builtins import type as mangle-type, print as mangle-print
+ from time import perf_counter as mangle-perf_counter
+ mangle-now = mangle-perf_counter()
+ ret-tmp = 1 + 1
+ mangle-print(f'Code ran in {mangle-perf_counter() - mangle-now}')
+ ret-tmp
+ ---- ---------------- ----
+ Code ran in 0.00013654199938173406
+ Out[3]: 2
+
+
+"""
+
+__skip_doctest__ = True
+
+
+from ast import NodeTransformer, Store, Load, Name, Expr, Assign, Module
+import ast
+import copy
+
+from typing import Dict, Optional
+
+
+mangle_all = lambda name: False if name in ("__ret__", "__code__") else True
+
+
+class Mangler(NodeTransformer):
+ """
+ Mangle given names in and ast tree to make sure they do not conflict with
+ user code.
+ """
+
+ enabled: bool = True
+ debug: bool = False
+
+ def log(self, *args, **kwargs):
+ if self.debug:
+ print(*args, **kwargs)
+
+ def __init__(self, predicate=None):
+ if predicate is None:
+ predicate = lambda name: name.startswith("___")
+ self.predicate = predicate
+
+ def visit_Name(self, node):
+ if self.predicate(node.id):
+ self.log("Mangling", node.id)
+ # Once in the ast we do not need
+ # names to be valid identifiers.
+ node.id = "mangle-" + node.id
+ else:
+ self.log("Not mangling", node.id)
+ return node
+
+ def visit_FunctionDef(self, node):
+ if self.predicate(node.name):
+ self.log("Mangling", node.name)
+ node.name = "mangle-" + node.name
+ else:
+ self.log("Not mangling", node.name)
+
+ for arg in node.args.args:
+ if self.predicate(arg.arg):
+ self.log("Mangling function arg", arg.arg)
+ arg.arg = "mangle-" + arg.arg
+ else:
+ self.log("Not mangling function arg", arg.arg)
+ return self.generic_visit(node)
+
+ def visit_ImportFrom(self, node):
+ return self._visit_Import_and_ImportFrom(node)
+
+ def visit_Import(self, node):
+ return self._visit_Import_and_ImportFrom(node)
+
+ def _visit_Import_and_ImportFrom(self, node):
+ for alias in node.names:
+ asname = alias.name if alias.asname is None else alias.asname
+ if self.predicate(asname):
+ new_name: str = "mangle-" + asname
+ self.log("Mangling Alias", new_name)
+ alias.asname = new_name
+ else:
+ self.log("Not mangling Alias", alias.asname)
+ return node
+
+
+class ReplaceCodeTransformer(NodeTransformer):
+ enabled: bool = True
+ debug: bool = False
+ mangler: Mangler
+
+ def __init__(
+ self, template: Module, mapping: Optional[Dict] = None, mangling_predicate=None
+ ):
+ assert isinstance(mapping, (dict, type(None)))
+ assert isinstance(mangling_predicate, (type(None), type(lambda: None)))
+ assert isinstance(template, ast.Module)
+ self.template = template
+ self.mangler = Mangler(predicate=mangling_predicate)
+ if mapping is None:
+ mapping = {}
+ self.mapping = mapping
+
+ @classmethod
+ def from_string(
+ cls, template: str, mapping: Optional[Dict] = None, mangling_predicate=None
+ ):
+ return cls(
+ ast.parse(template), mapping=mapping, mangling_predicate=mangling_predicate
+ )
+
+ def visit_Module(self, code):
+ if not self.enabled:
+ return code
+ # if not isinstance(code, ast.Module):
+ # recursively called...
+ # return generic_visit(self, code)
+ last = code.body[-1]
+ if isinstance(last, Expr):
+ code.body.pop()
+ code.body.append(Assign([Name("ret-tmp", ctx=Store())], value=last.value))
+ ast.fix_missing_locations(code)
+ ret = Expr(value=Name("ret-tmp", ctx=Load()))
+ ret = ast.fix_missing_locations(ret)
+ self.mapping["__ret__"] = ret
+ else:
+ self.mapping["__ret__"] = ast.parse("None").body[0]
+ self.mapping["__code__"] = code.body
+ tpl = ast.fix_missing_locations(self.template)
+
+ tx = copy.deepcopy(tpl)
+ tx = self.mangler.visit(tx)
+ node = self.generic_visit(tx)
+ node_2 = ast.fix_missing_locations(node)
+ if self.debug:
+ print("---- Transformed code ----")
+ print(ast.unparse(node_2))
+ print("---- ---------------- ----")
+ return node_2
+
+ # this does not work as the name might be in a list and one might want to extend the list.
+ # def visit_Name(self, name):
+ # if name.id in self.mapping and name.id == "__ret__":
+ # print(name, "in mapping")
+ # if isinstance(name.ctx, ast.Store):
+ # return Name("tmp", ctx=Store())
+ # else:
+ # return copy.deepcopy(self.mapping[name.id])
+ # return name
+
+ def visit_Expr(self, expr):
+ if isinstance(expr.value, Name) and expr.value.id in self.mapping:
+ if self.mapping[expr.value.id] is not None:
+ return copy.deepcopy(self.mapping[expr.value.id])
+ return self.generic_visit(expr)
diff --git a/contrib/python/ipython/py3/IPython/core/magics/basic.py b/contrib/python/ipython/py3/IPython/core/magics/basic.py
index 814dec72e2..54b0c2a4de 100644
--- a/contrib/python/ipython/py3/IPython/core/magics/basic.py
+++ b/contrib/python/ipython/py3/IPython/core/magics/basic.py
@@ -109,12 +109,12 @@ class BasicMagics(Magics):
Created `%%t` as an alias for `%%timeit`.
In [2]: %t -n1 pass
- 1 loops, best of 3: 954 ns per loop
+ 107 ns ± 43.6 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [3]: %%t -n1
...: pass
...:
- 1 loops, best of 3: 954 ns per loop
+ 107 ns ± 58.3 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [4]: %alias_magic --cell whereami pwd
UsageError: Cell magic function `%%pwd` not found.
diff --git a/contrib/python/ipython/py3/IPython/core/magics/execution.py b/contrib/python/ipython/py3/IPython/core/magics/execution.py
index 228cbd9da7..4147dac963 100644
--- a/contrib/python/ipython/py3/IPython/core/magics/execution.py
+++ b/contrib/python/ipython/py3/IPython/core/magics/execution.py
@@ -8,6 +8,7 @@
import ast
import bdb
import builtins as builtin_mod
+import copy
import cProfile as profile
import gc
import itertools
@@ -19,14 +20,28 @@ import shlex
import sys
import time
import timeit
-from ast import Module
+from typing import Dict, Any
+from ast import (
+ Assign,
+ Call,
+ Expr,
+ Load,
+ Module,
+ Name,
+ NodeTransformer,
+ Store,
+ parse,
+ unparse,
+)
from io import StringIO
from logging import error
from pathlib import Path
from pdb import Restart
+from textwrap import dedent, indent
from warnings import warn
from IPython.core import magic_arguments, oinspect, page
+from IPython.core.displayhook import DisplayHook
from IPython.core.error import UsageError
from IPython.core.macro import Macro
from IPython.core.magic import (
@@ -37,8 +52,8 @@ from IPython.core.magic import (
magics_class,
needs_local_scope,
no_var_expand,
- output_can_be_silenced,
on_off,
+ output_can_be_silenced,
)
from IPython.testing.skipdoctest import skip_doctest
from IPython.utils.capture import capture_output
@@ -47,7 +62,7 @@ from IPython.utils.ipstruct import Struct
from IPython.utils.module_paths import find_mod
from IPython.utils.path import get_py_filename, shellglob
from IPython.utils.timing import clock, clock2
-from IPython.core.displayhook import DisplayHook
+from IPython.core.magics.ast_mod import ReplaceCodeTransformer
#-----------------------------------------------------------------------------
# Magic implementation classes
@@ -164,9 +179,9 @@ class Timer(timeit.Timer):
@magics_class
class ExecutionMagics(Magics):
- """Magics related to code execution, debugging, profiling, etc.
+ """Magics related to code execution, debugging, profiling, etc."""
- """
+ _transformers: Dict[str, Any] = {}
def __init__(self, shell):
super(ExecutionMagics, self).__init__(shell)
@@ -1035,7 +1050,7 @@ class ExecutionMagics(Magics):
provided, <N> is determined so as to get sufficient accuracy.
-r<R>: number of repeats <R>, each consisting of <N> loops, and take the
- best result.
+ average result.
Default: 7
-t: use time.time to measure the time, which is the default on Unix.
@@ -1474,6 +1489,83 @@ class ExecutionMagics(Magics):
elif args.output:
self.shell.user_ns[args.output] = io
+ @skip_doctest
+ @magic_arguments.magic_arguments()
+ @magic_arguments.argument("name", type=str, default="default", nargs="?")
+ @magic_arguments.argument(
+ "--remove", action="store_true", help="remove the current transformer"
+ )
+ @magic_arguments.argument(
+ "--list", action="store_true", help="list existing transformers name"
+ )
+ @magic_arguments.argument(
+ "--list-all",
+ action="store_true",
+ help="list existing transformers name and code template",
+ )
+ @line_cell_magic
+ def code_wrap(self, line, cell=None):
+ """
+ Simple magic to quickly define a code transformer for all IPython's future imput.
+
+ ``__code__`` and ``__ret__`` are special variable that represent the code to run
+ and the value of the last expression of ``__code__`` respectively.
+
+ Examples
+ --------
+
+ .. ipython::
+
+ In [1]: %%code_wrap before_after
+ ...: print('before')
+ ...: __code__
+ ...: print('after')
+ ...: __ret__
+
+
+ In [2]: 1
+ before
+ after
+ Out[2]: 1
+
+ In [3]: %code_wrap --list
+ before_after
+
+ In [4]: %code_wrap --list-all
+ before_after :
+ print('before')
+ __code__
+ print('after')
+ __ret__
+
+ In [5]: %code_wrap --remove before_after
+
+ """
+ args = magic_arguments.parse_argstring(self.code_wrap, line)
+
+ if args.list:
+ for name in self._transformers.keys():
+ print(name)
+ return
+ if args.list_all:
+ for name, _t in self._transformers.items():
+ print(name, ":")
+ print(indent(ast.unparse(_t.template), " "))
+ print()
+ return
+
+ to_remove = self._transformers.pop(args.name, None)
+ if to_remove in self.shell.ast_transformers:
+ self.shell.ast_transformers.remove(to_remove)
+ if cell is None or args.remove:
+ return
+
+ _trs = ReplaceCodeTransformer(ast.parse(cell))
+
+ self._transformers[args.name] = _trs
+ self.shell.ast_transformers.append(_trs)
+
+
def parse_breakpoint(text, current_file):
'''Returns (file, line) for file:line and (current_file, line) for line'''
colon = text.find(':')
@@ -1519,4 +1611,4 @@ def _format_time(timespan, precision=3):
order = min(-int(math.floor(math.log10(timespan)) // 3), 3)
else:
order = 3
- return u"%.*g %s" % (precision, timespan * scaling[order], units[order])
+ return "%.*g %s" % (precision, timespan * scaling[order], units[order])
diff --git a/contrib/python/ipython/py3/IPython/core/pylabtools.py b/contrib/python/ipython/py3/IPython/core/pylabtools.py
index deadf038ea..e5715a9497 100644
--- a/contrib/python/ipython/py3/IPython/core/pylabtools.py
+++ b/contrib/python/ipython/py3/IPython/core/pylabtools.py
@@ -23,7 +23,7 @@ backends = {
"qt4": "Qt4Agg",
"qt5": "Qt5Agg",
"qt6": "QtAgg",
- "qt": "Qt5Agg",
+ "qt": "QtAgg",
"osx": "MacOSX",
"nbagg": "nbAgg",
"webagg": "WebAgg",
@@ -53,8 +53,8 @@ backend2gui["CocoaAgg"] = "osx"
# supports either Qt5 or Qt6 and the IPython qt event loop support Qt4, Qt5,
# and Qt6.
backend2gui["QtAgg"] = "qt"
-backend2gui["Qt4Agg"] = "qt"
-backend2gui["Qt5Agg"] = "qt"
+backend2gui["Qt4Agg"] = "qt4"
+backend2gui["Qt5Agg"] = "qt5"
# And some backends that don't need GUI integration
del backend2gui["nbAgg"]
@@ -208,10 +208,12 @@ def mpl_runner(safe_execfile):
#print '*** Matplotlib runner ***' # dbg
# turn off rendering until end of script
- is_interactive = matplotlib.rcParams['interactive']
- matplotlib.interactive(False)
- safe_execfile(fname,*where,**kw)
- matplotlib.interactive(is_interactive)
+ with matplotlib.rc_context({"interactive": False}):
+ safe_execfile(fname, *where, **kw)
+
+ if matplotlib.is_interactive():
+ plt.show()
+
# make rendering call now, if the user tried to do it
if plt.draw_if_interactive.called:
plt.draw()
@@ -317,9 +319,15 @@ def find_gui_and_backend(gui=None, gui_select=None):
import matplotlib
+ has_unified_qt_backend = getattr(matplotlib, "__version_info__", (0, 0)) >= (3, 5)
+
+ backends_ = dict(backends)
+ if not has_unified_qt_backend:
+ backends_["qt"] = "qt5agg"
+
if gui and gui != 'auto':
# select backend based on requested gui
- backend = backends[gui]
+ backend = backends_[gui]
if gui == 'agg':
gui = None
else:
@@ -336,7 +344,7 @@ def find_gui_and_backend(gui=None, gui_select=None):
# ones allowed.
if gui_select and gui != gui_select:
gui = gui_select
- backend = backends[gui]
+ backend = backends_[gui]
return gui, backend
diff --git a/contrib/python/ipython/py3/IPython/core/release.py b/contrib/python/ipython/py3/IPython/core/release.py
index 50080642ee..a1df4d1058 100644
--- a/contrib/python/ipython/py3/IPython/core/release.py
+++ b/contrib/python/ipython/py3/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 = 14
+_version_minor = 15
_version_patch = 0
_version_extra = ".dev"
# _version_extra = "rc1"
diff --git a/contrib/python/ipython/py3/IPython/core/ultratb.py b/contrib/python/ipython/py3/IPython/core/ultratb.py
index 61b5939398..cb0d8f951f 100644
--- a/contrib/python/ipython/py3/IPython/core/ultratb.py
+++ b/contrib/python/ipython/py3/IPython/core/ultratb.py
@@ -809,6 +809,7 @@ class VerboseTB(TBTools):
would appear in the traceback)."""
_tb_highlight = ""
+ _tb_highlight_style = "default"
def __init__(
self,
@@ -1110,7 +1111,7 @@ class VerboseTB(TBTools):
after = context // 2
before = context - after
if self.has_colors:
- style = get_style_by_name("default")
+ style = get_style_by_name(self._tb_highlight_style)
style = stack_data.style_with_executing_node(style, self._tb_highlight)
formatter = Terminal256Formatter(style=style)
else:
@@ -1245,7 +1246,13 @@ class VerboseTB(TBTools):
if etb and etb.tb_next:
etb = etb.tb_next
self.pdb.botframe = etb.tb_frame
- self.pdb.interaction(None, etb)
+ # last_value should be deprecated, but last-exc sometimme not set
+ # please check why later and remove the getattr.
+ exc = sys.last_value if sys.version_info < (3, 12) else getattr(sys, "last_exc", sys.last_value) # type: ignore[attr-defined]
+ if exc:
+ self.pdb.interaction(None, exc)
+ else:
+ self.pdb.interaction(None, etb)
if hasattr(self, 'tb'):
del self.tb
diff --git a/contrib/python/ipython/py3/IPython/sphinxext/ipython_directive.py b/contrib/python/ipython/py3/IPython/sphinxext/ipython_directive.py
index c428e7917f..10257a6d69 100644
--- a/contrib/python/ipython/py3/IPython/sphinxext/ipython_directive.py
+++ b/contrib/python/ipython/py3/IPython/sphinxext/ipython_directive.py
@@ -581,7 +581,9 @@ class EmbeddedSphinxShell(object):
s += "<<<" + ("-" * 73)
logger.warning(s)
if self.warning_is_error:
- raise RuntimeError('Non Expected exception in `{}` line {}'.format(filename, lineno))
+ raise RuntimeError(
+ "Unexpected exception in `{}` line {}".format(filename, lineno)
+ )
# output any warning raised during execution to stdout
# unless :okwarning: has been specified.
@@ -597,7 +599,9 @@ class EmbeddedSphinxShell(object):
s += "<<<" + ("-" * 73)
logger.warning(s)
if self.warning_is_error:
- raise RuntimeError('Non Expected warning in `{}` line {}'.format(filename, lineno))
+ raise RuntimeError(
+ "Unexpected warning in `{}` line {}".format(filename, lineno)
+ )
self.clear_cout()
return (ret, input_lines, processed_output,
diff --git a/contrib/python/ipython/py3/IPython/terminal/debugger.py b/contrib/python/ipython/py3/IPython/terminal/debugger.py
index 7a0623c847..19ed3c7f60 100644
--- a/contrib/python/ipython/py3/IPython/terminal/debugger.py
+++ b/contrib/python/ipython/py3/IPython/terminal/debugger.py
@@ -10,6 +10,7 @@ from . import embed
from pathlib import Path
from pygments.token import Token
+from prompt_toolkit.application import create_app_session
from prompt_toolkit.shortcuts.prompt import PromptSession
from prompt_toolkit.enums import EditingMode
from prompt_toolkit.formatted_text import PygmentsTokens
@@ -95,6 +96,17 @@ class TerminalPdb(Pdb):
self.pt_loop = asyncio.new_event_loop()
self.pt_app = PromptSession(**options)
+ def _prompt(self):
+ """
+ In case other prompt_toolkit apps have to run in parallel to this one (e.g. in madbg),
+ create_app_session must be used to prevent mixing up between them. According to the prompt_toolkit docs:
+
+ > If you need multiple applications running at the same time, you have to create a separate
+ > `AppSession` using a `with create_app_session():` block.
+ """
+ with create_app_session():
+ return self.pt_app.prompt()
+
def cmdloop(self, intro=None):
"""Repeatedly issue a prompt, accept input, parse an initial prefix
off the received input, and dispatch to action methods, passing them
@@ -128,9 +140,7 @@ class TerminalPdb(Pdb):
# Run the prompt in a different thread.
if not _use_simple_prompt:
try:
- line = self.thread_executor.submit(
- self.pt_app.prompt
- ).result()
+ line = self.thread_executor.submit(self._prompt).result()
except EOFError:
line = "EOF"
else:
diff --git a/contrib/python/ipython/py3/IPython/terminal/interactiveshell.py b/contrib/python/ipython/py3/IPython/terminal/interactiveshell.py
index 75cf25ea66..37e0b86981 100644
--- a/contrib/python/ipython/py3/IPython/terminal/interactiveshell.py
+++ b/contrib/python/ipython/py3/IPython/terminal/interactiveshell.py
@@ -914,6 +914,15 @@ class TerminalInteractiveShell(InteractiveShell):
active_eventloop = None
def enable_gui(self, gui=None):
+ if self.simple_prompt is True and gui is not None:
+ print(
+ f'Cannot install event loop hook for "{gui}" when running with `--simple-prompt`.'
+ )
+ print(
+ "NOTE: Tk is supported natively; use Tk apps and Tk backends with `--simple-prompt`."
+ )
+ return
+
if self._inputhook is None and gui is None:
print("No event loop hook running.")
return
diff --git a/contrib/python/ipython/py3/IPython/terminal/pt_inputhooks/osx.py b/contrib/python/ipython/py3/IPython/terminal/pt_inputhooks/osx.py
index 2754820efc..9b8a0cd98e 100644
--- a/contrib/python/ipython/py3/IPython/terminal/pt_inputhooks/osx.py
+++ b/contrib/python/ipython/py3/IPython/terminal/pt_inputhooks/osx.py
@@ -116,11 +116,8 @@ def _wake(NSApp):
msg(NSApp, n('postEvent:atStart:'), void_p(event), True)
-_triggered = Event()
-
def _input_callback(fdref, flags, info):
"""Callback to fire when there's input to be read"""
- _triggered.set()
CFFileDescriptorInvalidate(fdref)
CFRelease(fdref)
NSApp = _NSApp()
@@ -134,7 +131,6 @@ _c_input_callback = _c_callback_func_type(_input_callback)
def _stop_on_read(fd):
"""Register callback to stop eventloop when there's data on fd"""
- _triggered.clear()
fdref = CFFileDescriptorCreate(None, fd, False, _c_input_callback, None)
CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack)
source = CFFileDescriptorCreateRunLoopSource(None, fdref, 0)
@@ -149,9 +145,3 @@ def inputhook(context):
_stop_on_read(context.fileno())
objc.objc_msgSend.argtypes = [void_p, void_p]
msg(NSApp, n('run'))
- if not _triggered.is_set():
- # app closed without firing callback,
- # probably due to last window being closed.
- # Run the loop manually in this case,
- # since there may be events still to process (#9734)
- CoreFoundation.CFRunLoopRun()
diff --git a/contrib/python/ipython/py3/IPython/utils/_sysinfo.py b/contrib/python/ipython/py3/IPython/utils/_sysinfo.py
index 49941f7881..b3341865eb 100644
--- a/contrib/python/ipython/py3/IPython/utils/_sysinfo.py
+++ b/contrib/python/ipython/py3/IPython/utils/_sysinfo.py
@@ -1,2 +1,2 @@
# GENERATED BY setup.py
-commit = "f11276427"
+commit = "bc8b2d22f"
diff --git a/contrib/python/ipython/py3/IPython/utils/module_paths.py b/contrib/python/ipython/py3/IPython/utils/module_paths.py
index 6f8cb1004a..401e6a90a5 100644
--- a/contrib/python/ipython/py3/IPython/utils/module_paths.py
+++ b/contrib/python/ipython/py3/IPython/utils/module_paths.py
@@ -40,11 +40,13 @@ def find_mod(module_name):
"""
Find module `module_name` on sys.path, and return the path to module `module_name`.
- - If `module_name` refers to a module directory, then return path to __init__ file.
- - If `module_name` is a directory without an __init__file, return None.
- - If module is missing or does not have a `.py` or `.pyw` extension, return None.
- - Note that we are not interested in running bytecode.
- - Otherwise, return the fill path of the module.
+ * If `module_name` refers to a module directory, then return path to `__init__` file.
+ * If `module_name` is a directory without an __init__file, return None.
+
+ * If module is missing or does not have a `.py` or `.pyw` extension, return None.
+ * Note that we are not interested in running bytecode.
+
+ * Otherwise, return the fill path of the module.
Parameters
----------
diff --git a/contrib/python/ipython/py3/IPython/utils/tokenutil.py b/contrib/python/ipython/py3/IPython/utils/tokenutil.py
index 697d2b504a..5fd8a1fbe1 100644
--- a/contrib/python/ipython/py3/IPython/utils/tokenutil.py
+++ b/contrib/python/ipython/py3/IPython/utils/tokenutil.py
@@ -21,6 +21,36 @@ def generate_tokens(readline):
# catch EOF error
return
+
+def generate_tokens_catch_errors(readline, extra_errors_to_catch=None):
+ default_errors_to_catch = [
+ "unterminated string literal",
+ "invalid non-printable character",
+ "after line continuation character",
+ ]
+ assert extra_errors_to_catch is None or isinstance(extra_errors_to_catch, list)
+ errors_to_catch = default_errors_to_catch + (extra_errors_to_catch or [])
+
+ tokens = []
+ try:
+ for token in tokenize.generate_tokens(readline):
+ tokens.append(token)
+ yield token
+ except tokenize.TokenError as exc:
+ if any(error in exc.args[0] for error in errors_to_catch):
+ if tokens:
+ start = tokens[-1].start[0], tokens[-1].end[0]
+ end = start
+ line = tokens[-1].line
+ else:
+ start = end = (1, 0)
+ line = ""
+ yield tokenize.TokenInfo(tokenize.ERRORTOKEN, "", start, end, line)
+ else:
+ # Catch EOF
+ raise
+
+
def line_at_cursor(cell, cursor_pos=0):
"""Return the line in a cell at a given cursor position
@@ -123,5 +153,3 @@ def token_at_cursor(cell, cursor_pos=0):
return names[-1]
else:
return ''
-
-
diff --git a/contrib/python/ipython/py3/ya.make b/contrib/python/ipython/py3/ya.make
index e9d28face4..b4eeb0a830 100644
--- a/contrib/python/ipython/py3/ya.make
+++ b/contrib/python/ipython/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(8.14.0)
+VERSION(8.15.0)
LICENSE(BSD-3-Clause)
@@ -81,6 +81,7 @@ PY_SRCS(
IPython/core/magic.py
IPython/core/magic_arguments.py
IPython/core/magics/__init__.py
+ IPython/core/magics/ast_mod.py
IPython/core/magics/auto.py
IPython/core/magics/basic.py
IPython/core/magics/code.py