https://github.com/python/cpython/commit/c10c7e90befd8ba696c1f96323bf5602d4b6594b
commit: c10c7e90befd8ba696c1f96323bf5602d4b6594b
branch: main
author: Wenzel Jakob <[email protected]>
committer: encukou <[email protected]>
date: 2026-08-03T17:21:20+02:00
summary:
gh-151728: Clear the typing caches at interpreter shutdown (GH-155002)
Re-apply GH-154858, which was reverted in GH-154992 because it broke the
reference leak buildbots.
``atexit.register(_clear_caches)`` runs on every import of ``typing``, and the
registered handler reaches the module dict through
``_clear_caches.__globals__``.
Any throwaway copy of ``typing`` therefore stays alive until interpreter
shutdown. Two tests create such a copy on each iteration:
- ``InternalsTests.test_collect_parameters`` imports a fresh ``typing``.
- ``CollectionsAbcTests.test_bytestring`` drops ``typing`` from ``sys.modules``
and re-imports it.
Both now unregister the exit handler of the copy they created.
files:
A Misc/NEWS.d/next/Library/2026-07-29-11-05-00.gh-issue-151728.Kq3vTn.rst
M Lib/test/test_typing.py
M Lib/typing.py
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 106ffdede6fd4e..53c8c9fac69465 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -1,4 +1,5 @@
import annotationlib
+import atexit
import contextlib
import collections
import collections.abc
@@ -6550,6 +6551,14 @@ class F:
class InternalsTests(BaseTestCase):
def test_collect_parameters(self):
typing = import_helper.import_fresh_module("typing")
+ # Importing typing registers an internal function named _clear_caches
+ # with atexit. The throwaway module created here installs its own
+ # handler, which holds the module alive and keeps references until
+ # interpreter shutdown even after the test finishes. Each repetition
+ # of this test under -R would therefore leak another module copy. To
+ # avoid this, we unregister the handler once the test is done.
+ self.addCleanup(atexit.unregister, typing._clear_caches)
+
with self.assertWarnsRegex(
DeprecationWarning,
"The private _collect_parameters function is deprecated"
@@ -7719,6 +7728,10 @@ def test_bytestring(self):
with self.assertWarns(DeprecationWarning):
from typing import ByteString
+ # Drop the exit handler of this throwaway copy, see the comment in
+ # InternalsTests.test_collect_parameters.
+ self.addCleanup(atexit.unregister, sys.modules["typing"]._clear_caches)
+
with self.assertWarns(DeprecationWarning):
self.assertIsInstance(b'', ByteString)
with self.assertWarns(DeprecationWarning):
diff --git a/Lib/typing.py b/Lib/typing.py
index a05d73c29cf95e..809c0ff88607a5 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -19,6 +19,7 @@
"""
from abc import abstractmethod, ABCMeta
+import atexit
import collections
from collections import defaultdict
import collections.abc
@@ -397,6 +398,11 @@ def _clear_caches():
cleanup()
+# Release the LRU caches at shutdown, they otherwise redistribute reference
+# leaks of one extension to types of unrelated ones. See GH-151728.
+atexit.register(_clear_caches)
+
+
def _tp_cache(func=None, /, *, typed=False):
"""Internal wrapper caching __getitem__ of generic types.
diff --git
a/Misc/NEWS.d/next/Library/2026-07-29-11-05-00.gh-issue-151728.Kq3vTn.rst
b/Misc/NEWS.d/next/Library/2026-07-29-11-05-00.gh-issue-151728.Kq3vTn.rst
new file mode 100644
index 00000000000000..39c63a1aea3193
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-29-11-05-00.gh-issue-151728.Kq3vTn.rst
@@ -0,0 +1,4 @@
+Clear the internal :mod:`typing` caches from an exit handler. Previously, an
+extension module that leaked a reference to :mod:`typing` would also keep every
+subscripted type alive past interpreter shutdown, including types owned by
+unrelated extension modules.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]