Skip to content

gh-151728: Clear the typing caches at interpreter shutdown - #155002

Open
wjakob wants to merge 1 commit into
python:mainfrom
wjakob:gh-151728-typing-clear-caches-v2
Open

gh-151728: Clear the typing caches at interpreter shutdown#155002
wjakob wants to merge 1 commit into
python:mainfrom
wjakob:gh-151728-typing-clear-caches-v2

Conversation

@wjakob

@wjakob wjakob commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

typing caches every subscripted type in module-level lru_cachees. Those caches are only released when the typing module itself is torn down, which does not happen when some extension module leaks a reference to it. Since typing is nearly universal, a single such leak (e.g. via torch) keeps the cached types alive past interpreter shutdown, including types owned by unrelated, correctly written extensions. Those extensions then look like they are leaking: valgrind reports them, and nanobind prints warnings about types, functions, and instances that were never freed.

typing._clear_caches() already exists but nothing calls it at shutdown. This PR registers it with atexit so that the caches are dropped during finalization and the process terminates leak-free. This complements gh-98253, which broke a cycle inside _tp_cache but left the retention itself in place.

This re-lands GH-154858, which was reverted in GH-154992 because of reference leak failures. The problem there was that the handler runs on every import of typing and keeps the module alive through its __globals__. Two tests in test_typing import a throwaway copy of typing, which then leaks once per repetition under -R. Both now unregister the handler of the copy they made.

The PR adds no test because reproducing the problem requires an extension module that deliberately leaks a reference (i.e. one with a missing tp_traverse). A minimal one is at https://github.com/wjakob/typing-leak.

Re-apply pythonGH-154858, which was reverted in pythonGH-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.
@bedevere-bot

Copy link
Copy Markdown

🤖 New build scheduled with the buildbot fleet by @JelleZijlstra for commit f42f407 🤖

Results will be shown at:

https://buildbot.python.org/all/#/grid?branch=refs%2Fpull%2F155002%2Fmerge

If you want to schedule another build, you need to add the 🔨 test-with-refleak-buildbots label again.

@bedevere-bot bedevere-bot removed the 🔨 test-with-refleak-buildbots Test PR w/ refleak buildbots; report in status section label Jul 31, 2026
@JelleZijlstra

Copy link
Copy Markdown
Member

Makes sense, I triggered the refleak buildbots to confirm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants