]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
RFE #1491485: str/unicode.endswith()/startswith() now accept a tuple as first argument.
authorGeorg Brandl <georg@python.org>
Fri, 9 Jun 2006 18:45:48 +0000 (18:45 +0000)
committerGeorg Brandl <georg@python.org>
Fri, 9 Jun 2006 18:45:48 +0000 (18:45 +0000)
Doc/lib/libstdtypes.tex
Lib/test/string_tests.py
Misc/NEWS
Objects/stringobject.c
Objects/unicodeobject.c

index 576a5adbb1d6dd2a534a26085baa8c1f6ce14926..1fe2f60e072f9c96c0248893ce66cadb2d18feaa 100644 (file)
@@ -618,8 +618,11 @@ For a list of possible encodings, see section~\ref{standard-encodings}.
 
 \begin{methoddesc}[string]{endswith}{suffix\optional{, start\optional{, end}}}
 Return \code{True} if the string ends with the specified \var{suffix},
-otherwise return \code{False}.  With optional \var{start}, test beginning at
+otherwise return \code{False}.  \var{suffix} can also be a tuple of
+suffixes to look for.  With optional \var{start}, test beginning at
 that position.  With optional \var{end}, stop comparing at that position.
+
+\versionchanged[Accept tuples as \var{suffix}]{2.5}
 \end{methoddesc}
 
 \begin{methoddesc}[string]{expandtabs}{\optional{tabsize}}
@@ -829,9 +832,12 @@ boundaries.  Line breaks are not included in the resulting list unless
 \begin{methoddesc}[string]{startswith}{prefix\optional{,
                                        start\optional{, end}}}
 Return \code{True} if string starts with the \var{prefix}, otherwise
-return \code{False}.  With optional \var{start}, test string beginning at
+return \code{False}.  \var{prefix} can also be a tuple of
+suffixes to look for.  With optional \var{start}, test string beginning at
 that position.  With optional \var{end}, stop comparing string at that
 position.
+
+\versionchanged[Accept tuples as \var{prefix}]{2.5}
 \end{methoddesc}
 
 \begin{methoddesc}[string]{strip}{\optional{chars}}
index aaa2dc20a92c79d98f1427648375f09fd92b462e..5a1cc8aa03e545d0cebff22a00811093eab6f678 100644 (file)
@@ -819,6 +819,21 @@ class MixinStrUnicodeUserStringTest:
         self.checkraises(TypeError, 'hello', 'startswith')
         self.checkraises(TypeError, 'hello', 'startswith', 42)
 
+        # test tuple arguments
+        self.checkequal(True, 'hello', 'startswith', ('he', 'ha'))
+        self.checkequal(False, 'hello', 'startswith', ('lo', 'llo'))
+        self.checkequal(True, 'hello', 'startswith', ('hellox', 'hello'))
+        self.checkequal(False, 'hello', 'startswith', ())
+        self.checkequal(True, 'helloworld', 'startswith', ('hellowo',
+                                                           'rld', 'lowo'), 3)
+        self.checkequal(False, 'helloworld', 'startswith', ('hellowo', 'ello',
+                                                            'rld'), 3)
+        self.checkequal(True, 'hello', 'startswith', ('lo', 'he'), 0, -1)
+        self.checkequal(False, 'hello', 'startswith', ('he', 'hel'), 0, 1)
+        self.checkequal(True, 'hello', 'startswith', ('he', 'hel'), 0, 2)
+
+        self.checkraises(TypeError, 'hello', 'startswith', (42,))
+
     def test_endswith(self):
         self.checkequal(True, 'hello', 'endswith', 'lo')
         self.checkequal(False, 'hello', 'endswith', 'he')
@@ -853,6 +868,21 @@ class MixinStrUnicodeUserStringTest:
         self.checkraises(TypeError, 'hello', 'endswith')
         self.checkraises(TypeError, 'hello', 'endswith', 42)
 
+        # test tuple arguments
+        self.checkequal(False, 'hello', 'endswith', ('he', 'ha'))
+        self.checkequal(True, 'hello', 'endswith', ('lo', 'llo'))
+        self.checkequal(True, 'hello', 'endswith', ('hellox', 'hello'))
+        self.checkequal(False, 'hello', 'endswith', ())
+        self.checkequal(True, 'helloworld', 'endswith', ('hellowo',
+                                                           'rld', 'lowo'), 3)
+        self.checkequal(False, 'helloworld', 'endswith', ('hellowo', 'ello',
+                                                            'rld'), 3, -1)
+        self.checkequal(True, 'hello', 'endswith', ('hell', 'ell'), 0, -1)
+        self.checkequal(False, 'hello', 'endswith', ('he', 'hel'), 0, 1)
+        self.checkequal(True, 'hello', 'endswith', ('he', 'hell'), 0, 4)
+
+        self.checkraises(TypeError, 'hello', 'endswith', (42,))
+
     def test___contains__(self):
         self.checkequal(True, '', '__contains__', '')         # vereq('' in '', True)
         self.checkequal(True, 'abc', '__contains__', '')      # vereq('' in 'abc', True)
@@ -872,7 +902,7 @@ class MixinStrUnicodeUserStringTest:
         self.checkequal(u'abc', 'abc', '__getitem__', slice(0, 1000))
         self.checkequal(u'a', 'abc', '__getitem__', slice(0, 1))
         self.checkequal(u'', 'abc', '__getitem__', slice(0, 0))
-        # FIXME What about negative indizes? This is handled differently by [] and __getitem__(slice)
+        # FIXME What about negative indices? This is handled differently by [] and __getitem__(slice)
 
         self.checkraises(TypeError, 'abc', '__getitem__', 'def')
 
index 2fb9256ed280bc08d46df5d47c98065fc09d32ef..3bd732eafb2620c8d5f93b5a6a0121ed49ea96bb 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ What's New in Python 2.5 beta 1?
 Core and builtins
 -----------------
 
+- The string and unicode methods startswith() and endswith() now accept
+  a tuple of prefixes/suffixes to look for. Implements RFE #1491485.
+
 - Buffer objects, at the C level, never used the char buffer
   implementation even when the char buffer for the wrapped object was
   explicitly requested (originally returned the read or write buffer).
index a9803451a169fd1e26d41363c2083f113dbb7fc7..831d54a06609f02aa26969015dfbb25fbad86039 100644 (file)
@@ -3099,54 +3099,96 @@ string_replace(PyStringObject *self, PyObject *args)
 
 /** End DALKE **/
 
+/* Matches the end (direction > 0) or start (direction < 0) of self
+ * against substr, using the start and end arguments. Returns
+ * -1 on error, 0 if not found and 1 if found.
+ */
+Py_LOCAL(int)
+_string_tailmatch(PyStringObject *self, PyObject *substr, Py_ssize_t start,
+                 Py_ssize_t end, int direction)
+{
+       Py_ssize_t len = PyString_GET_SIZE(self);
+       Py_ssize_t slen;
+       const char* sub;
+       const char* str;
+
+       if (PyString_Check(substr)) {
+               sub = PyString_AS_STRING(substr);
+               slen = PyString_GET_SIZE(substr);
+       }
+#ifdef Py_USING_UNICODE
+       else if (PyUnicode_Check(substr))
+               return PyUnicode_Tailmatch((PyObject *)self,
+                                          substr, start, end, direction);
+#endif
+       else if (PyObject_AsCharBuffer(substr, &sub, &slen))
+               return -1;
+       str = PyString_AS_STRING(self);
+
+       string_adjust_indices(&start, &end, len);
+
+       if (direction < 0) {
+               /* startswith */
+               if (start+slen > len)
+                       return 0;
+
+               if (end-start >= slen)
+                       return ! memcmp(str+start, sub, slen);
+               else
+                       return 0;
+       } else {
+               /* endswith */
+               if (end-start < slen || start > len)
+                       return 0;
+
+               if (end-slen > start)
+                       start = end - slen;
+               if (end-start >= slen)
+                       return ! memcmp(str+start, sub, slen);
+               else
+                       return 0;
+       }
+}
+
+
 PyDoc_STRVAR(startswith__doc__,
 "S.startswith(prefix[, start[, end]]) -> bool\n\
 \n\
 Return True if S starts with the specified prefix, False otherwise.\n\
 With optional start, test S beginning at that position.\n\
-With optional end, stop comparing S at that position.");
+With optional end, stop comparing S at that position.\n\
+prefix can also be a tuple of strings to try.");
 
 static PyObject *
 string_startswith(PyStringObject *self, PyObject *args)
 {
-       const char* str = PyString_AS_STRING(self);
-       Py_ssize_t len = PyString_GET_SIZE(self);
-       const char* prefix;
-       Py_ssize_t plen;
        Py_ssize_t start = 0;
        Py_ssize_t end = PY_SSIZE_T_MAX;
        PyObject *subobj;
+       int result;
 
        if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &subobj,
                _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
                return NULL;
-       if (PyString_Check(subobj)) {
-               prefix = PyString_AS_STRING(subobj);
-               plen = PyString_GET_SIZE(subobj);
-       }
-#ifdef Py_USING_UNICODE
-       else if (PyUnicode_Check(subobj)) {
-               Py_ssize_t rc;
-               rc = PyUnicode_Tailmatch((PyObject *)self,
-                                         subobj, start, end, -1);
-               if (rc == -1)
-                       return NULL;
-               else
-                       return PyBool_FromLong((long) rc);
+       if (PyTuple_Check(subobj)) {
+               Py_ssize_t i;
+               for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) {
+                       result = _string_tailmatch(self,
+                                       PyTuple_GET_ITEM(subobj, i),
+                                       start, end, -1);
+                       if (result == -1)
+                               return NULL;
+                       else if (result) {
+                               Py_RETURN_TRUE;
+                       }
+               }
+               Py_RETURN_FALSE;
        }
-#endif
-       else if (PyObject_AsCharBuffer(subobj, &prefix, &plen))
+       result = _string_tailmatch(self, subobj, start, end, -1);
+       if (result == -1)
                return NULL;
-
-       string_adjust_indices(&start, &end, len);
-
-       if (start+plen > len)
-               return PyBool_FromLong(0);
-
-       if (end-start >= plen)
-               return PyBool_FromLong(!memcmp(str+start, prefix, plen));
        else
-               return PyBool_FromLong(0);
+               return PyBool_FromLong(result);
 }
 
 
@@ -3155,51 +3197,39 @@ PyDoc_STRVAR(endswith__doc__,
 \n\
 Return True if S ends with the specified suffix, False otherwise.\n\
 With optional start, test S beginning at that position.\n\
-With optional end, stop comparing S at that position.");
+With optional end, stop comparing S at that position.\n\
+suffix can also be a tuple of strings to try.");
 
 static PyObject *
 string_endswith(PyStringObject *self, PyObject *args)
 {
-       const char* str = PyString_AS_STRING(self);
-       Py_ssize_t len = PyString_GET_SIZE(self);
-       const char* suffix;
-       Py_ssize_t slen;
        Py_ssize_t start = 0;
        Py_ssize_t end = PY_SSIZE_T_MAX;
        PyObject *subobj;
+       int result;
 
        if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &subobj,
                _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
                return NULL;
-       if (PyString_Check(subobj)) {
-               suffix = PyString_AS_STRING(subobj);
-               slen = PyString_GET_SIZE(subobj);
-       }
-#ifdef Py_USING_UNICODE
-       else if (PyUnicode_Check(subobj)) {
-               Py_ssize_t rc;
-               rc = PyUnicode_Tailmatch((PyObject *)self,
-                                         subobj, start, end, +1);
-               if (rc == -1)
-                       return NULL;
-               else
-                       return PyBool_FromLong((long) rc);
+       if (PyTuple_Check(subobj)) {
+               Py_ssize_t i;
+               for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) {
+                       result = _string_tailmatch(self,
+                                       PyTuple_GET_ITEM(subobj, i),
+                                       start, end, +1);
+                       if (result == -1)
+                               return NULL;
+                       else if (result) {
+                               Py_RETURN_TRUE;
+                       }
+               }
+               Py_RETURN_FALSE;
        }
-#endif
-       else if (PyObject_AsCharBuffer(subobj, &suffix, &slen))
+       result = _string_tailmatch(self, subobj, start, end, +1);
+       if (result == -1)
                return NULL;
-
-       string_adjust_indices(&start, &end, len);
-
-       if (end-start < slen || start > len)
-               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);
+               return PyBool_FromLong(result);
 }
 
 
index 970e69f22736beff7fb4b461f8955302908c88cb..bf2425c3cf546300d44675b1c907dc25be31b281 100644 (file)
@@ -6667,29 +6667,44 @@ PyDoc_STRVAR(startswith__doc__,
 \n\
 Return True if S starts with the specified prefix, False otherwise.\n\
 With optional start, test S beginning at that position.\n\
-With optional end, stop comparing S at that position.");
+With optional end, stop comparing S at that position.\n\
+prefix can also be a tuple of strings to try.");
 
 static PyObject *
 unicode_startswith(PyUnicodeObject *self,
                   PyObject *args)
 {
+    PyObject *subobj;
     PyUnicodeObject *substring;
     Py_ssize_t start = 0;
     Py_ssize_t end = PY_SSIZE_T_MAX;
-    PyObject *result;
+    int result;
 
-    if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &substring,
+    if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &subobj,
                _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
        return NULL;
-    substring = (PyUnicodeObject *)PyUnicode_FromObject(
-                                               (PyObject *)substring);
+    if (PyTuple_Check(subobj)) {
+        Py_ssize_t i;
+        for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) {
+            substring = (PyUnicodeObject *)PyUnicode_FromObject(
+                            PyTuple_GET_ITEM(subobj, i));
+            if (substring == NULL)
+                return NULL;
+            result = tailmatch(self, substring, start, end, -1);
+            Py_DECREF(substring);
+            if (result) {
+                Py_RETURN_TRUE;
+            }
+        }
+        /* nothing matched */
+        Py_RETURN_FALSE;
+    }
+    substring = (PyUnicodeObject *)PyUnicode_FromObject(subobj);
     if (substring == NULL)
-       return NULL;
-
-    result = PyBool_FromLong(tailmatch(self, substring, start, end, -1));
-
+         return NULL;
+    result = tailmatch(self, substring, start, end, -1);
     Py_DECREF(substring);
-    return result;
+    return PyBool_FromLong(result);
 }
 
 
@@ -6698,29 +6713,44 @@ PyDoc_STRVAR(endswith__doc__,
 \n\
 Return True if S ends with the specified suffix, False otherwise.\n\
 With optional start, test S beginning at that position.\n\
-With optional end, stop comparing S at that position.");
+With optional end, stop comparing S at that position.\n\
+suffix can also be a tuple of strings to try.");
 
 static PyObject *
 unicode_endswith(PyUnicodeObject *self,
                 PyObject *args)
 {
+    PyObject *subobj;
     PyUnicodeObject *substring;
     Py_ssize_t start = 0;
     Py_ssize_t end = PY_SSIZE_T_MAX;
-    PyObject *result;
+    int result;
 
-    if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &substring,
-               _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
+    if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &subobj,
+        _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end))
        return NULL;
-    substring = (PyUnicodeObject *)PyUnicode_FromObject(
-                                               (PyObject *)substring);
+    if (PyTuple_Check(subobj)) {
+        Py_ssize_t i;
+        for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) {
+            substring = (PyUnicodeObject *)PyUnicode_FromObject(
+                            PyTuple_GET_ITEM(subobj, i));
+            if (substring == NULL)
+            return NULL;
+            result = tailmatch(self, substring, start, end, +1);
+            Py_DECREF(substring);
+            if (result) {
+                Py_RETURN_TRUE;
+            }
+        }
+        Py_RETURN_FALSE;
+    }
+    substring = (PyUnicodeObject *)PyUnicode_FromObject(subobj);
     if (substring == NULL)
-       return NULL;
-
-    result = PyBool_FromLong(tailmatch(self, substring, start, end, +1));
+    return NULL;
 
+    result = tailmatch(self, substring, start, end, +1);
     Py_DECREF(substring);
-    return result;
+    return PyBool_FromLong(result);
 }