| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent f1c6ae3 commit 2d03b73
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4923,6 +4923,23 @@ def __new__(cls): | |||
| 4923 | 4923 | cls.lst = [2**i for i in range(10000)] | |
| 4924 | 4924 | X.descr | |
| 4925 | 4925 | ||
| 4926 | + def test_remove_subclass(self): | ||
| 4927 | + # bpo-46417: when the last subclass of a type is deleted, | ||
| 4928 | + # remove_subclass() clears the internal dictionary of subclasses: | ||
| 4929 | + # set PyTypeObject.tp_subclasses to NULL. remove_subclass() is called | ||
| 4930 | + # when a type is deallocated. | ||
| 4931 | + class Parent: | ||
| 4932 | + pass | ||
| 4933 | + self.assertEqual(Parent.__subclasses__(), []) | ||
| 4934 | + | ||
| 4935 | + class Child(Parent): | ||
| 4936 | + pass | ||
| 4937 | + self.assertEqual(Parent.__subclasses__(), [Child]) | ||
| 4938 | + | ||
| 4939 | + del Child | ||
| 4940 | + gc.collect() | ||
| 4941 | + self.assertEqual(Parent.__subclasses__(), []) | ||
| 4942 | + | ||
| 4926 | 4943 | ||
| 4927 | 4944 | class DictProxyTests(unittest.TestCase): | |
| 4928 | 4945 | def setUp(self): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4137,29 +4137,28 @@ _PyType_GetSubclasses(PyTypeObject *self) | |||
| 4137 | 4137 | return NULL; | |
| 4138 | 4138 | } | |
| 4139 | 4139 | ||
| 4140 | - // Hold a strong reference to tp_subclasses while iterating on it | ||
| 4141 | - PyObject *dict = Py_XNewRef(self->tp_subclasses); | ||
| 4142 | - if (dict == NULL) { | ||
| 4140 | + PyObject *subclasses = self->tp_subclasses; // borrowed ref | ||
| 4141 | + if (subclasses == NULL) { | ||
| 4143 | 4142 | return list; | |
| 4144 | 4143 | } | |
| 4145 | - assert(PyDict_CheckExact(dict)); | ||
| 4144 | + assert(PyDict_CheckExact(subclasses)); | ||
| 4145 | + // The loop cannot modify tp_subclasses, there is no need | ||
| 4146 | + // to hold a strong reference (use a borrowed reference). | ||
| 4146 | 4147 | ||
| 4147 | 4148 | Py_ssize_t i = 0; | |
| 4148 | 4149 | PyObject *ref; // borrowed ref | |
| 4149 | - while (PyDict_Next(dict, &i, NULL, &ref)) { | ||
| 4150 | + while (PyDict_Next(subclasses, &i, NULL, &ref)) { | ||
| 4150 | 4151 | assert(PyWeakref_CheckRef(ref)); | |
| 4151 | 4152 | PyObject *obj = PyWeakref_GET_OBJECT(ref); // borrowed ref | |
| 4152 | 4153 | if (obj == Py_None) { | |
| 4153 | 4154 | continue; | |
| 4154 | 4155 | } | |
| 4155 | 4156 | assert(PyType_Check(obj)); | |
| 4156 | 4157 | if (PyList_Append(list, obj) < 0) { | |
| 4157 | - Py_CLEAR(list); | ||
| 4158 | - goto done; | ||
| 4158 | + Py_DECREF(list); | ||
| 4159 | + return NULL; | ||
| 4159 | 4160 | } | |
| 4160 | 4161 | } | |
| 4161 | - done: | ||
| 4162 | - Py_DECREF(dict); | ||
| 4163 | 4162 | return list; | |
| 4164 | 4163 | } | |
| 4165 | 4164 | ||
@@ -6568,6 +6567,13 @@ remove_subclass(PyTypeObject *base, PyTypeObject *type) | |||
| 6568 | 6567 | PyErr_Clear(); | |
| 6569 | 6568 | } | |
| 6570 | 6569 | Py_XDECREF(key); | |
| 6570 | + | ||
| 6571 | + if (PyDict_Size(dict) == 0) { | ||
| 6572 | + // Delete the dictionary to save memory. _PyStaticType_Dealloc() | ||
| 6573 | + // callers also test if tp_subclasses is NULL to check if a static type | ||
| 6574 | + // has no subclass. | ||
| 6575 | + Py_CLEAR(base->tp_subclasses); | ||
| 6576 | + } | ||
| 6571 | 6577 | } | |
| 6572 | 6578 | ||
| 6573 | 6579 | static void | |
| Back | FazBrowse Home | New Git URL |
0 commit comments