diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 64e891f9f6eb4d..c22bea75d2795c 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -244,8 +244,6 @@ struct _is { /* Using a cache is very effective since typically only a single slice is created and then deleted again. */ PySliceObject *slice_cache; - // The empty frozenset is a singleton. - PyObject *empty_frozenset; struct _Py_tuple_state tuple; struct _Py_list_state list; diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 9a3063aa2775f0..30ba48423f9ec4 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -62,7 +62,6 @@ extern void _PyFrame_Fini(PyThreadState *tstate); extern void _PyDict_Fini(PyThreadState *tstate); extern void _PyTuple_Fini(PyThreadState *tstate); extern void _PyList_Fini(PyThreadState *tstate); -extern void _PySet_Fini(PyThreadState *tstate); extern void _PyBytes_Fini(PyThreadState *tstate); extern void _PyFloat_Fini(PyThreadState *tstate); extern void _PySlice_Fini(PyThreadState *tstate); diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index ace1593999d4eb..b7f4dbb98e36d4 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -158,13 +158,6 @@ def test_sets(self): for constructor in (set, frozenset): self.helper(constructor(self.d.keys())) - @support.cpython_only - def test_empty_frozenset_singleton(self): - # marshal.loads() must reuse the empty frozenset singleton - obj = frozenset() - obj2 = marshal.loads(marshal.dumps(obj)) - self.assertIs(obj2, obj) - class BufferTestCase(unittest.TestCase, HelperMixin): diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 9851a998983f83..68d494213e5870 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -661,15 +661,6 @@ def test_init(self): s.__init__(self.otherword) self.assertEqual(s, set(self.word)) - def test_singleton_empty_frozenset(self): - f = frozenset() - efs = [frozenset(), frozenset([]), frozenset(()), frozenset(''), - frozenset(), frozenset([]), frozenset(()), frozenset(''), - frozenset(range(0)), frozenset(frozenset()), - frozenset(f), f] - # All of the empty frozensets should have just one id() - self.assertEqual(len(set(map(id, efs))), 1) - def test_constructor_identity(self): s = self.thetype(range(3)) t = self.thetype(s) diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst index 95fab369748f0a..a62383d2093ecd 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst @@ -2,8 +2,9 @@ Each interpreter now its has own free lists, singletons and caches: * Free lists: float, tuple, list, dict, frame, context, asynchronous generator. -* Singletons: empty tuple, empty frozenset, empty bytes string, +* Singletons: empty tuple, empty bytes string, single byte character. * Slice cache. They are no longer shared by all interpreters. + diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-23-07-35-11.bpo-40521.dMNA6k.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-23-07-35-11.bpo-40521.dMNA6k.rst new file mode 100644 index 00000000000000..25f146e35ef439 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-06-23-07-35-11.bpo-40521.dMNA6k.rst @@ -0,0 +1 @@ +Empty frozensets are no longer singletons. diff --git a/Objects/setobject.c b/Objects/setobject.c index 69bfc7d0a58fb2..b2711495b657bd 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -978,38 +978,16 @@ make_new_set_basetype(PyTypeObject *type, PyObject *iterable) static PyObject * make_new_frozenset(PyTypeObject *type, PyObject *iterable) { - PyObject *res; - if (type != &PyFrozenSet_Type) { return make_new_set(type, iterable); } - if (iterable != NULL) { - if (PyFrozenSet_CheckExact(iterable)) { - /* frozenset(f) is idempotent */ - Py_INCREF(iterable); - return iterable; - } - res = make_new_set((PyTypeObject *)type, iterable); - if (res == NULL || PySet_GET_SIZE(res) != 0) { - return res; - } - /* If the created frozenset is empty, return the empty frozenset singleton instead */ - Py_DECREF(res); + if (iterable != NULL && PyFrozenSet_CheckExact(iterable)) { + /* frozenset(f) is idempotent */ + Py_INCREF(iterable); + return iterable; } - - // The empty frozenset is a singleton - PyInterpreterState *interp = _PyInterpreterState_GET(); - res = interp->empty_frozenset; - if (res == NULL) { - interp->empty_frozenset = make_new_set((PyTypeObject *)type, NULL); - res = interp->empty_frozenset; - if (res == NULL) { - return NULL; - } - } - Py_INCREF(res); - return res; + return make_new_set((PyTypeObject *)type, iterable); } static PyObject * @@ -2304,12 +2282,6 @@ PySet_Add(PyObject *anyset, PyObject *key) return set_add_key((PySetObject *)anyset, key); } -void -_PySet_Fini(PyThreadState *tstate) -{ - Py_CLEAR(tstate->interp->empty_frozenset); -} - int _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash) { diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index f0770727f4de71..09945a8f7a6a07 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1253,7 +1253,6 @@ finalize_interp_types(PyThreadState *tstate, int is_main_interp) _PyAsyncGen_Fini(tstate); _PyContext_Fini(tstate); - _PySet_Fini(tstate); _PyDict_Fini(tstate); _PyList_Fini(tstate); _PyTuple_Fini(tstate);