-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
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
Changes from 21 commits
a3292c2
cd294a6
7e28cf2
30057a5
c4deb03
607237a
2735a71
c05db54
7f6bc4b
0b97604
948daed
08e22c3
62afc76
eab653e
37fc61d
4f8bda7
e9d42b4
4b39149
2859802
08f43c5
d12102c
5f8b1f2
ae5cc7f
786019d
f37f46e
afd8a5b
377e6d8
c62fa9a
69b728f
71d9eba
5c19de2
e7dd248
075b582
3dd4c95
8d864ef
e262963
6027391
6269f68
37af64f
ea3ba39
ff61155
6bb0d58
7a4859b
a2bcfdb
88c3f4a
be20046
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -9,6 +9,7 @@ | |||
| import warnings | ||||
| import weakref | ||||
| from random import randrange, shuffle | ||||
| import _testcapi | ||||
| from test import support | ||||
| from test.support import warnings_helper | ||||
|
|
||||
|
|
@@ -2212,6 +2213,31 @@ def test_cuboctahedron(self): | |||
| for cubevert in edge: | ||||
| self.assertIn(cubevert, g) | ||||
|
|
||||
| class TestPySet_Add(unittest.TestCase): | ||||
| def test_set(self): | ||||
| # Test the PySet_Add c-api for set objects | ||||
| s = set() | ||||
| self.assertEqual(_testcapi.pyset_add(s, 1), {1}) | ||||
| self.assertRaises(TypeError, _testcapi.pyset_add, s, []) | ||||
|
|
||||
| def test_frozenset(self): | ||||
| # Test the PySet_Add c-api for frozenset objects | ||||
| self.assertEqual(_testcapi.pyset_add(frozenset(), 1), frozenset([1])) | ||||
| frozen_set = frozenset() | ||||
| # if the argument to PySet_Add is a frozenset that is not uniquely references an error is generated | ||||
| self.assertRaises(SystemError, _testcapi.pyset_add, frozen_set, 1) | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand why the second test fails, whereas the first succeed.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The second test fails because the argument Line 2777 in ce4b0ed
I will add a comment to the test |
||||
|
|
||||
| def test_frozenset_gc_tracking(self): | ||||
| # see gh-140234 | ||||
|
efimov-mikhail marked this conversation as resolved.
Outdated
|
||||
| class TrackedHashableClass(): | ||||
| pass | ||||
|
|
||||
| a = TrackedHashableClass() | ||||
| result_set = _testcapi.pyset_add(frozenset(), 1) | ||||
| self.assertFalse(gc.is_tracked(result_set)) | ||||
| result_set = _testcapi.pyset_add(frozenset(), a) | ||||
| self.assertTrue(gc.is_tracked(result_set)) | ||||
|
|
||||
|
|
||||
| #============================================================================== | ||||
|
eendebakpt marked this conversation as resolved.
|
||||
|
|
||||
|
|
||||
| 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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2435,6 +2435,26 @@ test_critical_sections(PyObject *module, PyObject *Py_UNUSED(args)) | |
| } | ||
|
|
||
|
|
||
|
|
||
| static PyObject * | ||
| // Interface to PySet_Add, returning the set | ||
| pyset_add(PyObject* self, PyObject* const* args, Py_ssize_t nargsf) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please move this function to Modules/_testlimitedcapi/set.c.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I moved the function and converted the calling convention to METH_VARARGS. This adds another reference to the set causing the test to fail. I will convert back to METH_FASTCALL, or define the set at C level. |
||
| { | ||
| Py_ssize_t nargs = _PyVectorcall_NARGS(nargsf); | ||
| if (nargs != 2) { | ||
| PyErr_SetString(PyExc_ValueError, "pyset_add requires exactly two arguments"); | ||
| return NULL; | ||
|
eendebakpt marked this conversation as resolved.
Outdated
|
||
| } | ||
| PyObject *set = args[0]; | ||
| PyObject *item = args[1]; | ||
|
|
||
| int return_value = PySet_Add(set, item); | ||
| if (return_value < 0) { | ||
| return NULL; | ||
| } | ||
| return Py_NewRef(set); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should return None on success.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From #140234 (comment) I gathered we prefer to write the tests in Python (as much as possible). I can return None here, but then I have convert the code from
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vstinner A branch with the tests written in C is here: main...eendebakpt:cpython:frozenset_immutable_tracking_c Let me know if you prefer that approach, then I will merge the branch into this PR.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Why do you have to write tests in C if this function returns None? Are you talking about tests on frozenset? I see this code in PySet_Add(): if (PyFrozenSet_Check(anyset) && _PyObject_IsUniquelyReferenced(anyset)) {
// We can only change frozensets if they are uniquely referenced. The
// 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.
return set_add_key((PySetObject *)anyset, key);
}If you want to write tests for this code path, writing them in C would be more reliable to have a fine control on the reference count, right. But other tests on the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason for returning the set in fails (because the frozenset is not uniquely referenced). For that reason I used: Doing that in C indeed seems better. The other tests (for this PR the |
||
| } | ||
|
|
||
| // Used by `finalize_thread_hang`. | ||
|
eendebakpt marked this conversation as resolved.
|
||
| #if defined(_POSIX_THREADS) && !defined(__wasi__) | ||
| static void finalize_thread_hang_cleanup_callback(void *Py_UNUSED(arg)) { | ||
|
|
@@ -2658,7 +2678,7 @@ static PyMethodDef TestMethods[] = { | |
| {"return_null_without_error", return_null_without_error, METH_NOARGS}, | ||
| {"return_result_with_error", return_result_with_error, METH_NOARGS}, | ||
| {"getitem_with_error", getitem_with_error, METH_VARARGS}, | ||
| {"Py_CompileString", pycompilestring, METH_O}, | ||
| {"Py_CompileString", pycompilestring, METH_O}, | ||
|
eendebakpt marked this conversation as resolved.
Outdated
|
||
| {"raise_SIGINT_then_send_None", raise_SIGINT_then_send_None, METH_VARARGS}, | ||
| {"stack_pointer", stack_pointer, METH_NOARGS}, | ||
| #ifdef W_STOPCODE | ||
|
|
@@ -2679,6 +2699,7 @@ static PyMethodDef TestMethods[] = { | |
| {"gen_get_code", gen_get_code, METH_O, NULL}, | ||
| {"get_feature_macros", get_feature_macros, METH_NOARGS, NULL}, | ||
| {"test_code_api", test_code_api, METH_NOARGS, NULL}, | ||
| {"pyset_add", _PyCFunction_CAST(pyset_add), METH_FASTCALL, NULL}, | ||
| {"settrace_to_error", settrace_to_error, METH_O, NULL}, | ||
| {"settrace_to_record", settrace_to_record, METH_O, NULL}, | ||
| {"test_macros", test_macros, METH_NOARGS, NULL}, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move these tests to Lib/test/test_capi/test_set.py.