]> granicus.if.org Git - python/commitdiff
SF bug # 493951 string.{starts,ends}with vs slices
authorNeal Norwitz <nnorwitz@gmail.com>
Fri, 14 Jun 2002 00:50:42 +0000 (00:50 +0000)
committerNeal Norwitz <nnorwitz@gmail.com>
Fri, 14 Jun 2002 00:50:42 +0000 (00:50 +0000)
Handle negative indices similar to slices.

Doc/whatsnew/whatsnew23.tex
Lib/test/string_tests.py
Misc/NEWS
Objects/stringobject.c

index 0759c9df253adb02c5edb80e3c50db0375899235..6d3651a31e0aa40f964fbfd29d76656ea93bb930 100644 (file)
@@ -497,6 +497,10 @@ u'\u4001abc'
 >>>
 \end{verbatim}
 
+\item The \method{startswith()} and \method{endswith()}
+string methods now have accept negative numbers for
+start and end parameters.
+
 \item Another new string method is \method{zfill()}, originally a
 function in the \module{string} module.  \method{zfill()} pads a
 numeric string with zeros on the left until it's the specified width.
index 075e1c9105f298c8abee89570142fc3734ee2f4d..b645354a0c0807dea3c98dd6e4e938ee252196bf 100644 (file)
@@ -223,6 +223,18 @@ def run_method_tests(test):
     test('startswith', 'helloworld', 1, 'lowo', 3, 7)
     test('startswith', 'helloworld', 0, 'lowo', 3, 6)
 
+    # test negative indices in startswith
+    test('startswith', 'hello', 1, 'he', 0, -1)
+    test('startswith', 'hello', 1, 'he', -53, -1)
+    test('startswith', 'hello', 0, 'hello', 0, -1)
+    test('startswith', 'hello', 0, 'hello world', -1, -10)
+    test('startswith', 'hello', 0, 'ello', -5)
+    test('startswith', 'hello', 1, 'ello', -4)
+    test('startswith', 'hello', 0, 'o', -2)
+    test('startswith', 'hello', 1, 'o', -1)
+    test('startswith', 'hello', 1, '', -3, -3)
+    test('startswith', 'hello', 0, 'lo', -9)
+
     test('endswith', 'hello', 1, 'lo')
     test('endswith', 'hello', 0, 'he')
     test('endswith', 'hello', 1, '')
@@ -238,6 +250,21 @@ def run_method_tests(test):
     test('endswith', 'ab', 0, 'ab', 0, 1)
     test('endswith', 'ab', 0, 'ab', 0, 0)
 
+    # test negative indices in endswith
+    test('endswith', 'hello', 1, 'lo', -2)
+    test('endswith', 'hello', 0, 'he', -2)
+    test('endswith', 'hello', 1, '', -3, -3)
+    test('endswith', 'hello', 0, 'hello world', -10, -2)
+    test('endswith', 'helloworld', 0, 'worl', -6)
+    test('endswith', 'helloworld', 1, 'worl', -5, -1)
+    test('endswith', 'helloworld', 1, 'worl', -5, 9)
+    test('endswith', 'helloworld', 1, 'world', -7, 12)
+    test('endswith', 'helloworld', 1, 'lowo', -99, -3)
+    test('endswith', 'helloworld', 1, 'lowo', -8, -3)
+    test('endswith', 'helloworld', 1, 'lowo', -7, -3)
+    test('endswith', 'helloworld', 0, 'lowo', 3, -4)
+    test('endswith', 'helloworld', 0, 'lowo', -8, -2)
+
     test('zfill', '123', '123', 2)
     test('zfill', '123', '123', 3)
     test('zfill', '123', '0123', 4)
index 383614f0593a152d4d23ccf0fc6da51be8164726..4a949f4b377f58e5d26253a21255fe27936c891e 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -6,6 +6,9 @@ Type/class unification and new-style classes
 
 Core and builtins
 
+- Fixed string.startswith and string.endswith builtin methods
+  so they accept negative indices.  [SF bug 493951]
+
 - Fixed a bug with a continue inside a try block and a yield in the
   finally clause.  [SF bug 567538]
 
index 243fcc695e2c3a7aba09dddff5033f8f88b422c8..5d6478b783eea7713001f62247360694c0f24373 100644 (file)
@@ -1310,6 +1310,21 @@ _PyString_Join(PyObject *sep, PyObject *x)
        return string_join((PyStringObject *)sep, x);
 }
 
+static void
+string_adjust_indices(int *start, int *end, int len)
+{
+       if (*end > len)
+               *end = len;
+       else if (*end < 0)
+               *end += len;
+       if (*end < 0)
+               *end = 0;
+       if (*start < 0)
+               *start += len;
+       if (*start < 0)
+               *start = 0;
+}
+
 static long
 string_find_internal(PyStringObject *self, PyObject *args, int dir)
 {
@@ -1332,16 +1347,7 @@ string_find_internal(PyStringObject *self, PyObject *args, int dir)
        else if (PyObject_AsCharBuffer(subobj, &sub, &n))
                return -2;
 
-       if (last > len)
-               last = len;
-       if (last < 0)
-               last += len;
-       if (last < 0)
-               last = 0;
-       if (i < 0)
-               i += len;
-       if (i < 0)
-               i = 0;
+       string_adjust_indices(&i, &last, len);
 
        if (dir > 0) {
                if (n == 0 && i <= last)
@@ -1763,16 +1769,8 @@ string_count(PyStringObject *self, PyObject *args)
        else if (PyObject_AsCharBuffer(subobj, &sub, &n))
                return NULL;
 
-       if (last > len)
-               last = len;
-       if (last < 0)
-               last += len;
-       if (last < 0)
-               last = 0;
-       if (i < 0)
-               i += len;
-       if (i < 0)
-               i = 0;
+       string_adjust_indices(&i, &last, len);
+
        m = last + 1 - n;
        if (n == 0)
                return PyInt_FromLong((long) (m-i));
@@ -2169,7 +2167,7 @@ string_startswith(PyStringObject *self, PyObject *args)
        const char* prefix;
        int plen;
        int start = 0;
-       int end = -1;
+       int end = INT_MAX;
        PyObject *subobj;
 
        if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &subobj,
@@ -2193,23 +2191,15 @@ string_startswith(PyStringObject *self, PyObject *args)
        else if (PyObject_AsCharBuffer(subobj, &prefix, &plen))
                return NULL;
 
-       /* adopt Java semantics for index out of range.  it is legal for
-        * offset to be == plen, but this only returns true if prefix is
-        * the empty string.
-        */
-       if (start < 0 || start+plen > len)
+       string_adjust_indices(&start, &end, len);
+
+       if (start+plen > len)
                return PyBool_FromLong(0);
 
-       if (!memcmp(str+start, prefix, plen)) {
-               /* did the match end after the specified end? */
-               if (end < 0)
-                       return PyBool_FromLong(1);
-               else if (end - start < plen)
-                       return PyBool_FromLong(0);
-               else
-                       return PyBool_FromLong(1);
-       }
-       else return PyBool_FromLong(0);
+       if (end-start >= plen)
+               return PyBool_FromLong(!memcmp(str+start, prefix, plen));
+       else
+               return PyBool_FromLong(0);
 }
 
 
@@ -2228,8 +2218,7 @@ string_endswith(PyStringObject *self, PyObject *args)
        const char* suffix;
        int slen;
        int start = 0;
-       int end = -1;
-       int lower, upper;
+       int end = INT_MAX;
        PyObject *subobj;
 
        if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &subobj,
@@ -2253,15 +2242,17 @@ string_endswith(PyStringObject *self, PyObject *args)
        else if (PyObject_AsCharBuffer(subobj, &suffix, &slen))
                return NULL;
 
-       if (start < 0 || start > len || slen > len)
-               return PyBool_FromLong(0);
+       string_adjust_indices(&start, &end, len);
 
-       upper = (end >= 0 && end <= len) ? end : len;
-       lower = (upper - slen) > start ? (upper - slen) : start;
+       if (end-start < slen || start > len)
+               return PyBool_FromLong(0);
 
-       if (upper-lower >= slen && !memcmp(str+lower, suffix, slen))
-               return PyBool_FromLong(1);
-       else return PyBool_FromLong(0);
+       if (end-slen > start)
+               start = end - slen;
+       if (end-start >= slen)
+               return PyBool_FromLong(!memcmp(str+start, suffix, slen));
+       else
+               return PyBool_FromLong(0);
 }