diff options
-rw-r--r-- | Lib/test/support/__init__.py | 37 | ||||
-rw-r--r-- | Lib/test/test_gc.py | 14 |
2 files changed, 41 insertions, 10 deletions
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 780d04b..4baaeb7 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2213,3 +2213,40 @@ def requires_venv_with_pip(): # True if Python is built with the Py_DEBUG macro defined: if # Python is built in debug mode (./configure --with-pydebug). 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) diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index dbbd67b..087f727 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -1440,19 +1440,13 @@ class PythonFinalizationTests(unittest.TestCase): code = textwrap.dedent(""" import ast import codecs + from test import support # Small AST tree to keep their AST types alive 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. - # Codec search functions are only cleared at the end of - # interpreter_clear(). - def search_func(encoding): - return None - search_func.a = x - codecs.register(search_func) + + # Store the tree somewhere to survive until the last GC collection + support.late_deletion(tree) """) assert_python_ok("-c", code) |