startswith and endswith don't accept None as slice index. Patch by Torsten Becker. (closes #11828)

This commit is contained in:
Jesus Cea 2011-04-20 17:09:23 +02:00
parent d07eaf177c
commit ac4515063c
8 changed files with 197 additions and 64 deletions

View File

@ -1156,6 +1156,63 @@ class MixinStrUnicodeUserStringTest:
self.checkraises(ValueError, S, 'rpartition', '') self.checkraises(ValueError, S, 'rpartition', '')
self.checkraises(TypeError, S, 'rpartition', None) self.checkraises(TypeError, S, 'rpartition', None)
def test_none_arguments(self):
# issue 11828
s = 'hello'
self.checkequal(2, s, 'find', 'l', None)
self.checkequal(3, s, 'find', 'l', -2, None)
self.checkequal(2, s, 'find', 'l', None, -2)
self.checkequal(0, s, 'find', 'h', None, None)
self.checkequal(3, s, 'rfind', 'l', None)
self.checkequal(3, s, 'rfind', 'l', -2, None)
self.checkequal(2, s, 'rfind', 'l', None, -2)
self.checkequal(0, s, 'rfind', 'h', None, None)
self.checkequal(2, s, 'index', 'l', None)
self.checkequal(3, s, 'index', 'l', -2, None)
self.checkequal(2, s, 'index', 'l', None, -2)
self.checkequal(0, s, 'index', 'h', None, None)
self.checkequal(3, s, 'rindex', 'l', None)
self.checkequal(3, s, 'rindex', 'l', -2, None)
self.checkequal(2, s, 'rindex', 'l', None, -2)
self.checkequal(0, s, 'rindex', 'h', None, None)
self.checkequal(2, s, 'count', 'l', None)
self.checkequal(1, s, 'count', 'l', -2, None)
self.checkequal(1, s, 'count', 'l', None, -2)
self.checkequal(0, s, 'count', 'x', None, None)
self.checkequal(True, s, 'endswith', 'o', None)
self.checkequal(True, s, 'endswith', 'lo', -2, None)
self.checkequal(True, s, 'endswith', 'l', None, -2)
self.checkequal(False, s, 'endswith', 'x', None, None)
self.checkequal(True, s, 'startswith', 'h', None)
self.checkequal(True, s, 'startswith', 'l', -2, None)
self.checkequal(True, s, 'startswith', 'h', None, -2)
self.checkequal(False, s, 'startswith', 'x', None, None)
def test_find_etc_raise_correct_error_messages(self):
# issue 11828
s = 'hello'
x = 'x'
self.assertRaisesRegexp(TypeError, r'^find\(', s.find,
x, None, None, None)
self.assertRaisesRegexp(TypeError, r'^rfind\(', s.rfind,
x, None, None, None)
self.assertRaisesRegexp(TypeError, r'^index\(', s.index,
x, None, None, None)
self.assertRaisesRegexp(TypeError, r'^rindex\(', s.rindex,
x, None, None, None)
self.assertRaisesRegexp(TypeError, r'^count\(', s.count,
x, None, None, None)
self.assertRaisesRegexp(TypeError, r'^startswith\(', s.startswith,
x, None, None, None)
self.assertRaisesRegexp(TypeError, r'^endswith\(', s.endswith,
x, None, None, None)
class MixinStrUnicodeTest: class MixinStrUnicodeTest:
# Additional tests that only work with str and unicode. # Additional tests that only work with str and unicode.

View File

@ -459,6 +459,68 @@ class BaseBytesTest(unittest.TestCase):
self.assertRaises(ValueError, self.type2test.maketrans, b'abc', b'xyzq') self.assertRaises(ValueError, self.type2test.maketrans, b'abc', b'xyzq')
self.assertRaises(TypeError, self.type2test.maketrans, 'abc', 'def') self.assertRaises(TypeError, self.type2test.maketrans, 'abc', 'def')
def test_none_arguments(self):
# issue 11828
b = self.type2test(b'hello')
l = self.type2test(b'l')
h = self.type2test(b'h')
x = self.type2test(b'x')
o = self.type2test(b'o')
self.assertEqual(2, b.find(l, None))
self.assertEqual(3, b.find(l, -2, None))
self.assertEqual(2, b.find(l, None, -2))
self.assertEqual(0, b.find(h, None, None))
self.assertEqual(3, b.rfind(l, None))
self.assertEqual(3, b.rfind(l, -2, None))
self.assertEqual(2, b.rfind(l, None, -2))
self.assertEqual(0, b.rfind(h, None, None))
self.assertEqual(2, b.index(l, None))
self.assertEqual(3, b.index(l, -2, None))
self.assertEqual(2, b.index(l, None, -2))
self.assertEqual(0, b.index(h, None, None))
self.assertEqual(3, b.rindex(l, None))
self.assertEqual(3, b.rindex(l, -2, None))
self.assertEqual(2, b.rindex(l, None, -2))
self.assertEqual(0, b.rindex(h, None, None))
self.assertEqual(2, b.count(l, None))
self.assertEqual(1, b.count(l, -2, None))
self.assertEqual(1, b.count(l, None, -2))
self.assertEqual(0, b.count(x, None, None))
self.assertEqual(True, b.endswith(o, None))
self.assertEqual(True, b.endswith(o, -2, None))
self.assertEqual(True, b.endswith(l, None, -2))
self.assertEqual(False, b.endswith(x, None, None))
self.assertEqual(True, b.startswith(h, None))
self.assertEqual(True, b.startswith(l, -2, None))
self.assertEqual(True, b.startswith(h, None, -2))
self.assertEqual(False, b.startswith(x, None, None))
def test_find_etc_raise_correct_error_messages(self):
# issue 11828
b = self.type2test(b'hello')
x = self.type2test(b'x')
self.assertRaisesRegexp(TypeError, r'\bfind\b', b.find,
x, None, None, None)
self.assertRaisesRegexp(TypeError, r'\brfind\b', b.rfind,
x, None, None, None)
self.assertRaisesRegexp(TypeError, r'\bindex\b', b.index,
x, None, None, None)
self.assertRaisesRegexp(TypeError, r'\brindex\b', b.rindex,
x, None, None, None)
self.assertRaisesRegexp(TypeError, r'\bcount\b', b.count,
x, None, None, None)
self.assertRaisesRegexp(TypeError, r'\bstartswith\b', b.startswith,
x, None, None, None)
self.assertRaisesRegexp(TypeError, r'\bendswith\b', b.endswith,
x, None, None, None)
class BytesTest(BaseBytesTest): class BytesTest(BaseBytesTest):
type2test = bytes type2test = bytes

View File

@ -58,6 +58,7 @@ Donald Beaudry
David Beazley David Beazley
Robin Becker Robin Becker
Neal Becker Neal Becker
Torsten Becker
Bill Bedford Bill Bedford
Stefan Behnel Stefan Behnel
Reimer Behrends Reimer Behrends

View File

@ -38,6 +38,9 @@ Core and Builtins
- Issue #8278: On Windows and with a NTFS filesystem, os.stat() and os.utime() - Issue #8278: On Windows and with a NTFS filesystem, os.stat() and os.utime()
can now handle dates after 2038. can now handle dates after 2038.
- issue #11828: startswith and endswith don't accept None as slice index.
Patch by Torsten Becker.
- Issue #4236: PyModule_Create2 now checks the import machinery directly - Issue #4236: PyModule_Create2 now checks the import machinery directly
rather than the Py_IsInitialized flag, avoiding a Fatal Python rather than the Py_IsInitialized flag, avoiding a Fatal Python
error in certain circumstances when an import is done in __del__. error in certain circumstances when an import is done in __del__.

View File

@ -1082,8 +1082,8 @@ bytearray_find_internal(PyByteArrayObject *self, PyObject *args, int dir)
Py_ssize_t start=0, end=PY_SSIZE_T_MAX; Py_ssize_t start=0, end=PY_SSIZE_T_MAX;
Py_ssize_t res; Py_ssize_t res;
if (!PyArg_ParseTuple(args, "O|O&O&:find/rfind/index/rindex", &subobj, if (!stringlib_parse_args_finds("find/rfind/index/rindex",
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end)) args, &subobj, &start, &end))
return -2; return -2;
if (_getbuffer(subobj, &subbuf) < 0) if (_getbuffer(subobj, &subbuf) < 0)
return -2; return -2;
@ -1133,8 +1133,7 @@ bytearray_count(PyByteArrayObject *self, PyObject *args)
Py_buffer vsub; Py_buffer vsub;
PyObject *count_obj; PyObject *count_obj;
if (!PyArg_ParseTuple(args, "O|O&O&:count", &sub_obj, if (!stringlib_parse_args_finds("count", args, &sub_obj, &start, &end))
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
return NULL; return NULL;
if (_getbuffer(sub_obj, &vsub) < 0) if (_getbuffer(sub_obj, &vsub) < 0)
@ -1292,8 +1291,7 @@ bytearray_startswith(PyByteArrayObject *self, PyObject *args)
PyObject *subobj; PyObject *subobj;
int result; int result;
if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &subobj, if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end))
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
return NULL; return NULL;
if (PyTuple_Check(subobj)) { if (PyTuple_Check(subobj)) {
Py_ssize_t i; Py_ssize_t i;
@ -1332,8 +1330,7 @@ bytearray_endswith(PyByteArrayObject *self, PyObject *args)
PyObject *subobj; PyObject *subobj;
int result; int result;
if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &subobj, if (!stringlib_parse_args_finds("endswith", args, &subobj, &start, &end))
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
return NULL; return NULL;
if (PyTuple_Check(subobj)) { if (PyTuple_Check(subobj)) {
Py_ssize_t i; Py_ssize_t i;

View File

@ -1567,19 +1567,9 @@ bytes_find_internal(PyBytesObject *self, PyObject *args, int dir)
const char *sub; const char *sub;
Py_ssize_t sub_len; Py_ssize_t sub_len;
Py_ssize_t start=0, end=PY_SSIZE_T_MAX; Py_ssize_t start=0, end=PY_SSIZE_T_MAX;
PyObject *obj_start=Py_None, *obj_end=Py_None;
if (!PyArg_ParseTuple(args, "O|OO:find/rfind/index/rindex", &subobj, if (!stringlib_parse_args_finds("find/rfind/index/rindex",
&obj_start, &obj_end)) args, &subobj, &start, &end))
return -2;
/* To support None in "start" and "end" arguments, meaning
the same as if they were not passed.
*/
if (obj_start != Py_None)
if (!_PyEval_SliceIndex(obj_start, &start))
return -2;
if (obj_end != Py_None)
if (!_PyEval_SliceIndex(obj_end, &end))
return -2; return -2;
if (PyBytes_Check(subobj)) { if (PyBytes_Check(subobj)) {
@ -1826,8 +1816,7 @@ bytes_count(PyBytesObject *self, PyObject *args)
Py_ssize_t sub_len; Py_ssize_t sub_len;
Py_ssize_t start = 0, end = PY_SSIZE_T_MAX; Py_ssize_t start = 0, end = PY_SSIZE_T_MAX;
if (!PyArg_ParseTuple(args, "O|O&O&:count", &sub_obj, if (!stringlib_parse_args_finds("count", args, &sub_obj, &start, &end))
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
return NULL; return NULL;
if (PyBytes_Check(sub_obj)) { if (PyBytes_Check(sub_obj)) {
@ -2648,8 +2637,7 @@ bytes_startswith(PyBytesObject *self, PyObject *args)
PyObject *subobj; PyObject *subobj;
int result; int result;
if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &subobj, if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end))
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
return NULL; return NULL;
if (PyTuple_Check(subobj)) { if (PyTuple_Check(subobj)) {
Py_ssize_t i; Py_ssize_t i;
@ -2689,8 +2677,7 @@ bytes_endswith(PyBytesObject *self, PyObject *args)
PyObject *subobj; PyObject *subobj;
int result; int result;
if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &subobj, if (!stringlib_parse_args_finds("endswith", args, &subobj, &start, &end))
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
return NULL; return NULL;
if (PyTuple_Check(subobj)) { if (PyTuple_Check(subobj)) {
Py_ssize_t i; Py_ssize_t i;

View File

@ -102,32 +102,33 @@ stringlib_contains_obj(PyObject* str, PyObject* sub)
#endif /* STRINGLIB_STR */ #endif /* STRINGLIB_STR */
#ifdef FROM_UNICODE
/* /*
This function is a helper for the "find" family (find, rfind, index, This function is a helper for the "find" family (find, rfind, index,
rindex) of unicodeobject.c file, because they all have the same rindex) and for count, startswith and endswith, because they all have
behaviour for the arguments. the same behaviour for the arguments.
It does not touch the variables received until it knows everything It does not touch the variables received until it knows everything
is ok. is ok.
Note that we receive a pointer to the pointer of the substring object,
so when we create that object in this function we don't DECREF it,
because it continues living in the caller functions (those functions,
after finishing using the substring, must DECREF it).
*/ */
#define FORMAT_BUFFER_SIZE 50
Py_LOCAL_INLINE(int) Py_LOCAL_INLINE(int)
_ParseTupleFinds (PyObject *args, PyObject **substring, stringlib_parse_args_finds(const char * function_name, PyObject *args,
Py_ssize_t *start, Py_ssize_t *end) { PyObject **subobj,
PyObject *tmp_substring; Py_ssize_t *start, Py_ssize_t *end)
{
PyObject *tmp_subobj;
Py_ssize_t tmp_start = 0; Py_ssize_t tmp_start = 0;
Py_ssize_t tmp_end = PY_SSIZE_T_MAX; Py_ssize_t tmp_end = PY_SSIZE_T_MAX;
PyObject *obj_start=Py_None, *obj_end=Py_None; PyObject *obj_start=Py_None, *obj_end=Py_None;
char format[FORMAT_BUFFER_SIZE] = "O|OO:";
size_t len = strlen(format);
if (!PyArg_ParseTuple(args, "O|OO:find", &tmp_substring, strncpy(format + len, function_name, FORMAT_BUFFER_SIZE - len - 1);
&obj_start, &obj_end)) format[FORMAT_BUFFER_SIZE - 1] = '\0';
if (!PyArg_ParseTuple(args, format, &tmp_subobj, &obj_start, &obj_end))
return 0; return 0;
/* To support None in "start" and "end" arguments, meaning /* To support None in "start" and "end" arguments, meaning
@ -140,16 +141,44 @@ _ParseTupleFinds (PyObject *args, PyObject **substring,
if (!_PyEval_SliceIndex(obj_end, &tmp_end)) if (!_PyEval_SliceIndex(obj_end, &tmp_end))
return 0; return 0;
tmp_substring = PyUnicode_FromObject(tmp_substring);
if (!tmp_substring)
return 0;
*start = tmp_start; *start = tmp_start;
*end = tmp_end; *end = tmp_end;
*substring = tmp_substring; *subobj = tmp_subobj;
return 1; return 1;
} }
#undef FORMAT_BUFFER_SIZE
#ifdef FROM_UNICODE
/*
Wraps stringlib_parse_args_finds() and additionally ensures that the
first argument is a unicode object.
Note that we receive a pointer to the pointer of the substring object,
so when we create that object in this function we don't DECREF it,
because it continues living in the caller functions (those functions,
after finishing using the substring, must DECREF it).
*/
Py_LOCAL_INLINE(int)
stringlib_parse_args_finds_unicode(const char * function_name, PyObject *args,
PyUnicodeObject **substring,
Py_ssize_t *start, Py_ssize_t *end)
{
PyObject *tmp_substring;
if(stringlib_parse_args_finds(function_name, args, &tmp_substring,
start, end)) {
tmp_substring = PyUnicode_FromObject(tmp_substring);
if (!tmp_substring)
return 0;
*substring = (PyUnicodeObject *)tmp_substring;
return 1;
}
return 0;
}
#endif /* FROM_UNICODE */ #endif /* FROM_UNICODE */
#endif /* STRINGLIB_FIND_H */ #endif /* STRINGLIB_FIND_H */

View File

@ -7150,13 +7150,8 @@ unicode_count(PyUnicodeObject *self, PyObject *args)
Py_ssize_t end = PY_SSIZE_T_MAX; Py_ssize_t end = PY_SSIZE_T_MAX;
PyObject *result; PyObject *result;
if (!PyArg_ParseTuple(args, "O|O&O&:count", &substring, if (!stringlib_parse_args_finds_unicode("count", args, &substring,
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end)) &start, &end))
return NULL;
substring = (PyUnicodeObject *)PyUnicode_FromObject(
(PyObject *)substring);
if (substring == NULL)
return NULL; return NULL;
FIX_START_END(self); FIX_START_END(self);
@ -7306,12 +7301,13 @@ Return -1 on failure.");
static PyObject * static PyObject *
unicode_find(PyUnicodeObject *self, PyObject *args) unicode_find(PyUnicodeObject *self, PyObject *args)
{ {
PyObject *substring; PyUnicodeObject *substring;
Py_ssize_t start; Py_ssize_t start;
Py_ssize_t end; Py_ssize_t end;
Py_ssize_t result; Py_ssize_t result;
if (!_ParseTupleFinds(args, &substring, &start, &end)) if (!stringlib_parse_args_finds_unicode("find", args, &substring,
&start, &end))
return NULL; return NULL;
result = stringlib_find_slice( result = stringlib_find_slice(
@ -7368,11 +7364,12 @@ static PyObject *
unicode_index(PyUnicodeObject *self, PyObject *args) unicode_index(PyUnicodeObject *self, PyObject *args)
{ {
Py_ssize_t result; Py_ssize_t result;
PyObject *substring; PyUnicodeObject *substring;
Py_ssize_t start; Py_ssize_t start;
Py_ssize_t end; Py_ssize_t end;
if (!_ParseTupleFinds(args, &substring, &start, &end)) if (!stringlib_parse_args_finds_unicode("index", args, &substring,
&start, &end))
return NULL; return NULL;
result = stringlib_find_slice( result = stringlib_find_slice(
@ -8230,12 +8227,13 @@ Return -1 on failure.");
static PyObject * static PyObject *
unicode_rfind(PyUnicodeObject *self, PyObject *args) unicode_rfind(PyUnicodeObject *self, PyObject *args)
{ {
PyObject *substring; PyUnicodeObject *substring;
Py_ssize_t start; Py_ssize_t start;
Py_ssize_t end; Py_ssize_t end;
Py_ssize_t result; Py_ssize_t result;
if (!_ParseTupleFinds(args, &substring, &start, &end)) if (!stringlib_parse_args_finds_unicode("rfind", args, &substring,
&start, &end))
return NULL; return NULL;
result = stringlib_rfind_slice( result = stringlib_rfind_slice(
@ -8257,12 +8255,13 @@ Like S.rfind() but raise ValueError when the substring is not found.");
static PyObject * static PyObject *
unicode_rindex(PyUnicodeObject *self, PyObject *args) unicode_rindex(PyUnicodeObject *self, PyObject *args)
{ {
PyObject *substring; PyUnicodeObject *substring;
Py_ssize_t start; Py_ssize_t start;
Py_ssize_t end; Py_ssize_t end;
Py_ssize_t result; Py_ssize_t result;
if (!_ParseTupleFinds(args, &substring, &start, &end)) if (!stringlib_parse_args_finds_unicode("rindex", args, &substring,
&start, &end))
return NULL; return NULL;
result = stringlib_rfind_slice( result = stringlib_rfind_slice(
@ -8725,8 +8724,7 @@ unicode_startswith(PyUnicodeObject *self,
Py_ssize_t end = PY_SSIZE_T_MAX; Py_ssize_t end = PY_SSIZE_T_MAX;
int result; int result;
if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &subobj, if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end))
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
return NULL; return NULL;
if (PyTuple_Check(subobj)) { if (PyTuple_Check(subobj)) {
Py_ssize_t i; Py_ssize_t i;
@ -8771,8 +8769,7 @@ unicode_endswith(PyUnicodeObject *self,
Py_ssize_t end = PY_SSIZE_T_MAX; Py_ssize_t end = PY_SSIZE_T_MAX;
int result; int result;
if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &subobj, if (!stringlib_parse_args_finds("endswith", args, &subobj, &start, &end))
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
return NULL; return NULL;
if (PyTuple_Check(subobj)) { if (PyTuple_Check(subobj)) {
Py_ssize_t i; Py_ssize_t i;