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 66e66eae7ead9c..c6f40abfab4226 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 161928dfa9fb09..81be29a33b577d 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -1072,6 +1072,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 c475b6d78d0c56..12032954220483 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 54c6b2de7cc99d..694774bc5ab702 100644
--- a/Lib/test/test_gdb/util.py
+++ b/Lib/test/test_gdb/util.py
@@ -17,7 +17,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 f1f8ce57668f3b..112495512e192c 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -4546,11 +4546,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 9b73be0b95e947..bf77779b32c8ef 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -1803,9 +1803,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 c748e4bc849c43..15f59bba54d0db 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 eea638354fe8f3..62b70931778b1d 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -1442,7 +1442,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
);