Skip to content

Commit ebc24d5

Browse files
authored
gh-117174: Fix reference leak and gdb tests (#131095)
1 parent c00ac57 commit ebc24d5

File tree

8 files changed

+88
-83
lines changed

8 files changed

+88
-83
lines changed

Diff for: Lib/linecache.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@ def _getline_from_code(filename, lineno):
5151
return lines[lineno - 1]
5252
return ''
5353

54+
def _make_key(code):
55+
return (code.co_filename, code.co_qualname, code.co_firstlineno)
5456

5557
def _getlines_from_code(code):
56-
code_id = id(code)
58+
code_id = _make_key(code)
5759
if code_id in _interactive_cache:
5860
entry = _interactive_cache[code_id]
5961
if len(entry) != 1:
@@ -215,7 +217,6 @@ def get_lines(name=name, *args, **kwargs):
215217
return True
216218
return False
217219

218-
219220
def _register_code(code, string, name):
220221
entry = (len(string),
221222
None,
@@ -227,4 +228,4 @@ def _register_code(code, string, name):
227228
for const in code.co_consts:
228229
if isinstance(const, type(code)):
229230
stack.append(const)
230-
_interactive_cache[id(code)] = entry
231+
_interactive_cache[_make_key(code)] = entry

Diff for: Lib/test/libregrtest/refleak.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import warnings
44
from inspect import isabstract
55
from typing import Any
6+
import linecache
67

78
from test import support
89
from test.support import os_helper
@@ -73,6 +74,11 @@ def runtest_refleak(test_name, test_func,
7374
ps = copyreg.dispatch_table.copy()
7475
pic = sys.path_importer_cache.copy()
7576
zdc: dict[str, Any] | None
77+
# Linecache holds a cache with the source of interactive code snippets
78+
# (e.g. code typed in the REPL). This cache is not cleared by
79+
# linecache.clearcache(). We need to save and restore it to avoid false
80+
# positives.
81+
linecache_data = linecache.cache.copy(), linecache._interactive_cache.copy() # type: ignore[attr-defined]
7682
try:
7783
import zipimport
7884
except ImportError:
@@ -122,7 +128,7 @@ def get_pooled_int(value):
122128

123129
xml_filename = 'refleak-xml.tmp'
124130
result = None
125-
dash_R_cleanup(fs, ps, pic, zdc, abcs)
131+
dash_R_cleanup(fs, ps, pic, zdc, abcs, linecache_data)
126132

127133
for i in rep_range:
128134
support.gc_collect()
@@ -134,7 +140,7 @@ def get_pooled_int(value):
134140
refleak_helper._hunting_for_refleaks = current
135141

136142
save_support_xml(xml_filename)
137-
dash_R_cleanup(fs, ps, pic, zdc, abcs)
143+
dash_R_cleanup(fs, ps, pic, zdc, abcs, linecache_data)
138144
support.gc_collect()
139145

140146
# Read memory statistics immediately after the garbage collection.
@@ -223,7 +229,7 @@ def check_fd_deltas(deltas):
223229
return (failed, result)
224230

225231

226-
def dash_R_cleanup(fs, ps, pic, zdc, abcs):
232+
def dash_R_cleanup(fs, ps, pic, zdc, abcs, linecache_data):
227233
import copyreg
228234
import collections.abc
229235

@@ -233,6 +239,11 @@ def dash_R_cleanup(fs, ps, pic, zdc, abcs):
233239
copyreg.dispatch_table.update(ps)
234240
sys.path_importer_cache.clear()
235241
sys.path_importer_cache.update(pic)
242+
lcache, linteractive = linecache_data
243+
linecache._interactive_cache.clear()
244+
linecache._interactive_cache.update(linteractive)
245+
linecache.cache.clear()
246+
linecache.cache.update(lcache)
236247
try:
237248
import zipimport
238249
except ImportError:

Diff for: Lib/test/test_gdb/gdb_sample.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# Sample script for use by test_gdb
2-
from _typing import _idfunc
32

43
def foo(a, b, c):
54
bar(a=a, b=b, c=c)
@@ -8,6 +7,6 @@ def bar(a, b, c):
87
baz(a, b, c)
98

109
def baz(*args):
11-
_idfunc(42)
10+
id(42)
1211

1312
foo(1, 2, 3)

Diff for: Lib/test/test_gdb/test_backtrace.py

+13-15
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ def test_bt(self):
2020
self.assertMultilineMatches(bt,
2121
r'''^.*
2222
Traceback \(most recent call first\):
23-
<built-in method _idfunc of module object .*>
24-
File ".*gdb_sample.py", line 11, in baz
25-
_idfunc\(42\)
26-
File ".*gdb_sample.py", line 8, in bar
23+
<built-in method id of module object .*>
24+
File ".*gdb_sample.py", line 10, in baz
25+
id\(42\)
26+
File ".*gdb_sample.py", line 7, in bar
2727
baz\(a, b, c\)
28-
File ".*gdb_sample.py", line 5, in foo
28+
File ".*gdb_sample.py", line 4, in foo
2929
bar\(a=a, b=b, c=c\)
30-
File ".*gdb_sample.py", line 13, in <module>
30+
File ".*gdb_sample.py", line 12, in <module>
3131
foo\(1, 2, 3\)
3232
''')
3333

@@ -39,11 +39,11 @@ def test_bt_full(self):
3939
cmds_after_breakpoint=['py-bt-full'])
4040
self.assertMultilineMatches(bt,
4141
r'''^.*
42-
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 8, in bar \(a=1, b=2, c=3\)
42+
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
4343
baz\(a, b, c\)
44-
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 5, in foo \(a=1, b=2, c=3\)
44+
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\)
4545
bar\(a=a, b=b, c=c\)
46-
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 13, in <module> \(\)
46+
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
4747
foo\(1, 2, 3\)
4848
''')
4949

@@ -55,7 +55,6 @@ def test_threads(self):
5555
'Verify that "py-bt" indicates threads that are waiting for the GIL'
5656
cmd = '''
5757
from threading import Thread
58-
from _typing import _idfunc
5958
6059
class TestThread(Thread):
6160
# These threads would run forever, but we'll interrupt things with the
@@ -71,7 +70,7 @@ def run(self):
7170
t[i].start()
7271
7372
# Trigger a breakpoint on the main thread
74-
_idfunc(42)
73+
id(42)
7574
7675
'''
7776
# Verify with "py-bt":
@@ -91,8 +90,8 @@ def run(self):
9190
# unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
9291
def test_gc(self):
9392
'Verify that "py-bt" indicates if a thread is garbage-collecting'
94-
cmd = ('from gc import collect; from _typing import _idfunc\n'
95-
'_idfunc(42)\n'
93+
cmd = ('from gc import collect\n'
94+
'id(42)\n'
9695
'def foo():\n'
9796
' collect()\n'
9897
'def bar():\n'
@@ -114,12 +113,11 @@ def test_gc(self):
114113
"Python was compiled with optimizations")
115114
def test_wrapper_call(self):
116115
cmd = textwrap.dedent('''
117-
from typing import _idfunc
118116
class MyList(list):
119117
def __init__(self):
120118
super(*[]).__init__() # wrapper_call()
121119
122-
_idfunc("first break point")
120+
id("first break point")
123121
l = MyList()
124122
''')
125123
cmds_after_breakpoint = ['break wrapper_call', 'continue']

Diff for: Lib/test/test_gdb/test_misc.py

+19-21
Original file line numberDiff line numberDiff line change
@@ -35,42 +35,40 @@ def test_basic_command(self):
3535
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
3636
cmds_after_breakpoint=['py-list'])
3737

38-
self.assertListing(' 6 \n'
39-
' 7 def bar(a, b, c):\n'
40-
' 8 baz(a, b, c)\n'
41-
' 9 \n'
42-
' 10 def baz(*args):\n'
43-
' >11 _idfunc(42)\n'
44-
' 12 \n'
45-
' 13 foo(1, 2, 3)\n',
38+
self.assertListing(' 5 \n'
39+
' 6 def bar(a, b, c):\n'
40+
' 7 baz(a, b, c)\n'
41+
' 8 \n'
42+
' 9 def baz(*args):\n'
43+
' >10 id(42)\n'
44+
' 11 \n'
45+
' 12 foo(1, 2, 3)\n',
4646
bt)
4747

4848
def test_one_abs_arg(self):
4949
'Verify the "py-list" command with one absolute argument'
5050
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
5151
cmds_after_breakpoint=['py-list 9'])
5252

53-
self.assertListing(' 10 def baz(*args):\n'
54-
' >11 _idfunc(42)\n'
55-
' 12 \n'
56-
' 13 foo(1, 2, 3)\n',
53+
self.assertListing(' 9 def baz(*args):\n'
54+
' >10 id(42)\n'
55+
' 11 \n'
56+
' 12 foo(1, 2, 3)\n',
5757
bt)
5858

5959
def test_two_abs_args(self):
6060
'Verify the "py-list" command with two absolute arguments'
6161
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
62-
cmds_after_breakpoint=['py-list 1,4'])
62+
cmds_after_breakpoint=['py-list 1,3'])
6363

6464
self.assertListing(' 1 # Sample script for use by test_gdb\n'
65-
' 2 from _typing import _idfunc\n'
66-
' 3 \n'
67-
' 4 def foo(a, b, c):\n',
65+
' 2 \n'
66+
' 3 def foo(a, b, c):\n',
6867
bt)
6968

7069
SAMPLE_WITH_C_CALL = """
7170
7271
from _testcapi import pyobject_vectorcall
73-
from _typing import _idfunc
7472
7573
def foo(a, b, c):
7674
bar(a, b, c)
@@ -79,7 +77,7 @@ def bar(a, b, c):
7977
pyobject_vectorcall(baz, (a, b, c), None)
8078
8179
def baz(*args):
82-
_idfunc(42)
80+
id(42)
8381
8482
foo(1, 2, 3)
8583
@@ -96,7 +94,7 @@ def test_pyup_command(self):
9694
cmds_after_breakpoint=['py-up', 'py-up'])
9795
self.assertMultilineMatches(bt,
9896
r'''^.*
99-
#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 13, in baz \(args=\(1, 2, 3\)\)
97+
#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\)
10098
#[0-9]+ <built-in method pyobject_vectorcall of module object at remote 0x[0-9a-f]+>
10199
$''')
102100

@@ -125,9 +123,9 @@ def test_up_then_down(self):
125123
cmds_after_breakpoint=['py-up', 'py-up', 'py-down'])
126124
self.assertMultilineMatches(bt,
127125
r'''^.*
128-
#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 13, in baz \(args=\(1, 2, 3\)\)
126+
#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\)
129127
#[0-9]+ <built-in method pyobject_vectorcall of module object at remote 0x[0-9a-f]+>
130-
#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 13, in baz \(args=\(1, 2, 3\)\)
128+
#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\)
131129
$''')
132130

133131
class PyPrintTests(DebuggerTests):

0 commit comments

Comments
 (0)