mirror of https://github.com/python/cpython
Issue #1721812: Binary operations and copy operations on set/frozenset
subclasses need to return the base type, not the subclass itself.
This commit is contained in:
parent
4a1f593df5
commit
7d99f09f89
|
@ -71,7 +71,7 @@ class TestJointOps(unittest.TestCase):
|
||||||
for c in self.letters:
|
for c in self.letters:
|
||||||
self.assertEqual(c in u, c in self.d or c in self.otherword)
|
self.assertEqual(c in u, c in self.d or c in self.otherword)
|
||||||
self.assertEqual(self.s, self.thetype(self.word))
|
self.assertEqual(self.s, self.thetype(self.word))
|
||||||
self.assertEqual(type(u), self.thetype)
|
self.assertEqual(type(u), self.basetype)
|
||||||
self.assertRaises(PassThru, self.s.union, check_pass_thru())
|
self.assertRaises(PassThru, self.s.union, check_pass_thru())
|
||||||
self.assertRaises(TypeError, self.s.union, [[]])
|
self.assertRaises(TypeError, self.s.union, [[]])
|
||||||
for C in set, frozenset, dict.fromkeys, str, list, tuple:
|
for C in set, frozenset, dict.fromkeys, str, list, tuple:
|
||||||
|
@ -97,7 +97,7 @@ class TestJointOps(unittest.TestCase):
|
||||||
for c in self.letters:
|
for c in self.letters:
|
||||||
self.assertEqual(c in i, c in self.d and c in self.otherword)
|
self.assertEqual(c in i, c in self.d and c in self.otherword)
|
||||||
self.assertEqual(self.s, self.thetype(self.word))
|
self.assertEqual(self.s, self.thetype(self.word))
|
||||||
self.assertEqual(type(i), self.thetype)
|
self.assertEqual(type(i), self.basetype)
|
||||||
self.assertRaises(PassThru, self.s.intersection, check_pass_thru())
|
self.assertRaises(PassThru, self.s.intersection, check_pass_thru())
|
||||||
for C in set, frozenset, dict.fromkeys, str, list, tuple:
|
for C in set, frozenset, dict.fromkeys, str, list, tuple:
|
||||||
self.assertEqual(self.thetype('abcba').intersection(C('cdc')), set('cc'))
|
self.assertEqual(self.thetype('abcba').intersection(C('cdc')), set('cc'))
|
||||||
|
@ -142,7 +142,7 @@ class TestJointOps(unittest.TestCase):
|
||||||
for c in self.letters:
|
for c in self.letters:
|
||||||
self.assertEqual(c in i, c in self.d and c not in self.otherword)
|
self.assertEqual(c in i, c in self.d and c not in self.otherword)
|
||||||
self.assertEqual(self.s, self.thetype(self.word))
|
self.assertEqual(self.s, self.thetype(self.word))
|
||||||
self.assertEqual(type(i), self.thetype)
|
self.assertEqual(type(i), self.basetype)
|
||||||
self.assertRaises(PassThru, self.s.difference, check_pass_thru())
|
self.assertRaises(PassThru, self.s.difference, check_pass_thru())
|
||||||
self.assertRaises(TypeError, self.s.difference, [[]])
|
self.assertRaises(TypeError, self.s.difference, [[]])
|
||||||
for C in set, frozenset, dict.fromkeys, str, list, tuple:
|
for C in set, frozenset, dict.fromkeys, str, list, tuple:
|
||||||
|
@ -169,7 +169,7 @@ class TestJointOps(unittest.TestCase):
|
||||||
for c in self.letters:
|
for c in self.letters:
|
||||||
self.assertEqual(c in i, (c in self.d) ^ (c in self.otherword))
|
self.assertEqual(c in i, (c in self.d) ^ (c in self.otherword))
|
||||||
self.assertEqual(self.s, self.thetype(self.word))
|
self.assertEqual(self.s, self.thetype(self.word))
|
||||||
self.assertEqual(type(i), self.thetype)
|
self.assertEqual(type(i), self.basetype)
|
||||||
self.assertRaises(PassThru, self.s.symmetric_difference, check_pass_thru())
|
self.assertRaises(PassThru, self.s.symmetric_difference, check_pass_thru())
|
||||||
self.assertRaises(TypeError, self.s.symmetric_difference, [[]])
|
self.assertRaises(TypeError, self.s.symmetric_difference, [[]])
|
||||||
for C in set, frozenset, dict.fromkeys, str, list, tuple:
|
for C in set, frozenset, dict.fromkeys, str, list, tuple:
|
||||||
|
@ -325,6 +325,7 @@ class TestJointOps(unittest.TestCase):
|
||||||
|
|
||||||
class TestSet(TestJointOps):
|
class TestSet(TestJointOps):
|
||||||
thetype = set
|
thetype = set
|
||||||
|
basetype = set
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
s = self.thetype()
|
s = self.thetype()
|
||||||
|
@ -357,6 +358,7 @@ class TestSet(TestJointOps):
|
||||||
dup = self.s.copy()
|
dup = self.s.copy()
|
||||||
self.assertEqual(self.s, dup)
|
self.assertEqual(self.s, dup)
|
||||||
self.assertNotEqual(id(self.s), id(dup))
|
self.assertNotEqual(id(self.s), id(dup))
|
||||||
|
self.assertEqual(type(dup), self.basetype)
|
||||||
|
|
||||||
def test_add(self):
|
def test_add(self):
|
||||||
self.s.add('Q')
|
self.s.add('Q')
|
||||||
|
@ -595,6 +597,7 @@ class SetSubclass(set):
|
||||||
|
|
||||||
class TestSetSubclass(TestSet):
|
class TestSetSubclass(TestSet):
|
||||||
thetype = SetSubclass
|
thetype = SetSubclass
|
||||||
|
basetype = set
|
||||||
|
|
||||||
class SetSubclassWithKeywordArgs(set):
|
class SetSubclassWithKeywordArgs(set):
|
||||||
def __init__(self, iterable=[], newarg=None):
|
def __init__(self, iterable=[], newarg=None):
|
||||||
|
@ -608,6 +611,7 @@ class TestSetSubclassWithKeywordArgs(TestSet):
|
||||||
|
|
||||||
class TestFrozenSet(TestJointOps):
|
class TestFrozenSet(TestJointOps):
|
||||||
thetype = frozenset
|
thetype = frozenset
|
||||||
|
basetype = frozenset
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
s = self.thetype(self.word)
|
s = self.thetype(self.word)
|
||||||
|
@ -673,6 +677,7 @@ class FrozenSetSubclass(frozenset):
|
||||||
|
|
||||||
class TestFrozenSetSubclass(TestFrozenSet):
|
class TestFrozenSetSubclass(TestFrozenSet):
|
||||||
thetype = FrozenSetSubclass
|
thetype = FrozenSetSubclass
|
||||||
|
basetype = frozenset
|
||||||
|
|
||||||
def test_constructor_identity(self):
|
def test_constructor_identity(self):
|
||||||
s = self.thetype(range(3))
|
s = self.thetype(range(3))
|
||||||
|
|
|
@ -13,6 +13,11 @@ What's New in Python 3.0 release candiate 3?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #1721812: Binary set operations and copy() returned the input type
|
||||||
|
instead of the appropriate base type. This was incorrect because set
|
||||||
|
subclasses would be created without their __init__() method being called.
|
||||||
|
The corrected behavior brings sets into line with lists and dicts.
|
||||||
|
|
||||||
- Issue #4296: Fix PyObject_RichCompareBool so that "x in [x]" evaluates to
|
- Issue #4296: Fix PyObject_RichCompareBool so that "x in [x]" evaluates to
|
||||||
True, even when x doesn't compare equal to itself. This was a regression
|
True, even when x doesn't compare equal to itself. This was a regression
|
||||||
from 2.6.
|
from 2.6.
|
||||||
|
|
|
@ -1017,6 +1017,18 @@ make_new_set(PyTypeObject *type, PyObject *iterable)
|
||||||
return (PyObject *)so;
|
return (PyObject *)so;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
make_new_set_basetype(PyTypeObject *type, PyObject *iterable)
|
||||||
|
{
|
||||||
|
if (type != &PySet_Type && type != &PyFrozenSet_Type) {
|
||||||
|
if (PyType_IsSubtype(type, &PySet_Type))
|
||||||
|
type = &PySet_Type;
|
||||||
|
else
|
||||||
|
type = &PyFrozenSet_Type;
|
||||||
|
}
|
||||||
|
return make_new_set(type, iterable);
|
||||||
|
}
|
||||||
|
|
||||||
/* The empty frozenset is a singleton */
|
/* The empty frozenset is a singleton */
|
||||||
static PyObject *emptyfrozenset = NULL;
|
static PyObject *emptyfrozenset = NULL;
|
||||||
|
|
||||||
|
@ -1129,7 +1141,7 @@ set_swap_bodies(PySetObject *a, PySetObject *b)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
set_copy(PySetObject *so)
|
set_copy(PySetObject *so)
|
||||||
{
|
{
|
||||||
return make_new_set(Py_TYPE(so), (PyObject *)so);
|
return make_new_set_basetype(Py_TYPE(so), (PyObject *)so);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -1225,7 +1237,7 @@ set_intersection(PySetObject *so, PyObject *other)
|
||||||
if ((PyObject *)so == other)
|
if ((PyObject *)so == other)
|
||||||
return set_copy(so);
|
return set_copy(so);
|
||||||
|
|
||||||
result = (PySetObject *)make_new_set(Py_TYPE(so), NULL);
|
result = (PySetObject *)make_new_set_basetype(Py_TYPE(so), NULL);
|
||||||
if (result == NULL)
|
if (result == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -1520,7 +1532,7 @@ set_difference(PySetObject *so, PyObject *other)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = make_new_set(Py_TYPE(so), NULL);
|
result = make_new_set_basetype(Py_TYPE(so), NULL);
|
||||||
if (result == NULL)
|
if (result == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -1641,7 +1653,7 @@ set_symmetric_difference_update(PySetObject *so, PyObject *other)
|
||||||
Py_INCREF(other);
|
Py_INCREF(other);
|
||||||
otherset = (PySetObject *)other;
|
otherset = (PySetObject *)other;
|
||||||
} else {
|
} else {
|
||||||
otherset = (PySetObject *)make_new_set(Py_TYPE(so), other);
|
otherset = (PySetObject *)make_new_set_basetype(Py_TYPE(so), other);
|
||||||
if (otherset == NULL)
|
if (otherset == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -1672,7 +1684,7 @@ set_symmetric_difference(PySetObject *so, PyObject *other)
|
||||||
PyObject *rv;
|
PyObject *rv;
|
||||||
PySetObject *otherset;
|
PySetObject *otherset;
|
||||||
|
|
||||||
otherset = (PySetObject *)make_new_set(Py_TYPE(so), other);
|
otherset = (PySetObject *)make_new_set_basetype(Py_TYPE(so), other);
|
||||||
if (otherset == NULL)
|
if (otherset == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
rv = set_symmetric_difference_update(otherset, (PyObject *)so);
|
rv = set_symmetric_difference_update(otherset, (PyObject *)so);
|
||||||
|
|
Loading…
Reference in New Issue