gh-93353: Add test.support.late_deletion() (#93774)

This commit is contained in:
Victor Stinner 2022-06-14 01:09:23 +02:00 committed by GitHub
parent df22eec421
commit 7b2064b4b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 9 deletions

View File

@ -2213,3 +2213,40 @@ def requires_venv_with_pip():
# True if Python is built with the Py_DEBUG macro defined: if # True if Python is built with the Py_DEBUG macro defined: if
# Python is built in debug mode (./configure --with-pydebug). # Python is built in debug mode (./configure --with-pydebug).
Py_DEBUG = hasattr(sys, 'gettotalrefcount') Py_DEBUG = hasattr(sys, 'gettotalrefcount')
def late_deletion(obj):
"""
Keep a Python alive as long as possible.
Create a reference cycle and store the cycle in an object deleted late in
Python finalization. Try to keep the object alive until the very last
garbage collection.
The function keeps a strong reference by design. It should be called in a
subprocess to not mark a test as "leaking a reference".
"""
# Late CPython finalization:
# - finalize_interp_clear()
# - _PyInterpreterState_Clear(): Clear PyInterpreterState members
# (ex: codec_search_path, before_forkers)
# - clear os.register_at_fork() callbacks
# - clear codecs.register() callbacks
ref_cycle = [obj]
ref_cycle.append(ref_cycle)
# Store a reference in PyInterpreterState.codec_search_path
import codecs
def search_func(encoding):
return None
search_func.reference = ref_cycle
codecs.register(search_func)
if hasattr(os, 'register_at_fork'):
# Store a reference in PyInterpreterState.before_forkers
def atfork_func():
pass
atfork_func.reference = ref_cycle
os.register_at_fork(before=atfork_func)

View File

@ -1440,19 +1440,13 @@ class PythonFinalizationTests(unittest.TestCase):
code = textwrap.dedent(""" code = textwrap.dedent("""
import ast import ast
import codecs import codecs
from test import support
# Small AST tree to keep their AST types alive # Small AST tree to keep their AST types alive
tree = ast.parse("def f(x, y): return 2*x-y") tree = ast.parse("def f(x, y): return 2*x-y")
x = [tree]
x.append(x)
# Put the cycle somewhere to survive until the last GC collection. # Store the tree somewhere to survive until the last GC collection
# Codec search functions are only cleared at the end of support.late_deletion(tree)
# interpreter_clear().
def search_func(encoding):
return None
search_func.a = x
codecs.register(search_func)
""") """)
assert_python_ok("-c", code) assert_python_ok("-c", code)