8000 [3.13] gh-117174: Fix reference leak and gdb tests (GH-131095) by pablogsal · Pull Request #131120 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

[3.13] gh-117174: Fix reference leak and gdb tests (GH-131095) #131120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Lib/linecache.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ def _getline_from_code(filename, lineno):
return lines[lineno - 1]
return ''

def _make_key(code):
return (code.co_filename, code.co_qualname, code.co_firstlineno)

def _getlines_from_code(code):
code_id = id(code)
code_id = _make_key(code)
if code_id in _interactive_cache:
entry = _interactive_cache[code_id]
if len(entry) != 1:
Expand Down Expand Up @@ -215,7 +217,6 @@ def get_lines(name=name, *args, **kwargs):
return True
return False


def _register_code(code, string, name):
entry = (len(string),
None,
Expand All @@ -227,4 +228,4 @@ def _register_code(code, string, name):
for const in code.co_consts:
if isinstance(const, type(code)):
stack.append(const)
_interactive_cache[id(code)] = entry
_interactive_cache[_make_key(code)] = entry
17 changes: 14 additions & 3 deletions Lib/test/libregrtest/refleak.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import warnings
from inspect import isabstract
from typing import Any
import linecache

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

xml_filename = 'refleak-xml.tmp'
result = None
dash_R_cleanup(fs, ps, pic, zdc, abcs)
dash_R_cleanup(fs, ps, pic, zdc, abcs, linecache_data)
support.gc_collect()

for i in rep_range:
Expand All @@ -134,7 +140,7 @@ def get_pooled_int(value):
refleak_helper._hunting_for_refleaks = current

save_support_xml(xml_filename)
dash_R_cleanup(fs, ps, pic, zdc, abcs)
dash_R_cleanup(fs, ps, pic, zdc, abcs, linecache_data)
support.gc_collect()

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


def dash_R_cleanup(fs, ps, pic, zdc, abcs):
def dash_R_cleanup(fs, ps, pic, zdc, abcs, linecache_data):
import copyreg
import collections.abc

Expand All @@ -233,6 +239,11 @@ def dash_R_cleanup(fs, ps, pic, zdc, abcs):
copyreg.dispatch_table.update(ps)
sys.path_importer_cache.clear()
sys.path_importer_cache.update(pic)
lcache, linteractive = linecache_data
linecache._interactive_cache.clear()
linecache._interactive_cache.update(linteractive)
linecache.cache.clear()
linecache.cache.update(lcache)
try:
import zipimport
except ImportError:
Expand Down
3 changes: 1 addition & 2 deletions Lib/test/test_gdb/gdb_sample.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Sample script for use by test_gdb
from _typing import _idfunc

def foo(a, b, c):
bar(a=a, b=b, c=c)
Expand All @@ -8,6 +7,6 @@ def bar(a, b, c):
baz(a, b, c)

def baz(*args):
_idfunc(42)
id(42)

foo(1, 2, 3)
28 changes: 13 additions & 15 deletions Lib/test/test_gdb/test_backtrace.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ def test_bt(self):
self.assertMultilineMatches(bt,
r'''^.*
Traceback \(most recent call first\):
<built-in method _idfunc of module object .*>
File ".*gdb_sample.py", line 11, in baz
_idfunc\(42\)
File ".*gdb_sample.py", line 8, in bar
<built-in method id of module object .*>
File ".*gdb_sample.py", line 10, in baz
id\(42\)
File ".*gdb_sample.py", line 7, in bar
baz\(a, b, c\)
File ".*gdb_sample.py", line 5, in foo
File ".*gdb_sample.py", line 4, in foo
bar\(a=a, b=b, c=c\)
File ".*gdb_sample.py", line 13, in <module>
File ".*gdb_sample.py", line 12, in <module>
foo\(1, 2, 3\)
''')

Expand All @@ -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 8, in bar \(a=1, b=2, c=3\)
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, 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 5, in foo \(a=1, b=2, c=3\)
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, 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 13, in <module> \(\)
#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
foo\(1, 2, 3\)
''')

Expand All @@ -55,7 +55,6 @@ 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
Expand All @@ -71,7 +70,7 @@ def run(self):
t[i].start()

# Trigger a breakpoint on the main thread
_idfunc(42)
id(42)

'''
# Verify with "py-bt":
Expand All @@ -91,8 +90,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; from _typing import _idfunc\n'
'_idfunc(42)\n'
cmd = ('from gc import collect\n'
'id(42)\n'
'def foo():\n'
' collect()\n'
'def bar():\n'
Expand All @@ -114,12 +113,11 @@ 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()

_idfunc("first break point")
id("first break point")
l = MyList()
''')
cmds_after_breakpoint = ['break wrapper_call', 'continue']
Expand Down
40 changes: 19 additions & 21 deletions Lib/test/test_gdb/test_misc.py
AB58
Original file line number Diff line number Diff line change
Expand Up @@ -35,42 +35,40 @@ def test_basic_command(self):
bt = self.get_stack_trace(script=SAMPLE_SCRIPT,
cmds_after_breakpoint=['py-list'])

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',
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',
bt)

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

self.assertListing(' 10 def baz(*args):\n'
' >11 _idfunc(42)\n'
' 12 \n'
' 13 foo(1, 2, 3)\n',
self.assertListing(' 9 def baz(*args):\n'
' >10 id(42)\n'
' 11 \n'
' 12 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,4'])
cmds_after_breakpoint=['py-list 1,3'])

self.assertListing(' 1 # Sample script for use by test_gdb\n'
' 2 from _typing import _idfunc\n'
' 3 \n'
' 4 def foo(a, b, c):\n',
' 2 \n'
' 3 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)
Expand All @@ -79,7 +77,7 @@ def bar(a, b, c):
pyobject_vectorcall(baz, (a, b, c), None)

def baz(*args):
_idfunc(42)
id(42)

foo(1, 2, 3)

Expand All @@ -96,7 +94,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 <string>, line 13, in baz \(args=\(1, 2, 3\)\)
#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\)
#[0-9]+ <built-in method pyobject_vectorcall of module object at remote 0x[0-9a-f]+>
$''')

Expand Down Expand Up @@ -125,9 +123,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 <string>, line 13, in baz \(args=\(1, 2, 3\)\)
#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\)
#[0-9]+ <built-in method pyobject_vectorcall of module object at remote 0x[0-9a-f]+>
#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 13, in baz \(args=\(1, 2, 3\)\)
#[0-9]+ Frame 0x-?[0-9a-f]+, for file <string>, line 12, in baz \(args=\(1, 2, 3\)\)
$''')

class PyPrintTests(DebuggerTests):
Expand Down
Loading
Loading
0