Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-ipython for openSUSE:Factory checked in at 2023-05-02 16:18:18 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-ipython (Old) and /work/SRC/openSUSE:Factory/.python-ipython.new.1533 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-ipython" Tue May 2 16:18:18 2023 rev:39 rq:1083899 version:8.13.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-ipython/python-ipython.changes 2023-04-07 18:16:12.592505188 +0200 +++ /work/SRC/openSUSE:Factory/.python-ipython.new.1533/python-ipython.changes 2023-05-02 16:18:19.637561791 +0200 @@ -1,0 +2,22 @@ +Mon May 1 17:40:17 UTC 2023 - Ben Greiner <c...@bnavigator.de> + +- Update to 8.13.1 + * This release is significant in that it not only has a number of + bugfixes, but also drop support for Python 3.8 as per NEP 29 + (PR #14023). + * Pretty reprensentation for Counter has been fixed to match the + Python one and be in decreasing order. PR #14032 + * Module completion is better when jedi is disabled PR #14029. + * Improvment of %%bash magic that would get stuck PR #14019 + * PR #14004 Fix a bug introduced in IPython 8.12 that crash when + inspecting some docstrings. + * PR #14010 Fix fast traceback code that was not working in some + case. + * PR #14014 Fix %page magic broken in some case. + * PR #14026, PR #14027 Tweak default shortcut with respect to + autosuggestions. + * PR #14033 add back the ability to use .get() on OInfo object + for backward compatibility with h5py (this will be + re-deprecated later, and h5py will also get a fix). + +------------------------------------------------------------------- Old: ---- ipython-8.12.0.tar.gz New: ---- ipython-8.13.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-ipython.spec ++++++ --- /var/tmp/diff_new_pack.ZDUbvB/_old 2023-05-02 16:18:20.873569119 +0200 +++ /var/tmp/diff_new_pack.ZDUbvB/_new 2023-05-02 16:18:20.929569451 +0200 @@ -32,7 +32,7 @@ # extra tests are skipped automatically, don't require these packages for Ring1 %bcond_with localtest Name: python-ipython%{psuffix} -Version: 8.12.0 +Version: 8.13.1 Release: 0 Summary: Rich architecture for interactive computing with Python License: BSD-3-Clause @@ -40,7 +40,7 @@ URL: https://github.com/ipython/ipython Source: https://files.pythonhosted.org/packages/source/i/ipython/ipython-%{version}.tar.gz Source1: https://raw.githubusercontent.com/jupyter/qtconsole/4.0.0/qtconsole/resources/icon/JupyterConsole.svg -BuildRequires: %{python_module base >= 3.8} +BuildRequires: %{python_module base >= 3.9} BuildRequires: %{python_module pip} BuildRequires: %{python_module setuptools >= 51.0.0} BuildRequires: %{python_module wheel} ++++++ ipython-8.12.0.tar.gz -> ipython-8.13.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/__init__.py new/ipython-8.13.1/IPython/__init__.py --- old/ipython-8.12.0/IPython/__init__.py 2023-03-30 10:39:40.000000000 +0200 +++ new/ipython-8.13.1/IPython/__init__.py 2023-04-28 14:12:06.000000000 +0200 @@ -26,10 +26,11 @@ #----------------------------------------------------------------------------- # Don't forget to also update setup.py when this changes! -if sys.version_info < (3, 8): +if sys.version_info < (3, 9): raise ImportError( """ -IPython 8+ supports Python 3.8 and above, following NEP 29. +IPython 8.13+ supports Python 3.9 and above, following NEP 29. +IPython 8.0-8.12 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. Python 3.5 was supported with IPython 7.0 to 7.9. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/core/guarded_eval.py new/ipython-8.13.1/IPython/core/guarded_eval.py --- old/ipython-8.12.0/IPython/core/guarded_eval.py 2023-02-13 16:00:29.000000000 +0100 +++ new/ipython-8.13.1/IPython/core/guarded_eval.py 2023-04-28 14:12:06.000000000 +0200 @@ -508,9 +508,6 @@ return all_true if isinstance(node, ast.Constant): return node.value - if isinstance(node, ast.Index): - # deprecated since Python 3.9 - return eval_node(node.value, context) # pragma: no cover if isinstance(node, ast.Tuple): return tuple(eval_node(e, context) for e in node.elts) if isinstance(node, ast.List): @@ -530,9 +527,6 @@ eval_node(node.upper, context), eval_node(node.step, context), ) - if isinstance(node, ast.ExtSlice): - # deprecated since Python 3.9 - return tuple([eval_node(dim, context) for dim in node.dims]) # pragma: no cover if isinstance(node, ast.UnaryOp): value = eval_node(node.operand, context) dunders = _find_dunder(node.op, UNARY_OP_DUNDERS) @@ -637,6 +631,7 @@ dict_keys: Type[collections.abc.KeysView] = type({}.keys()) method_descriptor: Any = type(list.copy) +module = type(builtins) NUMERICS = {int, float, complex} @@ -686,6 +681,7 @@ *NUMERICS, dict_keys, method_descriptor, + module, } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/core/interactiveshell.py new/ipython-8.13.1/IPython/core/interactiveshell.py --- old/ipython-8.12.0/IPython/core/interactiveshell.py 2023-03-30 10:39:40.000000000 +0200 +++ new/ipython-8.13.1/IPython/core/interactiveshell.py 2023-04-28 11:49:16.000000000 +0200 @@ -1781,9 +1781,12 @@ This function is meant to be called by pdef, pdoc & friends. """ info: OInfo = self._object_find(oname, namespaces) - docformat = ( - sphinxify(self.object_inspect(oname)) if self.sphinxify_docstring else None - ) + if self.sphinxify_docstring: + if sphinxify is None: + raise ImportError("Module ``docrepr`` required but missing") + docformat = sphinxify(self.object_inspect(oname)) + else: + docformat = None if info.found or hasattr(info.parent, oinspect.HOOK_NAME): pmethod = getattr(self.inspector, meth) # TODO: only apply format_screen to the plain/text repr of the mime diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/core/magics/basic.py new/ipython-8.13.1/IPython/core/magics/basic.py --- old/ipython-8.12.0/IPython/core/magics/basic.py 2023-02-27 12:34:34.000000000 +0100 +++ new/ipython-8.13.1/IPython/core/magics/basic.py 2023-04-28 11:49:16.000000000 +0200 @@ -296,11 +296,11 @@ oname = args and args or '_' info = self.shell._ofind(oname) - if info['found']: + if info.found: if raw: - txt = str(info["obj"]) + txt = str(info.obj) else: - txt = pformat(info["obj"]) + txt = pformat(info.obj) page.page(txt) else: print('Object `%s` not found' % oname) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/core/magics/script.py new/ipython-8.13.1/IPython/core/magics/script.py --- old/ipython-8.12.0/IPython/core/magics/script.py 2023-02-13 16:00:29.000000000 +0100 +++ new/ipython-8.13.1/IPython/core/magics/script.py 2023-04-28 14:12:06.000000000 +0200 @@ -4,6 +4,7 @@ # Distributed under the terms of the Modified BSD License. import asyncio +import asyncio.exceptions import atexit import errno import os @@ -208,15 +209,23 @@ """Call a coroutine on the asyncio thread""" return asyncio.run_coroutine_threadsafe(coro, event_loop).result() + async def _readchunk(stream): + try: + return await stream.readuntil(b"\n") + except asyncio.exceptions.IncompleteReadError as e: + return e.partial + except asyncio.exceptions.LimitOverrunError as e: + return await stream.read(e.consumed) + async def _handle_stream(stream, stream_arg, file_object): while True: - line = (await stream.readline()).decode("utf8", errors="replace") - if not line: + chunk = (await _readchunk(stream)).decode("utf8", errors="replace") + if not chunk: break if stream_arg: - self.shell.user_ns[stream_arg] = line + self.shell.user_ns[stream_arg] = chunk else: - file_object.write(line) + file_object.write(chunk) file_object.flush() async def _stream_communicate(process, cell): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/core/oinspect.py new/ipython-8.13.1/IPython/core/oinspect.py --- old/ipython-8.12.0/IPython/core/oinspect.py 2023-03-30 10:39:40.000000000 +0200 +++ new/ipython-8.13.1/IPython/core/oinspect.py 2023-04-28 11:49:16.000000000 +0200 @@ -69,6 +69,23 @@ parent: Any obj: Any + def get(self, field): + """Get a field from the object for backward compatibility with before 8.12 + + see https://github.com/h5py/h5py/issues/2253 + """ + # We need to deprecate this at some point, but the warning will show in completion. + # Let's comment this for now and uncomment end of 2023 ish + # warnings.warn( + # f"OInfo dataclass with fields access since IPython 8.12 please use OInfo.{field} instead." + # "OInfo used to be a dict but a dataclass provide static fields verification with mypy." + # "This warning and backward compatibility `get()` method were added in 8.13.", + # DeprecationWarning, + # stacklevel=2, + # ) + return getattr(self, field) + + def pylight(code): return highlight(code, PythonLexer(), HtmlFormatter(noclasses=True)) @@ -835,7 +852,7 @@ att_name = oname.split(".")[-1] parents_docs = None prelude = "" - if info and info.parent and hasattr(info.parent, HOOK_NAME): + if info and info.parent is not None and hasattr(info.parent, HOOK_NAME): parents_docs_dict = getattr(info.parent, HOOK_NAME) parents_docs = parents_docs_dict.get(att_name, None) out = dict( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/core/release.py new/ipython-8.13.1/IPython/core/release.py --- old/ipython-8.12.0/IPython/core/release.py 2023-03-30 13:11:47.000000000 +0200 +++ new/ipython-8.13.1/IPython/core/release.py 2023-04-29 11:58:27.000000000 +0200 @@ -16,8 +16,8 @@ # release. 'dev' as a _version_extra string means this is a development # version _version_major = 8 -_version_minor = 12 -_version_patch = 0 +_version_minor = 13 +_version_patch = 1 _version_extra = ".dev" # _version_extra = "rc1" _version_extra = "" # Uncomment this for full releases diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/core/tests/test_async_helpers.py new/ipython-8.13.1/IPython/core/tests/test_async_helpers.py --- old/ipython-8.12.0/IPython/core/tests/test_async_helpers.py 2023-02-13 15:14:08.000000000 +0100 +++ new/ipython-8.13.1/IPython/core/tests/test_async_helpers.py 2023-04-28 14:12:06.000000000 +0200 @@ -276,12 +276,12 @@ """ ) - if sys.version_info < (3, 9) and platform.python_implementation() != "PyPy": - # new pgen parser in 3.9 does not raise MemoryError on too many nested - # parens anymore - def test_memory_error(self): - with self.assertRaises(MemoryError): - iprc("(" * 200 + ")" * 200) + def test_memory_error(self): + """ + The pgen parser in 3.8 or before use to raise MemoryError on too many + nested parens anymore""" + + iprc("(" * 200 + ")" * 200) @skip_without('curio') def test_autoawait_curio(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/core/tests/test_guarded_eval.py new/ipython-8.13.1/IPython/core/tests/test_guarded_eval.py --- old/ipython-8.12.0/IPython/core/tests/test_guarded_eval.py 2023-02-13 16:00:29.000000000 +0100 +++ new/ipython-8.13.1/IPython/core/tests/test_guarded_eval.py 2023-04-28 14:12:06.000000000 +0200 @@ -568,3 +568,15 @@ pass assert A.__getitem__ == B.__getitem__ + + +@dec.skip_without("numpy") +def test_module_access(): + import numpy + + context = limited(numpy=numpy) + assert guarded_eval("numpy.linalg.norm", context) == numpy.linalg.norm + + context = minimal(numpy=numpy) + with pytest.raises(GuardRejection): + guarded_eval("np.linalg.norm", context) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/core/tests/test_interactiveshell.py new/ipython-8.13.1/IPython/core/tests/test_interactiveshell.py --- old/ipython-8.12.0/IPython/core/tests/test_interactiveshell.py 2023-03-24 11:09:08.000000000 +0100 +++ new/ipython-8.13.1/IPython/core/tests/test_interactiveshell.py 2023-04-28 14:12:06.000000000 +0200 @@ -713,12 +713,10 @@ class Negator(ast.NodeTransformer): """Negates all number literals in an AST.""" - # for python 3.7 and earlier def visit_Num(self, node): node.n = -node.n return node - # for python 3.8+ def visit_Constant(self, node): if isinstance(node.value, int): return self.visit_Num(node) @@ -900,7 +898,6 @@ not be executed by throwing an InputRejected. """ - # 3.8 only def visit_Constant(self, node): if isinstance(node.value, str): raise InputRejected("test") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/core/tests/test_iplib.py new/ipython-8.13.1/IPython/core/tests/test_iplib.py --- old/ipython-8.12.0/IPython/core/tests/test_iplib.py 2023-03-13 13:54:15.000000000 +0100 +++ new/ipython-8.13.1/IPython/core/tests/test_iplib.py 2023-04-28 14:12:06.000000000 +0200 @@ -166,93 +166,48 @@ """ -if sys.version_info >= (3, 9): - if SV_VERSION < (0, 6): +if SV_VERSION < (0, 6): + + def doctest_tb_sysexit_verbose_stack_data_05(): + """ + In [18]: %run simpleerr.py exit + An exception has occurred, use %tb to see the full traceback. + SystemExit: (1, 'Mode = exit') + + In [19]: %run simpleerr.py exit 2 + An exception has occurred, use %tb to see the full traceback. + SystemExit: (2, 'Mode = exit') + + In [23]: %xmode verbose + Exception reporting mode: Verbose + + In [24]: %tb + --------------------------------------------------------------------------- + SystemExit Traceback (most recent call last) + <BLANKLINE> + ... + 30 except IndexError: + 31 mode = 'div' + ---> 33 bar(mode) + mode = 'exit' + <BLANKLINE> + ... in bar(mode='exit') + ... except: + ... stat = 1 + ---> ... sysexit(stat, mode) + mode = 'exit' + stat = 2 + ... else: + ... raise ValueError('Unknown mode') + <BLANKLINE> + ... in sysexit(stat=2, mode='exit') + 10 def sysexit(stat, mode): + ---> 11 raise SystemExit(stat, f"Mode = {mode}") + stat = 2 + <BLANKLINE> + SystemExit: (2, 'Mode = exit') + """ - def doctest_tb_sysexit_verbose_stack_data_05(): - """ - In [18]: %run simpleerr.py exit - An exception has occurred, use %tb to see the full traceback. - SystemExit: (1, 'Mode = exit') - - In [19]: %run simpleerr.py exit 2 - An exception has occurred, use %tb to see the full traceback. - SystemExit: (2, 'Mode = exit') - - In [23]: %xmode verbose - Exception reporting mode: Verbose - - In [24]: %tb - --------------------------------------------------------------------------- - SystemExit Traceback (most recent call last) - <BLANKLINE> - ... - 30 except IndexError: - 31 mode = 'div' - ---> 33 bar(mode) - mode = 'exit' - <BLANKLINE> - ... in bar(mode='exit') - ... except: - ... stat = 1 - ---> ... sysexit(stat, mode) - mode = 'exit' - stat = 2 - ... else: - ... raise ValueError('Unknown mode') - <BLANKLINE> - ... in sysexit(stat=2, mode='exit') - 10 def sysexit(stat, mode): - ---> 11 raise SystemExit(stat, f"Mode = {mode}") - stat = 2 - <BLANKLINE> - SystemExit: (2, 'Mode = exit') - """ - - else: - # currently the only difference is - # + mode = 'exit' - - def doctest_tb_sysexit_verbose_stack_data_06(): - """ - In [18]: %run simpleerr.py exit - An exception has occurred, use %tb to see the full traceback. - SystemExit: (1, 'Mode = exit') - - In [19]: %run simpleerr.py exit 2 - An exception has occurred, use %tb to see the full traceback. - SystemExit: (2, 'Mode = exit') - - In [23]: %xmode verbose - Exception reporting mode: Verbose - - In [24]: %tb - --------------------------------------------------------------------------- - SystemExit Traceback (most recent call last) - <BLANKLINE> - ... - 30 except IndexError: - 31 mode = 'div' - ---> 33 bar(mode) - mode = 'exit' - <BLANKLINE> - ... in bar(mode='exit') - ... except: - ... stat = 1 - ---> ... sysexit(stat, mode) - mode = 'exit' - stat = 2 - ... else: - ... raise ValueError('Unknown mode') - <BLANKLINE> - ... in sysexit(stat=2, mode='exit') - 10 def sysexit(stat, mode): - ---> 11 raise SystemExit(stat, f"Mode = {mode}") - stat = 2 - mode = 'exit' - <BLANKLINE> - SystemExit: (2, 'Mode = exit') - """ def test_run_cell(): import textwrap diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/core/tests/test_oinspect.py new/ipython-8.13.1/IPython/core/tests/test_oinspect.py --- old/ipython-8.12.0/IPython/core/tests/test_oinspect.py 2023-03-30 10:39:40.000000000 +0200 +++ new/ipython-8.13.1/IPython/core/tests/test_oinspect.py 2023-04-28 14:12:06.000000000 +0200 @@ -370,6 +370,23 @@ del ip.user_ns[k] +def test_pinfo_bool_raise(): + """ + Test that bool method is not called on parent. + """ + + class RaiseBool: + attr = None + + def __bool__(self): + raise ValueError("pinfo should not access this method") + + raise_bool = RaiseBool() + + with cleanup_user_ns(raise_bool=raise_bool): + ip._inspect("pinfo", "raise_bool.attr", detail_level=0) + + def test_pinfo_getindex(): def dummy(): """ @@ -551,20 +568,12 @@ signature(long_function), long_function.__name__, ) - if sys.version_info >= (3, 9): - expected = """\ + expected = """\ long_function( a_really_long_parameter: int, and_another_long_one: bool = False, let_us_make_sure_this_is_looong: Optional[str] = None, ) -> bool\ """ - else: - expected = """\ -long_function( - a_really_long_parameter: int, - and_another_long_one: bool = False, - let_us_make_sure_this_is_looong: Union[str, NoneType] = None, -) -> bool\ -""" + assert sig == expected diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/core/tests/test_ultratb.py new/ipython-8.13.1/IPython/core/tests/test_ultratb.py --- old/ipython-8.12.0/IPython/core/tests/test_ultratb.py 2023-02-13 15:14:08.000000000 +0100 +++ new/ipython-8.13.1/IPython/core/tests/test_ultratb.py 2023-04-28 14:12:06.000000000 +0200 @@ -244,15 +244,14 @@ import sys -if sys.version_info < (3, 9) and platform.python_implementation() != "PyPy": +if platform.python_implementation() != "PyPy": """ New 3.9 Pgen Parser does not raise Memory error, except on failed malloc. """ class MemoryErrorTest(unittest.TestCase): def test_memoryerror(self): memoryerror_code = "(" * 200 + ")" * 200 - with tt.AssertPrints("MemoryError"): - ip.run_cell(memoryerror_code) + ip.run_cell(memoryerror_code) class Python3ChainedExceptionsTest(unittest.TestCase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/core/ultratb.py new/ipython-8.13.1/IPython/core/ultratb.py --- old/ipython-8.12.0/IPython/core/ultratb.py 2023-03-30 10:39:40.000000000 +0200 +++ new/ipython-8.13.1/IPython/core/ultratb.py 2023-04-28 11:49:16.000000000 +0200 @@ -89,20 +89,22 @@ #***************************************************************************** +import functools import inspect import linecache import pydoc import sys import time import traceback +import types from types import TracebackType -from typing import Tuple, List, Any, Optional +from typing import Any, List, Optional, Tuple import stack_data -from stack_data import FrameInfo as SDFrameInfo from pygments.formatters.terminal256 import Terminal256Formatter from pygments.styles import get_style_by_name +import IPython.utils.colorable as colorable # IPython's own modules from IPython import get_ipython from IPython.core import debugger @@ -113,8 +115,6 @@ from IPython.utils import py3compat from IPython.utils.terminal import get_terminal_size -import IPython.utils.colorable as colorable - # Globals # amount of space to put line numbers before verbose tracebacks INDENT_SIZE = 8 @@ -135,6 +135,54 @@ # (SyntaxErrors have to be treated specially because they have no traceback) +@functools.lru_cache() +def count_lines_in_py_file(filename: str) -> int: + """ + Given a filename, returns the number of lines in the file + if it ends with the extension ".py". Otherwise, returns 0. + """ + if not filename.endswith(".py"): + return 0 + else: + try: + with open(filename, "r") as file: + s = sum(1 for line in file) + except UnicodeError: + return 0 + return s + + """ + Given a frame object, returns the total number of lines in the file + if the filename ends with the extension ".py". Otherwise, returns 0. + """ + + +def get_line_number_of_frame(frame: types.FrameType) -> int: + """ + Given a frame object, returns the total number of lines in the file + containing the frame's code object, or the number of lines in the + frame's source code if the file is not available. + + Parameters + ---------- + frame : FrameType + The frame object whose line number is to be determined. + + Returns + ------- + int + The total number of lines in the file containing the frame's + code object, or the number of lines in the frame's source code + if the file is not available. + """ + filename = frame.f_code.co_filename + if filename is None: + print("No file....") + lines, first = inspect.getsourcelines(frame) + return first + len(lines) + return count_lines_in_py_file(filename) + + def _format_traceback_lines(lines, Colors, has_colors: bool, lvals): """ Format tracebacks lines with pointing arrow, leading numbers... @@ -194,8 +242,8 @@ """ numbers_width = INDENT_SIZE - 1 res = [] - for i, line in enumerate(lines, lnum - index): + # assert isinstance(line, str) line = py3compat.cast_unicode(line) new_line, err = _line_format(line, "str") @@ -396,7 +444,7 @@ evalue: Optional[BaseException], etb: Optional[TracebackType] = None, tb_offset: Optional[int] = None, - context=5, + number_of_lines_of_context: int = 5, ): """Return a list of traceback frames. @@ -497,7 +545,7 @@ exception = self.get_parts_of_chained_exception(evalue) - if exception and not id(exception[1]) in chained_exc_ids: + if exception and (id(exception[1]) not in chained_exc_ids): chained_exception_message = ( self.prepare_chained_exception_message(evalue.__cause__)[0] if evalue is not None @@ -509,8 +557,12 @@ chained_exceptions_tb_offset = 0 out_list = ( self.structured_traceback( - etype, evalue, (etb, chained_exc_ids), - chained_exceptions_tb_offset, context) + etype, + evalue, + (etb, chained_exc_ids), # type: ignore + chained_exceptions_tb_offset, + context, + ) + chained_exception_message + out_list) @@ -673,27 +725,41 @@ """ description: Optional[str] - filename: str - lineno: int + filename: Optional[str] + lineno: Tuple[int] + # number of context lines to use + context: Optional[int] @classmethod def _from_stack_data_FrameInfo(cls, frame_info): return cls( getattr(frame_info, "description", None), - getattr(frame_info, "filename", None), - getattr(frame_info, "lineno", None), + getattr(frame_info, "filename", None), # type: ignore[arg-type] + getattr(frame_info, "lineno", None), # type: ignore[arg-type] getattr(frame_info, "frame", None), getattr(frame_info, "code", None), sd=frame_info, + context=None, ) - def __init__(self, description, filename, lineno, frame, code, sd=None): + def __init__( + self, + description: Optional[str], + filename: str, + lineno: Tuple[int], + frame, + code, + *, + sd=None, + context=None, + ): self.description = description self.filename = filename self.lineno = lineno self.frame = frame self.code = code self._sd = sd + self.context = context # self.lines = [] if sd is None: @@ -848,7 +914,6 @@ result = f'{link}{", " if call else ""}{call}\n' if frame_info._sd is None: - assert False # fast fallback if file is too long tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal) link = tpl_link % util_path.compress_user(frame_info.filename) @@ -858,13 +923,25 @@ ).format2 first_line = frame_info.code.co_firstlineno current_line = frame_info.lineno[0] + raw_lines = frame_info.raw_lines + index = current_line - first_line + + if index >= frame_info.context: + start = max(index - frame_info.context, 0) + stop = index + frame_info.context + index = frame_info.context + else: + start = 0 + stop = index + frame_info.context + raw_lines = raw_lines[start:stop] + return "%s%s" % ( level, "".join( _simple_format_traceback_lines( current_line, - current_line - first_line, - frame_info.raw_lines, + index, + raw_lines, Colors, lvals, _line_format, @@ -942,13 +1019,13 @@ # some locals orig_etype = etype try: - etype = etype.__name__ + etype = etype.__name__ # type: ignore except AttributeError: pass 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) + head = self.prepare_header(str(etype), self.long_header) records = ( self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else [] ) @@ -1018,23 +1095,34 @@ tbs = [] while cf is not None: try: - source_file = inspect.getsourcefile(etb.tb_frame) - lines, first = inspect.getsourcelines(etb.tb_frame) + mod = inspect.getmodule(cf.tb_frame) + if mod is not None: + mod_name = mod.__name__ + root_name, *_ = mod_name.split(".") + if root_name == "IPython": + cf = cf.tb_next + continue + max_len = get_line_number_of_frame(cf.tb_frame) + except OSError: - max_len = float("-inf") - break - max_len = max(max_len, first + len(lines)) + max_len = 0 + max_len = max(max_len, max_len) tbs.append(cf) - cf = cf.tb_next + cf = getattr(cf, "tb_next", None) if max_len > FAST_THRESHOLD: FIs = [] for tb in tbs: - frame = tb.tb_frame + frame = tb.tb_frame # type: ignore lineno = (frame.f_lineno,) code = frame.f_code filename = code.co_filename - FIs.append(FrameInfo("Raw frame", filename, lineno, frame, code)) + # TODO: Here we need to use before/after/ + FIs.append( + FrameInfo( + "Raw frame", filename, lineno, frame, code, context=context + ) + ) return FIs res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:] res = [FrameInfo._from_stack_data_FrameInfo(r) for r in res] @@ -1044,7 +1132,7 @@ self, etype: type, evalue: Optional[BaseException], - etb: Optional[TracebackType], + etb: Optional[TracebackType] = None, tb_offset: Optional[int] = None, number_of_lines_of_context: int = 5, ): @@ -1115,8 +1203,8 @@ with display_trap: self.pdb.reset() # Find the right frame so we don't pop up inside ipython itself - if hasattr(self, 'tb') and self.tb is not None: - etb = self.tb + if hasattr(self, "tb") and self.tb is not None: # type: ignore[has-type] + etb = self.tb # type: ignore[has-type] else: etb = self.tb = sys.last_traceback while self.tb is not None and self.tb.tb_next is not None: @@ -1291,24 +1379,23 @@ def structured_traceback( self, - etype=None, - value=None, - tb=None, - tb_offset=None, - number_of_lines_of_context=5, + etype: type, + evalue: Optional[BaseException], + etb: Optional[TracebackType] = None, + tb_offset: Optional[int] = None, + number_of_lines_of_context: int = 5, ): - etype: type - value: BaseException # tb: TracebackType or tupleof tb types ? if etype is None: - etype, value, tb = sys.exc_info() - if isinstance(tb, tuple): + etype, evalue, etb = sys.exc_info() + if isinstance(etb, tuple): # tb is a tuple if this is a chained exception. - self.tb = tb[0] + self.tb = etb[0] else: - self.tb = tb + self.tb = etb return FormattedTB.structured_traceback( - self, etype, value, tb, tb_offset, number_of_lines_of_context) + self, etype, evalue, etb, tb_offset, number_of_lines_of_context + ) #--------------------------------------------------------------------------- @@ -1366,7 +1453,7 @@ """Hopefully pretty robust repr equivalent.""" # this is pretty horrible but should always return *something* try: - return pydoc.text.repr(value) + return pydoc.text.repr(value) # type: ignore[call-arg] except KeyboardInterrupt: raise except: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/lib/lexers.py new/ipython-8.13.1/IPython/lib/lexers.py --- old/ipython-8.12.0/IPython/lib/lexers.py 2023-02-13 15:14:08.000000000 +0100 +++ new/ipython-8.13.1/IPython/lib/lexers.py 2023-04-28 11:49:16.000000000 +0200 @@ -194,6 +194,13 @@ aliases = ['ipythontb'] def __init__(self, **options): + """ + A subclass of `DelegatingLexer` which delegates to the appropriate to either IPyLexer, + IPythonPartialTracebackLexer. + """ + # note we need a __init__ doc, as otherwise it inherits the doc from the super class + # which will fail the documentation build as it references section of the pygments docs that + # do not exists when building IPython's docs. self.python3 = get_bool_opt(options, 'python3', False) if self.python3: self.aliases = ['ipython3tb'] @@ -503,6 +510,13 @@ aliases = ['ipy'] def __init__(self, **options): + """ + Create a new IPyLexer instance which dispatch to either an + IPythonCOnsoleLexer (if In prompts are present) or and IPythonLexer (if + In prompts are not present). + """ + # init docstring is necessary for docs not to fail to build do to parent + # docs referenceing a section in pygments docs. self.python3 = get_bool_opt(options, 'python3', False) if self.python3: self.aliases = ['ipy3'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/lib/pretty.py new/ipython-8.13.1/IPython/lib/pretty.py --- old/ipython-8.12.0/IPython/lib/pretty.py 2023-02-13 15:14:08.000000000 +0100 +++ new/ipython-8.13.1/IPython/lib/pretty.py 2023-04-28 14:12:06.000000000 +0200 @@ -918,7 +918,7 @@ if cycle: p.pretty(cls_ctor(RawText("..."))) elif len(obj): - p.pretty(cls_ctor(dict(obj))) + p.pretty(cls_ctor(dict(obj.most_common()))) else: p.pretty(cls_ctor()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/lib/tests/test_pretty.py new/ipython-8.13.1/IPython/lib/tests/test_pretty.py --- old/ipython-8.12.0/IPython/lib/tests/test_pretty.py 2023-02-13 15:14:08.000000000 +0100 +++ new/ipython-8.13.1/IPython/lib/tests/test_pretty.py 2023-04-28 14:12:06.000000000 +0200 @@ -422,6 +422,7 @@ (Counter(), 'Counter()'), (Counter(a=1), "Counter({'a': 1})"), (MyCounter(a=1), "MyCounter({'a': 1})"), + (Counter(a=1, c=22), "Counter({'c': 22, 'a': 1})"), ] for obj, expected in cases: assert pretty.pretty(obj) == expected diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/terminal/shortcuts/__init__.py new/ipython-8.13.1/IPython/terminal/shortcuts/__init__.py --- old/ipython-8.12.0/IPython/terminal/shortcuts/__init__.py 2023-03-30 10:39:40.000000000 +0200 +++ new/ipython-8.13.1/IPython/terminal/shortcuts/__init__.py 2023-04-28 11:49:16.000000000 +0200 @@ -229,7 +229,7 @@ ), Binding( auto_suggest.swap_autosuggestion_up, - ["up"], + ["c-up"], "navigable_suggestions" " & ~has_line_above" " & has_suggestion" @@ -237,7 +237,7 @@ ), Binding( auto_suggest.swap_autosuggestion_down, - ["down"], + ["c-down"], "navigable_suggestions" " & ~has_line_below" " & has_suggestion" @@ -245,12 +245,12 @@ ), Binding( auto_suggest.up_and_update_hint, - ["up"], + ["c-up"], "has_line_above & navigable_suggestions & default_buffer_focused", ), Binding( auto_suggest.down_and_update_hint, - ["down"], + ["c-down"], "has_line_below & navigable_suggestions & default_buffer_focused", ), Binding( @@ -265,8 +265,8 @@ ), Binding( auto_suggest.accept_and_keep_cursor, - ["c-down"], - "has_suggestion & default_buffer_focused & emacs_like_insert_mode", + ["escape", "down"], + "has_suggestion & default_buffer_focused & emacs_insert_mode", ), Binding( auto_suggest.backspace_and_resume_hint, @@ -277,10 +277,7 @@ Binding( auto_suggest.resume_hinting, ["right"], - # For now this binding is inactive (the filter includes `never`). - # TODO: remove `never` if we reach a consensus in #13991 - # TODO: use `emacs_like_insert_mode` once #13991 is in - "never & default_buffer_focused & ((vi_insert_mode & ebivim) | emacs_insert_mode)", + "default_buffer_focused & emacs_like_insert_mode", ), ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/IPython/utils/_sysinfo.py new/ipython-8.13.1/IPython/utils/_sysinfo.py --- old/ipython-8.12.0/IPython/utils/_sysinfo.py 2023-03-30 13:11:47.000000000 +0200 +++ new/ipython-8.13.1/IPython/utils/_sysinfo.py 2023-04-29 11:58:27.000000000 +0200 @@ -1,2 +1,2 @@ # GENERATED BY setup.py -commit = "37242ba43" +commit = "5d2cb48cd" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/MANIFEST.in new/ipython-8.13.1/MANIFEST.in --- old/ipython-8.12.0/MANIFEST.in 2023-02-13 15:14:08.000000000 +0100 +++ new/ipython-8.13.1/MANIFEST.in 2023-04-28 11:49:16.000000000 +0200 @@ -3,9 +3,7 @@ include LICENSE include setupbase.py include MANIFEST.in -include pytest.ini include py.typed -include mypy.ini include .mailmap include .flake8 include .pre-commit-config.yaml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/PKG-INFO new/ipython-8.13.1/PKG-INFO --- old/ipython-8.12.0/PKG-INFO 2023-03-30 13:11:47.000000000 +0200 +++ new/ipython-8.13.1/PKG-INFO 2023-04-29 11:58:27.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: ipython -Version: 8.12.0 +Version: 8.13.1 Summary: IPython: Productive Interactive Computing Home-page: https://ipython.org Author: The IPython Development Team @@ -23,7 +23,7 @@ Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Topic :: System :: Shells -Requires-Python: >=3.8 +Requires-Python: >=3.9 Description-Content-Type: text/x-rst Provides-Extra: black Provides-Extra: doc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/docs/source/whatsnew/version8.rst new/ipython-8.13.1/docs/source/whatsnew/version8.rst --- old/ipython-8.12.0/docs/source/whatsnew/version8.rst 2023-03-30 13:11:14.000000000 +0200 +++ new/ipython-8.13.1/docs/source/whatsnew/version8.rst 2023-04-28 14:12:06.000000000 +0200 @@ -2,6 +2,59 @@ 8.x Series ============ + +.. _version 8.13: + +IPython 8.13 +------------ + +As usual for the end of the month, minor release of IPython. This release is +significant in that it not only has a number of bugfixes, but also drop support +for Python 3.8 as per NEP 29 (:ghpull:`14023`). + +All the critical bugfixes have been backported onto the 8.12.1 release (see +below). In addition to that went into 8.12.1 you'll find: + + - Pretty reprensentation for ``Counter`` has been fixed to match the Python one + and be in decreasing order. :ghpull:`14032` + - Module completion is better when jedi is disabled :ghpull:`14029`. + - Improvment of ``%%bash`` magic that would get stuck :ghpull:`14019` + + +We hope you enjoy this release an will maybe see you at JupyterCon in less than +two weeks. + +As usual you can find the full list of PRs on GitHub under `the 8.12.1 milestone +<https://github.com/ipython/ipython/milestone/115?closed=1>`__. + +Thanks to the D.E. Shaw group for the request and sponsoring the work. + + +.. _version 8.12.1: + +IPython 8.12.1 +-------------- + +This is the twin release of IPython 8.13 that contain only critical UI and bug +fixes. The next minor version of IPython has dropped support for Python 3.8 â as +per Nep 29 and this IPython 8.12.x will now only receive bugfixes. + + + - :ghpull:`14004` Fix a bug introduced in IPython 8.12 that crash when + inspecting some docstrings. + - :ghpull:`14010` Fix fast traceback code that was not working in some case. + - :ghpull:`14014` Fix ``%page`` magic broken in some case. + - :ghpull:`14026`, :ghpull:`14027` Tweak default shortcut with respect to + autosuggestions. + - :ghpull:`14033` add back the ability to use ``.get()`` on OInfo object for + backward compatibility with h5py (this will be re-deprecated later, and h5py + will also get a fix). + +As usual you can find the full list of PRs on GitHub under `the 8.12.1 milestone +<https://github.com/ipython/ipython/milestone/116?closed=1>`__. + +Thanks to the D.E. Shaw group for the request and sponsoring the work. + .. _version 8.12.0: IPython 8.12 @@ -13,7 +66,7 @@ A number of PRs and bug fixes this month with close to 20 PRs merged ! -The IPython repo reached :ghpull:``14000`` !! Actually the PR that create those exact release +The IPython repo reached :ghpull:`14000` !! Actually the PR that create those exact release note is :ghpull:`14000`. Ok, more issues and PR is not always better, and I'd love to have more time to close issues and Pull Requests. @@ -21,8 +74,6 @@ `jupytercon.com <https://jupytercon.com>`__, and looking forward to see you there. - - Packagers should take note that ``typing_extension`` is now a mandatory dependency for Python versions ``<3.10``. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/ipython.egg-info/PKG-INFO new/ipython-8.13.1/ipython.egg-info/PKG-INFO --- old/ipython-8.12.0/ipython.egg-info/PKG-INFO 2023-03-30 13:11:47.000000000 +0200 +++ new/ipython-8.13.1/ipython.egg-info/PKG-INFO 2023-04-29 11:58:27.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: ipython -Version: 8.12.0 +Version: 8.13.1 Summary: IPython: Productive Interactive Computing Home-page: https://ipython.org Author: The IPython Development Team @@ -23,7 +23,7 @@ Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Topic :: System :: Shells -Requires-Python: >=3.8 +Requires-Python: >=3.9 Description-Content-Type: text/x-rst Provides-Extra: black Provides-Extra: doc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/ipython.egg-info/SOURCES.txt new/ipython-8.13.1/ipython.egg-info/SOURCES.txt --- old/ipython-8.12.0/ipython.egg-info/SOURCES.txt 2023-03-30 13:11:47.000000000 +0200 +++ new/ipython-8.13.1/ipython.egg-info/SOURCES.txt 2023-04-29 11:58:27.000000000 +0200 @@ -7,7 +7,6 @@ README.rst long_description.rst pyproject.toml -pytest.ini setup.cfg setup.py setupbase.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/pyproject.toml new/ipython-8.13.1/pyproject.toml --- old/ipython-8.12.0/pyproject.toml 2023-03-14 11:07:41.000000000 +0100 +++ new/ipython-8.13.1/pyproject.toml 2023-04-28 14:12:06.000000000 +0200 @@ -1,8 +1,9 @@ [build-system] requires = ["setuptools >= 51.0.0"] build-backend = "setuptools.build_meta" + [tool.mypy] -python_version = 3.8 +python_version = 3.9 ignore_missing_imports = true follow_imports = 'silent' exclude = [ @@ -19,7 +20,7 @@ #'IPython/core/interactiveshell.py', 'IPython/core/magic.py', 'IPython/core/profileapp.py', - 'IPython/core/ultratb.py', + # 'IPython/core/ultratb.py', 'IPython/lib/deepreload.py', 'IPython/lib/pretty.py', 'IPython/sphinxext/ipython_directive.py', @@ -28,5 +29,53 @@ 'IPython/utils/path.py', 'IPython/utils/timing.py', 'IPython/utils/text.py' - ] +] +[tool.pytest.ini_options] +addopts = [ + "--durations=10", + "-pIPython.testing.plugin.pytest_ipdoctest", + "--ipdoctest-modules", + "--ignore=docs", + "--ignore=examples", + "--ignore=htmlcov", + "--ignore=ipython_kernel", + "--ignore=ipython_parallel", + "--ignore=results", + "--ignore=tmp", + "--ignore=tools", + "--ignore=traitlets", + "--ignore=IPython/core/tests/daft_extension", + "--ignore=IPython/sphinxext", + "--ignore=IPython/terminal/pt_inputhooks", + "--ignore=IPython/__main__.py", + "--ignore=IPython/external/qt_for_kernel.py", + "--ignore=IPython/html/widgets/widget_link.py", + "--ignore=IPython/html/widgets/widget_output.py", + "--ignore=IPython/terminal/console.py", + "--ignore=IPython/utils/_process_cli.py", + "--ignore=IPython/utils/_process_posix.py", + "--ignore=IPython/utils/_process_win32.py", + "--ignore=IPython/utils/_process_win32_controller.py", + "--ignore=IPython/utils/daemonize.py", + "--ignore=IPython/utils/eventful.py", + "--ignore=IPython/kernel", + "--ignore=IPython/consoleapp.py", + "--ignore=IPython/core/inputsplitter.py", + "--ignore=IPython/lib/kernel.py", + "--ignore=IPython/utils/jsonutil.py", + "--ignore=IPython/utils/localinterfaces.py", + "--ignore=IPython/utils/log.py", + "--ignore=IPython/utils/signatures.py", + "--ignore=IPython/utils/traitlets.py", + "--ignore=IPython/utils/version.py" +] +doctest_optionflags = [ + "NORMALIZE_WHITESPACE", + "ELLIPSIS" +] +ipdoctest_optionflags = [ + "NORMALIZE_WHITESPACE", + "ELLIPSIS" +] +asyncio_mode = "strict" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/pytest.ini new/ipython-8.13.1/pytest.ini --- old/ipython-8.12.0/pytest.ini 2023-02-13 16:00:29.000000000 +0100 +++ new/ipython-8.13.1/pytest.ini 1970-01-01 01:00:00.000000000 +0100 @@ -1,40 +0,0 @@ -[pytest] -addopts = --durations=10 - -p IPython.testing.plugin.pytest_ipdoctest --ipdoctest-modules - --ignore=docs - --ignore=examples - --ignore=htmlcov - --ignore=ipython_kernel - --ignore=ipython_parallel - --ignore=results - --ignore=tmp - --ignore=tools - --ignore=traitlets - --ignore=IPython/core/tests/daft_extension - --ignore=IPython/sphinxext - --ignore=IPython/terminal/pt_inputhooks - --ignore=IPython/__main__.py - --ignore=IPython/external/qt_for_kernel.py - --ignore=IPython/html/widgets/widget_link.py - --ignore=IPython/html/widgets/widget_output.py - --ignore=IPython/terminal/console.py - --ignore=IPython/utils/_process_cli.py - --ignore=IPython/utils/_process_posix.py - --ignore=IPython/utils/_process_win32.py - --ignore=IPython/utils/_process_win32_controller.py - --ignore=IPython/utils/daemonize.py - --ignore=IPython/utils/eventful.py - - --ignore=IPython/kernel - --ignore=IPython/consoleapp.py - --ignore=IPython/core/inputsplitter.py - --ignore=IPython/lib/kernel.py - --ignore=IPython/utils/jsonutil.py - --ignore=IPython/utils/localinterfaces.py - --ignore=IPython/utils/log.py - --ignore=IPython/utils/signatures.py - --ignore=IPython/utils/traitlets.py - --ignore=IPython/utils/version.py -doctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS -ipdoctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS -asyncio_mode = strict diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/setup.cfg new/ipython-8.13.1/setup.cfg --- old/ipython-8.12.0/setup.cfg 2023-03-30 13:11:47.000000000 +0200 +++ new/ipython-8.13.1/setup.cfg 2023-04-29 11:58:27.000000000 +0200 @@ -26,7 +26,7 @@ [options] packages = find: -python_requires = >=3.8 +python_requires = >=3.9 zip_safe = False install_requires = appnope; sys_platform == "darwin" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ipython-8.12.0/setup.py new/ipython-8.13.1/setup.py --- old/ipython-8.12.0/setup.py 2023-02-13 16:00:29.000000000 +0100 +++ new/ipython-8.13.1/setup.py 2023-04-28 14:12:06.000000000 +0200 @@ -23,7 +23,7 @@ # # This check is also made in IPython/__init__, don't forget to update both when # changing Python version requirements. -if sys.version_info < (3, 8): +if sys.version_info < (3, 9): pip_message = 'This may be due to an out of date pip. Make sure you have pip >= 9.0.1.' try: import pip @@ -39,7 +39,8 @@ error = """ -IPython 8+ supports Python 3.8 and above, following NEP 29. +IPython 8.13+ supports Python 3.8 and above, following NEP 29. +IPython 8.0-8.12 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. Python 3.5 was supported with IPython 7.0 to 7.9.