Commit 3b5438eb authored by Stefan Behnel's avatar Stefan Behnel

restore singleton property of empty frozenset

parent d195f25a
......@@ -2027,15 +2027,17 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
])
def _handle_simple_function_frozenset(self, node, function, pos_args):
if len(pos_args) != 1:
if not pos_args:
pos_args = [ExprNodes.NullNode(node.pos)]
elif len(pos_args) > 1:
return node
# PyFrozenSet_New(it) is better than a generic Python call to frozenset(it)
return ExprNodes.PythonCapiCallNode(
node.pos, "PyFrozenSet_New",
node.pos, "__Pyx_PyFrozenSet_New",
self.PyFrozenSet_New_func_type,
args=pos_args,
is_temp=node.is_temp,
utility_code=UtilityCode.load_cached('pyset_compat', 'Builtins.c'),
utility_code=UtilityCode.load_cached('pyfrozenset_new', 'Builtins.c'),
py_name="frozenset")
PyObject_AsDouble_func_type = PyrexTypes.CFuncType(
......
......@@ -388,6 +388,9 @@ static CYTHON_INLINE PyObject* __Pyx_PyDict_ViewItems(PyObject* d) {
#define PySet_Size(anyset) \
PyObject_Size((anyset))
#define PySet_GET_SIZE(anyset) \
PyObject_Size((anyset))
#define PySet_Contains(anyset, key) \
PySequence_Contains((anyset), (key))
......@@ -414,3 +417,25 @@ static CYTHON_INLINE int PySet_Add(PyObject *set, PyObject *key) {
#endif /* PyAnySet_CheckExact (<= Py2.4) */
#endif /* < Py2.5 */
//////////////////// pyfrozenset_new.proto ////////////////////
//@substitute: naming
//@requires: pyset_compat
static CYTHON_INLINE PyObject* __Pyx_PyFrozenSet_New(PyObject* it) {
if (it) {
PyObject* result = PyFrozenSet_New(it);
if (unlikely(!result))
return NULL;
if (likely(PySet_GET_SIZE(result)))
return result;
// empty frozenset is a singleton
// seems wasteful, but CPython does the same
Py_DECREF(result);
}
#if CYTHON_COMPILING_IN_CPYTHON
return PyFrozenSet_Type.tp_new(&PyFrozenSet_Type, $empty_tuple, NULL);
#else
return PyObject_Call((PyObject*)&PyFrozenSet_Type, $empty_tuple, NULL);
#endif
}
......@@ -5,6 +5,8 @@ _frozenset = frozenset
cimport cython
import sys
def cython_set():
"""
......@@ -284,6 +286,37 @@ def test_frozenset_of_iterable(x):
return frozenset(x)
@cython.test_assert_path_exists("//PythonCapiCallNode")
@cython.test_fail_if_path_exists(
"//SimpleCallNode",
"//SetNode"
)
def test_empty_frozenset():
"""
>>> s = test_empty_frozenset()
>>> isinstance(s, _frozenset)
True
>>> len(s)
0
>>> sys.version_info < (2,5) or s is frozenset() # singleton!
True
"""
return frozenset()
def test_singleton_empty_frozenset():
"""
>>> test_singleton_empty_frozenset() # from CPython's test_set.py
1
"""
f = frozenset()
efs = [frozenset(), frozenset([]), frozenset(()), frozenset(''),
frozenset(), frozenset([]), frozenset(()), frozenset(''),
frozenset(range(0)), frozenset(frozenset()),
frozenset(f), f]
return len(set(map(id, efs))) if sys.version_info >= (2,5) else 1
def sorted(it):
# Py3 can't compare different types
chars = []
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment