mirror of
https://github.com/python/cpython.git
synced 2025-08-03 00:23:06 +00:00
Change filterstring() and filterunicode(): If the
object is not a real str or unicode but an instance of a subclass, construct the output via looping over __getitem__. This guarantees that the result is the same for function==None and function==lambda x:x This doesn't happen for tuples, because filtertuple() uses PyTuple_GetItem(). (This was discussed on SF bug #665835).
This commit is contained in:
parent
b4bb64e288
commit
1918f7755e
2 changed files with 74 additions and 55 deletions
|
@ -418,26 +418,40 @@ class BuiltinTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_filter_subclasses(self):
|
def test_filter_subclasses(self):
|
||||||
# test, that filter() never returns tuple, str or unicode subclasses
|
# test, that filter() never returns tuple, str or unicode subclasses
|
||||||
|
# and that the result always go's through __getitem__
|
||||||
|
# FIXME: For tuple currently it doesn't go through __getitem__
|
||||||
funcs = (None, lambda x: True)
|
funcs = (None, lambda x: True)
|
||||||
class tuple2(tuple):
|
class tuple2(tuple):
|
||||||
pass
|
def __getitem__(self, index):
|
||||||
|
return 2*tuple.__getitem__(self, index)
|
||||||
class str2(str):
|
class str2(str):
|
||||||
pass
|
def __getitem__(self, index):
|
||||||
|
return 2*str.__getitem__(self, index)
|
||||||
inputs = {
|
inputs = {
|
||||||
tuple2: [(), (1,2,3)],
|
tuple2: {(): (), (1, 2, 3): (1, 2, 3)}, # FIXME
|
||||||
str2: ["", "123"]
|
str2: {"": "", "123": "112233"}
|
||||||
}
|
}
|
||||||
if have_unicode:
|
if have_unicode:
|
||||||
class unicode2(unicode):
|
class unicode2(unicode):
|
||||||
pass
|
def __getitem__(self, index):
|
||||||
inputs[unicode2] = [unicode(), unicode("123")]
|
return 2*unicode.__getitem__(self, index)
|
||||||
|
inputs[unicode2] = {
|
||||||
|
unicode(): unicode(),
|
||||||
|
unicode("123"): unicode("112233")
|
||||||
|
}
|
||||||
|
|
||||||
for func in funcs:
|
for (cls, inps) in inputs.iteritems():
|
||||||
for (cls, inps) in inputs.iteritems():
|
for (inp, exp) in inps.iteritems():
|
||||||
for inp in inps:
|
# make sure the output goes through __getitem__
|
||||||
out = filter(func, cls(inp))
|
# even if func is None
|
||||||
self.assertEqual(inp, out)
|
self.assertEqual(
|
||||||
self.assert_(not isinstance(out, cls))
|
filter(funcs[0], cls(inp)),
|
||||||
|
filter(funcs[1], cls(inp))
|
||||||
|
)
|
||||||
|
for func in funcs:
|
||||||
|
outp = filter(func, cls(inp))
|
||||||
|
self.assertEqual(outp, exp)
|
||||||
|
self.assert_(not isinstance(outp, cls))
|
||||||
|
|
||||||
def test_float(self):
|
def test_float(self):
|
||||||
self.assertEqual(float(3.14), 3.14)
|
self.assertEqual(float(3.14), 3.14)
|
||||||
|
|
|
@ -1934,40 +1934,43 @@ filterstring(PyObject *func, PyObject *strobj)
|
||||||
int outlen = len;
|
int outlen = len;
|
||||||
|
|
||||||
if (func == Py_None) {
|
if (func == Py_None) {
|
||||||
/* No character is ever false -- share input string
|
/* If it's a real string we can return the original,
|
||||||
* (if it's not a subclass) */
|
* as no character is ever false and __getitem__
|
||||||
if (PyString_CheckExact(strobj))
|
* does return this character. If it's a subclass
|
||||||
|
* we must go through the __getitem__ loop */
|
||||||
|
if (PyString_CheckExact(strobj)) {
|
||||||
Py_INCREF(strobj);
|
Py_INCREF(strobj);
|
||||||
else
|
return strobj;
|
||||||
strobj = PyString_FromStringAndSize(
|
}
|
||||||
PyString_AS_STRING(strobj),
|
|
||||||
len
|
|
||||||
);
|
|
||||||
return strobj;
|
|
||||||
}
|
}
|
||||||
if ((result = PyString_FromStringAndSize(NULL, len)) == NULL)
|
if ((result = PyString_FromStringAndSize(NULL, len)) == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (i = j = 0; i < len; ++i) {
|
for (i = j = 0; i < len; ++i) {
|
||||||
PyObject *item, *arg, *good;
|
PyObject *item;
|
||||||
int ok;
|
int ok;
|
||||||
|
|
||||||
item = (*strobj->ob_type->tp_as_sequence->sq_item)(strobj, i);
|
item = (*strobj->ob_type->tp_as_sequence->sq_item)(strobj, i);
|
||||||
if (item == NULL)
|
if (item == NULL)
|
||||||
goto Fail_1;
|
goto Fail_1;
|
||||||
arg = Py_BuildValue("(O)", item);
|
if (func==Py_None) {
|
||||||
if (arg == NULL) {
|
ok = 1;
|
||||||
Py_DECREF(item);
|
} else {
|
||||||
goto Fail_1;
|
PyObject *arg, *good;
|
||||||
|
arg = Py_BuildValue("(O)", item);
|
||||||
|
if (arg == NULL) {
|
||||||
|
Py_DECREF(item);
|
||||||
|
goto Fail_1;
|
||||||
|
}
|
||||||
|
good = PyEval_CallObject(func, arg);
|
||||||
|
Py_DECREF(arg);
|
||||||
|
if (good == NULL) {
|
||||||
|
Py_DECREF(item);
|
||||||
|
goto Fail_1;
|
||||||
|
}
|
||||||
|
ok = PyObject_IsTrue(good);
|
||||||
|
Py_DECREF(good);
|
||||||
}
|
}
|
||||||
good = PyEval_CallObject(func, arg);
|
|
||||||
Py_DECREF(arg);
|
|
||||||
if (good == NULL) {
|
|
||||||
Py_DECREF(item);
|
|
||||||
goto Fail_1;
|
|
||||||
}
|
|
||||||
ok = PyObject_IsTrue(good);
|
|
||||||
Py_DECREF(good);
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
int reslen;
|
int reslen;
|
||||||
if (!PyString_Check(item)) {
|
if (!PyString_Check(item)) {
|
||||||
|
@ -2026,16 +2029,14 @@ filterunicode(PyObject *func, PyObject *strobj)
|
||||||
int outlen = len;
|
int outlen = len;
|
||||||
|
|
||||||
if (func == Py_None) {
|
if (func == Py_None) {
|
||||||
/* No character is ever false -- share input string
|
/* If it's a real string we can return the original,
|
||||||
* (it if's not a subclass) */
|
* as no character is ever false and __getitem__
|
||||||
if (PyUnicode_CheckExact(strobj))
|
* does return this character. If it's a subclass
|
||||||
|
* we must go through the __getitem__ loop */
|
||||||
|
if (PyUnicode_CheckExact(strobj)) {
|
||||||
Py_INCREF(strobj);
|
Py_INCREF(strobj);
|
||||||
else
|
return strobj;
|
||||||
strobj = PyUnicode_FromUnicode(
|
}
|
||||||
PyUnicode_AS_UNICODE(strobj),
|
|
||||||
len
|
|
||||||
);
|
|
||||||
return strobj;
|
|
||||||
}
|
}
|
||||||
if ((result = PyUnicode_FromUnicode(NULL, len)) == NULL)
|
if ((result = PyUnicode_FromUnicode(NULL, len)) == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -2047,19 +2048,23 @@ filterunicode(PyObject *func, PyObject *strobj)
|
||||||
item = (*strobj->ob_type->tp_as_sequence->sq_item)(strobj, i);
|
item = (*strobj->ob_type->tp_as_sequence->sq_item)(strobj, i);
|
||||||
if (item == NULL)
|
if (item == NULL)
|
||||||
goto Fail_1;
|
goto Fail_1;
|
||||||
arg = Py_BuildValue("(O)", item);
|
if (func == Py_None) {
|
||||||
if (arg == NULL) {
|
ok = 1;
|
||||||
Py_DECREF(item);
|
} else {
|
||||||
goto Fail_1;
|
arg = Py_BuildValue("(O)", item);
|
||||||
|
if (arg == NULL) {
|
||||||
|
Py_DECREF(item);
|
||||||
|
goto Fail_1;
|
||||||
|
}
|
||||||
|
good = PyEval_CallObject(func, arg);
|
||||||
|
Py_DECREF(arg);
|
||||||
|
if (good == NULL) {
|
||||||
|
Py_DECREF(item);
|
||||||
|
goto Fail_1;
|
||||||
|
}
|
||||||
|
ok = PyObject_IsTrue(good);
|
||||||
|
Py_DECREF(good);
|
||||||
}
|
}
|
||||||
good = PyEval_CallObject(func, arg);
|
|
||||||
Py_DECREF(arg);
|
|
||||||
if (good == NULL) {
|
|
||||||
Py_DECREF(item);
|
|
||||||
goto Fail_1;
|
|
||||||
}
|
|
||||||
ok = PyObject_IsTrue(good);
|
|
||||||
Py_DECREF(good);
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
int reslen;
|
int reslen;
|
||||||
if (!PyUnicode_Check(item)) {
|
if (!PyUnicode_Check(item)) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue