diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 371a3da..7587a1d 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -16,7 +16,6 @@ import ast import atexit import bdb import builtins as builtin_mod -import dis import functools import inspect import os @@ -3212,29 +3211,6 @@ class InteractiveShell(SingletonConfigurable): ast.fix_missing_locations(node) return node - def _update_code_co_name(self, code): - """Python 3.10 changed the behaviour so that whenever a code object - is assembled in the compile(ast) the co_firstlineno would be == 1. - - This makes pydevd/debugpy think that all cells invoked are the same - since it caches information based on (co_firstlineno, co_name, co_filename). - - Given that, this function changes the code 'co_name' to be unique - based on the first real lineno of the code (which also has a nice - side effect of customizing the name so that it's not always ). - - See: https://github.com/ipython/ipykernel/issues/841 - """ - if not hasattr(code, "replace"): - # It may not be available on older versions of Python (only - # available for 3.8 onwards). - return code - try: - first_real_line = next(dis.findlinestarts(code))[1] - except StopIteration: - return code - return code.replace(co_name="" % (first_real_line,)) - async def run_ast_nodes( self, nodelist: ListType[stmt], @@ -3333,7 +3309,6 @@ class InteractiveShell(SingletonConfigurable): else 0x0 ): code = compiler(mod, cell_name, mode) - code = self._update_code_co_name(code) asy = compare(code) if await self.run_code(code, result, async_=asy): return True diff --git a/IPython/core/tests/test_iplib.py b/IPython/core/tests/test_iplib.py index ec7007e..14474bb 100644 --- a/IPython/core/tests/test_iplib.py +++ b/IPython/core/tests/test_iplib.py @@ -45,7 +45,7 @@ def doctest_tb_plain(): In [19]: run simpleerr.py Traceback (most recent call last): - File ...:... in + File ...:... bar(mode) File ...:... in bar div0() @@ -64,7 +64,7 @@ def doctest_tb_context(): --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) - ... in + ... 30 except IndexError: 31 mode = 'div' ---> 33 bar(mode) @@ -93,7 +93,7 @@ def doctest_tb_verbose(): --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) - ... in + ... 30 except IndexError: 31 mode = 'div' ---> 33 bar(mode) @@ -134,7 +134,7 @@ def doctest_tb_sysexit(): Traceback (most recent call last): File ...:... in execfile exec(compiler(f.read(), fname, "exec"), glob, loc) - File ...:... in + File ...:... bar(mode) File ...:... in bar sysexit(stat, mode) @@ -152,7 +152,7 @@ def doctest_tb_sysexit(): ... with open(fname, "rb") as f: ... compiler = compiler or compile ---> ... exec(compiler(f.read(), fname, "exec"), glob, loc) - ... + ... 30 except IndexError: 31 mode = 'div' ---> 33 bar(mode) @@ -189,7 +189,7 @@ def doctest_tb_sysexit_verbose(): --------------------------------------------------------------------------- SystemExit Traceback (most recent call last) - ... in + ... 30 except IndexError: 31 mode = 'div' ---> 33 bar(mode) diff --git a/IPython/core/ultratb.py b/IPython/core/ultratb.py index 5649a78..125ee9a 100644 --- a/IPython/core/ultratb.py +++ b/IPython/core/ultratb.py @@ -187,7 +187,10 @@ def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None): if ipinst is not None and file in ipinst.compile._filename_map: file = "[%s]" % ipinst.compile._filename_map[file] - tpl_link = f"Input {ColorFilename}In {{file}}{ColorNormal}" + if lineno is None: + tpl_link = f"Cell {ColorFilename}In {{file}}{ColorNormal}" + else: + tpl_link = f"Cell {ColorFilename}In {{file}}, line {{lineno}}{ColorNormal}" else: file = util_path.compress_user( py3compat.cast_unicode(file, util_path.fs_encoding) @@ -463,34 +466,25 @@ class ListTB(TBTools): Colors = self.Colors list = [] - for filename, lineno, name, line in extracted_list[:-1]: - item = " %s in %s%s%s\n" % ( - _format_filename( - filename, Colors.filename, Colors.Normal, lineno=lineno - ), - Colors.name, - name, - Colors.Normal, + for ind, (filename, lineno, name, line) in enumerate(extracted_list): + normalCol, nameCol, fileCol, lineCol = ( + # Emphasize the last entry + (Colors.normalEm, Colors.nameEm, Colors.filenameEm, Colors.line) + if ind == len(extracted_list) - 1 + else (Colors.Normal, Colors.name, Colors.filename, "") ) + + fns = _format_filename(filename, fileCol, normalCol, lineno=lineno) + item = f"{normalCol} {fns}" + + if name != "": + item += f" in {nameCol}{name}{normalCol}\n" + else: + item += "\n" if line: - item += ' %s\n' % line.strip() + item += f"{lineCol} {line.strip()}{normalCol}\n" list.append(item) - # Emphasize the last entry - filename, lineno, name, line = extracted_list[-1] - item = "%s %s in %s%s%s%s\n" % ( - Colors.normalEm, - _format_filename( - filename, Colors.filenameEm, Colors.normalEm, lineno=lineno - ), - Colors.nameEm, - name, - Colors.normalEm, - Colors.Normal, - ) - if line: - item += '%s %s%s\n' % (Colors.line, line.strip(), - Colors.Normal) - list.append(item) + return list def _format_exception_only(self, etype, value): @@ -687,7 +681,7 @@ class VerboseTB(TBTools): func = frame_info.executing.code_qualname() if func == "": - call = tpl_call.format(file=func, scope="") + call = "" else: # Decide whether to include variable details or not var_repr = eqrepr if self.include_vars else nullrepr @@ -731,7 +725,7 @@ class VerboseTB(TBTools): if lvals_list: lvals = '%s%s' % (indent, em_normal.join(lvals_list)) - result = "%s, %s\n" % (link, call) + result = f'{link}{", " if call else ""}{call}\n' result += ''.join(_format_traceback_lines(frame_info.lines, Colors, self.has_colors, lvals)) return result diff --git a/docs/source/whatsnew/pr/restore-line-numbers.rst b/docs/source/whatsnew/pr/restore-line-numbers.rst new file mode 100644 index 0000000..fb07292 --- /dev/null +++ b/docs/source/whatsnew/pr/restore-line-numbers.rst @@ -0,0 +1,50 @@ +Restore line numbers for Input +================================== + +Line number information in tracebacks from input are restored. +Line numbers from input were removed during the transition to v8 enhanced traceback reporting. + +So, instead of:: + + --------------------------------------------------------------------------- + ZeroDivisionError Traceback (most recent call last) + Input In [3], in () + ----> 1 myfunc(2) + + Input In [2], in myfunc(z) + 1 def myfunc(z): + ----> 2 foo.boo(z-1) + + File ~/code/python/ipython/foo.py:3, in boo(x) + 2 def boo(x): + ----> 3 return 1/(1-x) + + ZeroDivisionError: division by zero + +The error traceback now looks like:: + + --------------------------------------------------------------------------- + ZeroDivisionError Traceback (most recent call last) + Cell In [3], line 1 + ----> 1 myfunc(2) + + Cell In [2], line 2, in myfunc(z) + 1 def myfunc(z): + ----> 2 foo.boo(z-1) + + File ~/code/python/ipython/foo.py:3, in boo(x) + 2 def boo(x): + ----> 3 return 1/(1-x) + + ZeroDivisionError: division by zero + +or, with xmode=Plain:: + + Traceback (most recent call last): + Cell In [12], line 1 + myfunc(2) + Cell In [6], line 2 in myfunc + foo.boo(z-1) + File ~/code/python/ipython/foo.py:3 in boo + return 1/(1-x) + ZeroDivisionError: division by zero