diff --git a/Lib/_pyrepl/console.py b/Lib/_pyrepl/console.py index 0d78890b4f45d5..df2b4222640829 100644 --- a/Lib/_pyrepl/console.py +++ b/Lib/_pyrepl/console.py @@ -24,6 +24,7 @@ from abc import ABC, abstractmethod import ast import code +import linecache from dataclasses import dataclass, field import os.path import sys @@ -193,6 +194,7 @@ def runsource(self, source, filename="", symbol="single"): item = wrapper([stmt]) try: code = self.compile.compiler(item, filename, the_symbol) + linecache._register_code(code, source, filename) except SyntaxError as e: if e.args[0] == "'await' outside function": python = os.path.basename(sys.executable) diff --git a/Lib/_pyrepl/simple_interact.py b/Lib/_pyrepl/simple_interact.py index a065174ad42fb6..6e46d7a3016751 100644 --- a/Lib/_pyrepl/simple_interact.py +++ b/Lib/_pyrepl/simple_interact.py @@ -26,7 +26,6 @@ from __future__ import annotations import _sitebuiltins -import linecache import functools import os import sys @@ -148,7 +147,6 @@ def maybe_run_command(statement: str) -> bool: continue input_name = f"" - linecache._register_code(input_name, statement, "") # type: ignore[attr-defined] more = console.push(_strip_final_indent(statement), filename=input_name, _symbol="single") # type: ignore[call-arg] assert not more input_n += 1 diff --git a/Lib/inspect.py b/Lib/inspect.py index facad478103668..7d20ff256b73c3 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -968,6 +968,8 @@ def findsource(object): module = getmodule(object, file) if module: lines = linecache.getlines(file, module.__dict__) + if not lines and file.startswith('<') and hasattr(object, "__code__"): + lines = linecache._getlines_from_code(object.__code__) else: lines = linecache.getlines(file) if not lines: diff --git a/Lib/linecache.py b/Lib/linecache.py index 8ba2df73d5a8fb..5d738ce520234f 100644 --- a/Lib/linecache.py +++ b/Lib/linecache.py @@ -11,6 +11,7 @@ # The cache. Maps filenames to either a thunk which will provide source code, # or a tuple (size, mtime, lines, fullname) once loaded. cache = {} +_interactive_cache = {} def clearcache(): @@ -44,6 +45,22 @@ def getlines(filename, module_globals=None): return [] +def _getline_from_code(filename, lineno): + lines = _getlines_from_code(filename) + if 1 <= lineno <= len(lines): + return lines[lineno - 1] + return '' + + +def _getlines_from_code(code): + code_id = id(code) + if code_id in _interactive_cache: + entry = _interactive_cache[code_id] + if len(entry) != 1: + return _interactive_cache[code_id][2] + return [] + + def checkcache(filename=None): """Discard cache entries that are out of date. (This is not checked upon each call!)""" @@ -88,9 +105,13 @@ def updatecache(filename, module_globals=None): # These imports are not at top level because linecache is in the critical # path of the interpreter startup and importing os and sys take a lot of time # and slows down the startup sequence. - import os - import sys - import tokenize + try: + import os + import sys + import tokenize + except ImportError: + # These import can fail if the interpreter is shutting down + return [] if filename in cache: if len(cache[filename]) != 1: @@ -196,8 +217,14 @@ def get_lines(name=name, *args, **kwargs): def _register_code(code, string, name): - cache[code] = ( - len(string), - None, - [line + '\n' for line in string.splitlines()], - name) + entry = (len(string), + None, + [line + '\n' for line in string.splitlines()], + name) + stack = [code] + while stack: + code = stack.pop() + for const in code.co_consts: + if isinstance(const, type(code)): + stack.append(const) + _interactive_cache[id(code)] = entry diff --git a/Lib/test/test_capi/test_exceptions.py b/Lib/test/test_capi/test_exceptions.py index 666e2f2ab09548..ade55338e63b69 100644 --- a/Lib/test/test_capi/test_exceptions.py +++ b/Lib/test/test_capi/test_exceptions.py @@ -85,9 +85,7 @@ def foo(): warnings = proc.err.splitlines() self.assertEqual(warnings, [ b':6: RuntimeWarning: Testing PyErr_WarnEx', - b' foo() # line 6', b':9: RuntimeWarning: Testing PyErr_WarnEx', - b' foo() # line 9', ]) def test_warn_during_finalization(self): diff --git a/Lib/test/test_gdb/gdb_sample.py b/Lib/test/test_gdb/gdb_sample.py index a7f23db73ea6e6..375d8987fa80c3 100644 --- a/Lib/test/test_gdb/gdb_sample.py +++ b/Lib/test/test_gdb/gdb_sample.py @@ -1,4 +1,5 @@ # Sample script for use by test_gdb +from _typing import _idfunc def foo(a, b, c): bar(a=a, b=b, c=c) @@ -7,6 +8,6 @@ def bar(a, b, c): baz(a, b, c) def baz(*args): - id(42) + _idfunc(42) foo(1, 2, 3) diff --git a/Lib/test/test_gdb/test_backtrace.py b/Lib/test/test_gdb/test_backtrace.py index 714853c7b4732d..656f0c125313aa 100644 --- a/Lib/test/test_gdb/test_backtrace.py +++ b/Lib/test/test_gdb/test_backtrace.py @@ -20,14 +20,14 @@ def test_bt(self): self.assertMultilineMatches(bt, r'''^.* Traceback \(most recent call first\): - - File ".*gdb_sample.py", line 10, in baz - id\(42\) - File ".*gdb_sample.py", line 7, in bar + + File ".*gdb_sample.py", line 11, in baz + _idfunc\(42\) + File ".*gdb_sample.py", line 8, in bar baz\(a, b, c\) - File ".*gdb_sample.py", line 4, in foo + File ".*gdb_sample.py", line 5, in foo bar\(a=a, b=b, c=c\) - File ".*gdb_sample.py", line 12, in + File ".*gdb_sample.py", line 13, in foo\(1, 2, 3\) ''') @@ -39,11 +39,11 @@ def test_bt_full(self): cmds_after_breakpoint=['py-bt-full']) self.assertMultilineMatches(bt, r'''^.* -#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 8, in bar \(a=1, b=2, c=3\) baz\(a, b, c\) -#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\) +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 5, in foo \(a=1, b=2, c=3\) bar\(a=a, b=b, c=c\) -#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in \(\) +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 13, in \(\) foo\(1, 2, 3\) ''') @@ -55,6 +55,7 @@ def test_threads(self): 'Verify that "py-bt" indicates threads that are waiting for the GIL' cmd = ''' from threading import Thread +from _typing import _idfunc class TestThread(Thread): # These threads would run forever, but we'll interrupt things with the @@ -70,7 +71,7 @@ def run(self): t[i].start() # Trigger a breakpoint on the main thread -id(42) +_idfunc(42) ''' # Verify with "py-bt": @@ -90,8 +91,8 @@ def run(self): # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround def test_gc(self): 'Verify that "py-bt" indicates if a thread is garbage-collecting' - cmd = ('from gc import collect\n' - 'id(42)\n' + cmd = ('from gc import collect; from _typing import _idfunc\n' + '_idfunc(42)\n' 'def foo():\n' ' collect()\n' 'def bar():\n' @@ -113,11 +114,12 @@ def test_gc(self): "Python was compiled with optimizations") def test_wrapper_call(self): cmd = textwrap.dedent(''' + from typing import _idfunc class MyList(list): def __init__(self): super(*[]).__init__() # wrapper_call() - id("first break point") + _idfunc("first break point") l = MyList() ''') cmds_after_breakpoint = ['break wrapper_call', 'continue'] diff --git a/Lib/test/test_gdb/test_misc.py b/Lib/test/test_gdb/test_misc.py index 1047f4867c1d03..b3dd24777cf1fb 100644 --- a/Lib/test/test_gdb/test_misc.py +++ b/Lib/test/test_gdb/test_misc.py @@ -35,14 +35,14 @@ def test_basic_command(self): bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-list']) - self.assertListing(' 5 \n' - ' 6 def bar(a, b, c):\n' - ' 7 baz(a, b, c)\n' - ' 8 \n' - ' 9 def baz(*args):\n' - ' >10 id(42)\n' - ' 11 \n' - ' 12 foo(1, 2, 3)\n', + self.assertListing(' 6 \n' + ' 7 def bar(a, b, c):\n' + ' 8 baz(a, b, c)\n' + ' 9 \n' + ' 10 def baz(*args):\n' + ' >11 _idfunc(42)\n' + ' 12 \n' + ' 13 foo(1, 2, 3)\n', bt) def test_one_abs_arg(self): @@ -50,25 +50,27 @@ def test_one_abs_arg(self): bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-list 9']) - self.assertListing(' 9 def baz(*args):\n' - ' >10 id(42)\n' - ' 11 \n' - ' 12 foo(1, 2, 3)\n', + self.assertListing(' 10 def baz(*args):\n' + ' >11 _idfunc(42)\n' + ' 12 \n' + ' 13 foo(1, 2, 3)\n', bt) def test_two_abs_args(self): 'Verify the "py-list" command with two absolute arguments' bt = self.get_stack_trace(script=SAMPLE_SCRIPT, - cmds_after_breakpoint=['py-list 1,3']) + cmds_after_breakpoint=['py-list 1,4']) self.assertListing(' 1 # Sample script for use by test_gdb\n' - ' 2 \n' - ' 3 def foo(a, b, c):\n', + ' 2 from _typing import _idfunc\n' + ' 3 \n' + ' 4 def foo(a, b, c):\n', bt) SAMPLE_WITH_C_CALL = """ from _testcapi import pyobject_vectorcall +from _typing import _idfunc def foo(a, b, c): bar(a, b, c) @@ -77,7 +79,7 @@ def bar(a, b, c): pyobject_vectorcall(baz, (a, b, c), None) def baz(*args): - id(42) + _idfunc(42) foo(1, 2, 3) @@ -94,7 +96,7 @@ def test_pyup_command(self): cmds_after_breakpoint=['py-up', 'py-up']) self.assertMultilineMatches(bt, r'''^.* -#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) +#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 13, in baz \(args=\(1, 2, 3\)\) #[0-9]+ $''') @@ -123,9 +125,9 @@ def test_up_then_down(self): cmds_after_breakpoint=['py-up', 'py-up', 'py-down']) self.assertMultilineMatches(bt, r'''^.* -#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) +#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 13, in baz \(args=\(1, 2, 3\)\) #[0-9]+ -#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) +#[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 13, in baz \(args=\(1, 2, 3\)\) $''') class PyPrintTests(DebuggerTests): diff --git a/Lib/test/test_gdb/test_pretty_print.py b/Lib/test/test_gdb/test_pretty_print.py index dfc77d65ab16a4..0ea714cea27674 100644 --- a/Lib/test/test_gdb/test_pretty_print.py +++ b/Lib/test/test_gdb/test_pretty_print.py @@ -17,7 +17,7 @@ def get_gdb_repr(self, source, import_site=False): # Given an input python source representation of data, # run "python -c'id(DATA)'" under gdb with a breakpoint on - # builtin_id and scrape out gdb's representation of the "op" + # _typing__idfunc and scrape out gdb's representation of the "op" # parameter, and verify that the gdb displays the same string # # Verify that the gdb displays the expected string @@ -29,6 +29,7 @@ def get_gdb_repr(self, source, # undecodable characters may lurk there in optimized mode # (issue #19743). cmds_after_breakpoint = cmds_after_breakpoint or ["backtrace 1"] + source = "from _typing import _idfunc\n" + source gdb_output = self.get_stack_trace(source, breakpoint=BREAKPOINT_FN, cmds_after_breakpoint=cmds_after_breakpoint, import_site=import_site) @@ -36,10 +37,10 @@ def get_gdb_repr(self, source, # in its output, depending on the width of the terminal it's connected # to (using its "wrap_here" function) m = re.search( - # Match '#0 builtin_id(self=..., v=...)' - r'#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)?\)' + # Match '#0 _typing_idfunc(module=..., x=...)' + r'#0\s+_typing__idfunc\s+\(module\=.*,\s+x=\s*(.*?)?\)' # Match ' at Python/bltinmodule.c'. - # bpo-38239: builtin_id() is defined in Python/bltinmodule.c, + # bpo-38239: typing_idfunc() is defined in Module/_typingmldule.c, # but accept any "Directory\file.c" to support Link Time # Optimization (LTO). r'\s+at\s+\S*[A-Za-z]+/[A-Za-z0-9_-]+\.c', @@ -49,13 +50,13 @@ def get_gdb_repr(self, source, return m.group(1), gdb_output def test_getting_backtrace(self): - gdb_output = self.get_stack_trace('id(42)') + gdb_output = self.get_stack_trace('from _typing import _idfunc;_idfunc(42)') self.assertTrue(BREAKPOINT_FN in gdb_output) def assertGdbRepr(self, val, exp_repr=None): # Ensure that gdb's rendering of the value in a debugged process # matches repr(value) in this process: - gdb_repr, gdb_output = self.get_gdb_repr('id(' + ascii(val) + ')') + gdb_repr, gdb_output = self.get_gdb_repr('_idfunc(' + ascii(val) + ')') if not exp_repr: exp_repr = repr(val) self.assertEqual(gdb_repr, exp_repr, @@ -173,7 +174,7 @@ def test_sets(self): # which happens on deletion: gdb_repr, gdb_output = self.get_gdb_repr('''s = set(['a','b']) s.remove('a') -id(s)''') +_idfunc(s)''') self.assertEqual(gdb_repr, "{'b'}") @support.requires_resource('cpu') @@ -194,7 +195,7 @@ def test_exceptions(self): try: raise RuntimeError("I am an error") except RuntimeError as e: - id(e) + _idfunc(e) ''') self.assertEqual(gdb_repr, "RuntimeError('I am an error',)") @@ -205,7 +206,7 @@ def test_exceptions(self): try: a = 1 / 0 except ZeroDivisionError as e: - id(e) + _idfunc(e) ''') self.assertEqual(gdb_repr, "ZeroDivisionError('division by zero',)") @@ -217,7 +218,7 @@ class Foo: pass foo = Foo() foo.an_int = 42 -id(foo)''') +_idfunc(foo)''') m = re.match(r'', gdb_repr) self.assertTrue(m, msg='Unexpected new-style class rendering %r' % gdb_repr) @@ -230,7 +231,7 @@ class Foo(list): foo = Foo() foo += [1, 2, 3] foo.an_int = 42 -id(foo)''') +_idfunc(foo)''') m = re.match(r'', gdb_repr) self.assertTrue(m, @@ -245,7 +246,7 @@ class Foo(tuple): pass foo = Foo((1, 2, 3)) foo.an_int = 42 -id(foo)''') +_idfunc(foo)''') m = re.match(r'', gdb_repr) self.assertTrue(m, @@ -283,8 +284,8 @@ def assertSane(self, source, corruption, exprepr=None): def test_NULL_ptr(self): 'Ensure that a NULL PyObject* is handled gracefully' gdb_repr, gdb_output = ( - self.get_gdb_repr('id(42)', - cmds_after_breakpoint=['set variable v=0', + self.get_gdb_repr('_idfunc(42)', + cmds_after_breakpoint=['set variable x=0', 'backtrace']) ) @@ -292,25 +293,25 @@ def test_NULL_ptr(self): def test_NULL_ob_type(self): 'Ensure that a PyObject* with NULL ob_type is handled gracefully' - self.assertSane('id(42)', - 'set v->ob_type=0') + self.assertSane('_idfunc(42)', + 'set x->ob_type=0') def test_corrupt_ob_type(self): 'Ensure that a PyObject* with a corrupt ob_type is handled gracefully' - self.assertSane('id(42)', - 'set v->ob_type=0xDEADBEEF', + self.assertSane('_idfunc(42)', + 'set x->ob_type=0xDEADBEEF', exprepr='42') def test_corrupt_tp_flags(self): 'Ensure that a PyObject* with a type with corrupt tp_flags is handled' - self.assertSane('id(42)', - 'set v->ob_type->tp_flags=0x0', + self.assertSane('_idfunc(42)', + 'set x->ob_type->tp_flags=0x0', exprepr='42') def test_corrupt_tp_name(self): 'Ensure that a PyObject* with a type with corrupt tp_name is handled' - self.assertSane('id(42)', - 'set v->ob_type->tp_name=0xDEADBEEF', + self.assertSane('_idfunc(42)', + 'set x->ob_type->tp_name=0xDEADBEEF', exprepr='42') def test_builtins_help(self): @@ -321,7 +322,7 @@ def test_builtins_help(self): # (this was the issue causing tracebacks in # http://bugs.python.org/issue8032#msg100537 ) - gdb_repr, gdb_output = self.get_gdb_repr('id(__builtins__.help)', import_site=True) + gdb_repr, gdb_output = self.get_gdb_repr('_idfunc(__builtins__.help)', import_site=True) m = re.match(r'<_Helper\(\) at remote 0x-?[0-9a-f]+>', gdb_repr) self.assertTrue(m, @@ -331,18 +332,18 @@ def test_selfreferential_list(self): '''Ensure that a reference loop involving a list doesn't lead proxyval into an infinite loop:''' gdb_repr, gdb_output = \ - self.get_gdb_repr("a = [3, 4, 5] ; a.append(a) ; id(a)") + self.get_gdb_repr("a = [3, 4, 5] ; a.append(a) ; _idfunc(a)") self.assertEqual(gdb_repr, '[3, 4, 5, [...]]') gdb_repr, gdb_output = \ - self.get_gdb_repr("a = [3, 4, 5] ; b = [a] ; a.append(b) ; id(a)") + self.get_gdb_repr("a = [3, 4, 5] ; b = [a] ; a.append(b) ; _idfunc(a)") self.assertEqual(gdb_repr, '[3, 4, 5, [[...]]]') def test_selfreferential_dict(self): '''Ensure that a reference loop involving a dict doesn't lead proxyval into an infinite loop:''' gdb_repr, gdb_output = \ - self.get_gdb_repr("a = {} ; b = {'bar':a} ; a['foo'] = b ; id(a)") + self.get_gdb_repr("a = {} ; b = {'bar':a} ; a['foo'] = b ; _idfunc(a)") self.assertEqual(gdb_repr, "{'foo': {'bar': {...}}}") @@ -353,7 +354,7 @@ class Foo: pass foo = Foo() foo.an_attr = foo -id(foo)''') +_idfunc(foo)''') self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>', gdb_repr), 'Unexpected gdb representation: %r\n%s' % \ @@ -366,7 +367,7 @@ class Foo(object): pass foo = Foo() foo.an_attr = foo -id(foo)''') +_idfunc(foo)''') self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>', gdb_repr), 'Unexpected gdb representation: %r\n%s' % \ @@ -380,7 +381,7 @@ class Foo(object): b = Foo() a.an_attr = b b.an_attr = a -id(a)''') +_idfunc(a)''') self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>\) at remote 0x-?[0-9a-f]+>', gdb_repr), 'Unexpected gdb representation: %r\n%s' % \ @@ -388,7 +389,7 @@ class Foo(object): def test_truncation(self): 'Verify that very long output is truncated' - gdb_repr, gdb_output = self.get_gdb_repr('id(list(range(1000)))') + gdb_repr, gdb_output = self.get_gdb_repr('_idfunc(list(range(1000)))') self.assertEqual(gdb_repr, "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, " "14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, " @@ -415,7 +416,7 @@ def test_truncation(self): 1024 + len('...(truncated)')) def test_builtin_method(self): - gdb_repr, gdb_output = self.get_gdb_repr('import sys; id(sys.stdout.readlines)') + gdb_repr, gdb_output = self.get_gdb_repr('import sys; _idfunc(sys.stdout.readlines)') self.assertTrue(re.match(r'', gdb_repr), 'Unexpected gdb representation: %r\n%s' % \ @@ -424,15 +425,16 @@ def test_builtin_method(self): def test_frames(self): gdb_output = self.get_stack_trace(''' import sys +from _typing import _idfunc def foo(a, b, c): return sys._getframe(0) f = foo(3, 4, 5) -id(f)''', - breakpoint='builtin_id', - cmds_after_breakpoint=['print (PyFrameObject*)v'] +_idfunc(f)''', + breakpoint='_typing__idfunc', + cmds_after_breakpoint=['print (PyFrameObject*)x'] ) - self.assertTrue(re.match(r'.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file , line 4, in foo \(a=3.*', + self.assertTrue(re.match(r'.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file , line 5, in foo \(a=3.*', gdb_output, re.DOTALL), 'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output)) diff --git a/Lib/test/test_gdb/util.py b/Lib/test/test_gdb/util.py index 8097fd52ababe6..81b602ebdaf517 100644 --- a/Lib/test/test_gdb/util.py +++ b/Lib/test/test_gdb/util.py @@ -16,7 +16,7 @@ 'python-gdb.py') SAMPLE_SCRIPT = os.path.join(os.path.dirname(__file__), 'gdb_sample.py') -BREAKPOINT_FN = 'builtin_id' +BREAKPOINT_FN = '_typing__idfunc' PYTHONHASHSEED = '123' diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index e59d3977df4134..df9178678b7738 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -4558,11 +4558,11 @@ def test_check_encoding_warning(self): ''') proc = assert_python_ok('-X', 'warn_default_encoding', '-c', code) warnings = proc.err.splitlines() - self.assertEqual(len(warnings), 4) + self.assertEqual(len(warnings), 2) self.assertTrue( warnings[0].startswith(b":5: EncodingWarning: ")) self.assertTrue( - warnings[2].startswith(b":8: EncodingWarning: ")) + warnings[1].startswith(b":8: EncodingWarning: ")) def test_text_encoding(self): # PEP 597, bpo-47000. io.text_encoding() returns "locale" or "utf-8" diff --git a/Lib/test/test_linecache.py b/Lib/test/test_linecache.py index 6f5955791407ea..e23e1cc942856b 100644 --- a/Lib/test/test_linecache.py +++ b/Lib/test/test_linecache.py @@ -8,6 +8,7 @@ from importlib.machinery import ModuleSpec from test import support from test.support import os_helper +from test.support.script_helper import assert_python_ok FILENAME = linecache.__file__ @@ -311,6 +312,12 @@ def test_invalid_names(self): # just to be sure that we did not mess with cache linecache.clearcache() + def test_linecache_python_string(self): + cmdline = "import linecache;assert len(linecache.cache) == 0" + retcode, stdout, stderr = assert_python_ok('-c', cmdline) + self.assertEqual(retcode, 0) + self.assertEqual(stdout, b'') + self.assertEqual(stderr, b'') class LineCacheInvalidationTests(unittest.TestCase): def setUp(self): diff --git a/Lib/test/test_repl.py b/Lib/test/test_repl.py index 356ff5b198d637..66262d352c2ec2 100644 --- a/Lib/test/test_repl.py +++ b/Lib/test/test_repl.py @@ -213,7 +213,7 @@ def bar(x): p.stdin.write(user_input) user_input2 = dedent(""" import linecache - print(linecache.cache['-1']) + print(linecache._interactive_cache[id(foo.__code__)]) """) p.stdin.write(user_input2) output = kill_python(p) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index e45701dfe033a6..29ba75bf01dd72 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1821,9 +1821,9 @@ def test_encoding_warning(self): cp = subprocess.run([sys.executable, "-Xwarn_default_encoding", "-c", code], capture_output=True) lines = cp.stderr.splitlines() - self.assertEqual(len(lines), 4, lines) + self.assertEqual(len(lines), 2, lines) self.assertTrue(lines[0].startswith(b":2: EncodingWarning: ")) - self.assertTrue(lines[2].startswith(b":3: EncodingWarning: ")) + self.assertTrue(lines[1].startswith(b":3: EncodingWarning: ")) def _get_test_grp_name(): diff --git a/Lib/traceback.py b/Lib/traceback.py index 31c73efcef5a52..dd9e88d24a0606 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -288,11 +288,11 @@ class FrameSummary: """ __slots__ = ('filename', 'lineno', 'end_lineno', 'colno', 'end_colno', - 'name', '_lines', '_lines_dedented', 'locals') + 'name', '_lines', '_lines_dedented', 'locals', '_code') def __init__(self, filename, lineno, name, *, lookup_line=True, locals=None, line=None, - end_lineno=None, colno=None, end_colno=None): + end_lineno=None, colno=None, end_colno=None, **kwargs): """Construct a FrameSummary. :param lookup_line: If True, `linecache` is consulted for the source @@ -308,6 +308,7 @@ def __init__(self, filename, lineno, name, *, lookup_line=True, self.colno = colno self.end_colno = end_colno self.name = name + self._code = kwargs.get("_code") self._lines = line self._lines_dedented = None if lookup_line: @@ -347,7 +348,10 @@ def _set_lines(self): lines = [] for lineno in range(self.lineno, self.end_lineno + 1): # treat errors (empty string) and empty lines (newline) as the same - lines.append(linecache.getline(self.filename, lineno).rstrip()) + line = linecache.getline(self.filename, lineno).rstrip() + if not line and self._code is not None and self.filename.startswith("<"): + line = linecache._getline_from_code(self._code, lineno).rstrip() + lines.append(line) self._lines = "\n".join(lines) + "\n" @property @@ -480,9 +484,13 @@ def _extract_from_extended_frame_gen(klass, frame_gen, *, limit=None, f_locals = f.f_locals else: f_locals = None - result.append(FrameSummary( - filename, lineno, name, lookup_line=False, locals=f_locals, - end_lineno=end_lineno, colno=colno, end_colno=end_colno)) + result.append( + FrameSummary(filename, lineno, name, + lookup_line=False, locals=f_locals, + end_lineno=end_lineno, colno=colno, end_colno=end_colno, + _code=f.f_code, + ) + ) for filename in fnames: linecache.checkcache(filename) diff --git a/Python/pythonrun.c b/Python/pythonrun.c index ae0df9685ac159..59edbe12dee9a4 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1383,7 +1383,7 @@ run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals, PyObject* result = PyObject_CallFunction( print_tb_func, "OOO", - interactive_filename, + co, interactive_src, filename ); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy