Skip to content

Commit

Permalink
bpo-40521: Empty frozenset is no longer a singleton (GH-21085)
Browse files Browse the repository at this point in the history
* Revert "bpo-40521: Make the empty frozenset per interpreter (GH-21068)"

This reverts commit 261cfed.

* bpo-40521: Empty frozensets are no longer singletons

* Complete the removal of the frozenset singleton
  • Loading branch information
rhettinger committed Jun 23, 2020
1 parent 522691c commit f9bd05e
Show file tree
Hide file tree
Showing 8 changed files with 8 additions and 54 deletions.
2 changes: 0 additions & 2 deletions Include/internal/pycore_interp.h
Expand Up @@ -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;
Expand Down
1 change: 0 additions & 1 deletion Include/internal/pycore_pylifecycle.h
Expand Up @@ -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);
Expand Down
7 changes: 0 additions & 7 deletions Lib/test/test_marshal.py
Expand Up @@ -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):

Expand Down
9 changes: 0 additions & 9 deletions Lib/test/test_set.py
Expand Up @@ -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)
Expand Down
Expand Up @@ -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.

@@ -0,0 +1 @@
Empty frozensets are no longer singletons.
38 changes: 5 additions & 33 deletions Objects/setobject.c
Expand Up @@ -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 *
Expand Down Expand Up @@ -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)
{
Expand Down
1 change: 0 additions & 1 deletion Python/pylifecycle.c
Expand Up @@ -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);
Expand Down

0 comments on commit f9bd05e

Please sign in to comment.