From f9bd05e83e32bece49de5af0c9a232325c57648a Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 23 Jun 2020 08:42:55 -0700 Subject: [PATCH] bpo-40521: Empty frozenset is no longer a singleton (GH-21085) * Revert "bpo-40521: Make the empty frozenset per interpreter (GH-21068)" This reverts commit 261cfedf7657a515e04428bba58eba2a9bb88208. * bpo-40521: Empty frozensets are no longer singletons * Complete the removal of the frozenset singleton --- Include/internal/pycore_interp.h | 2 - Include/internal/pycore_pylifecycle.h | 1 - Lib/test/test_marshal.py | 7 ---- Lib/test/test_set.py | 9 ----- .../2020-05-20-01-17-34.bpo-40521.wvAehI.rst | 3 +- .../2020-06-23-07-35-11.bpo-40521.dMNA6k.rst | 1 + Objects/setobject.c | 38 +++---------------- Python/pylifecycle.c | 1 - 8 files changed, 8 insertions(+), 54 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2020-06-23-07-35-11.bpo-40521.dMNA6k.rst diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 64e891f9f6e..c22bea75d27 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 9a3063aa277..30ba48423f9 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 ace1593999d..b7f4dbb98e3 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -158,13 +158,6 @@ class ContainerTestCase(unittest.TestCase, HelperMixin): 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 9851a998983..68d494213e5 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -661,15 +661,6 @@ class TestFrozenSet(TestJointOps, unittest.TestCase): 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 95fab369748..a62383d2093 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 00000000000..25f146e35ef --- /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 69bfc7d0a58..b2711495b65 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 f0770727f4d..09945a8f7a6 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);