Commit ac451506 authored by Jesus Cea's avatar Jesus Cea

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

parent d07eaf17
......@@ -1156,6 +1156,63 @@ class MixinStrUnicodeUserStringTest:
self.checkraises(ValueError, S, 'rpartition', '')
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:
# Additional tests that only work with str and unicode.
......
......@@ -459,6 +459,68 @@ class BaseBytesTest(unittest.TestCase):
self.assertRaises(ValueError, self.type2test.maketrans, b'abc', b'xyzq')
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):
type2test = bytes
......
......@@ -58,6 +58,7 @@ Donald Beaudry
David Beazley
Robin Becker
Neal Becker
Torsten Becker
Bill Bedford
Stefan Behnel
Reimer Behrends
......
......@@ -38,6 +38,9 @@ Core and Builtins
- Issue #8278: On Windows and with a NTFS filesystem, os.stat() and os.utime()
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
rather than the Py_IsInitialized flag, avoiding a Fatal Python
error in certain circumstances when an import is done in __del__.
......
......@@ -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 res;
if (!PyArg_ParseTuple(args, "O|O&O&:find/rfind/index/rindex", &subobj,
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
if (!stringlib_parse_args_finds("find/rfind/index/rindex",
args, &subobj, &start, &end))
return -2;
if (_getbuffer(subobj, &subbuf) < 0)
return -2;
......@@ -1133,8 +1133,7 @@ bytearray_count(PyByteArrayObject *self, PyObject *args)
Py_buffer vsub;
PyObject *count_obj;
if (!PyArg_ParseTuple(args, "O|O&O&:count", &sub_obj,
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
if (!stringlib_parse_args_finds("count", args, &sub_obj, &start, &end))
return NULL;
if (_getbuffer(sub_obj, &vsub) < 0)
......@@ -1292,8 +1291,7 @@ bytearray_startswith(PyByteArrayObject *self, PyObject *args)
PyObject *subobj;
int result;
if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &subobj,
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end))
return NULL;
if (PyTuple_Check(subobj)) {
Py_ssize_t i;
......@@ -1332,8 +1330,7 @@ bytearray_endswith(PyByteArrayObject *self, PyObject *args)
PyObject *subobj;
int result;
if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &subobj,
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
if (!stringlib_parse_args_finds("endswith", args, &subobj, &start, &end))
return NULL;
if (PyTuple_Check(subobj)) {
Py_ssize_t i;
......
......@@ -1567,19 +1567,9 @@ bytes_find_internal(PyBytesObject *self, PyObject *args, int dir)
const char *sub;
Py_ssize_t sub_len;
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,
&obj_start, &obj_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))
if (!stringlib_parse_args_finds("find/rfind/index/rindex",
args, &subobj, &start, &end))
return -2;
if (PyBytes_Check(subobj)) {
......@@ -1826,8 +1816,7 @@ bytes_count(PyBytesObject *self, PyObject *args)
Py_ssize_t sub_len;
Py_ssize_t start = 0, end = PY_SSIZE_T_MAX;
if (!PyArg_ParseTuple(args, "O|O&O&:count", &sub_obj,
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
if (!stringlib_parse_args_finds("count", args, &sub_obj, &start, &end))
return NULL;
if (PyBytes_Check(sub_obj)) {
......@@ -2648,8 +2637,7 @@ bytes_startswith(PyBytesObject *self, PyObject *args)
PyObject *subobj;
int result;
if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &subobj,
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end))
return NULL;
if (PyTuple_Check(subobj)) {
Py_ssize_t i;
......@@ -2689,8 +2677,7 @@ bytes_endswith(PyBytesObject *self, PyObject *args)
PyObject *subobj;
int result;
if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &subobj,
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
if (!stringlib_parse_args_finds("endswith", args, &subobj, &start, &end))
return NULL;
if (PyTuple_Check(subobj)) {
Py_ssize_t i;
......
......@@ -102,32 +102,33 @@ stringlib_contains_obj(PyObject* str, PyObject* sub)
#endif /* STRINGLIB_STR */
#ifdef FROM_UNICODE
/*
This function is a helper for the "find" family (find, rfind, index,
rindex) of unicodeobject.c file, because they all have the same
behaviour for the arguments.
rindex) and for count, startswith and endswith, because they all have
the same behaviour for the arguments.
It does not touch the variables received until it knows everything
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)
_ParseTupleFinds (PyObject *args, PyObject **substring,
Py_ssize_t *start, Py_ssize_t *end) {
PyObject *tmp_substring;
stringlib_parse_args_finds(const char * function_name, PyObject *args,
PyObject **subobj,
Py_ssize_t *start, Py_ssize_t *end)
{
PyObject *tmp_subobj;
Py_ssize_t tmp_start = 0;
Py_ssize_t tmp_end = PY_SSIZE_T_MAX;
PyObject *obj_start=Py_None, *obj_end=Py_None;
char format[FORMAT_BUFFER_SIZE] = "O|OO:";
size_t len = strlen(format);
strncpy(format + len, function_name, FORMAT_BUFFER_SIZE - len - 1);
format[FORMAT_BUFFER_SIZE - 1] = '\0';
if (!PyArg_ParseTuple(args, "O|OO:find", &tmp_substring,
&obj_start, &obj_end))
if (!PyArg_ParseTuple(args, format, &tmp_subobj, &obj_start, &obj_end))
return 0;
/* To support None in "start" and "end" arguments, meaning
......@@ -140,14 +141,42 @@ _ParseTupleFinds (PyObject *args, PyObject **substring,
if (!_PyEval_SliceIndex(obj_end, &tmp_end))
return 0;
*start = tmp_start;
*end = tmp_end;
*subobj = tmp_subobj;
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;
*start = tmp_start;
*end = tmp_end;
*substring = tmp_substring;
*substring = (PyUnicodeObject *)tmp_substring;
return 1;
}
return 0;
}
#endif /* FROM_UNICODE */
......
......@@ -7150,13 +7150,8 @@ unicode_count(PyUnicodeObject *self, PyObject *args)
Py_ssize_t end = PY_SSIZE_T_MAX;
PyObject *result;
if (!PyArg_ParseTuple(args, "O|O&O&:count", &substring,
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
return NULL;
substring = (PyUnicodeObject *)PyUnicode_FromObject(
(PyObject *)substring);
if (substring == NULL)
if (!stringlib_parse_args_finds_unicode("count", args, &substring,
&start, &end))
return NULL;
FIX_START_END(self);
......@@ -7306,12 +7301,13 @@ Return -1 on failure.");
static PyObject *
unicode_find(PyUnicodeObject *self, PyObject *args)
{
PyObject *substring;
PyUnicodeObject *substring;
Py_ssize_t start;
Py_ssize_t end;
Py_ssize_t result;
if (!_ParseTupleFinds(args, &substring, &start, &end))
if (!stringlib_parse_args_finds_unicode("find", args, &substring,
&start, &end))
return NULL;
result = stringlib_find_slice(
......@@ -7368,11 +7364,12 @@ static PyObject *
unicode_index(PyUnicodeObject *self, PyObject *args)
{
Py_ssize_t result;
PyObject *substring;
PyUnicodeObject *substring;
Py_ssize_t start;
Py_ssize_t end;
if (!_ParseTupleFinds(args, &substring, &start, &end))
if (!stringlib_parse_args_finds_unicode("index", args, &substring,
&start, &end))
return NULL;
result = stringlib_find_slice(
......@@ -8230,12 +8227,13 @@ Return -1 on failure.");
static PyObject *
unicode_rfind(PyUnicodeObject *self, PyObject *args)
{
PyObject *substring;
PyUnicodeObject *substring;
Py_ssize_t start;
Py_ssize_t end;
Py_ssize_t result;
if (!_ParseTupleFinds(args, &substring, &start, &end))
if (!stringlib_parse_args_finds_unicode("rfind", args, &substring,
&start, &end))
return NULL;
result = stringlib_rfind_slice(
......@@ -8257,12 +8255,13 @@ Like S.rfind() but raise ValueError when the substring is not found.");
static PyObject *
unicode_rindex(PyUnicodeObject *self, PyObject *args)
{
PyObject *substring;
PyUnicodeObject *substring;
Py_ssize_t start;
Py_ssize_t end;
Py_ssize_t result;
if (!_ParseTupleFinds(args, &substring, &start, &end))
if (!stringlib_parse_args_finds_unicode("rindex", args, &substring,
&start, &end))
return NULL;
result = stringlib_rfind_slice(
......@@ -8725,8 +8724,7 @@ unicode_startswith(PyUnicodeObject *self,
Py_ssize_t end = PY_SSIZE_T_MAX;
int result;
if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &subobj,
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end))
return NULL;
if (PyTuple_Check(subobj)) {
Py_ssize_t i;
......@@ -8771,8 +8769,7 @@ unicode_endswith(PyUnicodeObject *self,
Py_ssize_t end = PY_SSIZE_T_MAX;
int result;
if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &subobj,
_PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
if (!stringlib_parse_args_finds("endswith", args, &subobj, &start, &end))
return NULL;
if (PyTuple_Check(subobj)) {
Py_ssize_t i;
......
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