Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34k
gh-140232: Do not track frozenset objects with immutables#140234
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
vstinner merged 46 commits into python:main from eendebakpt:frozenset_immutable_trackingJan 28, 2026
+114 −3
Merged
Changes from all commits
Commits
Show all changes
46 commits Select commit Hold shift + click to select a range
a3292c2 Do not track frozenset objects with immutables
eendebakpt cd294a6 cleanup
eendebakpt 7e28cf2 cleanup
eendebakpt 30057a5 Merge branch 'main' into frozenset_immutable_tracking
eendebakpt c4deb03 fix test
eendebakpt 607237a 📜🤖 Added by blurb_it.
blurb-it[bot] 2735a71 Update Objects/setobject.c
eendebakpt c05db54 make sure PySet_Add tracks frozensets if needed
eendebakpt 7f6bc4b Merge branch 'frozenset_immutable_tracking' of github.com:eendebakpt/…
eendebakpt 0b97604 review comment
eendebakpt 948daed Merge branch 'main' into frozenset_immutable_tracking
eendebakpt 08e22c3 use _testcapi for testing
eendebakpt 62afc76 whitespace
eendebakpt eab653e Merge branch 'main' into frozenset_immutable_tracking
eendebakpt 37fc61d Apply suggestions from code review
eendebakpt 4f8bda7 review comment
eendebakpt e9d42b4 Merge branch 'frozenset_immutable_tracking' of github.com:eendebakpt/…
eendebakpt 4b39149 Apply suggestions from code review
eendebakpt 2859802 review comments
eendebakpt 08f43c5 review comments
eendebakpt d12102c Merge branch 'main' into frozenset_immutable_tracking
eendebakpt 5f8b1f2 Update Objects/setobject.c
eendebakpt ae5cc7f review comments
eendebakpt 786019d Update Lib/test/test_set.py
eendebakpt f37f46e Update Modules/_testcapimodule.c
eendebakpt afd8a5b Merge branch 'main' into frozenset_immutable_tracking
eendebakpt 377e6d8 Apply suggestions from code review
eendebakpt c62fa9a revert to fastcall
eendebakpt 69b728f Merge branch 'frozenset_immutable_tracking' of github.com:eendebakpt/…
eendebakpt 71d9eba fix header
eendebakpt 5c19de2 Fully write test in C
eendebakpt e7dd248 adjust tests
eendebakpt 075b582 cleanup tests
eendebakpt 3dd4c95 cleanup tests
eendebakpt 8d864ef rework
eendebakpt e262963 refactor code
eendebakpt 6027391 cleanup
eendebakpt 6269f68 Merge branch 'main' into frozenset_immutable_tracking
eendebakpt 37af64f Update Modules/_testlimitedcapi/set.c
eendebakpt ea3ba39 Merge branch 'main' into frozenset_immutable_tracking
eendebakpt ff61155 Update Modules/_testlimitedcapi/set.c
eendebakpt 6bb0d58 Apply suggestions from code review
eendebakpt 7a4859b Update Objects/setobject.c
vstinner a2bcfdb review comment
eendebakpt 88c3f4a whitespace
eendebakpt be20046 Merge branch 'main' into frozenset_immutable_tracking
eendebakpt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions 1 Misc/NEWS.d/next/Core_and_Builtins/2025-10-16-22-36-05.gh-issue-140232.u3srgv.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Frozenset objects with immutable elements are no longer tracked by the garbage collector. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -155,6 +155,72 @@ test_frozenset_add_in_capi(PyObject *self, PyObject *Py_UNUSED(obj)) | ||
| return NULL; | ||
| } | ||
| static PyObject * | ||
| raiseTestError(const char* test_name, const char* msg) | ||
sergey-miryanov marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| { | ||
| PyErr_Format(PyExc_AssertionError, "%s: %s", test_name, msg); | ||
| return NULL; | ||
| } | ||
| static PyObject * | ||
| test_frozenset_add_in_capi_tracking_immutable(PyObject *self, PyObject *Py_UNUSED(ignored)) | ||
| { | ||
| // Test: GC tracking - frozenset with only immutable items should not be tracked | ||
| PyObject *frozenset = PyFrozenSet_New(NULL); | ||
| if (frozenset == NULL){ | ||
| return NULL; | ||
| } | ||
| if (PySet_Add(frozenset, Py_True) < 0){ | ||
| Py_DECREF(frozenset); | ||
| return NULL; | ||
| } | ||
| if (PyObject_GC_IsTracked(frozenset)){ | ||
| Py_DECREF(frozenset); | ||
| return raiseTestError("test_frozenset_add_in_capi_tracking_immutable", | ||
| "frozenset with only bool should not be GC tracked"); | ||
| } | ||
| Py_DECREF(frozenset); | ||
| Py_RETURN_NONE; | ||
| } | ||
| static PyObject * | ||
| test_frozenset_add_in_capi_tracking(PyObject *self, PyObject *Py_UNUSED(ignored)) | ||
| { | ||
| // Test: GC tracking - frozenset with tracked object should be tracked | ||
| PyObject *frozenset = PyFrozenSet_New(NULL); | ||
| if (frozenset == NULL){ | ||
| return NULL; | ||
| } | ||
| PyObject *tracked_obj = PyErr_NewException("_testlimitedcapi.py_set_add", NULL, NULL); | ||
| if (tracked_obj == NULL){ | ||
| goto error; | ||
| } | ||
| if (!PyObject_GC_IsTracked(tracked_obj)){ | ||
| Py_DECREF(frozenset); | ||
| Py_DECREF(tracked_obj); | ||
| return raiseTestError("test_frozenset_add_in_capi_tracking", | ||
eendebakpt marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| "test object should be tracked"); | ||
| } | ||
| if (PySet_Add(frozenset, tracked_obj) < 0){ | ||
| goto error; | ||
| } | ||
| Py_DECREF(tracked_obj); | ||
| if (!PyObject_GC_IsTracked(frozenset)){ | ||
| Py_DECREF(frozenset); | ||
| return raiseTestError("test_frozenset_add_in_capi_tracking", | ||
| "frozenset with with GC tracked object should be tracked"); | ||
| } | ||
| Py_DECREF(frozenset); | ||
| Py_RETURN_NONE; | ||
| error: | ||
| Py_DECREF(frozenset); | ||
| Py_XDECREF(tracked_obj); | ||
| return NULL; | ||
| } | ||
| static PyObject * | ||
| test_set_contains_does_not_convert_unhashable_key(PyObject *self, PyObject *Py_UNUSED(obj)) | ||
| { | ||
| @@ -219,6 +285,8 @@ static PyMethodDef test_methods[] ={ | ||
| {"set_clear", set_clear, METH_O}, | ||
| {"test_frozenset_add_in_capi", test_frozenset_add_in_capi, METH_NOARGS}, | ||
| {"test_frozenset_add_in_capi_tracking", test_frozenset_add_in_capi_tracking, METH_NOARGS}, | ||
| {"test_frozenset_add_in_capi_tracking_immutable", test_frozenset_add_in_capi_tracking_immutable, METH_NOARGS}, | ||
| {"test_set_contains_does_not_convert_unhashable_key", | ||
| test_set_contains_does_not_convert_unhashable_key, METH_NOARGS}, | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1368,6 +1368,26 @@ make_new_set_basetype(PyTypeObject *type, PyObject *iterable) | ||
| return make_new_set(type, iterable); | ||
| } | ||
| // gh-140232: check whether a frozenset can be untracked from the GC | ||
| static void | ||
| _PyFrozenSet_MaybeUntrack(PyObject *op) | ||
| { | ||
| assert(op != NULL); | ||
| // subclasses of a frozenset can generate reference cycles, so do not untrack | ||
| if (!PyFrozenSet_CheckExact(op)){ | ||
| return; | ||
| } | ||
| // if no elements of a frozenset are tracked by the GC, we untrack the object | ||
| Py_ssize_t pos = 0; | ||
| setentry *entry; | ||
| while (set_next((PySetObject *)op, &pos, &entry)){ | ||
| if (_PyObject_GC_MAY_BE_TRACKED(entry->key)){ | ||
efimov-mikhail marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| return; | ||
| } | ||
| } | ||
| _PyObject_GC_UNTRACK(op); | ||
| } | ||
| static PyObject * | ||
| make_new_frozenset(PyTypeObject *type, PyObject *iterable) | ||
| { | ||
| @@ -1379,7 +1399,11 @@ make_new_frozenset(PyTypeObject *type, PyObject *iterable) | ||
| /* frozenset(f) is idempotent */ | ||
| return Py_NewRef(iterable); | ||
| } | ||
| return make_new_set(type, iterable); | ||
| PyObject *obj = make_new_set(type, iterable); | ||
| if (obj != NULL){ | ||
| _PyFrozenSet_MaybeUntrack(obj); | ||
| } | ||
| return obj; | ||
| } | ||
| static PyObject * | ||
| @@ -2932,7 +2956,11 @@ PySet_New(PyObject *iterable) | ||
| PyObject * | ||
| PyFrozenSet_New(PyObject *iterable) | ||
| { | ||
| return make_new_set(&PyFrozenSet_Type, iterable); | ||
| PyObject *result = make_new_set(&PyFrozenSet_Type, iterable); | ||
| if (result != NULL){ | ||
| _PyFrozenSet_MaybeUntrack(result); | ||
| } | ||
| return result; | ||
| } | ||
| Py_ssize_t | ||
| @@ -3010,6 +3038,11 @@ PySet_Add(PyObject *anyset, PyObject *key) | ||
| // API limits the usage of `PySet_Add` to "fill in the values of brand | ||
| // new frozensets before they are exposed to other code". In this case, | ||
| // this can be done without a lock. | ||
| // Since another key is added to the set, we must track the frozenset | ||
| // if needed. | ||
| if (PyFrozenSet_CheckExact(anyset) && !PyObject_GC_IsTracked(anyset) && PyObject_GC_IsTracked(key)){ | ||
| _PyObject_GC_TRACK(anyset); | ||
| } | ||
| return set_add_key((PySetObject *)anyset, key); | ||
| } | ||
Oops, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.